import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { isValidChannelSlug } from "yt-dlp-transcript-common/controller/channels"; import { channelCoverage } from "yt-dlp-transcript-common/controller/channelCoverage"; import { readRoute } from "../_read"; import { opsFail } from "../_lib"; export const dynamic = "force-dynamic"; // GET ?slug=[&gapDays=][&from=YYYYMMDD][&to=YYYYMMDD][&list=1] // -> { ok, slug, titlePattern, held, dated, byRecordedDate, byUploadDate, // undated: [id], first, last, byYear, byMonth, gapDays, // gaps: [{ after, before, days }], videos?: [{ id, date, uploadDate, // recordedDate?, title? }] } // // What a channel holds, by date, and the spans with nothing held // (controller/channelCoverage.ts): each held video dated with coverageDate — // its recorded date when the channel has a title rule, else its upload date — // off disk, so a video imported an hour ago counts. `gapDays` (30) is the // shortest span reported as a gap; `from`/`to` narrow the window; `list` // adds every dated video. Nothing is written. The MCP's channel_coverage. const DAY = /^\d{8}$/; export async function GET(request: Request) { return readRoute(request, ["slug", "gapDays", "from", "to", "list"], async (q) => { const slug = q.get("slug")?.trim() ?? ""; if (!slug) return opsFail('"slug" is required'); if (!isValidChannelSlug(slug)) return opsFail(`"${slug}" is not a valid channel slug`); const rawGap = q.get("gapDays"); const gapDays = rawGap === null ? undefined : Number(rawGap); if (gapDays !== undefined && (!Number.isInteger(gapDays) || gapDays < 1)) { return opsFail('"gapDays" must be a whole number of days above zero'); } const from = q.get("from") ?? undefined; const to = q.get("to") ?? undefined; for (const [k, v] of [["from", from], ["to", to]] as const) { if (v !== undefined && !DAY.test(v)) return opsFail(`"${k}" must be a date YYYYMMDD`); } const list = q.get("list"); if (list !== null && list !== "1" && list !== "true") return opsFail('"list" is 1 or absent'); try { const coverage = await channelCoverage(getPaths(), slug, { ...(gapDays !== undefined ? { gapDays } : {}), ...(from ? { from } : {}), ...(to ? { to } : {}), list: list !== null, }); return { ok: true, ...coverage }; } catch (e) { const message = (e as Error).message; return opsFail(message, /not found/.test(message) ? 404 : 503); } }); }