/** * FFmpeg ingest for motion-overlay. * * Thin side-effecting helpers over ffmpeg/ffprobe that the plan/execute layers * call to break an input video into the pieces the composer needs: * - `probeVideo` — duration (s) + width/height + derived aspect label. * - `extractAudio` — pull the audio track to a standalone file. * - `cutTake` — frame-accurate `-ss/-to` cut, re-encoded CRF18. * - `extractFrames` — sample reference frames for `readFrameObservations`. * - `readFrameObservations` — heuristic + notes stub from the sampled frames. * * Every ffmpeg/ffprobe call routes through an injectable `IngestDeps` so tests * run fully offline (no ffmpeg required) by supplying fakes. The defaults wrap * the proven `assemble/ffmpeg.ts` spawn primitives + a small ffprobe shell-out. */ import type { FrameObservations } from './types.js'; export interface VideoProbe { durationSeconds: number; width: number; height: number; /** "16:9" | "9:16" | "1:1" | ":" reduced label. */ aspect: string; } /** Run an ffmpeg arg array. Mirrors `runFfmpeg`'s shape (returns a command). */ export type RunFfmpegFn = (args: string[]) => Promise<{ command: string; }>; /** Probe a video → duration/width/height. */ export type ProbeFn = (path: string) => Promise<{ durationSeconds: number; width: number; height: number; }>; export interface IngestDeps { /** Real ffmpeg by default; injected fake in tests. */ runFfmpeg?: RunFfmpegFn; /** Real ffprobe by default; injected fake in tests. */ probe?: ProbeFn; } /** Reduce a width:height pair to a tidy aspect label. */ export declare function aspectLabel(width: number, height: number): string; /** Probe a video for duration + dimensions + aspect label. */ export declare function probeVideo(path: string, deps?: IngestDeps): Promise; /** Extract the input's audio track to `outPath` (copy codec, no re-encode). */ export declare function extractAudio(inputPath: string, outPath: string, deps?: IngestDeps): Promise<{ command: string; }>; /** * Cut a take frame-accurately from `inputPath` between `start` and `end` * (absolute seconds), re-encoding so the cut is exact (not keyframe-snapped). * Matches the design spec: `-ss/-to -c:v libx264 -crf 18 -preset fast -c:a aac`. */ export declare function cutTake(inputPath: string, start: number, end: number, outPath: string, deps?: IngestDeps): Promise<{ command: string; }>; /** * Sample `count` reference frames evenly across `[0, durationSeconds)` to * `/frame_%02d.jpg`. Returns the relative-ish filenames produced (the * caller owns the dir). One ffmpeg call using the `fps` filter. */ export declare function extractFrames(inputPath: string, outDir: string, durationSeconds: number, count: number, deps?: IngestDeps): Promise; /** * Derive `FrameObservations` from the sampled frames + the probe aspect. * * v1 is a deterministic heuristic stub: a portrait (9:16) talking-head is * assumed centred; a landscape source is assumed to have the speaker on one * side. It records a note about the untouchable region so the composer's GLOBAL * RULES carry it through. Pure given its inputs — no image decoding (that is a * later enhancement) — so it stays offline-testable. */ export declare function readFrameObservations(probe: { aspect: string; }, frameFiles: string[]): FrameObservations; //# sourceMappingURL=ingest.d.ts.map