import { existsSync } from "node:fs"; import { TRIM_SETS, stemPath } from "@/lib/trim"; import { readWavWindow, peakBuckets } from "@/lib/wav"; export const dynamic = "force-dynamic"; // Peaks over a window of the STEM. // // This deliberately duplicates ~15 lines of app/api/clip/[key]/peaks rather // than generalising it. That route's `sourceWav(video)` is a load-bearing // constraint on a well-exercised path -- the whole um-triage deck runs through // it -- and parameterising its source to also accept a stem would put this // editor's failure modes into that deck. Fifteen duplicated lines are cheaper. export async function GET(request: Request, ctx: { params: Promise<{ set: string }> }) { const { set: id } = await ctx.params; const set = TRIM_SETS[id]; if (!set) return Response.json({ error: "no such trim set" }, { status: 404 }); const url = new URL(request.url); const from = Number(url.searchParams.get("from")); const to = Number(url.searchParams.get("to")); const buckets = Math.max(64, Math.min(4000, Number(url.searchParams.get("n") ?? 1200))); if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) { return new Response("bad window", { status: 400 }); } const file = stemPath(set); if (!existsSync(file)) return Response.json({ error: "stem missing" }, { status: 404 }); const region = await readWavWindow(file, Math.max(0, from), to); const { min, max } = peakBuckets(region.x, buckets); return Response.json( { from: region.from, to: region.to, duration: region.duration, min, max }, { headers: { "cache-control": "no-store" } }, ); }