// A CLIP WINDOW CUT FROM A SAVED CONTAINER, not fetched (release 21 D2). // // A video whose whole source is in the saved-video store (a persist, a // `full: true` fetch, or a container attached from a local archive — release 21 // D1's `origin.kind: "local-archive"`) holds every second a window could ask // for. Fetching those seconds again spends the source's patience for bytes // already on a disk here — and for a deleted channel there is no source to ask: // the fetch fails every time. So the window paths (the single // `fetchWindowAction` and the `fetch-windows` batch) ask here after the // clip-window cache and BEFORE the network. // // THE THREE ANSWERS, and what each caller does with them (ruled 2026-10-10): // none no pointer: the network path, exactly as before. // unreadable a pointer whose container cannot be read — the store's drive // is not mounted or not answering, the file is missing, ffprobe // cannot open it. REFUSED with the sentence below, never a // silent fall-through to the network: the operator who can plug // the drive in is the one who needs to hear it. // not-covering a pointer whose container ends before the window does: the // network path. // covers cut it here. // // THE CUT IS evidenceClip-server's (`evidenceCutArgs`): seeked on the input // and re-encoded, so the window's first frame is the asked-for second; H.264 + // AAC in an mp4, fitted inside 1280×720 — the container a fetched window is // pinned to (lib/clipWindow.ts), at most the default clip height. A container // with no picture is cut as sound (still an `.mp4` name, the window's one // extension). The window lands in `data//clips/-.mp4` with a // provenance sidecar of the fetched shape plus `source: "saved-video"`, so // every reader of the cache (the video page, the build, umtool) sees it as a // window like any other. // // SERVER-ONLY (node:fs, ffmpeg). import { mkdir, rename, rm, stat } from "node:fs/promises"; import path from "node:path"; import { execa } from "execa"; import { WIN_EPS, clipWindowFile, type ClipProvenance, } from "./clipWindow"; import { clipWindowPath, writeClipProvenance, type ClipWindow, } from "./clipWindow-server"; import { evidenceCutArgs, probeMedia, type MediaProbe } from "./evidenceClip-server"; import { tmpPathFor } from "./jsonFile-server"; import { savedVideoPath, type SavedVideoPointer } from "./savedVideo"; import { loadSavedVideo } from "./savedVideo-server"; import { readSavedVideosMarker, SavedVideosStoreInTransitionError } from "./savedVideoStore"; import { isDriveNotAnswering, onDrive } from "./storageHealth"; import type { Paths } from "./paths"; // A window cut from a container is seconds of re-encoding; a whole container // on a platter seeks once. Generous, so only a wedged ffmpeg reaches it — // evidenceClip-server's limit for the same work. const CUT_TIMEOUT_MS = 10 * 60_000; export type SavedWindowSource = | { kind: "none" } | { kind: "unreadable"; error: string } | { kind: "not-covering"; container: string; durationSec: number } | { kind: "covers"; pointer: SavedVideoPointer; container: string; durationSec: number; probe: MediaProbe; }; // The media guard's sentence (lib/channelMedia.ts, ChannelMediaUnreachableError) // with the saved container as its detail, so a refusal here reads like every // other unreachable-media refusal the operator has seen. function unreachable(slug: string, detail: string): SavedWindowSource { return { kind: "unreadable", error: `Channel "${slug}": media is not reachable — ${detail}` }; } // Does this video's saved container hold [from, to]? Reads the pointer, stats // the container through the drive watchdog (an unanswering drive is refused in // seconds, not waited on), and probes it once for its duration and streams. export async function savedWindowSource(opts: { paths: Pick; slug: string; videoDir: string; from: number; to: number; }): Promise { const pointer = await loadSavedVideo(opts.videoDir); if (!pointer) return { kind: "none" }; const container = savedVideoPath(pointer); let isFile = false; try { isFile = await onDrive(pointer.dir, () => stat(container).then( (s) => s.isFile(), () => false, ), ); } catch (err) { if (isDriveNotAnswering(err)) { return unreachable(opts.slug, `its saved video ${container}: ${(err as Error).message}`); } throw err; } if (!isFile) { // The store mid-move is the one case with a sentence of its own: the // store guard's (assertSavedVideosStoreWritable), which names the move. const marker = await readSavedVideosMarker(opts.paths); if (marker) { return { kind: "unreadable", error: new SavedVideosStoreInTransitionError(marker).message }; } return unreachable( opts.slug, `its saved video ${container} is not there (drive not mounted?)`, ); } const probe = await probeMedia(container, opts.paths.ffprobeBin); if (!probe || probe.durationSec === null || (!probe.hasVideo && !probe.hasAudio)) { return unreachable(opts.slug, `its saved video ${container} could not be read by ffprobe`); } const durationSec = probe.durationSec; if (opts.from < 0 || opts.to > durationSec + WIN_EPS) { return { kind: "not-covering", container, durationSec }; } return { kind: "covers", pointer, container, durationSec, probe }; } export type SavedWindowProvenance = { requestedBy: string; manifest?: string; clipId?: string; reason?: string; pad?: number; requestedAt?: string; }; export class SavedWindowCutError extends Error { constructor(message: string) { super(message); this.name = "SavedWindowCutError"; } } // Cut [from, to] out of a covering container into the video's clips/, with its // sidecar. Written to a temp name first (which no window parser accepts) and // renamed, so a reader never sees half a window. export async function cutSavedVideoWindow(opts: { paths: Pick; videoDir: string; source: Extract; from: number; to: number; provenance: SavedWindowProvenance; onLog?: (line: string) => void; signal?: AbortSignal; }): Promise { const { from, to, source } = opts; const dest = clipWindowPath(opts.videoDir, from, to); await mkdir(path.dirname(dest), { recursive: true }); const tmp = tmpPathFor(dest); const kind = source.probe.hasVideo ? "video" : "audio"; try { const r = await execa(opts.paths.ffmpegBin, evidenceCutArgs(source.container, from, to, kind, tmp), { reject: false, timeout: CUT_TIMEOUT_MS, ...(opts.signal ? { cancelSignal: opts.signal } : {}), }); if (r.exitCode !== 0) { const err = String(r.stderr ?? "").trim().split("\n").slice(-3).join(" | "); throw new SavedWindowCutError( `ffmpeg failed cutting ${from.toFixed(2)}–${to.toFixed(2)} from the saved video ` + `${source.pointer.file}${err ? `: ${err}` : ` (exit ${r.exitCode ?? "?"})`}`, ); } const bytes = (await stat(tmp)).size; await rename(tmp, dest); const now = new Date().toISOString(); const provenance: ClipProvenance = { requestedBy: opts.provenance.requestedBy, ...(opts.provenance.manifest ? { manifest: opts.provenance.manifest } : {}), ...(opts.provenance.clipId ? { clipId: opts.provenance.clipId } : {}), ...(opts.provenance.reason ? { reason: opts.provenance.reason } : {}), requestedAt: opts.provenance.requestedAt ?? now, ...(typeof opts.provenance.pad === "number" ? { pad: opts.provenance.pad } : {}), bytes, // When the window landed — for a cut, when it was cut. fetchedAt: now, source: "saved-video", }; await writeClipProvenance(opts.videoDir, from, to, provenance); opts.onLog?.( `Cut ${from.toFixed(2)}–${to.toFixed(2)} from the saved video ${source.pointer.file} into ` + `clips/${clipWindowFile(from, to)} (${bytes} bytes); nothing fetched.\n`, ); return { file: clipWindowFile(from, to), path: dest, from, to, bytes, provenance, }; } finally { await rm(tmp, { force: true }).catch(() => {}); } }