refactor(media): trim unused ffmpeg wrapper options
Binary paths, the ffmpeg version, the probe's container name and a configurable stall timeout had no caller; detection is cached per process, so every helper looks the binaries up itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
parent
44702e3770
commit
f14d91d24a
@ -60,7 +60,6 @@ describe("probe parsing", () => {
|
||||
);
|
||||
expect(probe).toEqual({
|
||||
durationMs: 12_345,
|
||||
formatName: "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
video: {
|
||||
codec: "h264",
|
||||
width: 1080,
|
||||
|
||||
@ -13,12 +13,10 @@ import { delimiter, join } from "node:path";
|
||||
export interface FfmpegInfo {
|
||||
ffmpegPath: string;
|
||||
ffprobePath: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface MediaProbe {
|
||||
durationMs: number;
|
||||
formatName: string;
|
||||
/** Width and height as displayed: swapped when the stream is rotated by 90 or 270 degrees. */
|
||||
video?: {
|
||||
codec: string;
|
||||
@ -50,8 +48,6 @@ export interface RunFfmpegOptions {
|
||||
maxOutputBytes?: number;
|
||||
/** Files removed when the run fails or is aborted. */
|
||||
outputs?: readonly string[];
|
||||
/** Kills ffmpeg when it reports no progress for this long. */
|
||||
stallTimeoutMs?: number;
|
||||
}
|
||||
|
||||
export class FfmpegError extends Error {
|
||||
@ -65,7 +61,8 @@ export class FfmpegError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_STALL_TIMEOUT_MS = 2 * 60_000;
|
||||
/** ffmpeg reports progress twice a second while it works; silence this long means a hang. */
|
||||
const STALL_TIMEOUT_MS = 2 * 60_000;
|
||||
const PROBE_TIMEOUT_MS = 60_000;
|
||||
const STDERR_TAIL_BYTES = 8 * 1024;
|
||||
const KILL_GRACE_MS = 5_000;
|
||||
@ -101,9 +98,7 @@ async function locateFfmpeg(): Promise<FfmpegInfo | null> {
|
||||
capture(ffmpegPath, ["-hide_banner", "-version"], { timeoutMs: 10_000 }),
|
||||
capture(ffprobePath, ["-hide_banner", "-version"], { timeoutMs: 10_000 }),
|
||||
]);
|
||||
if (ffmpeg.code !== 0 || ffprobe.code !== 0) return null;
|
||||
const version = /version (\S+)/.exec(ffmpeg.stdout)?.[1] ?? "unknown";
|
||||
return { ffmpegPath, ffprobePath, version };
|
||||
return ffmpeg.code === 0 && ffprobe.code === 0 ? { ffmpegPath, ffprobePath } : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@ -129,7 +124,7 @@ async function findExecutable(name: string): Promise<string | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function requireFfmpeg(): Promise<FfmpegInfo> {
|
||||
async function requireFfmpeg(): Promise<FfmpegInfo> {
|
||||
const info = await detectFfmpeg();
|
||||
if (!info) throw new Error("ffmpeg and ffprobe are not installed on this server.");
|
||||
return info;
|
||||
@ -223,7 +218,7 @@ interface ProbeStream {
|
||||
/** Parses `ffprobe -print_format json -show_format -show_streams` output. */
|
||||
export function parseProbeOutput(json: string): MediaProbe {
|
||||
const data = JSON.parse(json) as {
|
||||
format?: { duration?: string; format_name?: string };
|
||||
format?: { duration?: string };
|
||||
streams?: ProbeStream[];
|
||||
};
|
||||
const streams = data.streams ?? [];
|
||||
@ -237,7 +232,6 @@ export function parseProbeOutput(json: string): MediaProbe {
|
||||
Math.max(0, ...streams.map((stream) => positive(Number(stream.duration)) ?? 0));
|
||||
return {
|
||||
durationMs: Math.round(seconds * 1000),
|
||||
formatName: data.format?.format_name ?? "",
|
||||
...(video?.width && video.height
|
||||
? { video: probeVideo(video, video.width, video.height) }
|
||||
: {}),
|
||||
@ -385,12 +379,11 @@ function spawnFfmpeg(
|
||||
setTimeout(() => finish(error), KILL_GRACE_MS).unref?.();
|
||||
};
|
||||
const abort = () => kill(signal?.reason ?? new Error("aborted"));
|
||||
const stallTimeoutMs = options.stallTimeoutMs ?? DEFAULT_STALL_TIMEOUT_MS;
|
||||
const armStallTimer = () => {
|
||||
clearTimeout(stallTimer);
|
||||
stallTimer = setTimeout(
|
||||
() => kill(new FfmpegError("ffmpeg stopped making progress.", null, tail(stderr))),
|
||||
stallTimeoutMs,
|
||||
STALL_TIMEOUT_MS,
|
||||
);
|
||||
stallTimer.unref?.();
|
||||
};
|
||||
|
||||
@ -13,7 +13,6 @@ function probe(
|
||||
const { durationMs = 10_000, hasAudio = true, ...video } = overrides;
|
||||
return {
|
||||
durationMs,
|
||||
formatName: "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
video: {
|
||||
codec: "h264",
|
||||
width: 1920,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user