import { spawn } from 'node:child_process'; /** * Check if FFmpeg is available */ export async function checkFFmpeg(): Promise { return new Promise((resolve) => { const proc = spawn('ffmpeg', ['-version']); proc.on('error', () => resolve(false)); proc.on('close', (code) => resolve(code === 0)); }); } /** * Check if MP4Box is available */ export async function checkMP4Box(): Promise { return new Promise((resolve) => { const proc = spawn('MP4Box', ['-version']); proc.on('error', () => resolve(false)); proc.on('close', (code) => resolve(code === 0)); }); } /** * Check if NVENC is available */ export async function checkNvenc(): Promise { return new Promise((resolve) => { const proc = spawn('ffmpeg', ['-hide_banner', '-encoders']); let output = ''; proc.stdout.on('data', (data) => { output += data.toString(); }); proc.on('error', () => resolve(false)); proc.on('close', (code) => { if (code !== 0) { resolve(false); } else { resolve(output.includes('h264_nvenc') || output.includes('hevc_nvenc')); } }); }); } /** * Execute FFmpeg command with progress tracking */ export async function execFFmpeg( args: string[], onProgress?: (percent: number) => void, duration?: number ): Promise { return new Promise((resolve, reject) => { const proc = spawn('ffmpeg', args); let stderrData = ''; proc.stderr.on('data', (data) => { const text = data.toString(); stderrData += text; if (onProgress && duration) { // Parse time from FFmpeg output: time=00:01:23.45 const timeMatch = text.match(/time=(\d{2}):(\d{2}):(\d{2}\.\d{2})/); if (timeMatch) { const hours = parseInt(timeMatch[1]); const minutes = parseInt(timeMatch[2]); const seconds = parseFloat(timeMatch[3]); const currentTime = hours * 3600 + minutes * 60 + seconds; const percent = Math.min(100, (currentTime / duration) * 100); onProgress(percent); } } }); proc.on('error', (err) => { reject(new Error(`FFmpeg error: ${err.message}`)); }); proc.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(`FFmpeg failed with exit code ${code}\n${stderrData}`)); } }); }); } /** * Execute MP4Box command */ export async function execMP4Box(args: string[]): Promise { return new Promise((resolve, reject) => { const proc = spawn('MP4Box', args); let stdoutData = ''; let stderrData = ''; proc.stdout.on('data', (data) => { stdoutData += data.toString(); }); proc.stderr.on('data', (data) => { stderrData += data.toString(); }); proc.on('error', (err) => { reject(new Error(`MP4Box error: ${err.message}`)); }); proc.on('close', (code) => { if (code === 0) { resolve(); } else { const output = stderrData || stdoutData; reject(new Error(`MP4Box failed with exit code ${code}\n${output}`)); } }); }); }