Files
hyperframes/packages/cli/src/browser/ffmpeg.ts
T
Miguel Angel Simon Sierra 88c049f525 refactor(parsers): single shared FFmpeg/FFprobe binary resolver
The cli, engine, and lint packages each carried their own copy of the
ffmpeg/ffprobe lookup, annotated fallow-ignore code-duplication, and the
copies had drifted: the engine copy handled Windows PATHEXT and executed
which/where without a shell but lacked the Homebrew-dirs fallback for
GUI-spawned processes; the cli copy had the opposite. One resolver in
@hyperframes/parsers (the dependency-graph bottom) now carries the union
of both hardenings, and all three packages delegate to it. Every
consumer gets strictly more robust resolution; env-override semantics
per call site are preserved via configuredMustExist.
2026-07-16 18:36:40 -04:00

62 lines
2.3 KiB
TypeScript

import { execFileSync } from "node:child_process";
import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
import { detectLinuxDistro, ffmpegInstallCommand } from "./linuxDeps.js";
export { FFMPEG_PATH_ENV, FFPROBE_PATH_ENV } from "@hyperframes/parsers/ff-binaries";
export type H264EncoderMode = "software" | "gpu";
/**
* Select the H.264 encoder class supported by an FFmpeg build.
*
* Some macOS FFmpeg distributions expose VideoToolbox but omit libx264. The
* default CPU render path must not pass libx264-only options such as `-preset`
* to those builds.
*/
export function resolveH264EncoderMode(
ffmpegEncodersOutput: string,
gpuRequested: boolean,
): H264EncoderMode {
if (gpuRequested) return "gpu";
if (/\blibx264\b/.test(ffmpegEncodersOutput)) return "software";
if (/\bh264_videotoolbox\b/.test(ffmpegEncodersOutput)) return "gpu";
throw new Error("This FFmpeg build has neither libx264 nor VideoToolbox H.264 encoding.");
}
export function detectH264EncoderMode(ffmpegPath: string, gpuRequested: boolean): H264EncoderMode {
const encoders = execFileSync(ffmpegPath, ["-hide_banner", "-encoders"], {
encoding: "utf-8",
stdio: ["ignore", "pipe", "pipe"],
timeout: 5000,
});
return resolveH264EncoderMode(encoders, gpuRequested);
}
// `configuredMustExist`: the CLI surfaces an install hint when a binary is
// missing, so an env override pointing at a nonexistent file reports as
// not-found instead of being handed to spawn.
export function findFFmpeg(): string | undefined {
return findFfBinary("ffmpeg", { configuredMustExist: true });
}
export function findFFprobe(): string | undefined {
return findFfBinary("ffprobe", { configuredMustExist: true });
}
export function getFFmpegInstallHint(): string {
switch (process.platform) {
case "darwin":
return "brew install ffmpeg";
case "linux": {
// Distro-aware so WSL/Fedora/Arch/Alpine users get a command that
// actually works instead of a Debian-only `apt` line.
const distro = detectLinuxDistro();
return ffmpegInstallCommand(distro.family);
}
case "win32":
return "Download the 64-bit Windows build from https://ffmpeg.org/download.html#build-windows and add its bin/ directory to PATH.";
default:
return "https://ffmpeg.org/download.html";
}
}