Search Scripts

The data.search object contains up to five scripts, one per search category. Each is optional, a category with no script is simply not offered as a search tab for that source.

data:
  search:
    songs: |
      // JavaScript code
    videos: |
      // JavaScript code
    albums: |
      // JavaScript code
    artists: |
      // JavaScript code
    playlists: |
      // JavaScript code

Invocation

When a user types a query for a specific category, Boppa runs the corresponding script. Each script receives a global params object:

PropertyTypePresent when
params.querystringAlways.
params.cookiesobjectAlways. A map of cookie name to value for the source's url domain. Empty if none are set.
params.contextobjectOnly if the media source declares a context and values have been gathered. See Context & Popups.
params.previousResultobjectOnly when fetching a subsequent page. See Pagination below.

The script must call the global postResult(data) function exactly once with the result, or postError(message) to signal failure. See Script Environment for the full JavaScript environment (fetch, timers, console, and so on) available to the script.

Result shape

postResult must be called with an object containing an items array. The shape of each item depends on the category.

songs, videos

postResult({
  items: [
    {
      id: "unique-id", // required
      title: "Track Title", // required
      subtitle: "Artist Name", // optional
      duration: 213000, // optional, milliseconds
      lowResArtworkUrl: "https://...", // optional
      highResArtworkUrl: "https://...", // optional
      url: "https://...", // optional
      metadata: { any: "JSON object" }, // optional
      artists: [ { id, name, lowResArtworkUrl, highResArtworkUrl } ], // optional
      albums:  [ { id, title, subtitle, lowResArtworkUrl, highResArtworkUrl } ] // optional
    }
  ]
});

id and title are the only required fields; every other field may be omitted.

metadata is opaque to Boppa: it may be any JSON object and is carried through unchanged to the boppaLoad call for the player page to interpret.

The nested artists and albums arrays let a search result carry enough information for Boppa to offer "Go to Artist" and "Go to Album" actions without an additional lookup.

albums, playlists

postResult({
  items: [
    {
      id: "unique-id", // required
      title: "Album Title", // required
      subtitle: "Artist Name", // optional
      year: 1975, // optional
      trackCount: 12, // optional
      lowResArtworkUrl: "https://...", // optional
      highResArtworkUrl: "https://..." // optional
    }
  ]
});

artists

postResult({
  items: [
    {
      id: "unique-id", // required
      name: "Artist Name", // required
      lowResArtworkUrl: "https://...", // optional
      highResArtworkUrl: "https://..." // optional
    }
  ]
});

Pagination

To support infinite scroll, include any additional fields alongside items in the object passed to postResult. Boppa treats every field other than items as continuation state and, if at least one such field is non-null, stores it. When the user scrolls to the end of the current results, the same script runs again with params.previousResult set to exactly that object:

const page = params.previousResult?.page ? params.previousResult.page + 1 : 1;

// ... fetch page ...

const result = { items: items };
if (items.length === pageSize) {
  result.page = page; // there may be more pages; continue
}
postResult(result); // omitting `page` signals there are no more pages

If no field besides items is present, or every such field is null, Boppa treats the result as the final page and does not request more.

Errors

If the script throws, or an awaited promise rejects, Boppa surfaces the error message and the search category is left empty. Call postError(message) directly for a specific, user-relevant error message.