mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 15:20:13 +00:00
fix: preserve shared FFmpeg resolver behavior
This commit is contained in:
@@ -108,6 +108,38 @@ describe("findFfBinary", () => {
|
||||
expect(findFfBinary("ffmpeg")).toBe(resolve("/opt/homebrew/bin/ffmpeg"));
|
||||
});
|
||||
|
||||
it("falls back to the project-local .hyperframes bin", async () => {
|
||||
delete process.env.HYPERFRAMES_FFMPEG_PATH;
|
||||
process.env.PATH = "";
|
||||
const projectBinary = resolve(
|
||||
".hyperframes",
|
||||
"bin",
|
||||
process.platform === "win32" ? "ffmpeg.exe" : "ffmpeg",
|
||||
);
|
||||
vi.resetModules();
|
||||
vi.doMock("node:child_process", () => {
|
||||
const mocked = {
|
||||
execFileSync: () => {
|
||||
throw new Error("not found");
|
||||
},
|
||||
};
|
||||
return { ...mocked, default: mocked };
|
||||
});
|
||||
vi.doMock("node:fs", () => {
|
||||
const mocked = {
|
||||
existsSync: (candidate: unknown) => candidate === projectBinary,
|
||||
accessSync: () => {
|
||||
throw new Error("not executable");
|
||||
},
|
||||
constants: { X_OK: 1 },
|
||||
};
|
||||
return { ...mocked, default: mocked };
|
||||
});
|
||||
const { findFfBinary } = await importFresh();
|
||||
|
||||
expect(findFfBinary("ffmpeg")).toBe(projectBinary);
|
||||
});
|
||||
|
||||
it("returns undefined when the binary is nowhere, and caches the miss until cleared", async () => {
|
||||
delete process.env.HYPERFRAMES_FFMPEG_PATH;
|
||||
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
|
||||
|
||||
@@ -4,7 +4,7 @@ import { delimiter, join, resolve } from "node:path";
|
||||
|
||||
/**
|
||||
* Shared FFmpeg/FFprobe binary resolution for every package that shells out
|
||||
* to them (engine, cli, lint). Node-only: import via the
|
||||
* to them (engine, cli, lint, studio-server). Node-only: import via the
|
||||
* `@hyperframes/parsers/ff-binaries` subpath, never from a browser bundle.
|
||||
*/
|
||||
|
||||
@@ -92,6 +92,12 @@ function findInCommonDirs(name: FfBinaryName): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findInProjectLocalBin(name: FfBinaryName): string | undefined {
|
||||
const extension = process.platform === "win32" ? ".exe" : "";
|
||||
const candidate = resolve(".hyperframes", "bin", `${name}${extension}`);
|
||||
return existsSync(candidate) ? candidate : undefined;
|
||||
}
|
||||
|
||||
function lookupOnSystem(name: FfBinaryName): string | undefined {
|
||||
if (pathLookupCache.has(name)) return pathLookupCache.get(name);
|
||||
let found: string | undefined;
|
||||
@@ -106,6 +112,7 @@ function lookupOnSystem(name: FfBinaryName): string | undefined {
|
||||
} catch {
|
||||
found = scanPath(name);
|
||||
}
|
||||
found ??= findInProjectLocalBin(name);
|
||||
found ??= findInCommonDirs(name);
|
||||
const resolved = found ? resolve(found) : undefined;
|
||||
pathLookupCache.set(name, resolved);
|
||||
@@ -125,9 +132,10 @@ export interface FindFfBinaryOptions {
|
||||
|
||||
/**
|
||||
* Resolve an FFmpeg-family binary: env override first, then `which`/`where`,
|
||||
* then a manual PATH scan (covers Windows PATHEXT), then well-known Unix
|
||||
* install dirs. System lookups are cached per binary for the process
|
||||
* lifetime; the env override is re-read on every call.
|
||||
* then a manual PATH scan (covers Windows PATHEXT), a project-local
|
||||
* `.hyperframes/bin`, then well-known Unix install dirs. System lookups are
|
||||
* cached per binary for the process lifetime; the env override is re-read on
|
||||
* every call.
|
||||
*/
|
||||
export function findFfBinary(
|
||||
name: FfBinaryName,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { existsSync, writeFileSync, mkdirSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
import { join } from "node:path";
|
||||
import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
|
||||
|
||||
const SAMPLE_RATE = 4000;
|
||||
const PEAK_COUNT = 4000;
|
||||
@@ -28,16 +29,10 @@ function computePeaks(floats: Float32Array, count: number): number[] {
|
||||
return peaks.map((p) => p / maxPeak);
|
||||
}
|
||||
|
||||
function ffmpegBinary(): string {
|
||||
const configured = process.env.HYPERFRAMES_FFMPEG_PATH?.trim();
|
||||
if (configured) return resolve(configured);
|
||||
return "ffmpeg";
|
||||
}
|
||||
|
||||
export function decodeAudioPeaks(audioPath: string): Promise<number[]> {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const proc = spawn(
|
||||
ffmpegBinary(),
|
||||
findFfBinary("ffmpeg") ?? "ffmpeg",
|
||||
[
|
||||
"-i",
|
||||
audioPath,
|
||||
|
||||
Reference in New Issue
Block a user