Skip to content

Commit

Permalink
Allow disabling merge metadata preservation
Browse files Browse the repository at this point in the history
#480 (comment)
Also make it more clear that chapters from meta is slow
  • Loading branch information
mifi committed Dec 11, 2020
1 parent 62c4965 commit 3cedc45
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 17 deletions.
1 change: 1 addition & 0 deletions public/configStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const defaults = {
hideNotifications: undefined,
autoLoadTimecode: false,
segmentsToChapters: false,
preserveMetadataOnMerge: false,
},
};

Expand Down
13 changes: 9 additions & 4 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ const App = memo(() => {
useEffect(() => safeSetConfig('exportConfirmEnabled', exportConfirmEnabled), [exportConfirmEnabled]);
const [segmentsToChapters, setSegmentsToChapters] = useState(configStore.get('segmentsToChapters'));
useEffect(() => safeSetConfig('segmentsToChapters', segmentsToChapters), [segmentsToChapters]);
const [preserveMetadataOnMerge, setPreserveMetadataOnMerge] = useState(configStore.get('preserveMetadataOnMerge'));
useEffect(() => safeSetConfig('preserveMetadataOnMerge', preserveMetadataOnMerge), [preserveMetadataOnMerge]);

useEffect(() => {
i18n.changeLanguage(language || fallbackLng).catch(console.error);
Expand Down Expand Up @@ -242,6 +244,8 @@ const App = memo(() => {

const toggleSegmentsToChapters = useCallback(() => setSegmentsToChapters((v) => !v), []);

const togglePreserveMetadataOnMerge = useCallback(() => setPreserveMetadataOnMerge((v) => !v), []);

const toggleKeyframesEnabled = useCallback(() => {
setKeyframesEnabled((old) => {
const enabled = !old;
Expand Down Expand Up @@ -630,7 +634,7 @@ const App = memo(() => {
const outPath = getOutPath(newCustomOutDir, firstPath, `merged${ext}`);

// console.log('merge', paths);
await ffmpegMergeFiles({ paths, outPath, allStreams, ffmpegExperimental, onProgress: setCutProgress, preserveMovData });
await ffmpegMergeFiles({ paths, outPath, allStreams, ffmpegExperimental, onProgress: setCutProgress, preserveMovData, preserveMetadataOnMerge });
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'));
Expand All @@ -639,7 +643,7 @@ const App = memo(() => {
setWorking();
setCutProgress();
}
}, [assureOutDirAccess, outputDir, ffmpegExperimental, preserveMovData]);
}, [assureOutDirAccess, outputDir, ffmpegExperimental, preserveMovData, preserveMetadataOnMerge]);

const toggleCaptureFormat = useCallback(() => setCaptureFormat(f => (f === 'png' ? 'jpeg' : 'png')), []);

Expand Down Expand Up @@ -1035,6 +1039,7 @@ const App = memo(() => {
onProgress: setCutProgress,
chapterNames,
autoDeleteMergedSegments,
preserveMetadataOnMerge,
});
}

Expand Down Expand Up @@ -1065,7 +1070,7 @@ const App = memo(() => {
setWorking();
setCutProgress();
}
}, [autoMerge, copyFileStreams, customOutDir, duration, effectiveRotation, exportExtraStreams, ffmpegExperimental, fileFormat, fileFormatData, filePath, handleCutFailed, isCustomFormatSelected, isRotationSet, keyframeCut, mainStreams, nonCopiedExtraStreams, outSegments, outputDir, shortestFlag, working, preserveMovData, avoidNegativeTs, numStreamsToCopy, hideAllNotifications, currentSegIndexSafe, invertCutSegments, autoDeleteMergedSegments, segmentsToChapters, customTagsByFile, customTagsByStreamId]);
}, [autoMerge, copyFileStreams, customOutDir, duration, effectiveRotation, exportExtraStreams, ffmpegExperimental, fileFormat, fileFormatData, filePath, handleCutFailed, isCustomFormatSelected, isRotationSet, keyframeCut, mainStreams, nonCopiedExtraStreams, outSegments, outputDir, shortestFlag, working, preserveMovData, avoidNegativeTs, numStreamsToCopy, hideAllNotifications, currentSegIndexSafe, invertCutSegments, autoDeleteMergedSegments, segmentsToChapters, customTagsByFile, customTagsByStreamId, preserveMetadataOnMerge]);

const onExportPress = useCallback(async () => {
if (working || !filePath) return;
Expand Down Expand Up @@ -2215,7 +2220,7 @@ const App = memo(() => {
</div>
</motion.div>

<ExportConfirm autoMerge={autoMerge} toggleAutoMerge={toggleAutoMerge} areWeCutting={areWeCutting} outSegments={outSegments} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} keyframeCut={keyframeCut} toggleKeyframeCut={toggleKeyframeCut} renderOutFmt={renderOutFmt} preserveMovData={preserveMovData} togglePreserveMovData={togglePreserveMovData} avoidNegativeTs={avoidNegativeTs} setAvoidNegativeTs={setAvoidNegativeTs} changeOutDir={changeOutDir} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} setStreamsSelectorShown={setStreamsSelectorShown} currentSegIndex={currentSegIndexSafe} invertCutSegments={invertCutSegments} exportConfirmEnabled={exportConfirmEnabled} toggleExportConfirmEnabled={toggleExportConfirmEnabled} segmentsToChapters={segmentsToChapters} toggleSegmentsToChapters={toggleSegmentsToChapters} outFormat={fileFormat} />
<ExportConfirm autoMerge={autoMerge} toggleAutoMerge={toggleAutoMerge} areWeCutting={areWeCutting} outSegments={outSegments} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} keyframeCut={keyframeCut} toggleKeyframeCut={toggleKeyframeCut} renderOutFmt={renderOutFmt} preserveMovData={preserveMovData} togglePreserveMovData={togglePreserveMovData} avoidNegativeTs={avoidNegativeTs} setAvoidNegativeTs={setAvoidNegativeTs} changeOutDir={changeOutDir} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} setStreamsSelectorShown={setStreamsSelectorShown} currentSegIndex={currentSegIndexSafe} invertCutSegments={invertCutSegments} exportConfirmEnabled={exportConfirmEnabled} toggleExportConfirmEnabled={toggleExportConfirmEnabled} segmentsToChapters={segmentsToChapters} toggleSegmentsToChapters={toggleSegmentsToChapters} outFormat={fileFormat} preserveMetadataOnMerge={preserveMetadataOnMerge} togglePreserveMetadataOnMerge={togglePreserveMetadataOnMerge} />

<HelpSheet
visible={helpVisible}
Expand Down
23 changes: 16 additions & 7 deletions src/ExportConfirm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const ExportConfirm = memo(({
toggleAutoMerge, renderOutFmt, preserveMovData, togglePreserveMovData, avoidNegativeTs, setAvoidNegativeTs,
changeOutDir, outputDir, numStreamsTotal, numStreamsToCopy, setStreamsSelectorShown, currentSegIndex, invertCutSegments,
exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, toggleSegmentsToChapters, outFormat,
preserveMetadataOnMerge, togglePreserveMetadataOnMerge,
}) => {
const { t } = useTranslation();

Expand All @@ -64,7 +65,11 @@ const ExportConfirm = memo(({
}

function onSegmentsToChaptersHelpPress() {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('When merging, do you want to create chapters in the merged file, according to the cut segments?') });
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('When merging, do you want to create chapters in the merged file, according to the cut segments? NOTE: This may dramatically increase processing time') });
}

function onPreserveMetadataOnMergeHelpPress() {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('When merging, do you want to preserve metadata from your original file? NOTE: This may dramatically increase processing time') });
}

function onAvoidNegativeTsHelpPress() {
Expand Down Expand Up @@ -99,7 +104,7 @@ const ExportConfirm = memo(({
<div style={boxStyle}>
<h2 style={{ marginTop: 0 }}>{t('Export options')}</h2>
<ul>
{outSegments.length > 1 && <li>{t('Merge {{segments}} cut segments to one file?', { segments: outSegments.length })} <MergeExportButton autoMerge={autoMerge} outSegments={outSegments} toggleAutoMerge={toggleAutoMerge} /></li>}
{outSegments.length >= 2 && <li>{t('Merge {{segments}} cut segments to one file?', { segments: outSegments.length })} <MergeExportButton autoMerge={autoMerge} outSegments={outSegments} toggleAutoMerge={toggleAutoMerge} /></li>}
<li>
{t('Output container format:')} {renderOutFmt({ height: 20, maxWidth: 150 })}
<HelpIcon onClick={onOutFmtHelpPress} />
Expand All @@ -115,14 +120,18 @@ const ExportConfirm = memo(({

<h3>{t('Advanced options')}</h3>

<ul>
{autoMerge && (
{autoMerge && outSegments.length >= 2 && (
<ul>
<li>
{t('Create chapters from segments?')} <Button height={20} onClick={toggleSegmentsToChapters}>{segmentsToChapters ? t('Yes') : t('No')}</Button>
{t('Create chapters from merged segments? (slow)')} <Button height={20} onClick={toggleSegmentsToChapters}>{segmentsToChapters ? t('Yes') : t('No')}</Button>
<HelpIcon onClick={onSegmentsToChaptersHelpPress} />
</li>
)}
</ul>
<li>
{t('Preserve original metadata when merging? (slow)')} <Button height={20} onClick={togglePreserveMetadataOnMerge}>{preserveMetadataOnMerge ? t('Yes') : t('No')}</Button>
<HelpIcon onClick={onPreserveMetadataOnMergeHelpPress} />
</li>
</ul>
)}

<p>{t('Depending on your specific file, you may have to try different options for best results.')}</p>

Expand Down
12 changes: 6 additions & 6 deletions src/ffmpeg.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ async function writeChaptersFfmetadata(outDir, chapters) {
return path;
}

export async function mergeFiles({ paths, outDir, outPath, allStreams, outFormat, ffmpegExperimental, onProgress = () => {}, preserveMovData, chapters }) {
export async function mergeFiles({ paths, outDir, outPath, allStreams, outFormat, ffmpegExperimental, onProgress = () => {}, preserveMovData, chapters, preserveMetadataOnMerge }) {
console.log('Merging files', { paths }, 'to', outPath);

const durations = await pMap(paths, getDuration, { concurrency: 1 });
Expand All @@ -531,8 +531,8 @@ export async function mergeFiles({ paths, outDir, outPath, allStreams, outFormat
// https://blog.yo1.dog/fix-for-ffmpeg-protocol-not-on-whitelist-error-for-urls/
'-f', 'concat', '-safe', '0', '-protocol_whitelist', 'file,pipe', '-i', '-',

// Use the first file for metadata. Can only do this if allStreams (-map 0) is set, or else ffmpeg might output this input instead of the concat
...(allStreams ? ['-i', paths[0]] : []),
// Add the first file for using its metadata. Can only do this if allStreams (-map 0) is set, or else ffmpeg might output this input instead of the concat
...(preserveMetadataOnMerge && allStreams ? ['-i', paths[0]] : []),

// Chapters?
...(ffmetadataPath ? ['-f', 'ffmetadata', '-i', ffmetadataPath] : []),
Expand All @@ -544,7 +544,7 @@ export async function mergeFiles({ paths, outDir, outPath, allStreams, outFormat
// Use the file index 1 for metadata
// -map_metadata 0 with concat demuxer doesn't seem to preserve metadata when merging.
// Can only do this if allStreams (-map 0) is set
...(allStreams ? ['-map_metadata', '1'] : []),
...(preserveMetadataOnMerge && allStreams ? ['-map_metadata', '1'] : []),

// https://video.stackexchange.com/questions/23741/how-to-prevent-ffmpeg-from-dropping-metadata
...getMovFlags(outFormat, preserveMovData),
Expand Down Expand Up @@ -599,15 +599,15 @@ async function createChaptersFromSegments({ segmentPaths, chapterNames }) {
return undefined;
}

export async function autoMergeSegments({ customOutDir, sourceFile, isCustomFormatSelected, outFormat, segmentPaths, ffmpegExperimental, onProgress, preserveMovData, autoDeleteMergedSegments, chapterNames }) {
export async function autoMergeSegments({ customOutDir, sourceFile, isCustomFormatSelected, outFormat, segmentPaths, ffmpegExperimental, onProgress, preserveMovData, autoDeleteMergedSegments, chapterNames, preserveMetadataOnMerge }) {
const ext = getOutFileExtension({ isCustomFormatSelected, outFormat, filePath: sourceFile });
const fileName = `cut-merged-${new Date().getTime()}${ext}`;
const outPath = getOutPath(customOutDir, sourceFile, fileName);
const outDir = getOutDir(customOutDir, sourceFile);

const chapters = await createChaptersFromSegments({ segmentPaths, chapterNames });

await mergeFiles({ paths: segmentPaths, outDir, outPath, outFormat, allStreams: true, ffmpegExperimental, onProgress, preserveMovData, chapters });
await mergeFiles({ paths: segmentPaths, outDir, outPath, outFormat, allStreams: true, ffmpegExperimental, onProgress, preserveMovData, chapters, preserveMetadataOnMerge });
if (autoDeleteMergedSegments) await pMap(segmentPaths, path => fs.unlink(path), { concurrency: 5 });
}

Expand Down

0 comments on commit 3cedc45

Please sign in to comment.