move merge options into separate dialog

also add option to clear batch

closes #1063
pull/901/head
Mikael Finstad 2022-03-09 22:43:35 +08:00
rodzic 6c10630996
commit 90126d92b1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 25AB36E3E81CBC26
2 zmienionych plików z 83 dodań i 75 usunięć

Wyświetl plik

@ -703,43 +703,6 @@ const App = memo(() => {
return { cancel: false, newCustomOutDir };
}, [customOutDir, setCustomOutDir]);
const userConcatFiles = useCallback(async ({ paths, includeAllStreams, streams, fileFormat: outFormat, isCustomFormatSelected: isCustomFormatSelected2 }) => {
if (workingRef.current) return;
try {
setConcatDialogVisible(false);
setWorking(i18n.t('Merging'));
const firstPath = paths[0];
const { newCustomOutDir, cancel } = await ensureAccessibleDirectories({ inputPath: firstPath });
if (cancel) return;
const ext = getOutFileExtension({ isCustomFormatSelected: isCustomFormatSelected2, outFormat, filePath: firstPath });
const outPath = getOutPath({ customOutDir: newCustomOutDir, filePath: firstPath, nameSuffix: `merged${ext}` });
const outDir = getOutDir(customOutDir, firstPath);
let chaptersFromSegments;
if (segmentsToChapters) {
const chapterNames = paths.map((path) => parsePath(path).name);
chaptersFromSegments = await createChaptersFromSegments({ segmentPaths: paths, chapterNames });
}
// console.log('merge', paths);
const metadataFromPath = paths[0];
await concatFiles({ paths, outPath, outDir, outFormat, metadataFromPath, includeAllStreams, streams, ffmpegExperimental, onProgress: setCutProgress, preserveMovData, movFastStart, preserveMetadataOnMerge, chapters: chaptersFromSegments, appendFfmpegCommandLog });
openDirToast({ icon: 'success', dirPath: outDir, text: i18n.t('Files merged!') });
} catch (err) {
if (isOutOfSpaceError(err)) {
showDiskFull();
return;
}
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();
}
}, [setWorking, ensureAccessibleDirectories, customOutDir, segmentsToChapters, concatFiles, ffmpegExperimental, preserveMovData, movFastStart, preserveMetadataOnMerge]);
const concatCurrentBatch = useCallback(() => {
if (batchFiles.length < 2) {
errorToast(i18n.t('Please open at least 2 files to merge, then try again'));
@ -1109,6 +1072,44 @@ const App = memo(() => {
});
}, []);
const userConcatFiles = useCallback(async ({ paths, includeAllStreams, streams, fileFormat: outFormat, isCustomFormatSelected: isCustomFormatSelected2, clearBatchFilesAfterConcat }) => {
if (workingRef.current) return;
try {
setConcatDialogVisible(false);
setWorking(i18n.t('Merging'));
const firstPath = paths[0];
const { newCustomOutDir, cancel } = await ensureAccessibleDirectories({ inputPath: firstPath });
if (cancel) return;
const ext = getOutFileExtension({ isCustomFormatSelected: isCustomFormatSelected2, outFormat, filePath: firstPath });
const outPath = getOutPath({ customOutDir: newCustomOutDir, filePath: firstPath, nameSuffix: `merged${ext}` });
const outDir = getOutDir(customOutDir, firstPath);
let chaptersFromSegments;
if (segmentsToChapters) {
const chapterNames = paths.map((path) => parsePath(path).name);
chaptersFromSegments = await createChaptersFromSegments({ segmentPaths: paths, chapterNames });
}
// console.log('merge', paths);
const metadataFromPath = paths[0];
await concatFiles({ paths, outPath, outDir, outFormat, metadataFromPath, includeAllStreams, streams, ffmpegExperimental, onProgress: setCutProgress, preserveMovData, movFastStart, preserveMetadataOnMerge, chapters: chaptersFromSegments, appendFfmpegCommandLog });
if (clearBatchFilesAfterConcat) closeBatch();
openDirToast({ icon: 'success', dirPath: outDir, text: i18n.t('Files merged!') });
} catch (err) {
if (isOutOfSpaceError(err)) {
showDiskFull();
return;
}
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();
}
}, [setWorking, ensureAccessibleDirectories, customOutDir, segmentsToChapters, concatFiles, ffmpegExperimental, preserveMovData, movFastStart, preserveMetadataOnMerge, closeBatch]);
const cleanupFilesDialog = useCallback(async () => {
if (!isFileOpened) return;

Wyświetl plik

@ -1,6 +1,6 @@
import React, { memo, useState, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Dialog, Checkbox, Button, Paragraph } from 'evergreen-ui';
import { Dialog, Checkbox, Button, Paragraph, CogIcon } from 'evergreen-ui';
import { AiOutlineMergeCells } from 'react-icons/ai';
import { readFileMeta, getSmarterOutFormat } from '../ffmpeg';
@ -26,6 +26,8 @@ const ConcatDialog = memo(({
const [paths, setPaths] = useState(initialPaths);
const [includeAllStreams, setIncludeAllStreams] = useState(false);
const [fileMeta, setFileMeta] = useState();
const [clearBatchFilesAfterConcat, setClearBatchFilesAfterConcat] = useState(false);
const [settingsVisible, setSettingsVisible] = useState(false);
const { fileFormat, setFileFormat, detectedFileFormat, setDetectedFileFormat, isCustomFormatSelected } = useFileFormatState();
@ -60,53 +62,58 @@ const ConcatDialog = memo(({
const onOutputFormatUserChange = useCallback((newFormat) => setFileFormat(newFormat), [setFileFormat]);
const onConcatClick = useCallback(() => onConcat({ paths, includeAllStreams, streams: fileMeta.streams, fileFormat, isCustomFormatSelected }), [fileFormat, fileMeta, includeAllStreams, isCustomFormatSelected, onConcat, paths]);
const onConcatClick = useCallback(() => onConcat({ paths, includeAllStreams, streams: fileMeta.streams, fileFormat, isCustomFormatSelected, clearBatchFilesAfterConcat }), [clearBatchFilesAfterConcat, fileFormat, fileMeta, includeAllStreams, isCustomFormatSelected, onConcat, paths]);
return (
<Dialog
title={t('Merge/concatenate files')}
isShown={isShown}
onCloseComplete={onHide}
topOffset="3vh"
width="90vw"
footer={(
<>
{fileFormat && detectedFileFormat && <OutputFormatSelect style={{ maxWidth: 150 }} detectedFileFormat={detectedFileFormat} fileFormat={fileFormat} onOutputFormatUserChange={onOutputFormatUserChange} />}
<Button onClick={onHide} style={{ marginLeft: 10 }}>Cancel</Button>
<Button iconBefore={<AiOutlineMergeCells />} isLoading={detectedFileFormat == null} appearance="primary" onClick={onConcatClick}>{t('Merge!')}</Button>
</>
)}
>
<div style={containerStyle}>
<div style={{ whiteSpace: 'pre-wrap', fontSize: 14, marginBottom: 10 }}>
{t('This dialog can be used to concatenate files in series, e.g. one after the other:\n[file1][file2][file3]\nIt can NOT be used for merging tracks in parallell (like adding an audio track to a video).\nMake sure all files are of the exact same codecs & codec parameters (fps, resolution etc).')}
<>
<Dialog
title={t('Merge/concatenate files')}
isShown={isShown}
onCloseComplete={onHide}
topOffset="3vh"
width="90vw"
footer={(
<>
<Button iconBefore={CogIcon} onClick={() => setSettingsVisible(true)}>{t('Options')}</Button>
{fileFormat && detectedFileFormat && <OutputFormatSelect style={{ maxWidth: 180 }} detectedFileFormat={detectedFileFormat} fileFormat={fileFormat} onOutputFormatUserChange={onOutputFormatUserChange} />}
<Button onClick={onHide} style={{ marginLeft: 10 }}>Cancel</Button>
<Button iconBefore={<AiOutlineMergeCells />} isLoading={detectedFileFormat == null} appearance="primary" onClick={onConcatClick}>{t('Merge!')}</Button>
</>
)}
>
<div style={containerStyle}>
<div style={{ whiteSpace: 'pre-wrap', fontSize: 14, marginBottom: 10 }}>
{t('This dialog can be used to concatenate files in series, e.g. one after the other:\n[file1][file2][file3]\nIt can NOT be used for merging tracks in parallell (like adding an audio track to a video).\nMake sure all files are of the exact same codecs & codec parameters (fps, resolution etc).')}
</div>
<div>
{paths.map((path, index) => (
<div key={path} style={rowStyle} title={path}>
{index + 1}
{'. '}
<span style={{ color: 'rgba(0,0,0,0.7)' }}>{basename(path)}</span>
</div>
))}
</div>
</div>
</Dialog>
<div style={{ marginTop: 10, marginBottom: 10 }}>
<Checkbox checked={includeAllStreams} onChange={(e) => setIncludeAllStreams(e.target.checked)} label={`${t('Include all tracks?')} ${t('If this is checked, all audio/video/subtitle/data tracks will be included. This may not always work for all file types. If not checked, only default streams will be included.')}`} />
<Dialog isShown={settingsVisible} onCloseComplete={() => setSettingsVisible(false)} title={t('Options')} hasCancel={false} confirmLabel={t('Close')}>
<Checkbox checked={includeAllStreams} onChange={(e) => setIncludeAllStreams(e.target.checked)} label={`${t('Include all tracks?')} ${t('If this is checked, all audio/video/subtitle/data tracks will be included. This may not always work for all file types. If not checked, only default streams will be included.')}`} />
<Checkbox checked={preserveMetadataOnMerge} onChange={(e) => setPreserveMetadataOnMerge(e.target.checked)} label={t('Preserve original metadata when merging? (slow)')} />
<Checkbox checked={preserveMetadataOnMerge} onChange={(e) => setPreserveMetadataOnMerge(e.target.checked)} label={t('Preserve original metadata when merging? (slow)')} />
<Checkbox checked={preserveMovData} onChange={(e) => setPreserveMovData(e.target.checked)} label={t('Preserve all MP4/MOV metadata?')} />
<Checkbox checked={preserveMovData} onChange={(e) => setPreserveMovData(e.target.checked)} label={t('Preserve all MP4/MOV metadata?')} />
<Checkbox checked={segmentsToChapters} onChange={(e) => setSegmentsToChapters(e.target.checked)} label={t('Create chapters from merged segments? (slow)')} />
<Checkbox checked={segmentsToChapters} onChange={(e) => setSegmentsToChapters(e.target.checked)} label={t('Create chapters from merged segments? (slow)')} />
<Checkbox checked={alwaysConcatMultipleFiles} onChange={(e) => setAlwaysConcatMultipleFiles(e.target.checked)} label={t('Always open this dialog when opening multiple files')} />
<Checkbox checked={alwaysConcatMultipleFiles} onChange={(e) => setAlwaysConcatMultipleFiles(e.target.checked)} label={t('Always open this dialog when opening multiple files')} />
<Paragraph>{t('Note that also other settings from the normal export dialog apply to this merge function. For more information about all options, see the export dialog.')}</Paragraph>
</div>
<Checkbox checked={clearBatchFilesAfterConcat} onChange={(e) => setClearBatchFilesAfterConcat(e.target.checked)} label={t('Clear batch file list after merge')} />
<div>
{paths.map((path, index) => (
<div style={rowStyle} title={path}>
{index + 1}
{'. '}
<span style={{ color: 'rgba(0,0,0,0.7)' }}>{basename(path)}</span>
</div>
))}
</div>
</div>
</Dialog>
<Paragraph>{t('Note that also other settings from the normal export dialog apply to this merge function. For more information about all options, see the export dialog.')}</Paragraph>
</Dialog>
</>
);
});