mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
* fix(cli): discover project-local ffmpeg binaries * style(cli): format project-local ffmpeg discovery * style(media-use): format resolver script * chore(skills): refresh bundled manifest
107 lines
3.9 KiB
TypeScript
107 lines
3.9 KiB
TypeScript
import { execFileSync, execSync } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("node:child_process", () => ({ execFileSync: vi.fn(), execSync: vi.fn() }));
|
|
vi.mock("node:fs", () => ({ existsSync: vi.fn() }));
|
|
|
|
const mockExec = vi.mocked(execSync);
|
|
const mockExecFile = vi.mocked(execFileSync);
|
|
const mockExists = vi.mocked(existsSync);
|
|
|
|
// The common-dir fallback list is platform-gated (empty on win32), so pin the
|
|
// platform to a POSIX value to keep the test deterministic on Windows CI.
|
|
const originalPlatform = process.platform;
|
|
beforeEach(() => {
|
|
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
|
|
vi.resetModules();
|
|
});
|
|
|
|
afterEach(() => {
|
|
Object.defineProperty(process, "platform", { value: originalPlatform, configurable: true });
|
|
vi.clearAllMocks();
|
|
delete process.env.HYPERFRAMES_FFMPEG_PATH;
|
|
delete process.env.HYPERFRAMES_FFPROBE_PATH;
|
|
});
|
|
|
|
describe("findFFmpeg", () => {
|
|
it("prefers the real Windows exe when where lists a cmd shim first", async () => {
|
|
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
|
|
mockExec.mockReturnValue("C:\\tools\\ffmpeg.cmd\r\nC:\\tools\\ffmpeg.exe\r\n");
|
|
|
|
const { findFFmpeg } = await import("./ffmpeg.js");
|
|
expect(findFFmpeg()).toBe(resolve("C:\\tools\\ffmpeg.exe"));
|
|
});
|
|
|
|
it("falls back to a common install dir when `which` fails (GUI-launched PATH)", async () => {
|
|
// Simulate a process whose PATH lacks /opt/homebrew/bin: `which ffmpeg` throws.
|
|
mockExec.mockImplementation(() => {
|
|
throw new Error("which: no ffmpeg in PATH");
|
|
});
|
|
mockExists.mockImplementation((p) => p === "/opt/homebrew/bin/ffmpeg");
|
|
|
|
const { findFFmpeg } = await import("./ffmpeg.js");
|
|
expect(findFFmpeg()).toBe("/opt/homebrew/bin/ffmpeg");
|
|
});
|
|
|
|
it("returns undefined when ffmpeg is on neither PATH nor a common dir", async () => {
|
|
mockExec.mockImplementation(() => {
|
|
throw new Error("not found");
|
|
});
|
|
mockExists.mockReturnValue(false);
|
|
|
|
const { findFFmpeg } = await import("./ffmpeg.js");
|
|
expect(findFFmpeg()).toBeUndefined();
|
|
});
|
|
|
|
it("finds project-local FFmpeg binaries when they are not on PATH", async () => {
|
|
mockExec.mockImplementation(() => {
|
|
throw new Error("not found");
|
|
});
|
|
const localFFmpeg = resolve(".hyperframes", "bin", "ffmpeg");
|
|
const localFFprobe = resolve(".hyperframes", "bin", "ffprobe");
|
|
mockExists.mockImplementation((path) => path === localFFmpeg || path === localFFprobe);
|
|
|
|
const { findFFmpeg, findFFprobe } = await import("./ffmpeg.js");
|
|
expect(findFFmpeg()).toBe(localFFmpeg);
|
|
expect(findFFprobe()).toBe(localFFprobe);
|
|
});
|
|
});
|
|
|
|
describe("resolveH264EncoderMode", () => {
|
|
it("falls back to VideoToolbox when libx264 is absent", async () => {
|
|
const { resolveH264EncoderMode } = await import("./ffmpeg.js");
|
|
const encoders = `
|
|
V....D h264_videotoolbox VideoToolbox H.264 Encoder
|
|
`;
|
|
|
|
expect(resolveH264EncoderMode(encoders, false)).toBe("gpu");
|
|
});
|
|
|
|
it("does not treat a compiled Linux hardware encoder as usable", async () => {
|
|
const { resolveH264EncoderMode } = await import("./ffmpeg.js");
|
|
const encoders = `
|
|
V....D h264_vaapi H.264/AVC (VAAPI)
|
|
`;
|
|
|
|
expect(() => resolveH264EncoderMode(encoders, false)).toThrow(
|
|
"neither libx264 nor VideoToolbox",
|
|
);
|
|
});
|
|
|
|
it("inspects the configured FFmpeg binary", async () => {
|
|
mockExecFile.mockReturnValue(
|
|
" V....D h264_videotoolbox VideoToolbox H.264 Encoder\n" as never,
|
|
);
|
|
const { detectH264EncoderMode } = await import("./ffmpeg.js");
|
|
|
|
expect(detectH264EncoderMode("/custom/ffmpeg", false)).toBe("gpu");
|
|
expect(mockExecFile).toHaveBeenCalledWith(
|
|
"/custom/ffmpeg",
|
|
["-hide_banner", "-encoders"],
|
|
expect.objectContaining({ encoding: "utf-8" }),
|
|
);
|
|
});
|
|
});
|