\n {/* Background Image Layer */}\n
\n
\n
\n \n\n {/* Subtle Overlay Wash */}\n
\n\n {/* Animated Blobs for depth */}\n
\n
\n\n {/* Noise Texture Overlay */}\n
\n
\n\n {/* Content */}\n
\n {/* Album Cover / Disc */}\n
\n
\n \n
\n {/* Vinyl Texture */}\n \n {/* Disc Center */}\n \n \n \n \n \n
\n\n {/* Info Section */}\n
\n
\n
\n {title}\n
\n \n {artist}\n \n \n
\n\n {/* Spotify Branding */}\n
\n \n
\n
\n
\n );\n },\n);\n",
"type": "registry:component"
},
{
"path": "registry/spark-ui/spotify/metadata-route.ts",
"content": "import * as cheerio from \"cheerio\";\nimport got from \"got\";\nimport { NextRequest, NextResponse } from \"next/server\";\n\n// SSRF guard: the outbound request destination is always built server-side\n// from a validated Spotify track ID — never from a caller-supplied URL.\nconst TRACK_ID_RE = /^[A-Za-z0-9]{1,64}$/;\nconst MAX_RESPONSE_BYTES = 1_000_000;\n\nexport async function GET(req: NextRequest) {\n const { searchParams } = new URL(req.url);\n const trackId = searchParams.get(\"trackId\") ?? \"\";\n\n if (!TRACK_ID_RE.test(trackId)) {\n return NextResponse.json({ error: \"Invalid track ID\" }, { status: 400 });\n }\n\n try {\n const request = got(`https://open.spotify.com/track/${trackId}`, {\n headers: {\n \"user-agent\": \"Mozilla/5.0 (compatible; SparkUI-SpotifyCard/1.0)\",\n accept: \"text/html\",\n },\n followRedirect: false,\n retry: { limit: 0 },\n timeout: { request: 5000 },\n });\n request.on(\"downloadProgress\", ({ transferred }) => {\n if (transferred > MAX_RESPONSE_BYTES) request.cancel();\n });\n const { headers, body: html } = await request;\n\n if (!headers[\"content-type\"]?.includes(\"text/html\")) {\n return NextResponse.json(\n { error: \"Failed to fetch metadata\" },\n { status: 502 },\n );\n }\n\n const $ = cheerio.load(html);\n\n const title =\n $('meta[property=\"og:title\"]').attr(\"content\") || \"Unknown Track\";\n const image = $('meta[property=\"og:image\"]').attr(\"content\") || \"\";\n const previewUrl =\n $('meta[property=\"og:audio\"]').attr(\"content\") ||\n $('meta[name=\"twitter:audio:src\"]').attr(\"content\") ||\n \"\";\n\n // Spotify's og:description is often: \"Artist · Album · Song · Year\"\n const description =\n $('meta[property=\"og:description\"]').attr(\"content\") || \"\";\n // Usually the first part is the Artist\n const artist = description.split(\"·\")[0]?.trim() || \"Unknown Artist\";\n\n return NextResponse.json({\n title,\n artist,\n albumArt: image,\n previewUrl,\n });\n } catch {\n // Non-2xx, redirect attempt, timeout, or oversized response. Keep the\n // client response generic — no upstream details.\n return NextResponse.json(\n { error: \"Failed to fetch metadata\" },\n { status: 502 },\n );\n }\n}\n",
"type": "registry:component",
"target": "app/api/spotify/metadata/route.ts"
}
],
"type": "registry:component"
}