import { parseMedia } from "@remotion/media-parser"; import { nodeReader } from "@remotion/media-parser/node"; import { execSync } from "node:child_process"; export async function measureAudioDuration( filePath: string ): Promise { // Primary: parse audio container headers via @remotion/media-parser (fast, no full decode) try { const result = await parseMedia({ src: filePath, reader: nodeReader, fields: { durationInSeconds: true }, }); if (result.durationInSeconds != null && result.durationInSeconds > 0) { return result.durationInSeconds; } } catch { // Fall through to ffprobe } // Fallback: ffprobe (available on system) try { const output = execSync( `ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${filePath}"`, { encoding: "utf-8", timeout: 5000 } ).trim(); const dur = parseFloat(output); if (Number.isFinite(dur) && dur > 0) return dur; } catch { // Fall through to bitrate estimate } // Last resort: estimate from file size (mp3 @ 128kbps) const { statSync } = await import("node:fs"); const size = statSync(filePath).size; return (size * 8) / 128000; }