@ -356,9 +356,6 @@ const App = memo(() => {
const currentSegIndexSafe = Math . min ( currentSegIndex , cutSegments . length - 1 ) ;
const currentCutSeg = useMemo ( ( ) => cutSegments [ currentSegIndexSafe ] , [ currentSegIndexSafe , cutSegments ] ) ;
const currentApparentCutSeg = useMemo ( ( ) => apparentCutSegments [ currentSegIndexSafe ] , [ apparentCutSegments , currentSegIndexSafe ] ) ;
const areWeCutting = apparentCutSegments . length > 1
|| isCuttingStart ( currentApparentCutSeg . start )
|| isCuttingEnd ( currentApparentCutSeg . end , duration ) ;
const jumpSegStart = useCallback ( ( index ) => seekAbs ( apparentCutSegments [ index ] . start ) , [ apparentCutSegments , seekAbs ] ) ;
const jumpSegEnd = useCallback ( ( index ) => seekAbs ( apparentCutSegments [ index ] . end ) , [ apparentCutSegments , seekAbs ] ) ;
@ -1023,13 +1020,17 @@ const App = memo(() => {
}
} , [ previewFilePath , filePath , edlFilePath , cleanupChoices , isFileOpened , resetState , removeBatchFile , setWorking ] ) ;
const out Segments = useMemo ( ( ) => ( invertCutSegments ? inverseCutSegments : apparentCutSegments ) ,
const inverseOrNormal Segments = useMemo ( ( ) => ( invertCutSegments ? inverseCutSegments : apparentCutSegments ) ,
[ invertCutSegments , inverseCutSegments , apparentCutSegments ] ) ;
/ / F o r i n v e r t C u t S e g m e n t s w e d o n o t s u p p o r t f i l t e r i n g
const enabledOutSegmentsRaw = useMemo ( ( ) => ( invertCutSegments ? outSegments : outSegments . filter ( ( s ) => ! disabledSegmentIds [ s . segId ] ) ) , [ outSegments , invertCutSegments , disabledSegmentIds ] ) ;
const enabledSegmentsRaw = useMemo ( ( ) => {
/ / F o r i n v e r t C u t S e g m e n t s w e d o n o t s u p p o r t f i l t e r i n g
if ( invertCutSegments ) return inverseCutSegments ;
return apparentCutSegments . filter ( ( s ) => ! disabledSegmentIds [ s . segId ] ) ;
} , [ invertCutSegments , inverseCutSegments , apparentCutSegments , disabledSegmentIds ] ) ;
/ / I f u s e r h a s s e l e c t e d n o n e t o e x p o r t , i t m a k e s n o s e n s e , s o e x p o r t a l l i n s t e a d
const enabledOutSegments = enabledOutSegmentsRaw . length > 0 ? enabledOutSegmentsRaw : outSegments ;
const enabled Segments = enabled SegmentsRaw. length > 0 ? enabled SegmentsRaw : inverseOrNormal Segments;
const onExportSingleSegmentClick = useCallback ( ( activeSeg ) => setDisabledSegmentIds ( Object . fromEntries ( cutSegments . filter ( ( s ) => s . segId !== activeSeg . segId ) . map ( ( s ) => [ s . segId , true ] ) ) ) , [ cutSegments ] ) ;
const onExportSegmentEnabledToggle = useCallback ( ( toggleSeg ) => setDisabledSegmentIds ( ( existing ) => ( { ... existing , [ toggleSeg . segId ] : ! existing [ toggleSeg . segId ] } ) ) , [ ] ) ;
@ -1038,7 +1039,16 @@ const App = memo(() => {
const filenamifyOrNot = useCallback ( ( name ) => ( safeOutputFileName ? filenamify ( name ) : name ) . substr ( 0 , maxLabelLength ) , [ safeOutputFileName , maxLabelLength ] ) ;
const generateOutSegFileNames = useCallback ( ( { segments = enabledOutSegments , template } ) => (
const segmentsToExport = useMemo ( ( ) => {
/ / T h i s i s a s p e c i a l m o d e w h e r e a l l s e g m e n t s w i l l b e s i m p l y w r i t t e n o u t a s c h a p t e r s t o o n e f i l e : h t t p s : / / g i t h u b . c o m / m i f i / l o s s l e s s - c u t / i s s u e s / 9 9 3 # i s s u e c o m m e n t - 1 0 3 7 9 2 7 5 9 5
if ( ! segmentsToChaptersOnly ) return enabledSegments ;
/ / C h a p t e r s e x p o r t m o d e : E m u l a t e a s i n g l e s e g m e n t w i t h n o c u t s ( f u l l t i m e l i n e )
return [ { start : 0 , end : getSegApparentEnd ( { } ) } ] ;
} , [ enabledSegments , getSegApparentEnd , segmentsToChaptersOnly ] ) ;
const areWeCutting = useMemo ( ( ) => segmentsToExport . some ( ( { start , end } ) => isCuttingStart ( start ) || isCuttingEnd ( end , duration ) ) , [ duration , segmentsToExport ] ) ;
const generateOutSegFileNames = useCallback ( ( { segments = segmentsToExport , template } ) => (
segments . map ( ( segment , i ) => {
const { start , end , name = '' } = segment ;
const cutFromStr = formatDuration ( { seconds : start , fileNameFriendly : true } ) ;
@ -1061,7 +1071,7 @@ const App = memo(() => {
const generated = generateSegFileName ( { template , segSuffix , inputFileNameWithoutExt : fileNameWithoutExt , ext , segNum , segLabel : nameSanitized , cutFrom : cutFromStr , cutTo : cutToStr , tags : tagsSanitized } ) ;
return safeOutputFileName ? generated . substr ( 0 , 200 ) : generated ; / / I f s a n i t a t i o n i s e n a b l e d , m a k e s u r e f i l e n a m e i s n o t t o o l o n g
} )
) , [ enabledOutSegments , filenamifyOrNot , isCustomFormatSelected , fileFormat , filePath , safeOutputFileName ] ) ;
) , [ segmentsToExport , filenamifyOrNot , isCustomFormatSelected , fileFormat , filePath , safeOutputFileName ] ) ;
/ / T O D O i m p r o v e u s e r f e e d b a c k
const isOutSegFileNamesValid = useCallback ( ( fileNames ) => fileNames . every ( ( fileName ) => {
@ -1107,6 +1117,8 @@ const App = memo(() => {
const closeExportConfirm = useCallback ( ( ) => setExportConfirmVisible ( false ) , [ ] ) ;
const willMerge = segmentsToExport . length > 1 && autoMerge ;
const onExportConfirm = useCallback ( async ( ) => {
if ( numStreamsToCopy === 0 ) {
errorToast ( i18n . t ( 'No tracks selected for export' ) ) ;
@ -1120,15 +1132,8 @@ const App = memo(() => {
try {
setWorking ( i18n . t ( 'Exporting' ) ) ;
/ / T h i s i s a s p e c i a l m o d e w h e r e s e g m e n t s w i l l b e s i m p l y w r i t t e n o u t a s c h a p t e r s : h t t p s : / / g i t h u b . c o m / m i f i / l o s s l e s s - c u t / i s s u e s / 9 9 3 # i s s u e c o m m e n t - 1 0 3 7 9 2 7 5 9 5
let chaptersToAdd ;
let segmentsToExport = enabledOutSegments ;
if ( segmentsToChaptersOnly ) {
if ( invertCutSegments ) throw new Error ( 'Inverted cut segments not supported for chapters only export' ) ;
/ / E m u l a t e a s i n g l e s e g m e n t w i t h n o c u t s ( f u l l t i m e l i n e )
segmentsToExport = [ { start : 0 , end : getSegApparentEnd ( { } ) } ] ;
chaptersToAdd = sortBy ( enabledOutSegments , 'start' ) . map ( ( segment ) => ( { start : segment . start , end : segment . end , name : segment . name } ) ) ;
}
/ / S p e c i a l s e g m e n t s - t o - c h a p t e r s m o d e :
const chaptersToAdd = segmentsToChaptersOnly && sortBy ( enabledSegments , 'start' ) . map ( ( segment ) => ( { start : segment . start , end : segment . end , name : segment . name } ) ) ;
console . log ( 'outSegTemplateOrDefault' , outSegTemplateOrDefault ) ;
@ -1161,7 +1166,7 @@ const App = memo(() => {
chapters : chaptersToAdd ,
} ) ;
if ( outFiles. length > 1 && auto Merge) {
if ( will Merge) {
setCutProgress ( 0 ) ;
setWorking ( i18n . t ( 'Merging' ) ) ;
@ -1215,7 +1220,7 @@ const App = memo(() => {
setWorking ( ) ;
setCutProgress ( ) ;
}
} , [ numStreamsToCopy , setWorking , enabledOutSegments, segmentsToChaptersOnly, outSegTemplateOrDefault, generateOutSegFileNames , isOutSegFileNamesValid , cutMultiple , outputDir , fileFormat , duration , isRotationSet , effectiveRotation , copyFileStreams , keyframeCut , shortestFlag , ffmpegExperimental , preserveMovData , movFastStart , avoidNegativeTs , customTagsByFile , customTagsByStreamId , dispositionByStreamId , auto Merge, fileFormatData , mainStreams , exportExtraStreams , hideAllNotifications , invertCutSegments, getSegApparentEnd , segmentsToChapter s, autoMergeSegments , customOutDir , isCustomFormatSelected , autoDeleteMergedSegments , preserveMetadataOnMerge , filePath , nonCopiedExtraStreams , handleCutFailed ] ) ;
} , [ numStreamsToCopy , setWorking , segmentsToChaptersOnly, enabledSegments, outSegTemplateOrDefault, generateOutSegFileNames , segmentsToExport , isOutSegFileNamesValid , cutMultiple , outputDir , fileFormat , duration , isRotationSet , effectiveRotation , copyFileStreams , keyframeCut , shortestFlag , ffmpegExperimental , preserveMovData , movFastStart , avoidNegativeTs , customTagsByFile , customTagsByStreamId , dispositionByStreamId , will Merge, fileFormatData , mainStreams , exportExtraStreams , hideAllNotifications , segmentsToChapters, invertCutSegment s, autoMergeSegments , customOutDir , isCustomFormatSelected , autoDeleteMergedSegments , preserveMetadataOnMerge , filePath , nonCopiedExtraStreams , handleCutFailed ] ) ;
const onExportPress = useCallback ( async ( ) => {
if ( ! filePath || workingRef . current ) return ;
@ -1225,14 +1230,14 @@ const App = memo(() => {
return ;
}
if ( enabledOutSegments . length < 1 ) {
if ( segmentsToExport . length < 1 ) {
errorToast ( i18n . t ( 'No segments to export' ) ) ;
return ;
}
if ( exportConfirmEnabled ) setExportConfirmVisible ( true ) ;
else await onExportConfirm ( ) ;
} , [ filePath , haveInvalidSegs , enabledOutSegments , exportConfirmEnabled , onExportConfirm ] ) ;
} , [ filePath , haveInvalidSegs , segmentsToExport , exportConfirmEnabled , onExportConfirm ] ) ;
const capture = useCallback ( async ( ) => {
if ( ! filePath ) return ;
@ -2258,7 +2263,7 @@ const App = memo(() => {
numStreamsToCopy = { numStreamsToCopy }
numStreamsTotal = { numStreamsTotal }
setStreamsSelectorShown = { setStreamsSelectorShown }
enabled Out Segments= { enabled Out Segments}
enabled Segments= { enabled Segments}
autoMerge = { autoMerge }
setAutoMerge = { setAutoMerge }
autoDeleteMergedSegments = { autoDeleteMergedSegments }
@ -2395,8 +2400,8 @@ const App = memo(() => {
< SegmentList
simpleMode = { simpleMode }
currentSegIndex = { currentSegIndexSafe }
outSegments= { o utSegments}
cutSegments= { apparent CutSegments}
apparentCutSegments= { apparentC utSegments}
inverseCutSegments= { inverse CutSegments}
getFrameCount = { getFrameCount }
formatTimecode = { formatTimecode }
invertCutSegments = { invertCutSegments }
@ -2410,8 +2415,8 @@ const App = memo(() => {
removeCutSegment = { removeCutSegment }
toggleSideBar = { toggleRightBar }
splitCurrentSegment = { splitCurrentSegment }
enabled Out SegmentsRaw= { enabled Out SegmentsRaw}
enabled Out Segments= { enabled Out Segments}
enabled SegmentsRaw= { enabled SegmentsRaw}
enabled Segments= { enabled Segments}
onExportSingleSegmentClick = { onExportSingleSegmentClick }
onExportSegmentEnabledToggle = { onExportSegmentEnabledToggle }
onExportSegmentDisableAll = { onExportSegmentDisableAll }
@ -2473,7 +2478,7 @@ const App = memo(() => {
renderCaptureFormatButton = { renderCaptureFormatButton }
capture = { capture }
onExportPress = { onExportPress }
enabled Out Segments= { enabled Out Segments}
enabled Segments= { enabled Segments}
exportConfirmEnabled = { exportConfirmEnabled }
toggleExportConfirmEnabled = { toggleExportConfirmEnabled }
seekAbs = { seekAbs }
@ -2539,7 +2544,7 @@ const App = memo(() => {
/ >
< / SideSheet >
< ExportConfirm filePath = { filePath } autoMerge = { autoMerge } setAutoMerge = { setAutoMerge } areWeCutting = { areWeCutting } enabled Out Segments= { enabled Out Segments} visible = { exportConfirmVisible } onClosePress = { closeExportConfirm } onExportConfirm = { onExportConfirm } keyframeCut = { keyframeCut } toggleKeyframeCut = { toggleKeyframeCut } renderOutFmt = { renderOutFmt } preserveMovData = { preserveMovData } togglePreserveMovData = { togglePreserveMovData } movFastStart = { movFastStart } toggleMovFastStart = { toggleMovFastStart } avoidNegativeTs = { avoidNegativeTs } setAvoidNegativeTs = { setAvoidNegativeTs } changeOutDir = { changeOutDir } outputDir = { outputDir } numStreamsTotal = { numStreamsTotal } numStreamsToCopy = { numStreamsToCopy } setStreamsSelectorShown = { setStreamsSelectorShown } exportConfirmEnabled = { exportConfirmEnabled } toggleExportConfirmEnabled = { toggleExportConfirmEnabled } segmentsToChapters = { segmentsToChapters } toggleSegmentsToChapters = { toggleSegmentsToChapters } outFormat = { fileFormat } preserveMetadataOnMerge = { preserveMetadataOnMerge } togglePreserveMetadataOnMerge = { togglePreserveMetadataOnMerge } setOutSegTemplate = { setOutSegTemplate } outSegTemplate = { outSegTemplateOrDefault } generateOutSegFileNames = { generateOutSegFileNames } currentSegIndexSafe = { currentSegIndexSafe } isOutSegFileNamesValid = { isOutSegFileNamesValid } autoDeleteMergedSegments = { autoDeleteMergedSegments } setAutoDeleteMergedSegments = { setAutoDeleteMergedSegments } safeOutputFileName = { safeOutputFileName } toggleSafeOutputFileName = { toggleSafeOutputFileName } segmentsToChaptersOnly = { segmentsToChaptersOnly } setSegmentsToChaptersOnly = { setSegmentsToChaptersOnly } / >
< ExportConfirm filePath = { filePath } autoMerge = { autoMerge } setAutoMerge = { setAutoMerge } areWeCutting = { areWeCutting } enabled Segments= { enabled Segments} willMerge = { willMerge } visible = { exportConfirmVisible } onClosePress = { closeExportConfirm } onExportConfirm = { onExportConfirm } keyframeCut = { keyframeCut } toggleKeyframeCut = { toggleKeyframeCut } renderOutFmt = { renderOutFmt } preserveMovData = { preserveMovData } togglePreserveMovData = { togglePreserveMovData } movFastStart = { movFastStart } toggleMovFastStart = { toggleMovFastStart } avoidNegativeTs = { avoidNegativeTs } setAvoidNegativeTs = { setAvoidNegativeTs } changeOutDir = { changeOutDir } outputDir = { outputDir } numStreamsTotal = { numStreamsTotal } numStreamsToCopy = { numStreamsToCopy } setStreamsSelectorShown = { setStreamsSelectorShown } exportConfirmEnabled = { exportConfirmEnabled } toggleExportConfirmEnabled = { toggleExportConfirmEnabled } segmentsToChapters = { segmentsToChapters } toggleSegmentsToChapters = { toggleSegmentsToChapters } outFormat = { fileFormat } preserveMetadataOnMerge = { preserveMetadataOnMerge } togglePreserveMetadataOnMerge = { togglePreserveMetadataOnMerge } setOutSegTemplate = { setOutSegTemplate } outSegTemplate = { outSegTemplateOrDefault } generateOutSegFileNames = { generateOutSegFileNames } currentSegIndexSafe = { currentSegIndexSafe } isOutSegFileNamesValid = { isOutSegFileNamesValid } autoDeleteMergedSegments = { autoDeleteMergedSegments } setAutoDeleteMergedSegments = { setAutoDeleteMergedSegments } safeOutputFileName = { safeOutputFileName } toggleSafeOutputFileName = { toggleSafeOutputFileName } segmentsToChaptersOnly = { segmentsToChaptersOnly } setSegmentsToChaptersOnly = { setSegmentsToChaptersOnly } / >
< HelpSheet
visible = { helpVisible }