feat(tools): 2.0 phase 5 wave 3a - video depth (22 tools) (#221)

This commit is contained in:
SnapOtter
2026-06-13 10:19:00 +08:00
parent 2f39e38162
commit 5f98b48593
109 changed files with 12086 additions and 838 deletions
+18 -7
View File
@@ -6,6 +6,7 @@ export interface MediaStreamInfo {
codec: string;
width?: number;
height?: number;
sampleRate?: number;
}
export interface MediaInfo {
@@ -70,7 +71,13 @@ export async function probeMedia(filePath: string, opts: ProbeOptions = {}): Pro
});
const parsed = JSON.parse(stdout) as {
format?: { format_name?: string; duration?: string; bit_rate?: string };
streams?: Array<{ codec_type?: string; codec_name?: string; width?: number; height?: number }>;
streams?: Array<{
codec_type?: string;
codec_name?: string;
width?: number;
height?: number;
sample_rate?: string;
}>;
};
const duration = parsed.format?.duration ? Number(parsed.format.duration) : null;
const bitRate = parsed.format?.bit_rate ? Number(parsed.format.bit_rate) : null;
@@ -78,11 +85,15 @@ export async function probeMedia(filePath: string, opts: ProbeOptions = {}): Pro
container: parsed.format?.format_name ?? "unknown",
durationS: Number.isFinite(duration as number) ? (duration as number) : null,
bitrateKbps: Number.isFinite(bitRate as number) ? Math.round((bitRate as number) / 1000) : null,
streams: (parsed.streams ?? []).map((s) => ({
type: s.codec_type === "video" ? "video" : s.codec_type === "audio" ? "audio" : "other",
codec: s.codec_name ?? "unknown",
width: s.width,
height: s.height,
})),
streams: (parsed.streams ?? []).map((s) => {
const sr = s.sample_rate ? Number(s.sample_rate) : undefined;
return {
type: s.codec_type === "video" ? "video" : s.codec_type === "audio" ? "audio" : "other",
codec: s.codec_name ?? "unknown",
width: s.width,
height: s.height,
sampleRate: Number.isFinite(sr) && (sr as number) > 0 ? sr : undefined,
} as MediaStreamInfo;
}),
};
}
+31
View File
@@ -0,0 +1,31 @@
import { existsSync } from "node:fs";
export interface FontRef {
file: string;
family: string;
}
const CANDIDATES: FontRef[] = [
// Debian/Ubuntu container (fonts-dejavu-core, present since wave 2)
{ file: "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", family: "DejaVu Sans" },
// macOS development hosts
{ file: "/System/Library/Fonts/Supplemental/Arial.ttf", family: "Arial" },
{ file: "/System/Library/Fonts/Helvetica.ttc", family: "Helvetica" },
];
/**
* Static ffmpeg builds carry no fontconfig database, so drawtext/libass
* filters need an explicit font file. Env overrides first, then known
* platform paths. Returns null when nothing exists (callers raise a clear
* tool error instead of a cryptic ffmpeg one).
*/
export function resolveFontFile(): FontRef | null {
const file = process.env.SNAPOTTER_FONT_FILE;
if (file) {
return { file, family: process.env.SNAPOTTER_FONT_FAMILY ?? "Sans" };
}
for (const c of CANDIDATES) {
if (existsSync(c.file)) return c;
}
return null;
}
+1
View File
@@ -2,4 +2,5 @@ export { ffmpegAvailable, resolveFfmpeg, resolveFfprobe } from "./binaries.js";
export { type EncoderTarget, resolveEncoder } from "./encoders.js";
export { type RunFfmpegOptions, runFfmpeg } from "./ffmpeg.js";
export { type MediaInfo, type MediaStreamInfo, type ProbeOptions, probeMedia } from "./ffprobe.js";
export { type FontRef, resolveFontFile } from "./fonts.js";
export { type FfmpegProgress, parseProgressBlock } from "./progress.js";