All checks were successful
Build and Push Docker Image / build (push) Successful in 1m30s
Abraham Lincoln crashed with exit 234 because the file had 14 dvd_subtitle
streams: our extraction dict only keyed on the long form (dvd_subtitle)
while jellyfin stores the short form (dvdsub), so the lookup fell back
to .srt, ffmpeg picked the srt muxer, and srt can't encode image-based
subs. textbook silent dict miss.
replaced the extension dict with an EXTRACTABLE map that pairs codec →
{ext, codecArg} and explicitly enumerates every codec we can route to a
single-file sidecar. everything else (dvd_subtitle/dvdsub, dvb_subtitle/
dvbsub, unknown codecs) is now skipped at command-build time. the plan
picks up a note like '14 subtitle(s) dropped: dvdsub (eng, est, ind,
kor, jpn, lav, lit, may, chi, chi, tha, vie, rus, ukr) — not extractable
to sidecar' so the user sees exactly what didn't make it.
also added extractErrorSummary in execute.ts: when a job errors, scan
the last 60 stderr lines for fatal keywords (Error:, Conversion failed!,
Unsupported, Invalid argument, Permission denied, No space left, …),
dedupe, prepend the summary to the job's stored output. the review_plan
notes get the same summary — surfaces the real cause next to the plan
instead of burying it under ffmpeg's 200-line banner.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
50 lines
2.3 KiB
TypeScript
50 lines
2.3 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { extractErrorSummary } from "../execute";
|
|
|
|
describe("extractErrorSummary", () => {
|
|
test("pulls the real error line out of ffmpeg's banner", () => {
|
|
const lines = [
|
|
"[stderr] ffmpeg version 7.1.3 ...",
|
|
"[stderr] built with gcc 14",
|
|
"[stderr] Stream #0:2(eng): Subtitle: dvd_subtitle (dvdsub), 1280x720",
|
|
"[stderr] Stream mapping:",
|
|
"[stderr] Stream #0:2 -> #0:0 (copy)",
|
|
"[stderr] [srt @ 0x55] Unsupported subtitles codec: dvd_subtitle",
|
|
"[stderr] [out#0/srt @ 0x55] Could not write header (incorrect codec parameters ?): Invalid argument",
|
|
"[stderr] Conversion failed!",
|
|
];
|
|
const summary = extractErrorSummary(lines, new Error("FFmpeg exited with code 234"));
|
|
expect(summary).toContain("Unsupported subtitles codec: dvd_subtitle");
|
|
expect(summary).toContain("Invalid argument");
|
|
expect(summary).toContain("Conversion failed!");
|
|
// Should NOT include the banner lines.
|
|
expect(summary).not.toContain("ffmpeg version");
|
|
expect(summary).not.toContain("Stream #0:2");
|
|
});
|
|
|
|
test("dedupes identical fatal lines (e.g. repeated warnings)", () => {
|
|
const lines = ["[stderr] Conversion failed!", "[stderr] Conversion failed!", "[stderr] Conversion failed!"];
|
|
const summary = extractErrorSummary(lines);
|
|
expect(summary?.split("\n").length).toBe(1);
|
|
});
|
|
|
|
test("falls back to the thrown error when no fatal line is found", () => {
|
|
const lines = ["[stderr] ffmpeg version 7", "[stderr] Duration: 00:10:00"];
|
|
const summary = extractErrorSummary(lines, new Error("FFmpeg exited with code 1"));
|
|
expect(summary).toBe("Error: FFmpeg exited with code 1");
|
|
});
|
|
|
|
test("returns null when neither a fatal line nor a thrown error is available", () => {
|
|
expect(extractErrorSummary([])).toBe(null);
|
|
expect(extractErrorSummary(["[stderr] ffmpeg version 7"])).toBe(null);
|
|
});
|
|
|
|
test("only scans the tail — a banner from a prior run doesn't leak through", () => {
|
|
// 70 filler lines, real error at the very end; scan window is 60.
|
|
const filler = Array.from({ length: 70 }, (_, i) => `[stderr] banner line ${i}`);
|
|
const lines = [...filler, "[stderr] Error: no space left on device"];
|
|
const summary = extractErrorSummary(lines);
|
|
expect(summary).toBe("Error: no space left on device");
|
|
});
|
|
});
|