filter out initial segment from export

to prevent cutting when not needed (if duration changes)
pull/2233/head
Mikael Finstad 1 year ago
parent 99e4d497bd
commit ccf3e5ab04
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26

@ -337,7 +337,7 @@ function App() {
}, [isFileOpened]);
const {
cutSegments, cutSegmentsHistory, createSegmentsFromKeyframes, shuffleSegments, detectBlackScenes, detectSilentScenes, detectSceneChanges, removeSegment, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, combineSelectedSegments, shiftAllSegmentTimes, alignSegmentTimesToKeyframes, updateSegOrder, updateSegOrders, reorderSegsByStartTime, addSegment, setCutStart, setCutEnd, labelSegment, splitCurrentSegment, focusSegmentAtCursor, createNumSegments, createFixedDurationSegments, createFixedByteSizedSegments, createRandomSegments, haveInvalidSegs, currentSegIndexSafe, currentCutSeg, inverseCutSegments, clearSegments, clearSegColorCounter, loadCutSegments, isSegmentSelected, setCutTime, setCurrentSegIndex, labelSelectedSegments, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, toggleCurrentSegmentSelected, invertSelectedSegments, removeSelectedSegments, setDeselectedSegmentIds, selectSegmentsByLabel, selectSegmentsByExpr, selectAllMarkers, mutateSegmentsByExpr, toggleSegmentSelected, selectOnlySegment, selectedSegments, selectedSegmentsOrInverse, nonFilteredSegmentsOrInverse, segmentsToExport, duplicateCurrentSegment, duplicateSegment, updateSegAtIndex, findSegmentsAtCursor, maybeCreateFullLengthSegment,
cutSegments, cutSegmentsHistory, createSegmentsFromKeyframes, shuffleSegments, detectBlackScenes, detectSilentScenes, detectSceneChanges, removeSegment, invertAllSegments, fillSegmentsGaps, combineOverlappingSegments, combineSelectedSegments, shiftAllSegmentTimes, alignSegmentTimesToKeyframes, updateSegOrder, updateSegOrders, reorderSegsByStartTime, addSegment, setCutStart, setCutEnd, labelSegment, splitCurrentSegment, focusSegmentAtCursor, createNumSegments, createFixedDurationSegments, createFixedByteSizedSegments, createRandomSegments, haveInvalidSegs, currentSegIndexSafe, currentCutSeg, inverseCutSegments, clearSegments, clearSegColorCounter, loadCutSegments, isSegmentSelected, setCutTime, setCurrentSegIndex, labelSelectedSegments, deselectAllSegments, selectAllSegments, selectOnlyCurrentSegment, toggleCurrentSegmentSelected, invertSelectedSegments, removeSelectedSegments, setDeselectedSegmentIds, selectSegmentsByLabel, selectSegmentsByExpr, selectAllMarkers, mutateSegmentsByExpr, toggleSegmentSelected, selectOnlySegment, selectedSegments, segmentsOrInverse, segmentsToExport, duplicateCurrentSegment, duplicateSegment, updateSegAtIndex, findSegmentsAtCursor, maybeCreateFullLengthSegment,
} = useSegments({ filePath, workingRef, setWorking, setProgress, videoStream: activeVideoStream, fileDuration, getRelevantTime, maxLabelLength, checkFileOpened, invertCutSegments, segmentsToChaptersOnly, timecodePlaceholder, parseTimecode, appendFfmpegCommandLog, fileDurationNonZero, mainFileMeta });
const { getEdlFilePath, projectFileSavePath, getProjectFileSavePath } = useSegmentsAutoSave({ autoSaveProjectFile, storeProjectInWorkingDir, filePath, customOutDir, cutSegments });
@ -1045,7 +1045,7 @@ function App() {
// Special segments-to-chapters mode:
let chaptersToAdd: Chapter[] | undefined;
if (segmentsToChaptersOnly) {
const sortedSegments = sortSegments(selectedSegmentsOrInverse);
const sortedSegments = sortSegments(segmentsOrInverse.selected);
if (hasAnySegmentOverlap(sortedSegments)) {
errorToast(i18n.t('Make sure you have no overlapping segments.'));
return;
@ -1191,7 +1191,7 @@ function App() {
setWorking(undefined);
setProgress(undefined);
}
}, [filePath, numStreamsToCopy, segmentsToExport, haveInvalidSegs, workingRef, setWorking, segmentsToChaptersOnly, outSegTemplateOrDefault, generateOutSegFileNames, cutMultiple, outputDir, customOutDir, fileFormat, fileDuration, 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, t, mergedFileTemplateOrDefault, segmentsToChapters, invertCutSegments, generateMergedFileNames, autoConcatCutSegments, autoDeleteMergedSegments, nonCopiedExtraStreams, extractStreams, showOsNotification, handleExportFailed]);
}, [filePath, numStreamsToCopy, segmentsToExport, haveInvalidSegs, workingRef, setWorking, segmentsToChaptersOnly, outSegTemplateOrDefault, generateOutSegFileNames, cutMultiple, outputDir, customOutDir, fileFormat, fileDuration, 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, segmentsOrInverse, t, mergedFileTemplateOrDefault, segmentsToChapters, invertCutSegments, generateMergedFileNames, autoConcatCutSegments, autoDeleteMergedSegments, nonCopiedExtraStreams, extractStreams, showOsNotification, handleExportFailed]);
const onExportPress = useCallback(async () => {
if (!filePath) return;
@ -2440,7 +2440,7 @@ function App() {
numStreamsToCopy={numStreamsToCopy}
numStreamsTotal={numStreamsTotal}
setStreamsSelectorShown={setStreamsSelectorShown}
selectedSegments={selectedSegmentsOrInverse}
selectedSegments={segmentsOrInverse.selected}
/>
<div style={{ flexGrow: 1, display: 'flex', overflowY: 'hidden' }}>
@ -2559,7 +2559,7 @@ function App() {
toggleSegmentsList={toggleSegmentsList}
splitCurrentSegment={splitCurrentSegment}
isSegmentSelected={isSegmentSelected}
selectedSegments={selectedSegmentsOrInverse}
selectedSegments={segmentsOrInverse.selected}
onSelectSingleSegment={selectOnlySegment}
onToggleSegmentSelected={toggleSegmentSelected}
onDeselectAllSegments={deselectAllSegments}
@ -2677,7 +2677,7 @@ function App() {
{/* Dialogs */}
<ExportConfirm areWeCutting={areWeCutting} nonFilteredSegmentsOrInverse={nonFilteredSegmentsOrInverse} selectedSegments={selectedSegmentsOrInverse} segmentsToExport={segmentsToExport} willMerge={willMerge} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} renderOutFmt={renderOutFmt} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} onShowStreamsSelectorClick={handleShowStreamsSelectorClick} outFormat={fileFormat} setOutSegTemplate={setOutSegTemplate} outSegTemplate={outSegTemplateOrDefault} mergedFileTemplate={mergedFileTemplateOrDefault} setMergedFileTemplate={setMergedFileTemplate} generateOutSegFileNames={generateOutSegFileNames} generateMergedFileNames={generateMergedFileNames} currentSegIndexSafe={currentSegIndexSafe} mainCopiedThumbnailStreams={mainCopiedThumbnailStreams} needSmartCut={needSmartCut} smartCutBitrate={smartCutBitrate} setSmartCutBitrate={setSmartCutBitrate} toggleSettings={toggleSettings} outputPlaybackRate={outputPlaybackRate} />
<ExportConfirm areWeCutting={areWeCutting} segmentsOrInverse={segmentsOrInverse} segmentsToExport={segmentsToExport} willMerge={willMerge} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} renderOutFmt={renderOutFmt} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} onShowStreamsSelectorClick={handleShowStreamsSelectorClick} outFormat={fileFormat} setOutSegTemplate={setOutSegTemplate} outSegTemplate={outSegTemplateOrDefault} mergedFileTemplate={mergedFileTemplateOrDefault} setMergedFileTemplate={setMergedFileTemplate} generateOutSegFileNames={generateOutSegFileNames} generateMergedFileNames={generateMergedFileNames} currentSegIndexSafe={currentSegIndexSafe} mainCopiedThumbnailStreams={mainCopiedThumbnailStreams} needSmartCut={needSmartCut} smartCutBitrate={smartCutBitrate} setSmartCutBitrate={setSmartCutBitrate} toggleSettings={toggleSettings} outputPlaybackRate={outputPlaybackRate} />
<Sheet visible={streamsSelectorShown} onClosePress={() => setStreamsSelectorShown(false)} maxWidth={1000}>
{mainStreams && filePath != null && (

@ -17,7 +17,7 @@ import { useSegColors } from './contexts';
import { mySpring } from './animations';
import { getSegmentTags } from './segments';
import TagEditor from './components/TagEditor';
import { ContextMenuTemplate, FormatTimecode, GetFrameCount, InverseCutSegment, SegmentBase, SegmentTags, StateSegment } from './types';
import { ContextMenuTemplate, DefiniteSegmentBase, FormatTimecode, GetFrameCount, InverseCutSegment, SegmentBase, SegmentTags, StateSegment } from './types';
import { UseSegments } from './hooks/useSegments';
@ -290,7 +290,7 @@ function SegmentList({
segmentAtCursor: StateSegment | undefined,
toggleSegmentsList: () => void,
splitCurrentSegment: UseSegments['splitCurrentSegment'],
selectedSegments: UseSegments['selectedSegmentsOrInverse'],
selectedSegments: DefiniteSegmentBase[],
isSegmentSelected: UseSegments['isSegmentSelected'],
onSelectSingleSegment: UseSegments['selectOnlySegment'],
onToggleSegmentSelected: UseSegments['toggleSegmentSelected'],

@ -21,11 +21,12 @@ import { toast } from '../swal';
import { isMov as ffmpegIsMov } from '../util/streams';
import useUserSettings from '../hooks/useUserSettings';
import styles from './ExportConfirm.module.css';
import { SegmentBase, SegmentToExport } from '../types';
import { SegmentToExport } from '../types';
import { defaultMergedFileTemplate, defaultOutSegTemplate, GenerateOutFileNames } from '../util/outputNameTemplate';
import { FFprobeStream } from '../../../../ffprobe';
import { AvoidNegativeTs, PreserveMetadata } from '../../../../types';
import TextInput from './TextInput';
import { UseSegments } from '../hooks/useSegments';
const boxStyle: CSSProperties = { margin: '15px 15px 50px 15px', borderRadius: 10, padding: '10px 20px', minHeight: 500, position: 'relative' };
@ -52,7 +53,6 @@ function ShiftTimes({ values, num, setNum }: { values: number[], num: number, se
function ExportConfirm({
areWeCutting,
selectedSegments,
segmentsToExport,
willMerge,
visible,
@ -71,7 +71,7 @@ function ExportConfirm({
generateOutSegFileNames,
generateMergedFileNames,
currentSegIndexSafe,
nonFilteredSegmentsOrInverse,
segmentsOrInverse,
mainCopiedThumbnailStreams,
needSmartCut,
smartCutBitrate,
@ -80,7 +80,6 @@ function ExportConfirm({
outputPlaybackRate,
} : {
areWeCutting: boolean,
selectedSegments: unknown[],
segmentsToExport: SegmentToExport[],
willMerge: boolean,
visible: boolean,
@ -99,7 +98,7 @@ function ExportConfirm({
generateOutSegFileNames: GenerateOutFileNames,
generateMergedFileNames: GenerateOutFileNames,
currentSegIndexSafe: number,
nonFilteredSegmentsOrInverse: SegmentBase[],
segmentsOrInverse: UseSegments['segmentsOrInverse'],
mainCopiedThumbnailStreams: FFprobeStream[],
needSmartCut: boolean,
smartCutBitrate: number | undefined,
@ -266,10 +265,10 @@ function ExportConfirm({
</tr>
))}
{selectedSegments.length !== nonFilteredSegmentsOrInverse.length && (
{segmentsOrInverse.selected.length !== segmentsOrInverse.all.length && (
<tr>
<td colSpan={2}>
<FaRegCheckCircle size={12} style={{ marginRight: 3 }} />{t('{{selectedSegments}} of {{nonFilteredSegments}} segments selected', { selectedSegments: selectedSegments.length, nonFilteredSegments: nonFilteredSegmentsOrInverse.length })}
<FaRegCheckCircle size={12} style={{ marginRight: 3 }} />{t('{{selectedSegments}} of {{nonFilteredSegments}} segments selected', { selectedSegments: segmentsOrInverse.selected.length, nonFilteredSegments: segmentsOrInverse.all.length })}
</td>
<td />
</tr>
@ -277,10 +276,10 @@ function ExportConfirm({
<tr>
<td>
{selectedSegments.length > 1 ? t('Export mode for {{segments}} segments', { segments: selectedSegments.length }) : t('Export mode')}
{segmentsOrInverse.selected.length > 1 ? t('Export mode for {{segments}} segments', { segments: segmentsOrInverse.selected.length }) : t('Export mode')}
</td>
<td>
<ExportModeButton selectedSegments={selectedSegments} />
<ExportModeButton selectedSegments={segmentsOrInverse.selected} />
</td>
<td>
{effectiveExportMode === 'segments_to_chapters' ? (

@ -13,7 +13,7 @@ import { createNumSegments as createNumSegmentsDialog, createFixedByteSixedSegme
import { createSegment, sortSegments, invertSegments, combineOverlappingSegments as combineOverlappingSegments2, combineSelectedSegments as combineSelectedSegments2, isDurationValid, addSegmentColorIndex, filterNonMarkers, makeDurationSegments, isInitialSegment } from '../segments';
import { parameters as allFfmpegParameters, FfmpegDialog } from '../ffmpegParameters';
import { maxSegmentsAllowed } from '../util/constants';
import { ParseTimecode, SegmentBase, segmentTagsSchema, SegmentToExport, StateSegment, UpdateSegAtIndex } from '../types';
import { DefiniteSegmentBase, ParseTimecode, SegmentBase, segmentTagsSchema, SegmentToExport, StateSegment, UpdateSegAtIndex } from '../types';
import safeishEval from '../worker/eval';
import { ScopeSegment } from '../../../../types';
import { FFprobeFormat, FFprobeStream } from '../../../../ffprobe';
@ -256,7 +256,8 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
if (haveInvalidSegs || !isDurationValid(fileDuration)) return [];
// exclude segments that don't have a length (markers)
const sortedSegments = sortSegments(filterNonMarkers(cutSegments));
// also exclude initial segment (will cause problems later on)
const sortedSegments = sortSegments(filterNonMarkers(cutSegments).filter((seg) => !seg.initial));
return invertSegments(sortedSegments, true, true, fileDuration).map(({ segId, end, ...rest }) => {
// in order to please TS:
@ -701,26 +702,30 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
});
}, [createIndexedSegment, setCutSegments]);
const selectedSegmentsOrInverse = useMemo<{ start: number, end: number }[]>(() => {
const segmentsOrInverse = useMemo<{ selected: DefiniteSegmentBase[], all: DefiniteSegmentBase[] }>(() => {
// For invertCutSegments we do not support filtering (selecting) segments
if (invertCutSegments) return inverseCutSegments;
// exclude markers (segments without end)
return filterNonMarkers(selectedSegments);
}, [inverseCutSegments, invertCutSegments, selectedSegments]);
if (invertCutSegments) {
return {
selected: inverseCutSegments,
all: inverseCutSegments,
};
}
// non filtered includes also non selected
const nonFilteredSegmentsOrInverse = useMemo(() => (invertCutSegments
? inverseCutSegments
: filterNonMarkers(cutSegments)
), [invertCutSegments, inverseCutSegments, cutSegments]);
return {
// exclude markers (segments without any end)
// and exclude the initial segment, to prevent cutting when not really needed (if duration changes after the segment was created)
selected: filterNonMarkers(selectedSegments).filter((seg) => !seg.initial),
// `all` includes also all non selected segments:
all: filterNonMarkers(cutSegments).filter((seg) => !seg.initial),
};
}, [cutSegments, inverseCutSegments, invertCutSegments, selectedSegments]);
const segmentsToExport = useMemo<SegmentToExport[]>(() => {
// segmentsToChaptersOnly is a special mode where all segments will be simply written out as chapters to one file: https://github.com/mifi/lossless-cut/issues/993#issuecomment-1037927595
// Chapters export mode: Emulate a single segment with no cuts (full timeline)
if (segmentsToChaptersOnly) return [];
return selectedSegmentsOrInverse;
}, [segmentsToChaptersOnly, selectedSegmentsOrInverse]);
return segmentsOrInverse.selected;
}, [segmentsOrInverse.selected, segmentsToChaptersOnly]);
const removeSelectedSegments = useCallback(() => removeSegments(selectedSegmentsRaw.map((seg) => seg.segId)), [removeSegments, selectedSegmentsRaw]);
@ -779,8 +784,7 @@ function useSegments({ filePath, workingRef, setWorking, setProgress, videoStrea
loadCutSegments,
isSegmentSelected,
selectedSegments,
selectedSegmentsOrInverse,
nonFilteredSegmentsOrInverse,
segmentsOrInverse,
segmentsToExport,
maybeCreateFullLengthSegment,

@ -53,6 +53,11 @@ export interface SegmentBase {
name?: string | undefined,
}
export interface DefiniteSegmentBase {
start: number,
end: number,
}
export interface SegmentColorIndex {
segColorIndex: number,
}
@ -64,16 +69,12 @@ export interface StateSegment extends SegmentBase, SegmentColorIndex {
initial?: true,
}
export interface SegmentToExport {
start: number,
end: number,
export interface SegmentToExport extends DefiniteSegmentBase {
name?: string | undefined;
tags?: SegmentTags | undefined;
}
export interface InverseCutSegment {
start: number,
end: number,
export interface InverseCutSegment extends DefiniteSegmentBase {
segId: string;
}

Loading…
Cancel
Save