mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 02:12:30 +01:00
allow customising preservation
of map_metadata and chapters fixes #2176 see #1027
This commit is contained in:
parent
789f857e26
commit
8024884363
@ -106,6 +106,9 @@ const defaults: Config = {
|
|||||||
wheelSensitivity: 0.2,
|
wheelSensitivity: 0.2,
|
||||||
language: undefined,
|
language: undefined,
|
||||||
ffmpegExperimental: false,
|
ffmpegExperimental: false,
|
||||||
|
preserveChapters: true,
|
||||||
|
preserveMetadata: 'default',
|
||||||
|
preserveMetadataOnMerge: false,
|
||||||
preserveMovData: false,
|
preserveMovData: false,
|
||||||
movFastStart: true,
|
movFastStart: true,
|
||||||
avoidNegativeTs: 'make_zero',
|
avoidNegativeTs: 'make_zero',
|
||||||
@ -113,7 +116,6 @@ const defaults: Config = {
|
|||||||
hideOsNotifications: undefined,
|
hideOsNotifications: undefined,
|
||||||
autoLoadTimecode: false,
|
autoLoadTimecode: false,
|
||||||
segmentsToChapters: false,
|
segmentsToChapters: false,
|
||||||
preserveMetadataOnMerge: false,
|
|
||||||
simpleMode: true,
|
simpleMode: true,
|
||||||
outSegTemplate: undefined,
|
outSegTemplate: undefined,
|
||||||
mergedFileTemplate: undefined,
|
mergedFileTemplate: undefined,
|
||||||
|
@ -171,7 +171,7 @@ function App() {
|
|||||||
const allUserSettings = useUserSettingsRoot();
|
const allUserSettings = useUserSettingsRoot();
|
||||||
|
|
||||||
const {
|
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;
|
} = allUserSettings;
|
||||||
|
|
||||||
const { working, setWorking, workingRef, abortWorking } = useLoading();
|
const { working, setWorking, workingRef, abortWorking } = useLoading();
|
||||||
@ -238,10 +238,6 @@ function App() {
|
|||||||
return newVal;
|
return newVal;
|
||||||
}), [setExportConfirmEnabled, showNotification]);
|
}), [setExportConfirmEnabled, showNotification]);
|
||||||
|
|
||||||
const toggleSegmentsToChapters = useCallback(() => setSegmentsToChapters((v) => !v), [setSegmentsToChapters]);
|
|
||||||
|
|
||||||
const togglePreserveMetadataOnMerge = useCallback(() => setPreserveMetadataOnMerge((v) => !v), [setPreserveMetadataOnMerge]);
|
|
||||||
|
|
||||||
const toggleShowKeyframes = useCallback(() => {
|
const toggleShowKeyframes = useCallback(() => {
|
||||||
setKeyframesEnabled((old) => {
|
setKeyframesEnabled((old) => {
|
||||||
const enabled = !old;
|
const enabled = !old;
|
||||||
@ -429,10 +425,6 @@ function App() {
|
|||||||
return newVal;
|
return newVal;
|
||||||
}), [showNotification, setKeyframeCut]);
|
}), [showNotification, setKeyframeCut]);
|
||||||
|
|
||||||
const togglePreserveMovData = useCallback(() => setPreserveMovData((val) => !val), [setPreserveMovData]);
|
|
||||||
|
|
||||||
const toggleMovFastStart = useCallback(() => setMovFastStart((val) => !val), [setMovFastStart]);
|
|
||||||
|
|
||||||
const toggleSimpleMode = useCallback(() => setSimpleMode((v) => {
|
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') });
|
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;
|
const newValue = !v;
|
||||||
@ -477,8 +469,8 @@ function App() {
|
|||||||
}, [ensureAccessToSourceDir, getProjectFileSavePath, setStoreProjectInWorkingDir, storeProjectInWorkingDir]);
|
}, [ensureAccessToSourceDir, getProjectFileSavePath, setStoreProjectInWorkingDir, storeProjectInWorkingDir]);
|
||||||
|
|
||||||
const userSettingsContext = useMemo<UserSettingsContextType>(() => ({
|
const userSettingsContext = useMemo<UserSettingsContextType>(() => ({
|
||||||
...allUserSettings, toggleCaptureFormat, changeOutDir, toggleKeyframeCut, togglePreserveMovData, toggleMovFastStart, toggleExportConfirmEnabled, toggleSegmentsToChapters, togglePreserveMetadataOnMerge, toggleSimpleMode, toggleSafeOutputFileName, effectiveExportMode,
|
...allUserSettings, toggleCaptureFormat, changeOutDir, toggleKeyframeCut, toggleExportConfirmEnabled, toggleSimpleMode, toggleSafeOutputFileName, effectiveExportMode,
|
||||||
}), [allUserSettings, changeOutDir, effectiveExportMode, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleMovFastStart, togglePreserveMetadataOnMerge, togglePreserveMovData, toggleSafeOutputFileName, toggleSegmentsToChapters, toggleSimpleMode]);
|
}), [allUserSettings, changeOutDir, effectiveExportMode, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleSafeOutputFileName, toggleSimpleMode]);
|
||||||
|
|
||||||
const segColorsContext = useMemo(() => ({
|
const segColorsContext = useMemo(() => ({
|
||||||
getSegColor: (seg: SegmentColorIndex) => {
|
getSegColor: (seg: SegmentColorIndex) => {
|
||||||
@ -804,10 +796,12 @@ function App() {
|
|||||||
effectiveExportMode,
|
effectiveExportMode,
|
||||||
outSegTemplate,
|
outSegTemplate,
|
||||||
mergedFileTemplate,
|
mergedFileTemplate,
|
||||||
|
preserveMetadata,
|
||||||
|
preserveChapters,
|
||||||
};
|
};
|
||||||
|
|
||||||
openSendReportDialog(err, state);
|
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 openSendConcatReportDialogWithState = useCallback(async (err: unknown, reportState?: object) => {
|
||||||
const state = { ...commonSettings, ...reportState };
|
const state = { ...commonSettings, ...reportState };
|
||||||
@ -1025,8 +1019,10 @@ function App() {
|
|||||||
onProgress: setProgress,
|
onProgress: setProgress,
|
||||||
shortestFlag,
|
shortestFlag,
|
||||||
ffmpegExperimental,
|
ffmpegExperimental,
|
||||||
preserveMovData,
|
preserveMetadata,
|
||||||
preserveMetadataOnMerge,
|
preserveMetadataOnMerge,
|
||||||
|
preserveMovData,
|
||||||
|
preserveChapters,
|
||||||
movFastStart,
|
movFastStart,
|
||||||
avoidNegativeTs,
|
avoidNegativeTs,
|
||||||
customTagsByFile,
|
customTagsByFile,
|
||||||
@ -1134,7 +1130,7 @@ function App() {
|
|||||||
setWorking(undefined);
|
setWorking(undefined);
|
||||||
setProgress(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 () => {
|
const onExportPress = useCallback(async () => {
|
||||||
if (!filePath) return;
|
if (!filePath) return;
|
||||||
|
@ -9,8 +9,6 @@ import type { SweetAlertIcon } from 'sweetalert2';
|
|||||||
|
|
||||||
import ExportButton from './ExportButton';
|
import ExportButton from './ExportButton';
|
||||||
import ExportModeButton from './ExportModeButton';
|
import ExportModeButton from './ExportModeButton';
|
||||||
import PreserveMovDataButton from './PreserveMovDataButton';
|
|
||||||
import MovFastStartButton from './MovFastStartButton';
|
|
||||||
import ToggleExportConfirm from './ToggleExportConfirm';
|
import ToggleExportConfirm from './ToggleExportConfirm';
|
||||||
import FileNameTemplateEditor from './FileNameTemplateEditor';
|
import FileNameTemplateEditor from './FileNameTemplateEditor';
|
||||||
import HighlightedText, { highlightedTextStyle } from './HighlightedText';
|
import HighlightedText, { highlightedTextStyle } from './HighlightedText';
|
||||||
@ -26,7 +24,7 @@ import styles from './ExportConfirm.module.css';
|
|||||||
import { InverseCutSegment, SegmentToExport } from '../types';
|
import { InverseCutSegment, SegmentToExport } from '../types';
|
||||||
import { defaultMergedFileTemplate, defaultOutSegTemplate, GenerateOutFileNames } from '../util/outputNameTemplate';
|
import { defaultMergedFileTemplate, defaultOutSegTemplate, GenerateOutFileNames } from '../util/outputNameTemplate';
|
||||||
import { FFprobeStream } from '../../../../ffprobe';
|
import { FFprobeStream } from '../../../../ffprobe';
|
||||||
import { AvoidNegativeTs } from '../../../../types';
|
import { AvoidNegativeTs, PreserveMetadata } from '../../../../types';
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
|
|
||||||
|
|
||||||
@ -95,7 +93,13 @@ function ExportConfirm({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
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 isMov = ffmpegIsMov(outFormat);
|
||||||
const isIpod = outFormat === 'ipod';
|
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 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(() => {
|
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') });
|
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(() => {
|
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.') });
|
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?')}
|
{t('Enable MOV Faststart?')}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<MovFastStartButton />
|
<Switch checked={movFastStart} onCheckedChange={toggleMovFastStart} />
|
||||||
{isIpod && !movFastStart && <div style={warningStyle}>{t('For the ipod format, it is recommended to activate this option')}</div>}
|
{isIpod && !movFastStart && <div style={warningStyle}>{t('For the ipod format, it is recommended to activate this option')}</div>}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -366,13 +378,41 @@ function ExportConfirm({
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{t('Preserve chapters')}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Switch checked={preserveChapters} onCheckedChange={togglePreserveChapters} />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<HelpIcon onClick={onPreserveChaptersPress} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{t('Preserve metadata')}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Select value={preserveMetadata} onChange={(e) => setPreserveMetadata(e.target.value as PreserveMetadata)} style={{ height: 20, marginLeft: 5 }}>
|
||||||
|
<option value={'default' as PreserveMetadata}>{t('Default')}</option>
|
||||||
|
<option value={'none' satisfies PreserveMetadata}>{t('None')}</option>
|
||||||
|
<option value={'nonglobal' satisfies PreserveMetadata}>{t('Non-global')}</option>
|
||||||
|
</Select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<HelpIcon onClick={onPreserveMetadataHelpPress} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{t('Preserve all MP4/MOV metadata?')}
|
{t('Preserve all MP4/MOV metadata?')}
|
||||||
{isIpod && preserveMovData && <div style={warningStyle}>{t('For the ipod format, it is recommended to deactivate this option')}</div>}
|
{isIpod && preserveMovData && <div style={warningStyle}>{t('For the ipod format, it is recommended to deactivate this option')}</div>}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<PreserveMovDataButton />
|
<Switch checked={preserveMovData} onCheckedChange={togglePreserveMovData} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{isIpod && preserveMovData ? (
|
{isIpod && preserveMovData ? (
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
import useUserSettings from '../hooks/useUserSettings';
|
|
||||||
import Switch from './Switch';
|
|
||||||
|
|
||||||
|
|
||||||
function MovFastStartButton() {
|
|
||||||
const { movFastStart, toggleMovFastStart } = useUserSettings();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Switch checked={movFastStart} onCheckedChange={toggleMovFastStart} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(MovFastStartButton);
|
|
@ -1,15 +0,0 @@
|
|||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
import useUserSettings from '../hooks/useUserSettings';
|
|
||||||
import Switch from './Switch';
|
|
||||||
|
|
||||||
|
|
||||||
function PreserveMovDataButton() {
|
|
||||||
const { preserveMovData, togglePreserveMovData } = useUserSettings();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Switch checked={preserveMovData} onCheckedChange={togglePreserveMovData} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(PreserveMovDataButton);
|
|
@ -4,15 +4,12 @@ import Color from 'color';
|
|||||||
import useUserSettingsRoot from './hooks/useUserSettingsRoot';
|
import useUserSettingsRoot from './hooks/useUserSettingsRoot';
|
||||||
import { ExportMode, SegmentColorIndex } from './types';
|
import { ExportMode, SegmentColorIndex } from './types';
|
||||||
|
|
||||||
|
|
||||||
export type UserSettingsContextType = ReturnType<typeof useUserSettingsRoot> & {
|
export type UserSettingsContextType = ReturnType<typeof useUserSettingsRoot> & {
|
||||||
toggleCaptureFormat: () => void,
|
toggleCaptureFormat: () => void,
|
||||||
changeOutDir: () => Promise<void>,
|
changeOutDir: () => Promise<void>,
|
||||||
toggleKeyframeCut: (showMessage?: boolean) => void,
|
toggleKeyframeCut: (showMessage?: boolean) => void,
|
||||||
togglePreserveMovData: () => void,
|
|
||||||
toggleMovFastStart: () => void,
|
|
||||||
toggleExportConfirmEnabled: () => void,
|
toggleExportConfirmEnabled: () => void,
|
||||||
toggleSegmentsToChapters: () => void,
|
|
||||||
togglePreserveMetadataOnMerge: () => void,
|
|
||||||
toggleSimpleMode: () => void,
|
toggleSimpleMode: () => void,
|
||||||
toggleSafeOutputFileName: () => void,
|
toggleSafeOutputFileName: () => void,
|
||||||
effectiveExportMode: ExportMode,
|
effectiveExportMode: ExportMode,
|
||||||
|
@ -10,7 +10,7 @@ import { getMapStreamsArgs, getStreamIdsToCopy } from '../util/streams';
|
|||||||
import { getSmartCutParams } from '../smartcut';
|
import { getSmartCutParams } from '../smartcut';
|
||||||
import { isDurationValid } from '../segments';
|
import { isDurationValid } from '../segments';
|
||||||
import { FFprobeStream } from '../../../../ffprobe';
|
import { FFprobeStream } from '../../../../ffprobe';
|
||||||
import { AvoidNegativeTs, Html5ifyMode } from '../../../../types';
|
import { AvoidNegativeTs, Html5ifyMode, PreserveMetadata } from '../../../../types';
|
||||||
import { AllFilesMeta, Chapter, CopyfileStreams, CustomTagsByFile, ParamsByStreamId, SegmentToExport } from '../types';
|
import { AllFilesMeta, Chapter, CopyfileStreams, CustomTagsByFile, ParamsByStreamId, SegmentToExport } from '../types';
|
||||||
|
|
||||||
const { join, resolve, dirname } = window.require('path');
|
const { join, resolve, dirname } = window.require('path');
|
||||||
@ -233,14 +233,14 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
|
|
||||||
const losslessCutSingle = useCallback(async ({
|
const losslessCutSingle = useCallback(async ({
|
||||||
keyframeCut: ssBeforeInput, avoidNegativeTs, copyFileStreams, cutFrom, cutTo, chaptersPath, onProgress, outPath,
|
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,
|
keyframeCut: boolean,
|
||||||
avoidNegativeTs: AvoidNegativeTs | undefined,
|
avoidNegativeTs: AvoidNegativeTs | undefined,
|
||||||
copyFileStreams: CopyfileStreams,
|
copyFileStreams: CopyfileStreams,
|
||||||
cutFrom: number,
|
cutFrom: number,
|
||||||
cutTo: number,
|
cutTo: number,
|
||||||
chaptersPath?: string | undefined,
|
chaptersPath: string | undefined,
|
||||||
onProgress: (p: number) => void,
|
onProgress: (p: number) => void,
|
||||||
outPath: string,
|
outPath: string,
|
||||||
videoDuration: number | undefined,
|
videoDuration: number | undefined,
|
||||||
@ -249,7 +249,9 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
outFormat: string,
|
outFormat: string,
|
||||||
shortestFlag: boolean,
|
shortestFlag: boolean,
|
||||||
ffmpegExperimental: boolean,
|
ffmpegExperimental: boolean,
|
||||||
|
preserveMetadata: PreserveMetadata,
|
||||||
preserveMovData: boolean,
|
preserveMovData: boolean,
|
||||||
|
preserveChapters: boolean,
|
||||||
movFastStart: boolean,
|
movFastStart: boolean,
|
||||||
customTagsByFile: CustomTagsByFile,
|
customTagsByFile: CustomTagsByFile,
|
||||||
paramsByStreamId: ParamsByStreamId,
|
paramsByStreamId: ParamsByStreamId,
|
||||||
@ -360,6 +362,19 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
return ret;
|
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 = [
|
const ffmpegArgs = [
|
||||||
'-hide_banner',
|
'-hide_banner',
|
||||||
// No progress if we set loglevel warning :(
|
// No progress if we set loglevel warning :(
|
||||||
@ -371,9 +386,9 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
|
|
||||||
...mapStreamsArgs,
|
...mapStreamsArgs,
|
||||||
|
|
||||||
'-map_metadata', '0',
|
...getPreserveMetadata(),
|
||||||
|
|
||||||
...(chaptersPath ? ['-map_chapters', String(chaptersInputIndex)] : []),
|
...getPreserveChapters(),
|
||||||
|
|
||||||
...(shortestFlag ? ['-shortest'] : []),
|
...(shortestFlag ? ['-shortest'] : []),
|
||||||
|
|
||||||
@ -404,7 +419,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
}, [appendFfmpegCommandLog, cutFromAdjustmentFrames, filePath, getOutputPlaybackRateArgs, shouldSkipExistingFile, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart]);
|
}, [appendFfmpegCommandLog, cutFromAdjustmentFrames, filePath, getOutputPlaybackRateArgs, shouldSkipExistingFile, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart]);
|
||||||
|
|
||||||
const cutMultiple = useCallback(async ({
|
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,
|
outputDir: string,
|
||||||
customOutDir: string | undefined,
|
customOutDir: string | undefined,
|
||||||
@ -420,13 +435,15 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
outFormat: string | undefined,
|
outFormat: string | undefined,
|
||||||
shortestFlag: boolean,
|
shortestFlag: boolean,
|
||||||
ffmpegExperimental: boolean,
|
ffmpegExperimental: boolean,
|
||||||
|
preserveMetadata: PreserveMetadata,
|
||||||
preserveMovData: boolean,
|
preserveMovData: boolean,
|
||||||
|
preserveMetadataOnMerge: boolean,
|
||||||
|
preserveChapters: boolean,
|
||||||
movFastStart: boolean,
|
movFastStart: boolean,
|
||||||
avoidNegativeTs: AvoidNegativeTs | undefined,
|
avoidNegativeTs: AvoidNegativeTs | undefined,
|
||||||
customTagsByFile: CustomTagsByFile,
|
customTagsByFile: CustomTagsByFile,
|
||||||
paramsByStreamId: ParamsByStreamId,
|
paramsByStreamId: ParamsByStreamId,
|
||||||
chapters: Chapter[] | undefined,
|
chapters: Chapter[] | undefined,
|
||||||
preserveMetadataOnMerge,
|
|
||||||
}) => {
|
}) => {
|
||||||
console.log('customTagsByFile', customTagsByFile);
|
console.log('customTagsByFile', customTagsByFile);
|
||||||
console.log('paramsByStreamId', paramsByStreamId);
|
console.log('paramsByStreamId', paramsByStreamId);
|
||||||
@ -456,7 +473,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
|
|||||||
const outPath = await makeSegmentOutPath();
|
const outPath = await makeSegmentOutPath();
|
||||||
invariant(outFormat != null);
|
invariant(outFormat != null);
|
||||||
await losslessCutSingle({
|
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;
|
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
|
// for smart cut we need to use keyframe cut here, and no avoid_negative_ts
|
||||||
await losslessCutSingle({
|
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)
|
// OK, just return the single cut file (we may need smart cut in other segments though)
|
||||||
|
@ -47,8 +47,14 @@ export default () => {
|
|||||||
useEffect(() => safeSetConfig({ customOutDir }), [customOutDir]);
|
useEffect(() => safeSetConfig({ customOutDir }), [customOutDir]);
|
||||||
const [keyframeCut, setKeyframeCut] = useState(safeGetConfigInitial('keyframeCut'));
|
const [keyframeCut, setKeyframeCut] = useState(safeGetConfigInitial('keyframeCut'));
|
||||||
useEffect(() => safeSetConfig({ keyframeCut }), [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'));
|
const [preserveMovData, setPreserveMovData] = useState(safeGetConfigInitial('preserveMovData'));
|
||||||
useEffect(() => safeSetConfig({ preserveMovData }), [preserveMovData]);
|
useEffect(() => safeSetConfig({ preserveMovData }), [preserveMovData]);
|
||||||
|
const [preserveChapters, setPreserveChapters] = useState(safeGetConfigInitial('preserveChapters'));
|
||||||
|
useEffect(() => safeSetConfig({ preserveChapters }), [preserveChapters]);
|
||||||
const [movFastStart, setMovFastStart] = useState(safeGetConfigInitial('movFastStart'));
|
const [movFastStart, setMovFastStart] = useState(safeGetConfigInitial('movFastStart'));
|
||||||
useEffect(() => safeSetConfig({ movFastStart }), [movFastStart]);
|
useEffect(() => safeSetConfig({ movFastStart }), [movFastStart]);
|
||||||
const [avoidNegativeTs, setAvoidNegativeTs] = useState(safeGetConfigInitial('avoidNegativeTs'));
|
const [avoidNegativeTs, setAvoidNegativeTs] = useState(safeGetConfigInitial('avoidNegativeTs'));
|
||||||
@ -91,8 +97,6 @@ export default () => {
|
|||||||
useEffect(() => safeSetConfig({ exportConfirmEnabled }), [exportConfirmEnabled]);
|
useEffect(() => safeSetConfig({ exportConfirmEnabled }), [exportConfirmEnabled]);
|
||||||
const [segmentsToChapters, setSegmentsToChapters] = useState(safeGetConfigInitial('segmentsToChapters'));
|
const [segmentsToChapters, setSegmentsToChapters] = useState(safeGetConfigInitial('segmentsToChapters'));
|
||||||
useEffect(() => safeSetConfig({ segmentsToChapters }), [segmentsToChapters]);
|
useEffect(() => safeSetConfig({ segmentsToChapters }), [segmentsToChapters]);
|
||||||
const [preserveMetadataOnMerge, setPreserveMetadataOnMerge] = useState(safeGetConfigInitial('preserveMetadataOnMerge'));
|
|
||||||
useEffect(() => safeSetConfig({ preserveMetadataOnMerge }), [preserveMetadataOnMerge]);
|
|
||||||
const [simpleMode, setSimpleMode] = useState(safeGetConfigInitial('simpleMode'));
|
const [simpleMode, setSimpleMode] = useState(safeGetConfigInitial('simpleMode'));
|
||||||
useEffect(() => safeSetConfig({ simpleMode }), [simpleMode]);
|
useEffect(() => safeSetConfig({ simpleMode }), [simpleMode]);
|
||||||
const [outSegTemplate, setOutSegTemplate] = useState(safeGetConfigInitial('outSegTemplate'));
|
const [outSegTemplate, setOutSegTemplate] = useState(safeGetConfigInitial('outSegTemplate'));
|
||||||
@ -180,8 +184,14 @@ export default () => {
|
|||||||
setCustomOutDir,
|
setCustomOutDir,
|
||||||
keyframeCut,
|
keyframeCut,
|
||||||
setKeyframeCut,
|
setKeyframeCut,
|
||||||
|
preserveMetadata,
|
||||||
|
setPreserveMetadata,
|
||||||
|
preserveMetadataOnMerge,
|
||||||
|
setPreserveMetadataOnMerge,
|
||||||
preserveMovData,
|
preserveMovData,
|
||||||
setPreserveMovData,
|
setPreserveMovData,
|
||||||
|
preserveChapters,
|
||||||
|
setPreserveChapters,
|
||||||
movFastStart,
|
movFastStart,
|
||||||
setMovFastStart,
|
setMovFastStart,
|
||||||
avoidNegativeTs,
|
avoidNegativeTs,
|
||||||
@ -224,8 +234,6 @@ export default () => {
|
|||||||
setExportConfirmEnabled,
|
setExportConfirmEnabled,
|
||||||
segmentsToChapters,
|
segmentsToChapters,
|
||||||
setSegmentsToChapters,
|
setSegmentsToChapters,
|
||||||
preserveMetadataOnMerge,
|
|
||||||
setPreserveMetadataOnMerge,
|
|
||||||
simpleMode,
|
simpleMode,
|
||||||
setSimpleMode,
|
setSimpleMode,
|
||||||
outSegTemplate,
|
outSegTemplate,
|
||||||
|
6
types.ts
6
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 ModifierKey = 'ctrl' | 'shift' | 'alt' | 'meta';
|
||||||
|
|
||||||
|
export type PreserveMetadata = 'default' | 'nonglobal' | 'none'
|
||||||
|
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
captureFormat: CaptureFormat,
|
captureFormat: CaptureFormat,
|
||||||
@ -65,6 +67,9 @@ export interface Config {
|
|||||||
wheelSensitivity: number,
|
wheelSensitivity: number,
|
||||||
language: LanguageKey | undefined,
|
language: LanguageKey | undefined,
|
||||||
ffmpegExperimental: boolean,
|
ffmpegExperimental: boolean,
|
||||||
|
preserveChapters: boolean,
|
||||||
|
preserveMetadata: PreserveMetadata,
|
||||||
|
preserveMetadataOnMerge: boolean,
|
||||||
preserveMovData: boolean,
|
preserveMovData: boolean,
|
||||||
movFastStart: boolean,
|
movFastStart: boolean,
|
||||||
avoidNegativeTs: AvoidNegativeTs,
|
avoidNegativeTs: AvoidNegativeTs,
|
||||||
@ -72,7 +77,6 @@ export interface Config {
|
|||||||
hideOsNotifications: 'all' | undefined,
|
hideOsNotifications: 'all' | undefined,
|
||||||
autoLoadTimecode: boolean,
|
autoLoadTimecode: boolean,
|
||||||
segmentsToChapters: boolean,
|
segmentsToChapters: boolean,
|
||||||
preserveMetadataOnMerge: boolean,
|
|
||||||
simpleMode: boolean,
|
simpleMode: boolean,
|
||||||
outSegTemplate: string | undefined,
|
outSegTemplate: string | undefined,
|
||||||
mergedFileTemplate: string | undefined,
|
mergedFileTemplate: string | undefined,
|
||||||
|
Loading…
Reference in New Issue
Block a user