Add shared package: types, schemas, plans and API client
Holds everything web, mobile and API must agree on: DTOs, zod input schemas, plan entitlements (the single place feature gates live), streaming-service link helpers, feed ordering and a typed API client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
/** Services a listener can open a song in. */
|
||||
export const STREAMING_SERVICES = [
|
||||
'spotify',
|
||||
'apple_music',
|
||||
'youtube_music',
|
||||
'deezer',
|
||||
'tidal',
|
||||
] as const;
|
||||
export type StreamingService = (typeof STREAMING_SERVICES)[number];
|
||||
|
||||
/** Sources we can read "now playing" from. */
|
||||
export const NOW_PLAYING_PROVIDERS = [
|
||||
'spotify',
|
||||
'lastfm',
|
||||
'listenbrainz',
|
||||
'plex',
|
||||
'jellyfin',
|
||||
'apple_music',
|
||||
'android_media_session',
|
||||
] as const;
|
||||
export type NowPlayingProvider = (typeof NOW_PLAYING_PROVIDERS)[number];
|
||||
|
||||
export const SERVICE_LABELS: Record<StreamingService | NowPlayingProvider, string> = {
|
||||
spotify: 'Spotify',
|
||||
apple_music: 'Apple Music',
|
||||
youtube_music: 'YouTube Music',
|
||||
deezer: 'Deezer',
|
||||
tidal: 'TIDAL',
|
||||
lastfm: 'Last.fm',
|
||||
listenbrainz: 'ListenBrainz',
|
||||
plex: 'Plex',
|
||||
jellyfin: 'Jellyfin',
|
||||
android_media_session: 'Android',
|
||||
};
|
||||
|
||||
/** Fallback when we have no direct link: a search on that service. */
|
||||
export function searchUrl(service: StreamingService, title: string, artist: string): string {
|
||||
const q = encodeURIComponent(`${artist} ${title}`);
|
||||
switch (service) {
|
||||
case 'spotify':
|
||||
return `https://open.spotify.com/search/${q}`;
|
||||
case 'apple_music':
|
||||
return `https://music.apple.com/search?term=${q}`;
|
||||
case 'youtube_music':
|
||||
return `https://music.youtube.com/search?q=${q}`;
|
||||
case 'deezer':
|
||||
return `https://www.deezer.com/search/${q}`;
|
||||
case 'tidal':
|
||||
return `https://listen.tidal.com/search?q=${q}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to open a track in the given service. All direct links are https
|
||||
* universal/app links, so they open the native app when installed.
|
||||
*/
|
||||
export function openUrl(
|
||||
track: { title: string; artist: string; links: Partial<Record<StreamingService, string>> },
|
||||
service: StreamingService,
|
||||
): { url: string; exact: boolean } {
|
||||
const direct = track.links[service];
|
||||
if (direct) return { url: direct, exact: true };
|
||||
return { url: searchUrl(service, track.title, track.artist), exact: false };
|
||||
}
|
||||
Reference in New Issue
Block a user