This commit is contained in:
TheOperator 2026-06-22 23:29:55 +00:00 committed by GitHub
commit adccfed81b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 420 additions and 17 deletions

View file

@ -1,5 +1,5 @@
import { beforeEach, expect, test } from "bun:test";
import { convert } from "../../src/converters/ffmpeg";
import { convert, resetNvidiaGpuCache } from "../../src/converters/ffmpeg";
let calls: string[][] = [];
@ -11,6 +11,58 @@ function mockExecFile(
calls.push(args);
if (args.includes("fail.mov")) {
callback(new Error("mock failure"), "", "Fake stderr: fail");
} else if (_cmd === "nvidia-smi") {
// Mock nvidia-smi - assume GPU is available for tests
callback(
null,
"GPU 0: NVIDIA GeForce RTX 3080 (UUID: GPU-12345678-1234-1234-1234-123456789012)\n",
"",
);
} else if (_cmd === "ffprobe") {
// Mock ffprobe responses for codec detection
// Return H.264 codec for .mp4 files, no video stream for images
if (args.includes("in.mp4") || args.includes("in.mkv") || args.includes("in.avi")) {
callback(
null,
JSON.stringify({
streams: [
{
codec_type: "video",
codec_name: "h264",
},
],
}),
"",
);
} else if (args.includes("in.jpg") || args.includes("in.png")) {
// Image files have no video stream
callback(
null,
JSON.stringify({
streams: [
{
codec_type: "audio",
codec_name: "pcm",
},
],
}),
"",
);
} else {
// Default: assume H.264 for video files
callback(
null,
JSON.stringify({
streams: [
{
codec_type: "video",
codec_name: "h264",
},
],
}),
"",
);
}
} else {
callback(null, "Fake stdout", "");
}
@ -19,6 +71,9 @@ function mockExecFile(
beforeEach(() => {
calls = [];
delete process.env.FFMPEG_ARGS;
delete process.env.FFMPEG_PREFER_HARDWARE;
// Reset the GPU availability cache between tests
resetNvidiaGpuCache();
});
test("converts a normal file", async () => {
@ -121,6 +176,138 @@ test("uses libx266 for h266.mp4", async () => {
expect(loggedMessage).toBe("stdout: Fake stdout");
});
test("uses h264_nvenc for h264.mp4 when hardware preferred", async () => {
process.env.FFMPEG_PREFER_HARDWARE = "true";
const originalConsoleLog = console.log;
let loggedMessage = "";
console.log = (msg) => {
loggedMessage = msg;
};
await convert("in.mkv", "mkv", "h264.mp4", "out.mp4", undefined, mockExecFile);
console.log = originalConsoleLog;
// calls[0] is nvidia-smi, calls[1] is ffprobe, calls[2] is ffmpeg
expect(calls[2]).toEqual(expect.arrayContaining(["-c:v", "h264_nvenc"]));
expect(loggedMessage).toBe("stdout: Fake stdout");
delete process.env.FFMPEG_PREFER_HARDWARE;
});
test("uses hevc_nvenc for h265.mp4 when hardware preferred", async () => {
process.env.FFMPEG_PREFER_HARDWARE = "true";
const originalConsoleLog = console.log;
let loggedMessage = "";
console.log = (msg) => {
loggedMessage = msg;
};
await convert("in.mkv", "mkv", "h265.mp4", "out.mp4", undefined, mockExecFile);
console.log = originalConsoleLog;
// calls[0] is nvidia-smi, calls[1] is ffprobe, calls[2] is ffmpeg
expect(calls[2]).toEqual(expect.arrayContaining(["-c:v", "hevc_nvenc"]));
expect(loggedMessage).toBe("stdout: Fake stdout");
delete process.env.FFMPEG_PREFER_HARDWARE;
});
test("uses libx264 for h264.mp4 when hardware not preferred", async () => {
process.env.FFMPEG_PREFER_HARDWARE = "false";
const originalConsoleLog = console.log;
let loggedMessage = "";
console.log = (msg) => {
loggedMessage = msg;
};
await convert("in.mkv", "mkv", "h264.mp4", "out.mp4", undefined, mockExecFile);
console.log = originalConsoleLog;
expect(calls[0]).toEqual(expect.arrayContaining(["-c:v", "libx264"]));
expect(loggedMessage).toBe("stdout: Fake stdout");
delete process.env.FFMPEG_PREFER_HARDWARE;
});
test("adds CUDA hwaccel for video input when hardware preferred", async () => {
process.env.FFMPEG_PREFER_HARDWARE = "true";
delete process.env.FFMPEG_ARGS;
const originalConsoleLog = console.log;
let loggedMessage = "";
console.log = (msg) => {
loggedMessage = msg;
};
await convert("in.mp4", "mp4", "avi", "out.avi", undefined, mockExecFile);
console.log = originalConsoleLog;
// calls[0] is nvidia-smi, calls[1] is ffprobe, calls[2] is ffmpeg
expect(calls[2]).toEqual(expect.arrayContaining(["-hwaccel", "cuda"]));
expect(loggedMessage).toBe("stdout: Fake stdout");
delete process.env.FFMPEG_PREFER_HARDWARE;
});
test("does not add CUDA hwaccel for image input when hardware preferred", async () => {
process.env.FFMPEG_PREFER_HARDWARE = "true";
delete process.env.FFMPEG_ARGS;
const originalConsoleLog = console.log;
let loggedMessage = "";
console.log = (msg) => {
loggedMessage = msg;
};
await convert("in.jpg", "jpg", "png", "out.png", undefined, mockExecFile);
console.log = originalConsoleLog;
// calls[0] is nvidia-smi, calls[1] is ffprobe, calls[2] is ffmpeg
expect(calls[2]).not.toEqual(expect.arrayContaining(["-hwaccel", "cuda"]));
expect(loggedMessage).toBe("stdout: Fake stdout");
delete process.env.FFMPEG_PREFER_HARDWARE;
});
test("does not add CUDA hwaccel if FFMPEG_ARGS already specifies hwaccel", async () => {
process.env.FFMPEG_PREFER_HARDWARE = "true";
process.env.FFMPEG_ARGS = "-hwaccel vaapi";
const originalConsoleLog = console.log;
let loggedMessage = "";
console.log = (msg) => {
loggedMessage = msg;
};
await convert("in.mp4", "mp4", "avi", "out.avi", undefined, mockExecFile);
console.log = originalConsoleLog;
// When FFMPEG_ARGS already has hwaccel, no ffprobe call is made
// calls[0] is nvidia-smi, calls[1] is ffmpeg
// Should use vaapi from FFMPEG_ARGS, not add cuda
expect(calls[1]).toEqual(expect.arrayContaining(["-hwaccel", "vaapi"]));
expect(calls[0]).not.toEqual(expect.arrayContaining(["-hwaccel", "cuda"]));
expect(loggedMessage).toBe("stdout: Fake stdout");
delete process.env.FFMPEG_PREFER_HARDWARE;
delete process.env.FFMPEG_ARGS;
});
test("respects FFMPEG_ARGS", async () => {
process.env.FFMPEG_ARGS = "-hide_banner -y";