import { test } from "node:test"; import assert from "node:assert/strict"; import { classifyFfmpegProbe } from "./ffmpegStreamClassify"; // Run with: // pnpm --filter yt-dlp-transcript-common exec tsx --test ytdlp/ffmpegStreamClassify.test.ts // // The ffmpeg probe-result classifier. Pure: an exit code and a stderr string // in, a verdict out. (These lived in the editor's e2e suite as // audio-check-classifier.spec.ts, booting a dev server they never used.) const DECODER_ERROR = "[aist#0:1/aac @ 0x2] [dec:aac @ 0x3] Error submitting packet to decoder: Invalid data found when processing input\n"; const PARTIAL_FILE = "[in#0/mov,mp4,m4a,3gp,3g2,mj2 @ 0x4] stream 1, offset 0x1626f8a: partial file\n"; test("exit 0 with empty stderr → clean", () => { assert.equal(classifyFfmpegProbe(0, ""), "clean"); assert.equal(classifyFfmpegProbe(0, "\n \t\n"), "clean"); }); test("exit 0 with 'partial file' stderr → partial", () => { assert.equal( classifyFfmpegProbe( 0, "[in#0/mov,mp4,m4a,3gp,3g2,mj2 @ 0x1234] stream 1, offset 0x10483924: partial file\n", ), "partial", ); }); test("exit 0 with many decoder errors and no 'partial file' → malformed", () => { // ffmpeg can exit 0 even when the av_codec layer rejects hundreds of // packets — the encoder keeps producing output from whatever decoded. A wall // of "Error submitting packet to decoder" lines without a "partial file" // demuxer warning is mid-stream corruption, not a clean truncation. const aacStorm = Array.from( { length: 50 }, (_, i) => `[aac @ 0x1] channel element ${i % 3}.${i % 16} is not allocated\n` + DECODER_ERROR, ).join(""); assert.equal(classifyFfmpegProbe(0, aacStorm), "malformed"); }); test("exit 0 with many decoder errors AND 'partial file' → malformed (corruption wins over truncation)", () => { const stormPlusPartial = Array.from({ length: 50 }, () => DECODER_ERROR).join("") + PARTIAL_FILE; assert.equal(classifyFfmpegProbe(0, stormPlusPartial), "malformed"); }); test("exit 0 with a small tail of decoder errors AND 'partial file' → partial", () => { // Truncated containers often emit a couple of trailing decoder errors as the // encoder eats the last partial packets. Below the threshold the file is // still classifiable as partial. const tail = DECODER_ERROR + DECODER_ERROR + PARTIAL_FILE; assert.equal(classifyFfmpegProbe(0, tail), "partial"); }); test("non-zero exit → malformed (regardless of stderr)", () => { assert.equal( classifyFfmpegProbe( 1, "[aac @ 0x1] Sample rate index in program config element does not match the sample rate index configured by the container.\n", ), "malformed", ); assert.equal(classifyFfmpegProbe(2, ""), "malformed"); assert.equal(classifyFfmpegProbe(null, "killed by signal"), "malformed"); });