import superjson from "superjson"; import { schema, type OutputType } from "./metadata-enrich_POST.schema"; const MB_HEADERS = { "User-Agent": "Aurora-SpotiRip/1.0 (" + ((process.env as Record).SPOTIRIP_BASE_URL || "https://spotirip.auroradragon.dev") + ")", }; let musicBrainzQueue: Promise = Promise.resolve(); let lastMusicBrainzRequestAt = 0; type MusicBrainzSearch = { recordings?: Array<{ id?: string; isrcs?: string[]; "first-release-date"?: string; }>; }; type MusicBrainzRecording = { id?: string; isrcs?: string[]; "first-release-date"?: string; genres?: Array<{ name?: string; count?: number }>; tags?: Array<{ name?: string; count?: number }>; "artist-credit"?: Array<{ artist?: { name?: string } }>; relations?: Array<{ type?: string; work?: { id?: string } }>; }; type MusicBrainzWork = { relations?: Array<{ type?: string; artist?: { name?: string } }>; }; type DeezerTrack = { id?: number; isrc?: string; album?: { id?: number; cover_xl?: string; cover_big?: string }; }; type DeezerAlbum = { genres?: { data?: Array<{ name?: string }> }; artist?: { name?: string }; cover_xl?: string; cover_big?: string; }; type LrclibResult = { plainLyrics?: string | null; syncedLyrics?: string | null; }; function bestGenre(values: Array<{ name?: string; count?: number }> | undefined) { return [...(values ?? [])] .filter((entry) => entry.name) .sort((a, b) => (b.count ?? 0) - (a.count ?? 0))[0]?.name; } async function fetchJson(url: string, headers?: Record) { const response = await fetch(url, { headers }); if (!response.ok) return undefined; return (await response.json()) as T; } function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function fetchMusicBrainz(url: string) { const request = musicBrainzQueue.then(async () => { const elapsed = Date.now() - lastMusicBrainzRequestAt; if (elapsed < 1100) await delay(1100 - elapsed); lastMusicBrainzRequestAt = Date.now(); return fetchJson(url, MB_HEADERS); }); musicBrainzQueue = request.then( () => undefined, () => undefined, ); return request; } async function fetchLrclib(url: string) { for (let attempt = 0; attempt < 2; attempt += 1) { const response = await fetch(url, { headers: { "User-Agent": "Aurora-SpotiRip/1.0" }, }); if (response.status === 404) return undefined; if (response.status === 429 && attempt === 0) { const retryAfter = Number(response.headers.get("retry-after") || 1); await delay(Math.min(5000, Math.max(500, retryAfter * 1000))); continue; } if (!response.ok) return undefined; return (await response.json()) as T; } return undefined; } async function findRecording(input: { title: string; artist: string; isrc?: string; }) { const query = input.isrc ? `isrc:${input.isrc}` : `recording:"${input.title.replace(/"/g, "")}" AND artist:"${input.artist.replace(/"/g, "")}"`; const search = await fetchMusicBrainz( `https://musicbrainz.org/ws/2/recording?query=${encodeURIComponent(query)}&fmt=json&limit=5`, ); return search?.recordings?.[0]; } export async function handle(request: Request) { try { const input = schema.parse(superjson.parse(await request.text())); const sources = new Set(); const output: OutputType = { sources: [] }; try { const candidate = await findRecording(input); if (candidate?.id) { sources.add("MusicBrainz"); const recording = await fetchMusicBrainz( `https://musicbrainz.org/ws/2/recording/${candidate.id}?inc=artist-credits+isrcs+work-rels+genres+tags&fmt=json`, ); if (recording) { output.musicBrainzRecordingId = recording.id || candidate.id; output.isrc = input.isrc || recording.isrcs?.[0] || candidate.isrcs?.[0]; output.releaseDate = recording["first-release-date"] || candidate["first-release-date"]; output.genre = bestGenre(recording.genres) || bestGenre(recording.tags); output.albumArtist = recording["artist-credit"] ?.map((entry) => entry.artist?.name) .filter(Boolean) .join(", "); const workId = recording.relations?.find( (relation) => relation.type === "performance" && relation.work?.id, )?.work?.id; if (workId) { const work = await fetchMusicBrainz( `https://musicbrainz.org/ws/2/work/${workId}?inc=artist-rels&fmt=json`, ); const composers = work?.relations ?.filter((relation) => ["composer", "writer", "lyricist"].includes(relation.type || ""), ) .map((relation) => relation.artist?.name) .filter(Boolean); if (composers?.length) { output.composer = [...new Set(composers)].join(", "); } } } } } catch { } const resolvedIsrc = output.isrc || input.isrc; if (resolvedIsrc) { try { const deezerTrack = await fetchJson( `https://api.deezer.com/track/isrc:${encodeURIComponent(resolvedIsrc)}`, ); if (deezerTrack?.id) { sources.add("Deezer"); output.isrc = output.isrc || deezerTrack.isrc; output.artworkUrl = deezerTrack.album?.cover_xl || deezerTrack.album?.cover_big; if (deezerTrack.album?.id) { const album = await fetchJson( `https://api.deezer.com/album/${deezerTrack.album.id}`, ); output.genre = output.genre || album?.genres?.data?.find((entry) => entry.name)?.name; output.albumArtist = output.albumArtist || album?.artist?.name; output.artworkUrl = output.artworkUrl || album?.cover_xl || album?.cover_big; } } } catch { } } try { const params = new URLSearchParams({ artist_name: input.artist, track_name: input.title, }); if (input.album) params.set("album_name", input.album); if (input.durationMs) { params.set("duration", String(Math.round(input.durationMs / 1000))); } const lyrics = await fetchLrclib( `https://lrclib.net/api/get?${params.toString()}`, ); if (lyrics?.plainLyrics || lyrics?.syncedLyrics) { sources.add("LRCLIB"); output.lyrics = { plain: lyrics.plainLyrics || undefined, synced: lyrics.syncedLyrics || undefined, }; } } catch { } output.sources = [...sources]; return new Response(superjson.stringify(output)); } catch (error) { const message = error instanceof Error ? error.message : "Unable to enrich track metadata."; return new Response(superjson.stringify({ error: message }), { status: 400 }); } }