import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync, } from "node:fs"; import { createHash } from "node:crypto"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { spawnSync } from "node:child_process"; import { parseVideoElements, parseImageElements, extractAllVideoFrames, createFrameLookupTable, resolveProjectRelativeSrc, codecMayHaveAlpha, decoderForCodec, type VideoElement, type ExtractedFrames, } from "./videoFrameExtractor.js"; import { extractVideoMetadata } from "../utils/ffprobe.js"; import { runFfmpeg } from "../utils/runFfmpeg.js"; // ffmpeg is not preinstalled on GitHub's ubuntu-24.04 runners. The producer // regression test at packages/producer/tests/vfr-screen-recording/ runs inside // Dockerfile.test (which does include ffmpeg) and is the primary CI signal // for this bug. Locally and in any CI job with ffmpeg on PATH, the tests // below run too — they exercise the extractor in isolation against a // synthesized VFR fixture. const HAS_FFMPEG = spawnSync("ffmpeg", ["-version"]).status === 0; // Codec-based alpha defaulting replaces tag-based detection (the // alpha_mode/ALPHA_MODE case bug — see ffprobe.test.ts for the regression // pin on that). The extractor uses these helpers for two decisions: // 1. whether to force the alpha-aware decoder (libvpx-vp9 for VP9, libvpx // for VP8) // 2. whether to default the cached frame format to PNG (with alpha) vs JPG // The "default to capable" trade is small file-size growth on opaque VP9 // content for correctness on alpha-having content even when the sidecar tag // is missing or muxed with the wrong case. describe("codec alpha capability", () => { it("flags VP9, VP8, and ProRes as alpha-capable", () => { expect(codecMayHaveAlpha("vp9")).toBe(true); expect(codecMayHaveAlpha("VP9")).toBe(true); expect(codecMayHaveAlpha("vp8")).toBe(true); expect(codecMayHaveAlpha("prores")).toBe(true); }); it("does not flag h264 / h265 / mpeg4 (no alpha in their bitstreams)", () => { expect(codecMayHaveAlpha("h264")).toBe(false); expect(codecMayHaveAlpha("h265")).toBe(false); expect(codecMayHaveAlpha("hevc")).toBe(false); expect(codecMayHaveAlpha("mpeg4")).toBe(false); }); it("treats undefined / empty input as non-alpha", () => { expect(codecMayHaveAlpha(undefined)).toBe(false); expect(codecMayHaveAlpha("")).toBe(false); }); it("returns the alpha-aware decoder name for VP9 and VP8", () => { expect(decoderForCodec("vp9")).toBe("libvpx-vp9"); expect(decoderForCodec("VP9")).toBe("libvpx-vp9"); expect(decoderForCodec("vp8")).toBe("libvpx"); }); }); // Regression: a long-standing footgun where `