Fix video progress: parse each chunk, -progress pipe:2, out_time support

- Parse time= from each stderr chunk (not accumulated buffer)
- Add -progress pipe:2 flag for frequent progress output
- Support both time= and out_time= formats
- Progress only increases (no jumps back)
This commit is contained in:
treamz 2026-03-22 00:22:48 +03:00
parent 6aca7e9e47
commit 3353253cef

View File

@ -33,24 +33,26 @@ const jobs = new Map();
function runFFmpeg(args, jobId) { function runFFmpeg(args, jobId) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const proc = spawn('ffmpeg', args, { timeout: 600000 }); // 10 min timeout const proc = spawn('ffmpeg', args, { timeout: 600000 }); // 10 min timeout
let stderr = ''; let stderrBuf = '';
proc.stderr.on('data', (d) => { proc.stderr.on('data', (d) => {
stderr += d.toString(); const chunk = d.toString();
// Parse progress stderrBuf += chunk;
const timeMatch = stderr.match(/time=(\d{2}):(\d{2}):(\d{2})/); // Parse progress — look for time= or out_time= in chunk
const timeMatch = chunk.match(/(?:out_time|time)=\s*(\d{2}):(\d{2}):(\d{2})[\.\d]*/);
if (timeMatch && jobs.has(jobId)) { if (timeMatch && jobs.has(jobId)) {
const secs = parseInt(timeMatch[1]) * 3600 + parseInt(timeMatch[2]) * 60 + parseInt(timeMatch[3]); const secs = parseInt(timeMatch[1]) * 3600 + parseInt(timeMatch[2]) * 60 + parseInt(timeMatch[3]);
const job = jobs.get(jobId); const job = jobs.get(jobId);
if (job.duration > 0) { if (job.duration > 0) {
job.progress = Math.min(99, Math.round((secs / job.duration) * 100)); const pct = Math.min(99, Math.round((secs / job.duration) * 100));
if (pct > job.progress) job.progress = pct; // only increase
} }
} }
}); });
proc.on('close', (code) => { proc.on('close', (code) => {
if (code === 0) resolve(); if (code === 0) resolve();
else reject(new Error(`ffmpeg exited with code ${code}: ${stderr.slice(-500)}`)); else reject(new Error(`ffmpeg exited with code ${code}: ${stderrBuf.slice(-500)}`));
}); });
proc.on('error', reject); proc.on('error', reject);
@ -144,7 +146,7 @@ router.post('/convert', express.json(), async (req, res) => {
job.outputPath = outFile; job.outputPath = outFile;
try { try {
let args = ['-y', '-i', job.inputPath]; let args = ['-y', '-progress', 'pipe:2', '-i', job.inputPath];
// Time range (for GIF or trim) // Time range (for GIF or trim)
if (startTime !== undefined && startTime > 0) args.push('-ss', String(startTime)); if (startTime !== undefined && startTime > 0) args.push('-ss', String(startTime));