import { spawn } from "node:child_process"; import { mkdir, mkdtemp, rm, stat } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import ffmpegInstaller from "@ffmpeg-installer/ffmpeg"; /** Path of the on-the-fly fixture video. Cached per Vitest worker. */ let fixturePath: string | undefined; let fixtureDir: string | undefined; /** * Synthesise a 5-second 320×240 colour-gradient MP4 once per worker and return * its absolute path. Subsequent calls return the cached path. Uses ffmpeg's * `lavfi` virtual source so no checked-in binary is required. */ export async function getFixtureVideo(): Promise { if (fixturePath !== undefined) { return fixturePath; } const dir = await mkdtemp(join(tmpdir(), "vt-fixture-")); const out = join(dir, "fixture.mp4"); await new Promise((resolve, reject) => { const child = spawn( ffmpegInstaller.path, [ "-y", "-f", "lavfi", "-i", "testsrc=duration=5:size=320x240:rate=10", "-pix_fmt", "yuv420p", "-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency", out, ], { stdio: "pipe" } ); let stderr = ""; child.stderr.on("data", (chunk) => { stderr += String(chunk); }); child.once("error", reject); child.once("exit", (code) => { if (code === 0) { resolve(); } else { reject(new Error(`ffmpeg fixture build failed: ${stderr}`)); } }); }); const fileStat = await stat(out); if (fileStat.size === 0) { throw new Error("fixture video has zero bytes"); } fixtureDir = dir; fixturePath = out; return out; } /** Create a unique tmp output directory for one test. */ export async function makeOutputDir(label: string): Promise { const dir = await mkdtemp(join(tmpdir(), `vt-${label}-`)); await mkdir(dir, { recursive: true }); return dir; } /** Best-effort cleanup; safe to ignore failures (Windows / locked files). */ export async function safeRm(path: string): Promise { try { await rm(path, { recursive: true, force: true }); } catch { // ignored } } /** Clear the cached fixture (used by setup teardown). */ export async function clearFixtureCache(): Promise { if (fixtureDir !== undefined) { await safeRm(fixtureDir); } fixturePath = undefined; fixtureDir = undefined; }