mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
The render pre-flight check shells out to `which ffmpeg`, which only searches the server process's PATH. When Studio is launched from a GUI/Dock/launchd context that PATH lacks /opt/homebrew/bin, so `which` fails even when ffmpeg is installed — and POST /render returns 503 "FFmpeg not found". Fall back to probing well-known install dirs (Homebrew on Apple Silicon and Intel, plus system/Linux locations) when the PATH lookup fails. Also drop the [kf:static]/[kf:runtime] keyframe diagnostics that were spamming the Studio console in prod, and fix two unrelated CI breakages the branch inherited: a Windows-sensitive ffmpeg test (pin platform) and a stale player test mock missing onRuntimeReady.
73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
// fallow-ignore-file code-duplication
|
|
import { execSync } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
export const FFMPEG_PATH_ENV = "HYPERFRAMES_FFMPEG_PATH";
|
|
export const FFPROBE_PATH_ENV = "HYPERFRAMES_FFPROBE_PATH";
|
|
|
|
function findOnPath(name: "ffmpeg" | "ffprobe"): string | undefined {
|
|
try {
|
|
const cmd = process.platform === "win32" ? `where ${name}` : `which ${name}`;
|
|
const output = execSync(cmd, {
|
|
encoding: "utf-8",
|
|
stdio: ["pipe", "pipe", "pipe"],
|
|
timeout: 5000,
|
|
});
|
|
const first = output
|
|
.split(/\r?\n/)
|
|
.map((s) => s.trim())
|
|
.find(Boolean);
|
|
return first ? resolve(first) : undefined;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// GUI/Dock/launchd-spawned processes on macOS don't inherit the shell PATH, so
|
|
// `which ffmpeg` fails even when ffmpeg is installed via Homebrew. Probe the
|
|
// well-known install dirs as a fallback. (No-op on Windows, where `where` and
|
|
// installer-added PATH entries cover it.)
|
|
const COMMON_BIN_DIRS =
|
|
process.platform === "win32"
|
|
? []
|
|
: ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/snap/bin"];
|
|
|
|
function findInCommonDirs(name: "ffmpeg" | "ffprobe"): string | undefined {
|
|
for (const dir of COMMON_BIN_DIRS) {
|
|
const candidate = `${dir}/${name}`;
|
|
if (existsSync(candidate)) return candidate;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function findConfiguredBinary(
|
|
envName: string,
|
|
binaryName: "ffmpeg" | "ffprobe",
|
|
): string | undefined {
|
|
const configured = process.env[envName]?.trim();
|
|
if (configured) return existsSync(configured) ? resolve(configured) : undefined;
|
|
return findOnPath(binaryName) ?? findInCommonDirs(binaryName);
|
|
}
|
|
|
|
export function findFFmpeg(): string | undefined {
|
|
return findConfiguredBinary(FFMPEG_PATH_ENV, "ffmpeg");
|
|
}
|
|
|
|
export function findFFprobe(): string | undefined {
|
|
return findConfiguredBinary(FFPROBE_PATH_ENV, "ffprobe");
|
|
}
|
|
|
|
export function getFFmpegInstallHint(): string {
|
|
switch (process.platform) {
|
|
case "darwin":
|
|
return "brew install ffmpeg";
|
|
case "linux":
|
|
return "sudo apt install ffmpeg";
|
|
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";
|
|
}
|
|
}
|