Allow disabling merge metadata preservation

https://github.com/mifi/lossless-cut/issues/480#issuecomment-743195727
Also make it more clear that chapters from meta is slow
pull/554/head
Mikael Finstad 5 years ago
parent 62c49659f6
commit 3cedc453e3

@ -24,6 +24,7 @@ const defaults = {
hideNotifications: undefined,
autoLoadTimecode: false,
segmentsToChapters: false,
preserveMetadataOnMerge: false,
},
};

@ -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);
@ -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;
@ -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'));
@ -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')), []);
@ -1035,6 +1039,7 @@ const App = memo(() => {
onProgress: setCutProgress,
chapterNames,
autoDeleteMergedSegments,
preserveMetadataOnMerge,
});
}
@ -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;
@ -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}

@ -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();
@ -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() {
@ -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} />
@ -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>

@ -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 });
@ -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] : []),
@ -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),
@ -599,7 +599,7 @@ 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);
@ -607,7 +607,7 @@ export async function autoMergeSegments({ customOutDir, sourceFile, isCustomForm
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 });
}

Loading…
Cancel
Save