From 802488436318a468fb22f140d6721afb17074f89 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Sun, 6 Oct 2024 23:39:19 +0200 Subject: [PATCH] allow customising preservation of map_metadata and chapters fixes #2176 see #1027 --- src/main/configStore.ts | 4 +- src/renderer/src/App.tsx | 24 ++++----- src/renderer/src/components/ExportConfirm.tsx | 52 ++++++++++++++++--- .../src/components/MovFastStartButton.tsx | 15 ------ .../src/components/PreserveMovDataButton.tsx | 15 ------ src/renderer/src/contexts.ts | 5 +- src/renderer/src/hooks/useFfmpegOperations.ts | 35 +++++++++---- src/renderer/src/hooks/useUserSettingsRoot.ts | 16 ++++-- types.ts | 6 ++- 9 files changed, 103 insertions(+), 69 deletions(-) delete mode 100644 src/renderer/src/components/MovFastStartButton.tsx delete mode 100644 src/renderer/src/components/PreserveMovDataButton.tsx diff --git a/src/main/configStore.ts b/src/main/configStore.ts index 390bb956..42d97a12 100644 --- a/src/main/configStore.ts +++ b/src/main/configStore.ts @@ -106,6 +106,9 @@ const defaults: Config = { wheelSensitivity: 0.2, language: undefined, ffmpegExperimental: false, + preserveChapters: true, + preserveMetadata: 'default', + preserveMetadataOnMerge: false, preserveMovData: false, movFastStart: true, avoidNegativeTs: 'make_zero', @@ -113,7 +116,6 @@ const defaults: Config = { hideOsNotifications: undefined, autoLoadTimecode: false, segmentsToChapters: false, - preserveMetadataOnMerge: false, simpleMode: true, outSegTemplate: undefined, mergedFileTemplate: undefined, diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 735073e7..67106520 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -171,7 +171,7 @@ function App() { const allUserSettings = useUserSettingsRoot(); const { - captureFormat, setCaptureFormat, customOutDir, setCustomOutDir, keyframeCut, setKeyframeCut, preserveMovData, setPreserveMovData, movFastStart, setMovFastStart, avoidNegativeTs, autoMerge, timecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, askBeforeClose, enableAskForImportChapters, enableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, wheelSensitivity, invertTimelineScroll, language, ffmpegExperimental, hideNotifications, hideOsNotifications, autoLoadTimecode, autoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, simpleMode, setSimpleMode, outSegTemplate, setOutSegTemplate, mergedFileTemplate, setMergedFileTemplate, keyboardSeekAccFactor, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, enableAutoHtml5ify, segmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, customFfPath, storeProjectInWorkingDir, setStoreProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, mouseWheelFrameSeekModifierKey, mouseWheelKeyframeSeekModifierKey, captureFrameMethod, captureFrameQuality, captureFrameFileNameFormat, enableNativeHevc, cleanupChoices, setCleanupChoices, darkMode, setDarkMode, preferStrongColors, outputFileNameMinZeroPadding, cutFromAdjustmentFrames, + captureFormat, setCaptureFormat, customOutDir, setCustomOutDir, keyframeCut, setKeyframeCut, preserveMetadata, preserveChapters, preserveMovData, movFastStart, avoidNegativeTs, autoMerge, timecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, askBeforeClose, enableAskForImportChapters, enableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, wheelSensitivity, invertTimelineScroll, language, ffmpegExperimental, hideNotifications, hideOsNotifications, autoLoadTimecode, autoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, segmentsToChapters, preserveMetadataOnMerge, simpleMode, setSimpleMode, outSegTemplate, setOutSegTemplate, mergedFileTemplate, setMergedFileTemplate, keyboardSeekAccFactor, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, enableAutoHtml5ify, segmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, customFfPath, storeProjectInWorkingDir, setStoreProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, mouseWheelFrameSeekModifierKey, mouseWheelKeyframeSeekModifierKey, captureFrameMethod, captureFrameQuality, captureFrameFileNameFormat, enableNativeHevc, cleanupChoices, setCleanupChoices, darkMode, setDarkMode, preferStrongColors, outputFileNameMinZeroPadding, cutFromAdjustmentFrames, } = allUserSettings; const { working, setWorking, workingRef, abortWorking } = useLoading(); @@ -238,10 +238,6 @@ function App() { return newVal; }), [setExportConfirmEnabled, showNotification]); - const toggleSegmentsToChapters = useCallback(() => setSegmentsToChapters((v) => !v), [setSegmentsToChapters]); - - const togglePreserveMetadataOnMerge = useCallback(() => setPreserveMetadataOnMerge((v) => !v), [setPreserveMetadataOnMerge]); - const toggleShowKeyframes = useCallback(() => { setKeyframesEnabled((old) => { const enabled = !old; @@ -429,10 +425,6 @@ function App() { return newVal; }), [showNotification, setKeyframeCut]); - const togglePreserveMovData = useCallback(() => setPreserveMovData((val) => !val), [setPreserveMovData]); - - const toggleMovFastStart = useCallback(() => setMovFastStart((val) => !val), [setMovFastStart]); - const toggleSimpleMode = useCallback(() => setSimpleMode((v) => { showNotification({ text: v ? i18n.t('Advanced view has been enabled. You will now also see non-essential buttons and functions') : i18n.t('Advanced view disabled. You will now see only the most essential buttons and functions') }); const newValue = !v; @@ -477,8 +469,8 @@ function App() { }, [ensureAccessToSourceDir, getProjectFileSavePath, setStoreProjectInWorkingDir, storeProjectInWorkingDir]); const userSettingsContext = useMemo(() => ({ - ...allUserSettings, toggleCaptureFormat, changeOutDir, toggleKeyframeCut, togglePreserveMovData, toggleMovFastStart, toggleExportConfirmEnabled, toggleSegmentsToChapters, togglePreserveMetadataOnMerge, toggleSimpleMode, toggleSafeOutputFileName, effectiveExportMode, - }), [allUserSettings, changeOutDir, effectiveExportMode, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleMovFastStart, togglePreserveMetadataOnMerge, togglePreserveMovData, toggleSafeOutputFileName, toggleSegmentsToChapters, toggleSimpleMode]); + ...allUserSettings, toggleCaptureFormat, changeOutDir, toggleKeyframeCut, toggleExportConfirmEnabled, toggleSimpleMode, toggleSafeOutputFileName, effectiveExportMode, + }), [allUserSettings, changeOutDir, effectiveExportMode, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleSafeOutputFileName, toggleSimpleMode]); const segColorsContext = useMemo(() => ({ getSegColor: (seg: SegmentColorIndex) => { @@ -804,10 +796,12 @@ function App() { effectiveExportMode, outSegTemplate, mergedFileTemplate, + preserveMetadata, + preserveChapters, }; openSendReportDialog(err, state); - }, [commonSettings, copyStreamIdsByFile, cutSegments, effectiveExportMode, externalFilesMeta, fileFormat, filePath, mainFileFormatData, mainStreams, mergedFileTemplate, outSegTemplate, rotation, shortestFlag]); + }, [commonSettings, copyStreamIdsByFile, cutSegments, effectiveExportMode, externalFilesMeta, fileFormat, filePath, mainFileFormatData, mainStreams, mergedFileTemplate, outSegTemplate, preserveChapters, preserveMetadata, rotation, shortestFlag]); const openSendConcatReportDialogWithState = useCallback(async (err: unknown, reportState?: object) => { const state = { ...commonSettings, ...reportState }; @@ -1025,8 +1019,10 @@ function App() { onProgress: setProgress, shortestFlag, ffmpegExperimental, - preserveMovData, + preserveMetadata, preserveMetadataOnMerge, + preserveMovData, + preserveChapters, movFastStart, avoidNegativeTs, customTagsByFile, @@ -1134,7 +1130,7 @@ function App() { setWorking(undefined); setProgress(undefined); } - }, [filePath, numStreamsToCopy, segmentsToExport, haveInvalidSegs, workingRef, setWorking, segmentsToChaptersOnly, outSegTemplateOrDefault, generateOutSegFileNames, cutMultiple, outputDir, customOutDir, fileFormat, duration, isRotationSet, effectiveRotation, copyFileStreams, allFilesMeta, keyframeCut, shortestFlag, ffmpegExperimental, preserveMovData, preserveMetadataOnMerge, movFastStart, avoidNegativeTs, customTagsByFile, paramsByStreamId, detectedFps, willMerge, enableOverwriteOutput, exportConfirmEnabled, mainFileFormatData, mainStreams, exportExtraStreams, areWeCutting, hideAllNotifications, cleanupChoices.cleanupAfterExport, cleanupFilesWithDialog, selectedSegmentsOrInverse, mergedFileTemplateOrDefault, segmentsToChapters, invertCutSegments, generateMergedFileNames, autoConcatCutSegments, autoDeleteMergedSegments, nonCopiedExtraStreams, showOsNotification, handleExportFailed]); + }, [filePath, numStreamsToCopy, segmentsToExport, haveInvalidSegs, workingRef, setWorking, segmentsToChaptersOnly, outSegTemplateOrDefault, generateOutSegFileNames, cutMultiple, outputDir, customOutDir, fileFormat, duration, isRotationSet, effectiveRotation, copyFileStreams, allFilesMeta, keyframeCut, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, avoidNegativeTs, customTagsByFile, paramsByStreamId, detectedFps, willMerge, enableOverwriteOutput, exportConfirmEnabled, mainFileFormatData, mainStreams, exportExtraStreams, areWeCutting, hideAllNotifications, cleanupChoices.cleanupAfterExport, cleanupFilesWithDialog, selectedSegmentsOrInverse, mergedFileTemplateOrDefault, segmentsToChapters, invertCutSegments, generateMergedFileNames, autoConcatCutSegments, autoDeleteMergedSegments, nonCopiedExtraStreams, showOsNotification, handleExportFailed]); const onExportPress = useCallback(async () => { if (!filePath) return; diff --git a/src/renderer/src/components/ExportConfirm.tsx b/src/renderer/src/components/ExportConfirm.tsx index 9814b06f..2ca6bcaf 100644 --- a/src/renderer/src/components/ExportConfirm.tsx +++ b/src/renderer/src/components/ExportConfirm.tsx @@ -9,8 +9,6 @@ import type { SweetAlertIcon } from 'sweetalert2'; import ExportButton from './ExportButton'; import ExportModeButton from './ExportModeButton'; -import PreserveMovDataButton from './PreserveMovDataButton'; -import MovFastStartButton from './MovFastStartButton'; import ToggleExportConfirm from './ToggleExportConfirm'; import FileNameTemplateEditor from './FileNameTemplateEditor'; import HighlightedText, { highlightedTextStyle } from './HighlightedText'; @@ -26,7 +24,7 @@ import styles from './ExportConfirm.module.css'; import { InverseCutSegment, SegmentToExport } from '../types'; import { defaultMergedFileTemplate, defaultOutSegTemplate, GenerateOutFileNames } from '../util/outputNameTemplate'; import { FFprobeStream } from '../../../../ffprobe'; -import { AvoidNegativeTs } from '../../../../types'; +import { AvoidNegativeTs, PreserveMetadata } from '../../../../types'; import TextInput from './TextInput'; @@ -95,7 +93,13 @@ function ExportConfirm({ }) { const { t } = useTranslation(); - const { changeOutDir, keyframeCut, toggleKeyframeCut, preserveMovData, movFastStart, avoidNegativeTs, setAvoidNegativeTs, autoDeleteMergedSegments, exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, toggleSegmentsToChapters, preserveMetadataOnMerge, togglePreserveMetadataOnMerge, enableSmartCut, setEnableSmartCut, effectiveExportMode, enableOverwriteOutput, setEnableOverwriteOutput, ffmpegExperimental, setFfmpegExperimental, cutFromAdjustmentFrames, setCutFromAdjustmentFrames } = useUserSettings(); + const { changeOutDir, keyframeCut, toggleKeyframeCut, preserveMovData, setPreserveMovData, preserveMetadata, setPreserveMetadata, preserveChapters, setPreserveChapters, movFastStart, setMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoDeleteMergedSegments, exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, enableSmartCut, setEnableSmartCut, effectiveExportMode, enableOverwriteOutput, setEnableOverwriteOutput, ffmpegExperimental, setFfmpegExperimental, cutFromAdjustmentFrames, setCutFromAdjustmentFrames } = useUserSettings(); + + const togglePreserveChapters = useCallback(() => setPreserveChapters((val) => !val), [setPreserveChapters]); + const togglePreserveMovData = useCallback(() => setPreserveMovData((val) => !val), [setPreserveMovData]); + const toggleMovFastStart = useCallback(() => setMovFastStart((val) => !val), [setMovFastStart]); + const toggleSegmentsToChapters = useCallback(() => setSegmentsToChapters((v) => !v), [setSegmentsToChapters]); + const togglePreserveMetadataOnMerge = useCallback(() => setPreserveMetadataOnMerge((v) => !v), [setPreserveMetadataOnMerge]); const isMov = ffmpegIsMov(outFormat); const isIpod = outFormat === 'ipod'; @@ -120,10 +124,18 @@ function ExportConfirm({ const showHelpText = useCallback(({ icon = 'info', timer = 10000, text }: { icon?: SweetAlertIcon, timer?: number, text: string }) => toast.fire({ icon, timer, text }), []); + const onPreserveChaptersPress = useCallback(() => { + toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Whether to preserve chapters from source file.') }); + }, []); + const onPreserveMovDataHelpPress = useCallback(() => { toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Preserve all MOV/MP4 metadata tags (e.g. EXIF, GPS position etc.) from source file? Note that some players have trouble playing back files where all metadata is preserved, like iTunes and other Apple software') }); }, []); + const onPreserveMetadataHelpPress = useCallback(() => { + toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Whether to preserve metadata from source file. Default: Global (file metadata), per-track and per-chapter metadata will be copied. Non-global: Only per-track and per-chapter metadata will be copied. None: No metadata will be copied') }); + }, []); + const onMovFastStartHelpPress = useCallback(() => { toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Enabling this will allow faster playback of the exported file. This makes processing use 3 times as much export I/O, which is negligible for small files but might slow down exporting of large files.') }); }, []); @@ -354,7 +366,7 @@ function ExportConfirm({ {t('Enable MOV Faststart?')} - + {isIpod && !movFastStart &&
{t('For the ipod format, it is recommended to activate this option')}
} @@ -366,13 +378,41 @@ function ExportConfirm({ + + + {t('Preserve chapters')} + + + + + + + + + + + + {t('Preserve metadata')} + + + + + + + + + {t('Preserve all MP4/MOV metadata?')} {isIpod && preserveMovData &&
{t('For the ipod format, it is recommended to deactivate this option')}
} - + {isIpod && preserveMovData ? ( diff --git a/src/renderer/src/components/MovFastStartButton.tsx b/src/renderer/src/components/MovFastStartButton.tsx deleted file mode 100644 index a038fef1..00000000 --- a/src/renderer/src/components/MovFastStartButton.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { memo } from 'react'; - -import useUserSettings from '../hooks/useUserSettings'; -import Switch from './Switch'; - - -function MovFastStartButton() { - const { movFastStart, toggleMovFastStart } = useUserSettings(); - - return ( - - ); -} - -export default memo(MovFastStartButton); diff --git a/src/renderer/src/components/PreserveMovDataButton.tsx b/src/renderer/src/components/PreserveMovDataButton.tsx deleted file mode 100644 index e906ddb0..00000000 --- a/src/renderer/src/components/PreserveMovDataButton.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { memo } from 'react'; - -import useUserSettings from '../hooks/useUserSettings'; -import Switch from './Switch'; - - -function PreserveMovDataButton() { - const { preserveMovData, togglePreserveMovData } = useUserSettings(); - - return ( - - ); -} - -export default memo(PreserveMovDataButton); diff --git a/src/renderer/src/contexts.ts b/src/renderer/src/contexts.ts index a8d35c9a..c9183df2 100644 --- a/src/renderer/src/contexts.ts +++ b/src/renderer/src/contexts.ts @@ -4,15 +4,12 @@ import Color from 'color'; import useUserSettingsRoot from './hooks/useUserSettingsRoot'; import { ExportMode, SegmentColorIndex } from './types'; + export type UserSettingsContextType = ReturnType & { toggleCaptureFormat: () => void, changeOutDir: () => Promise, toggleKeyframeCut: (showMessage?: boolean) => void, - togglePreserveMovData: () => void, - toggleMovFastStart: () => void, toggleExportConfirmEnabled: () => void, - toggleSegmentsToChapters: () => void, - togglePreserveMetadataOnMerge: () => void, toggleSimpleMode: () => void, toggleSafeOutputFileName: () => void, effectiveExportMode: ExportMode, diff --git a/src/renderer/src/hooks/useFfmpegOperations.ts b/src/renderer/src/hooks/useFfmpegOperations.ts index 726ad8cb..238669f7 100644 --- a/src/renderer/src/hooks/useFfmpegOperations.ts +++ b/src/renderer/src/hooks/useFfmpegOperations.ts @@ -10,7 +10,7 @@ import { getMapStreamsArgs, getStreamIdsToCopy } from '../util/streams'; import { getSmartCutParams } from '../smartcut'; import { isDurationValid } from '../segments'; import { FFprobeStream } from '../../../../ffprobe'; -import { AvoidNegativeTs, Html5ifyMode } from '../../../../types'; +import { AvoidNegativeTs, Html5ifyMode, PreserveMetadata } from '../../../../types'; import { AllFilesMeta, Chapter, CopyfileStreams, CustomTagsByFile, ParamsByStreamId, SegmentToExport } from '../types'; const { join, resolve, dirname } = window.require('path'); @@ -233,14 +233,14 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea const losslessCutSingle = useCallback(async ({ keyframeCut: ssBeforeInput, avoidNegativeTs, copyFileStreams, cutFrom, cutTo, chaptersPath, onProgress, outPath, - videoDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMovData, movFastStart, customTagsByFile, paramsByStreamId, videoTimebase, detectedFps, + videoDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, customTagsByFile, paramsByStreamId, videoTimebase, detectedFps, }: { keyframeCut: boolean, avoidNegativeTs: AvoidNegativeTs | undefined, copyFileStreams: CopyfileStreams, cutFrom: number, cutTo: number, - chaptersPath?: string | undefined, + chaptersPath: string | undefined, onProgress: (p: number) => void, outPath: string, videoDuration: number | undefined, @@ -249,7 +249,9 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea outFormat: string, shortestFlag: boolean, ffmpegExperimental: boolean, + preserveMetadata: PreserveMetadata, preserveMovData: boolean, + preserveChapters: boolean, movFastStart: boolean, customTagsByFile: CustomTagsByFile, paramsByStreamId: ParamsByStreamId, @@ -360,6 +362,19 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea return ret; })(); + function getPreserveMetadata() { + if (preserveMetadata === 'default') return ['-map_metadata', '0']; // todo isn't this ffmpeg default and can be omitted? https://stackoverflow.com/a/67508734/6519037 + if (preserveMetadata === 'none') return ['-map_metadata', '-1']; + if (preserveMetadata === 'nonglobal') return ['-map_metadata:g', '-1']; // https://superuser.com/a/1546267/658247 + return []; + } + + function getPreserveChapters() { + if (chaptersPath) return ['-map_chapters', String(chaptersInputIndex)]; + if (!preserveChapters) return ['-map_chapters', '-1']; // https://github.com/mifi/lossless-cut/issues/2176 + return []; // default: includes chapters from input + } + const ffmpegArgs = [ '-hide_banner', // No progress if we set loglevel warning :( @@ -371,9 +386,9 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea ...mapStreamsArgs, - '-map_metadata', '0', + ...getPreserveMetadata(), - ...(chaptersPath ? ['-map_chapters', String(chaptersInputIndex)] : []), + ...getPreserveChapters(), ...(shortestFlag ? ['-shortest'] : []), @@ -404,7 +419,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea }, [appendFfmpegCommandLog, cutFromAdjustmentFrames, filePath, getOutputPlaybackRateArgs, shouldSkipExistingFile, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart]); const cutMultiple = useCallback(async ({ - outputDir, customOutDir, segments, outSegFileNames, videoDuration, rotation, detectedFps, onProgress: onTotalProgress, keyframeCut, copyFileStreams, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMovData, movFastStart, avoidNegativeTs, customTagsByFile, paramsByStreamId, chapters, preserveMetadataOnMerge, + outputDir, customOutDir, segments, outSegFileNames, videoDuration, rotation, detectedFps, onProgress: onTotalProgress, keyframeCut, copyFileStreams, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, avoidNegativeTs, customTagsByFile, paramsByStreamId, chapters, }: { outputDir: string, customOutDir: string | undefined, @@ -420,13 +435,15 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea outFormat: string | undefined, shortestFlag: boolean, ffmpegExperimental: boolean, + preserveMetadata: PreserveMetadata, preserveMovData: boolean, + preserveMetadataOnMerge: boolean, + preserveChapters: boolean, movFastStart: boolean, avoidNegativeTs: AvoidNegativeTs | undefined, customTagsByFile: CustomTagsByFile, paramsByStreamId: ParamsByStreamId, chapters: Chapter[] | undefined, - preserveMetadataOnMerge, }) => { console.log('customTagsByFile', customTagsByFile); console.log('paramsByStreamId', paramsByStreamId); @@ -456,7 +473,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea const outPath = await makeSegmentOutPath(); invariant(outFormat != null); await losslessCutSingle({ - cutFrom: desiredCutFrom, cutTo, chaptersPath, outPath, copyFileStreams, keyframeCut, avoidNegativeTs, videoDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMovData, movFastStart, customTagsByFile, paramsByStreamId, onProgress: (progress) => onSingleProgress(i, progress), + cutFrom: desiredCutFrom, cutTo, chaptersPath, outPath, copyFileStreams, keyframeCut, avoidNegativeTs, videoDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, customTagsByFile, paramsByStreamId, onProgress: (progress) => onSingleProgress(i, progress), }); return outPath; } @@ -519,7 +536,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea // for smart cut we need to use keyframe cut here, and no avoid_negative_ts await losslessCutSingle({ - cutFrom: losslessCutFrom, cutTo, chaptersPath, outPath: losslessPartOutPath, copyFileStreams: copyFileStreamsFiltered, keyframeCut: true, avoidNegativeTs: undefined, videoDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMovData, movFastStart, customTagsByFile, paramsByStreamId, videoTimebase, onProgress, + cutFrom: losslessCutFrom, cutTo, chaptersPath, outPath: losslessPartOutPath, copyFileStreams: copyFileStreamsFiltered, keyframeCut: true, avoidNegativeTs: undefined, videoDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, customTagsByFile, paramsByStreamId, videoTimebase, onProgress, }); // OK, just return the single cut file (we may need smart cut in other segments though) diff --git a/src/renderer/src/hooks/useUserSettingsRoot.ts b/src/renderer/src/hooks/useUserSettingsRoot.ts index b8667f37..73bb0086 100644 --- a/src/renderer/src/hooks/useUserSettingsRoot.ts +++ b/src/renderer/src/hooks/useUserSettingsRoot.ts @@ -47,8 +47,14 @@ export default () => { useEffect(() => safeSetConfig({ customOutDir }), [customOutDir]); const [keyframeCut, setKeyframeCut] = useState(safeGetConfigInitial('keyframeCut')); useEffect(() => safeSetConfig({ keyframeCut }), [keyframeCut]); + const [preserveMetadata, setPreserveMetadata] = useState(safeGetConfigInitial('preserveMetadata')); + useEffect(() => safeSetConfig({ preserveMetadata }), [preserveMetadata]); + const [preserveMetadataOnMerge, setPreserveMetadataOnMerge] = useState(safeGetConfigInitial('preserveMetadataOnMerge')); + useEffect(() => safeSetConfig({ preserveMetadataOnMerge }), [preserveMetadataOnMerge]); const [preserveMovData, setPreserveMovData] = useState(safeGetConfigInitial('preserveMovData')); useEffect(() => safeSetConfig({ preserveMovData }), [preserveMovData]); + const [preserveChapters, setPreserveChapters] = useState(safeGetConfigInitial('preserveChapters')); + useEffect(() => safeSetConfig({ preserveChapters }), [preserveChapters]); const [movFastStart, setMovFastStart] = useState(safeGetConfigInitial('movFastStart')); useEffect(() => safeSetConfig({ movFastStart }), [movFastStart]); const [avoidNegativeTs, setAvoidNegativeTs] = useState(safeGetConfigInitial('avoidNegativeTs')); @@ -91,8 +97,6 @@ export default () => { useEffect(() => safeSetConfig({ exportConfirmEnabled }), [exportConfirmEnabled]); const [segmentsToChapters, setSegmentsToChapters] = useState(safeGetConfigInitial('segmentsToChapters')); useEffect(() => safeSetConfig({ segmentsToChapters }), [segmentsToChapters]); - const [preserveMetadataOnMerge, setPreserveMetadataOnMerge] = useState(safeGetConfigInitial('preserveMetadataOnMerge')); - useEffect(() => safeSetConfig({ preserveMetadataOnMerge }), [preserveMetadataOnMerge]); const [simpleMode, setSimpleMode] = useState(safeGetConfigInitial('simpleMode')); useEffect(() => safeSetConfig({ simpleMode }), [simpleMode]); const [outSegTemplate, setOutSegTemplate] = useState(safeGetConfigInitial('outSegTemplate')); @@ -180,8 +184,14 @@ export default () => { setCustomOutDir, keyframeCut, setKeyframeCut, + preserveMetadata, + setPreserveMetadata, + preserveMetadataOnMerge, + setPreserveMetadataOnMerge, preserveMovData, setPreserveMovData, + preserveChapters, + setPreserveChapters, movFastStart, setMovFastStart, avoidNegativeTs, @@ -224,8 +234,6 @@ export default () => { setExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, - preserveMetadataOnMerge, - setPreserveMetadataOnMerge, simpleMode, setSimpleMode, outSegTemplate, diff --git a/types.ts b/types.ts index 82183ab3..00ea4e64 100644 --- a/types.ts +++ b/types.ts @@ -44,6 +44,8 @@ export type AvoidNegativeTs = 'make_zero' | 'auto' | 'make_non_negative' | 'disa export type ModifierKey = 'ctrl' | 'shift' | 'alt' | 'meta'; +export type PreserveMetadata = 'default' | 'nonglobal' | 'none' + export interface Config { captureFormat: CaptureFormat, @@ -65,6 +67,9 @@ export interface Config { wheelSensitivity: number, language: LanguageKey | undefined, ffmpegExperimental: boolean, + preserveChapters: boolean, + preserveMetadata: PreserveMetadata, + preserveMetadataOnMerge: boolean, preserveMovData: boolean, movFastStart: boolean, avoidNegativeTs: AvoidNegativeTs, @@ -72,7 +77,6 @@ export interface Config { hideOsNotifications: 'all' | undefined, autoLoadTimecode: boolean, segmentsToChapters: boolean, - preserveMetadataOnMerge: boolean, simpleMode: boolean, outSegTemplate: string | undefined, mergedFileTemplate: string | undefined,