-
{song.unattributed.map((v) => (
- {v.rel}{" "} {fmtBytes(v.size)} ))}
import Link from "next/link"; import { notFound } from "next/navigation"; import BrowseHeader from "@/components/BrowseHeader"; import VerdictChip from "@/components/VerdictChip"; import SpecSheet from "@/components/SpecSheet"; import NoteField from "@/components/NoteField"; import CopyButton from "@/components/CopyButton"; import { readSong, probeAll, thumbAliasesFor, type Cut, type Song } from "@/lib/browse"; import ThumbBench from "@/components/ThumbBench"; import { thumbView } from "@/lib/thumbs"; import { fmtAgo, fmtBytes, fmtDur } from "@/lib/format"; import { Markdown } from "@/lib/markdown"; import { operationsFor, readSpec, validateSpec } from "@/lib/spec"; import { readNotes, type NoteMap } from "@/lib/notes"; import { TRIM_SETS } from "@/lib/trim"; // Moved out of app/browse/[song]/page.tsx unchanged, because [song] and // [...path] cannot both be dynamic segments at the same level. The dispatcher // in ProjectView.tsx hands it an id; nothing else about the page differs. export default async function SongProject({ id }: { id: string }) { const song = await readSong(id); if (!song) notFound(); // Durations are worth an ffprobe HERE but not on the index: this page is a // handful of files and "which of these is the short cut" is exactly the // question it answers. Memoised by mtime in lib/browse.ts. const rels = [ ...song.cuts.flatMap((c) => [c.shipped?.rel, ...c.variants.map((v) => v.rel)]), ...song.unattributed.map((v) => v.rel), ].filter((r): r is string => !!r); const info = await probeAll(id, rels); const spec = await readSpec(id); const problems = await validateSpec(spec); const notes = await readNotes(id); // Two small JSON reads, no probing -- the bench draws what the manifests say. const thumbs = await thumbView(id, thumbAliasesFor(id)); return (