import { json, readJsonBody, requireAdmin } from "../../../_lib/auth"; import { ALL_DAYS, MAX_TVS, playlistUrl, readSettings, saveSettings, type TvSetting, validPlaylistId, validTvId } from "../../../_lib/store"; import { CHECK_IN_MINUTES, readTvStatus, tvConfig } from "../../../_lib/tvs"; export const dynamic = "force-dynamic"; async function view(tvs: TvSetting[]) { const status = await readTvStatus(); const known = new Set(tvs.map((tv) => tv.id)); return { checkInMinutes: CHECK_IN_MINUTES, tvs: tvs.map((tv) => ({ ...tv, playlistUrl: tv.music.playlistId ? playlistUrl(tv.music.playlistId) : "", version: tvConfig(tv).version, status: status[tv.id] ?? null, })), // TVs running the photo app that have checked in but are not configured yet. found: Object.values(status) .filter((entry) => !known.has(entry.id)) .sort((left, right) => right.lastSeenAt - left.lastSeenAt), }; } export async function GET(request: Request) { const denied = await requireAdmin(request); if (denied) return denied; return json(await view((await readSettings()).tvs)); } type IncomingTv = { id?: string; name?: string; wake?: { enabled?: boolean; time?: string; days?: number[] }; music?: { enabled?: boolean; playlist?: string }; volume?: { enabled?: boolean; level?: number }; }; export async function PUT(request: Request) { const denied = await requireAdmin(request, { mutating: true }); if (denied) return denied; const body = await readJsonBody<{ tvs?: IncomingTv[] }>(request); if (!body || !Array.isArray(body.tvs)) return json({ error: "Expected a list of TVs." }, 400); if (body.tvs.length > MAX_TVS) return json({ error: `At most ${MAX_TVS} TVs.` }, 400); const tvs: TvSetting[] = []; for (const [position, entry] of body.tvs.entries()) { const label = entry.name?.trim() || `TV ${position + 1}`; const id = validTvId(entry.id); if (!id) return json({ error: `${label}: unknown TV id; reload the page.` }, 400); const time = String(entry.wake?.time ?? ""); if (entry.wake?.enabled && !/^([01]\d|2[0-3]):[0-5]\d$/.test(time)) return json({ error: `${label}: pick a wake time.` }, 400); const playlistText = String(entry.music?.playlist ?? "").trim(); const playlistId = playlistText ? validPlaylistId(playlistText) : ""; if (playlistText && !playlistId) return json({ error: `${label}: paste a YouTube Music playlist link (https://music.youtube.com/playlist?list=…).` }, 400); if (entry.music?.enabled && !playlistId) return json({ error: `${label}: music is on but no playlist is set.` }, 400); const days = Array.isArray(entry.wake?.days) ? entry.wake.days : ALL_DAYS; if (entry.wake?.enabled && !days.length) return json({ error: `${label}: pick at least one day.` }, 400); tvs.push({ id, name: (entry.name ?? "").trim().slice(0, 40), wake: { enabled: entry.wake?.enabled === true, time: time || "06:30", days }, music: { enabled: entry.music?.enabled === true, playlistId: playlistId || "" }, volume: { enabled: entry.volume?.enabled === true, level: Number(entry.volume?.level ?? 40) }, }); } if (new Set(tvs.map((tv) => tv.id)).size !== tvs.length) return json({ error: "The same TV is listed twice." }, 400); const saved = await saveSettings({ tvs }, `Saved TV settings (${tvs.length} TV(s))`); console.log(`[admin] TV settings saved (${tvs.length} TV(s))`); return json(await view(saved.tvs)); }