import superjson from "superjson"; import { schema, type OutputType } from "./lyrics_POST.schema"; type LrclibResult = { plainLyrics?: string | null; syncedLyrics?: string | null; instrumental?: boolean; }; function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function handle(request: Request) { try { const input = schema.parse(superjson.parse(await request.text())); 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))); } let response: Response | undefined; for (let attempt = 0; attempt < 2; attempt += 1) { response = await fetch(`https://lrclib.net/api/get?${params.toString()}`, { headers: { "User-Agent": "Aurora-SpotiRip/1.0" }, }); if (response.status !== 429 || attempt > 0) break; const retryAfter = Number(response.headers.get("retry-after") || 1); await delay(Math.min(5000, Math.max(500, retryAfter * 1000))); } if (!response) { throw new Error("Lyrics service did not respond."); } if (response.status === 404) { return new Response(superjson.stringify({} satisfies OutputType)); } if (!response.ok) { return new Response( superjson.stringify({ error: `Lyrics service returned HTTP ${response.status}.` }), { status: 502 }, ); } const result = (await response.json()) as LrclibResult; const output: OutputType = { plain: result.plainLyrics || undefined, synced: result.syncedLyrics || undefined, instrumental: Boolean(result.instrumental), }; return new Response(superjson.stringify(output)); } catch (error) { const message = error instanceof Error ? error.message : "Unable to load lyrics."; return new Response(superjson.stringify({ error: message }), { status: 400 }); } }