import superjson from "superjson"; import { appendSetCookies, ensureSpotifyAccessToken, } from "../helpers/spotifyServer"; import type { OutputType, SpotifyArtistAlbum, } from "./spotify-artist-albums_GET.schema"; type ImageRef = { url?: string }; type SpotifyArtist = { id?: string; name?: string; images?: ImageRef[]; external_urls?: { spotify?: string }; }; type SpotifyAlbum = { id?: string; name?: string; album_type?: string; release_date?: string; total_tracks?: number; images?: ImageRef[]; external_urls?: { spotify?: string }; }; type AlbumPage = { items?: SpotifyAlbum[]; total?: number; offset?: number; limit?: number; next?: string | null; }; async function spotifyGet(url: string, token: string) { const response = await fetch(url, { headers: { Authorization: "Bearer " + token }, }); if (!response.ok) { const message = await response.text(); throw new Error( "Spotify returned " + response.status + ": " + message.slice(0, 220), ); } return (await response.json()) as T; } function firstImage(images?: ImageRef[]) { return images?.find((entry) => entry.url)?.url; } export async function handle(request: Request) { try { const url = new URL(request.url); const artistId = url.searchParams.get("artistId")?.trim(); const offset = Math.max(0, Number(url.searchParams.get("offset") || 0) || 0); if (!artistId) { return new Response( superjson.stringify({ error: "Artist ID is required." }), { status: 400 }, ); } const session = await ensureSpotifyAccessToken(request); if (!session.accessToken) { return new Response( superjson.stringify({ error: "Connect Spotify first." }), { status: 401 }, ); } const token = session.accessToken; const [artist, page] = await Promise.all([ spotifyGet( "https://api.spotify.com/v1/artists/" + encodeURIComponent(artistId), token, ), spotifyGet( "https://api.spotify.com/v1/artists/" + encodeURIComponent(artistId) + "/albums?include_groups=album,single,compilation&limit=50&offset=" + offset, token, ), ]); if (!artist.id) { throw new Error("Spotify did not return the requested artist."); } const seen = new Set(); const albums: SpotifyArtistAlbum[] = []; for (const album of page.items || []) { if (!album.id || seen.has(album.id)) continue; seen.add(album.id); albums.push({ id: album.id, name: album.name || "Untitled release", albumType: album.album_type, releaseDate: album.release_date, totalTracks: album.total_tracks, imageUrl: firstImage(album.images), externalUrl: album.external_urls?.spotify, }); } const output: OutputType = { artist: { id: artist.id, name: artist.name || "Unknown artist", imageUrl: firstImage(artist.images), externalUrl: artist.external_urls?.spotify, }, albums, total: page.total, nextOffset: page.next ? offset + (page.limit || 50) : undefined, }; const headers = new Headers({ "Content-Type": "application/json", "Cache-Control": "no-store", }); appendSetCookies(headers, session.setCookies); return new Response(superjson.stringify(output), { headers }); } catch (error) { const message = error instanceof Error ? error.message : "Unable to load artist discography."; return new Response(superjson.stringify({ error: message }), { status: 500 }); } }