diff --git a/src/App.jsx b/src/App.jsx index 6db76ce8..cbf0c782 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -621,12 +621,14 @@ const App = memo(() => { const outPath = getOutPath(newCustomOutDir, firstPath, `merged${ext}`); // console.log('merge', paths); - await ffmpegMergeFiles({ paths, outPath, allStreams }); + await ffmpegMergeFiles({ paths, outPath, allStreams, onProgress: setCutProgress }); + openDirToast({ icon: 'success', dirPath: outputDir, text: i18n.t('Files merged!') }); } catch (err) { errorToast(i18n.t('Failed to merge files. Make sure they are all of the exact same codecs')); console.error('Failed to merge files', err); } finally { setWorking(); + setCutProgress(); } }, [assureOutDirAccess]); @@ -997,7 +999,8 @@ const App = memo(() => { }); if (outFiles.length > 1 && autoMerge) { - setCutProgress(0); // TODO implement progress + setCutProgress(0); + setWorking(i18n.t('Merging')); await autoMergeSegments({ customOutDir, @@ -1005,6 +1008,7 @@ const App = memo(() => { outFormat: fileFormat, isCustomFormatSelected, segmentPaths: outFiles, + onProgress: setCutProgress, }); } @@ -1035,6 +1039,7 @@ const App = memo(() => { showFfmpegFail(err); } finally { setWorking(); + setCutProgress(); } }, [ effectiveRotation, outSegments, handleCutFailed, isRotationSet, diff --git a/src/ffmpeg.js b/src/ffmpeg.js index 0fa3686c..06d6b52b 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -422,9 +422,12 @@ export async function html5ifyDummy(filePath, outPath, onProgress) { await transferTimestamps(filePath, outPath); } -export async function mergeFiles({ paths, outPath, allStreams, outFormat }) { +export async function mergeFiles({ paths, outPath, allStreams, outFormat, onProgress = () => {} }) { console.log('Merging files', { paths }, 'to', outPath); + const durations = await pMap(paths, getDuration, { concurrency: 1 }); + const totalDuration = sum(durations); + // Keep this similar to cut() const ffmpegArgs = [ '-hide_banner', @@ -456,18 +459,20 @@ export async function mergeFiles({ paths, outPath, allStreams, outFormat }) { const ffmpegPath = getFfmpegPath(); const process = execa(ffmpegPath, ffmpegArgs); + handleProgress(process, totalDuration, onProgress); + stringToStream(concatTxt).pipe(process.stdin); const result = await process; console.log(result.stdout); } -export async function autoMergeSegments({ customOutDir, sourceFile, isCustomFormatSelected, outFormat, segmentPaths }) { +export async function autoMergeSegments({ customOutDir, sourceFile, isCustomFormatSelected, outFormat, segmentPaths, onProgress }) { const ext = getOutFileExtension({ isCustomFormatSelected, outFormat, filePath: sourceFile }); const fileName = `cut-merged-${new Date().getTime()}${ext}`; const outPath = getOutPath(customOutDir, sourceFile, fileName); - await mergeFiles({ paths: segmentPaths, outPath, outFormat, allStreams: true }); + await mergeFiles({ paths: segmentPaths, outPath, outFormat, allStreams: true, onProgress }); await pMap(segmentPaths, path => fs.unlink(path), { concurrency: 5 }); }