Files
R3ap3r 3178b8f4f7 Aurora SpotiRip desktop and web app
- Spotify playlist, library, album and artist browsing with in-app playback
- Song, playlist and album downloads via spotdl with YouTube and Bandcamp fallbacks
- Electron desktop app (castLabs Widevine) with a permanent sidebar layout
- Linux and Windows packaging scripts
2026-09-25 23:20:57 -05:00

58 lines
2.0 KiB
TypeScript

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 });
}
}