mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 07:09:59 +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"));
|
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 () => {
|
it("returns undefined when the binary is nowhere, and caches the miss until cleared", async () => {
|
||||||
delete process.env.HYPERFRAMES_FFMPEG_PATH;
|
delete process.env.HYPERFRAMES_FFMPEG_PATH;
|
||||||
Object.defineProperty(process, "platform", { value: "linux", configurable: true });
|
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
|
* 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.
|
* `@hyperframes/parsers/ff-binaries` subpath, never from a browser bundle.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -92,6 +92,12 @@ function findInCommonDirs(name: FfBinaryName): string | undefined {
|
|||||||
return 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 {
|
function lookupOnSystem(name: FfBinaryName): string | undefined {
|
||||||
if (pathLookupCache.has(name)) return pathLookupCache.get(name);
|
if (pathLookupCache.has(name)) return pathLookupCache.get(name);
|
||||||
let found: string | undefined;
|
let found: string | undefined;
|
||||||
@@ -106,6 +112,7 @@ function lookupOnSystem(name: FfBinaryName): string | undefined {
|
|||||||
} catch {
|
} catch {
|
||||||
found = scanPath(name);
|
found = scanPath(name);
|
||||||
}
|
}
|
||||||
|
found ??= findInProjectLocalBin(name);
|
||||||
found ??= findInCommonDirs(name);
|
found ??= findInCommonDirs(name);
|
||||||
const resolved = found ? resolve(found) : undefined;
|
const resolved = found ? resolve(found) : undefined;
|
||||||
pathLookupCache.set(name, resolved);
|
pathLookupCache.set(name, resolved);
|
||||||
@@ -125,9 +132,10 @@ export interface FindFfBinaryOptions {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an FFmpeg-family binary: env override first, then `which`/`where`,
|
* Resolve an FFmpeg-family binary: env override first, then `which`/`where`,
|
||||||
* then a manual PATH scan (covers Windows PATHEXT), then well-known Unix
|
* then a manual PATH scan (covers Windows PATHEXT), a project-local
|
||||||
* install dirs. System lookups are cached per binary for the process
|
* `.hyperframes/bin`, then well-known Unix install dirs. System lookups are
|
||||||
* lifetime; the env override is re-read on every call.
|
* cached per binary for the process lifetime; the env override is re-read on
|
||||||
|
* every call.
|
||||||
*/
|
*/
|
||||||
export function findFfBinary(
|
export function findFfBinary(
|
||||||
name: FfBinaryName,
|
name: FfBinaryName,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
import { existsSync, writeFileSync, mkdirSync } from "node:fs";
|
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 SAMPLE_RATE = 4000;
|
||||||
const PEAK_COUNT = 4000;
|
const PEAK_COUNT = 4000;
|
||||||
@@ -28,16 +29,10 @@ function computePeaks(floats: Float32Array, count: number): number[] {
|
|||||||
return peaks.map((p) => p / maxPeak);
|
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[]> {
|
export function decodeAudioPeaks(audioPath: string): Promise<number[]> {
|
||||||
return new Promise((resolvePromise, reject) => {
|
return new Promise((resolvePromise, reject) => {
|
||||||
const proc = spawn(
|
const proc = spawn(
|
||||||
ffmpegBinary(),
|
findFfBinary("ffmpeg") ?? "ffmpeg",
|
||||||
[
|
[
|
||||||
"-i",
|
"-i",
|
||||||
audioPath,
|
audioPath,
|
||||||
|
|||||||
Reference in New Issue
Block a user