sync
This commit is contained in:
125
src/utils/system.ts
Normal file
125
src/utils/system.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
/**
|
||||
* Check if FFmpeg is available
|
||||
*/
|
||||
export async function checkFFmpeg(): Promise<boolean> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user