- 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
58 lines
2.0 KiB
TypeScript
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 });
|
|
}
|
|
}
|