mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(tools): 2.0 phase 5 wave 3a - video depth (22 tools) (#221)
This commit is contained in:
@@ -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;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user