Files
EpicNext-Cms/src/app/api/radio/now-playing/route.ts
T
openhands 41be6835bf Add missing admin action files and navigation links
- Add 11 missing server action files: badges, bulk-users, catalog, catalog-bc, catalog-items, import-badges, import-furni, multi-account-detect, permissions, rooms, soundtracks
- Add missing admin navigation links: tickets, sounds, translations, import, radio sub-pages
- Add translation keys for all new navigation items
2026-07-11 12:01:05 +02:00

53 lines
1.5 KiB
TypeScript

import { apiJson } from "@/lib/api";
import { siteSettings } from "@/lib/services/site-settings";
// Proxy for the configured radio "now playing" provider. The provider URL is
// stored in radio_now_playing_api_url; we fetch it server-side (never exposing
// the URL or any provider key to the browser) with a short, hard timeout.
export const dynamic = "force-dynamic";
const FETCH_TIMEOUT_MS = 4000;
export async function GET(_req: Request) {
let url: string | null = null;
try {
url = (await siteSettings.get("radio_now_playing_api_url", "")) || null;
} catch {
url = null;
}
// Not configured — there is nothing to play.
if (!url) {
return apiJson({ nowPlaying: null });
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
try {
const res = await fetch(url, {
signal: controller.signal,
cache: "no-store",
headers: { accept: "application/json, text/plain, */*" },
});
const raw = (await res.text()).trim();
if (raw === "") {
return apiJson({ nowPlaying: null });
}
// Return parsed JSON when the provider speaks JSON, else the raw text body.
try {
return apiJson(JSON.parse(raw));
} catch {
return apiJson({ nowPlaying: raw.slice(0, 2000) });
}
} catch (e) {
const aborted = e instanceof Error && e.name === "AbortError";
return apiJson({
error: aborted ? `Timed out after ${FETCH_TIMEOUT_MS / 1000}s` : "Fetch failed",
});
} finally {
clearTimeout(timer);
}
}