@music-vine/setlist
v0.1.0
Published
Uppbeat's catalogue queries: one layer on top of Typesense, shared by every app that lists assets
Readme
@music-vine/setlist
Uppbeat's catalogue queries. One layer on top of Typesense, so every app that lists assets gets the same assets in the same order as uppbeat.io.
npm install @music-vine/setlistIt ships ES modules for a bundler (Vite, Next.js, webpack) and runs in the browser or on a server.
Setup
import { createSetlist } from '@music-vine/setlist';
export const setlist = createSetlist({
nodes: [{ host: 'xxx.a1.typesense.net', port: 443, protocol: 'https' }],
apiKey: searchOnlyKey,
onError: (error, { operation, ...extra }) => report(error, operation, extra),
});- The key must be search-only. In a browser app it is public.
- The package never reads environment variables. Pass hosts and the key in.
onErroris called when a query fails and the caller gets an empty result instead. Queries that have no sensible empty result throw.
Pages
const { hits, found } = await setlist.getAssetsForPage({
type: 'sfx',
slug: '/sfx/category/whoosh',
});type is music, sfx, motion-graphics, luts or stock-video. slug is
the page's full path on the site.
| Option | Default | Meaning |
| --- | --- | --- |
| isSubscribed | false | Rank and tier results as a subscriber sees them. |
| intersperse | true | Mix free and premium results. Only applies to non-subscribers on the default sort. |
| sort | relevancy | Or latest, undiscovered. |
| page, perPage | 1, per type | Pagination. |
| filterBy, q | none | Extra Typesense filter and search text. |
It returns hits in the order the page shows them, found, and results (the
raw result sets, [premium, free] when interspersed).
Music category pages need pageFilter. Their rule is compiled by the API
(GET /api/v1/track-tags/page/{slug}, field filter), not stored in Typesense,
so the caller passes it and the call throws without it. Music collection pages
and every other asset type resolve from slug alone.
await setlist.getAssetsForPage({
type: 'music',
slug: '/music/category/jazz',
pageFilter: page.filter,
});A slug with no category throws rather than returning an empty list.
Search and similar assets
await setlist.searchAssets({ type: 'luts', q: 'moody' });
await setlist.getSimilarAssets({
type: 'music',
reference: trackDocument,
excludeId: trackDocument.track_id,
});getSimilarAssets takes the reference document rather than an id, so an asset
can be checked before it is indexed. It covers music and SFX. Pass excludeId
to leave the reference itself out of the results.
Carousels
A page's carousels are two searches, one for the carousels curated for the page and one for those matched from its search term:
import { getPageCarouselSearchParameters } from '@music-vine/setlist';
const { pageSearchParameters, autoSearchParameters } =
getPageCarouselSearchParameters({
library: 'music',
name: 'Jazz',
url: '/music/category/jazz',
searchTerm: 'jazz',
});
await setlist.getCarouselsMulti(pageSearchParameters, autoSearchParameters);Errors
Two behaviours, depending on the query:
- Listings throw.
getAssetsForPage,searchAssets,getSimilarAssetsandgetCarouselsMultireject when Typesense fails or times out, when a slug has no category, and when a music category page has nopageFilter. Catch them where you render. - Lookups report and return empty.
getTrack,getSfx,getSfxVariantsand the contributor queries resolve tonull,[]or{}on failure and callonError(error, { operation, ...details }). Without anonErrorthe failure is silent, so pass one.
The client retries across the nodes you give it before a query fails.
Reference
Every query is a method on the instance. Arguments and results are typed, so an editor shows the exact shape.
| To show | Call |
| --- | --- |
| What a page lists | getAssetsForPage |
| Search results for one asset type | searchAssets |
| Search results as the site's search page builds them | fetchSearchResults |
| Assets similar to a music or SFX document | getSimilarAssets |
| Similar motion graphics or LUTs, similar stock videos | fetchSimilarMotionGraphics, fetchSimilarStockVideos |
| A page's carousels | getCarouselsMulti (see above), getCarousels |
| One track, one SFX, an SFX's other variants | getTrack, getSfx, getSfxVariants |
| A motion graphics or LUT category, and the filters it preselects | getMotionGraphicsCategory, getCategoryPreselectedFilters, getLutCategoryPreselectedFilters |
| The LUT categories an asset appears in | fetchLutCategoriesForAsset |
| A stock video category, its groups, related categories | getVideoCategory, getVideoCategoryGroups, getVideoCategoriesByGroup, getSimilarCategories |
| Stock video listings | getStockFootage, getStockFootageByCategoryEmbedding, getStockFootageZoneSampled, fetchBatchStockVideos |
| A contributor's profile, slug or asset count | getContributor, getContributorSlugByName, getContributorSlugsByNames, getContributorAssetCount |
| Where an old URL redirects | getPageRedirect |
The package also exports the request builders, collection names and document types behind these.
setlist.multiSearch and setlist.searchCollection send a request as-is. They
exist for queries with no named function yet; prefer a named one, and add one
here when several callers need the same request.
Changing a query
The requests are pinned by src/golden-requests.test.ts. A snapshot diff there
means the site and every other consumer will show different results, so update
it only when that is the intent.
