@ -79,7 +79,7 @@ import {
import { toast , errorToast , showPlaybackFailedMessage } from './swal' ;
import { toast , errorToast , showPlaybackFailedMessage } from './swal' ;
import { adjustRate } from './util/rate-calculator' ;
import { adjustRate } from './util/rate-calculator' ;
import { askExtractFramesAsImages } from './dialogs/extractFrames' ;
import { askExtractFramesAsImages } from './dialogs/extractFrames' ;
import { askForOutDir , askForImportChapters , askForFileOpenAction , showCleanupFilesDialog , showDiskFull , showExportFailedDialog , showConcatFailedDialog , openYouTubeChaptersDialog , showRefuseToOverwrite , openDirToast, openExportFinishedToast , openConcatFinishedToast , showOpenDialog, showMuxNotSupported , promptDownloadMediaUrl , CleanupChoicesType , showOutputNotWritable } from './dialogs' ;
import { askForOutDir , askForImportChapters , askForFileOpenAction , showCleanupFilesDialog , showDiskFull , showExportFailedDialog , showConcatFailedDialog , openYouTubeChaptersDialog , showRefuseToOverwrite , showOpenDialog, showMuxNotSupported , promptDownloadMediaUrl , CleanupChoicesType , showOutputNotWritable } from './dialogs' ;
import { openSendReportDialog } from './reporting' ;
import { openSendReportDialog } from './reporting' ;
import { fallbackLng } from './i18n' ;
import { fallbackLng } from './i18n' ;
import { sortSegments , convertSegmentsToChaptersWithGaps , hasAnySegmentOverlap , isDurationValid , getPlaybackAction , getSegmentTags , filterNonMarkers } from './segments' ;
import { sortSegments , convertSegmentsToChaptersWithGaps , hasAnySegmentOverlap , isDurationValid , getPlaybackAction , getSegmentTags , filterNonMarkers } from './segments' ;
@ -178,7 +178,7 @@ function App() {
const { withErrorHandling , handleError , genericError , setGenericError } = useErrorHandling ( ) ;
const { withErrorHandling , handleError , genericError , setGenericError } = useErrorHandling ( ) ;
const { showGenericDialog , genericDialog , closeGenericDialog , confirmDialog } = useDialog ( ) ;
const { showGenericDialog , genericDialog , closeGenericDialog , confirmDialog , openExportFinishedDialog , openCutFinishedDialog , openConcatFinishedDialog } = useDialog ( ) ;
// Note that each action may be multiple key bindings and this will only be the first binding for each action
// Note that each action may be multiple key bindings and this will only be the first binding for each action
const keyBindingByAction = useMemo ( ( ) = > Object . fromEntries ( keyBindings . map ( ( binding ) = > [ binding . action , binding ] ) ) , [ keyBindings ] ) ;
const keyBindingByAction = useMemo ( ( ) = > Object . fromEntries ( keyBindings . map ( ( binding ) = > [ binding . action , binding ] ) ) , [ keyBindings ] ) ;
@ -894,7 +894,7 @@ function App() {
if ( ! hideAllNotifications ) {
if ( ! hideAllNotifications ) {
showOsNotification ( i18n . t ( 'Merge finished' ) ) ;
showOsNotification ( i18n . t ( 'Merge finished' ) ) ;
openConcatFinished Toast ( { filePath : outPath , notices , warnings } ) ;
openConcatFinished Dialog ( { filePath : outPath , notices , warnings } ) ;
}
}
} catch ( err ) {
} catch ( err ) {
if ( err instanceof DirectoryAccessDeclinedError || isAbortedError ( err ) ) return ;
if ( err instanceof DirectoryAccessDeclinedError || isAbortedError ( err ) ) return ;
@ -926,7 +926,7 @@ function App() {
setWorking ( undefined ) ;
setWorking ( undefined ) ;
setProgress ( undefined ) ;
setProgress ( undefined ) ;
}
}
} , [ workingRef , setWorking , ensureWritableOutDir , customOutDir , segmentsToChapters , concatFiles , ffmpegExperimental , preserveMovData , movFastStart , preserveMetadataOnMerge , closeBatch , hideAllNotifications , showOsNotification , handleConcatFailed] ) ;
} , [ workingRef , setWorking , ensureWritableOutDir , customOutDir , segmentsToChapters , concatFiles , ffmpegExperimental , preserveMovData , movFastStart , preserveMetadataOnMerge , closeBatch , hideAllNotifications , showOsNotification , openConcatFinishedDialog, handleConcatFailed] ) ;
const cleanupFiles = useCallback ( async ( cleanupChoices2 : CleanupChoicesType ) = > {
const cleanupFiles = useCallback ( async ( cleanupChoices2 : CleanupChoicesType ) = > {
// Store paths before we reset state
// Store paths before we reset state
@ -1133,7 +1133,7 @@ function App() {
const revealPath = willMerge && mergedOutFilePath != null ? mergedOutFilePath : outFiles [ 0 ] ! . path ;
const revealPath = willMerge && mergedOutFilePath != null ? mergedOutFilePath : outFiles [ 0 ] ! . path ;
if ( ! hideAllNotifications ) {
if ( ! hideAllNotifications ) {
showOsNotification ( i18n . t ( 'Export finished' ) ) ;
showOsNotification ( i18n . t ( 'Export finished' ) ) ;
open ExportFinishedToast ( { filePath : revealPath , warnings , notices } ) ;
open CutFinishedDialog ( { filePath : revealPath , warnings , notices } ) ;
}
}
shootConfetti ( { ticks : 50 } ) ;
shootConfetti ( { ticks : 50 } ) ;
@ -1171,7 +1171,7 @@ function App() {
setWorking ( undefined ) ;
setWorking ( undefined ) ;
setProgress ( undefined ) ;
setProgress ( undefined ) ;
}
}
} , [ filePath , numStreamsToCopy , haveInvalidSegs , workingRef , setWorking , segmentsToChaptersOnly , outSegTemplateOrDefault , generateOutSegFileNames , cutMultiple , outputDir , customOutDir , fileFormat , fileDuration , isRotationSet , effectiveRotation , copyFileStreams , allFilesMeta , keyframeCut , segmentsToExport , shortestFlag , ffmpegExperimental , preserveMetadata , preserveMetadataOnMerge , preserveMovData , preserveChapters , movFastStart , avoidNegativeTs , customTagsByFile , paramsByStreamId , detectedFps , willMerge , enableOverwriteOutput , exportConfirmEnabled , mainFileFormatData , mainStreams , exportExtraStreams , areWeCutting , hideAllNotifications , cleanupChoices . cleanupAfterExport , cleanupFilesWithDialog , segmentsOrInverse . selected , t , mergedFileTemplateOrDefault , segmentsToChapters , invertCutSegments , generateMergedFileNames , concatCutSegments , autoDeleteMergedSegments , tryDeleteFiles , nonCopiedExtraStreams , extractStreams , showOsNotification , handleExportFailed] ) ;
} , [ filePath , numStreamsToCopy , haveInvalidSegs , workingRef , setWorking , segmentsToChaptersOnly , outSegTemplateOrDefault , generateOutSegFileNames , cutMultiple , outputDir , customOutDir , fileFormat , fileDuration , isRotationSet , effectiveRotation , copyFileStreams , allFilesMeta , keyframeCut , segmentsToExport , shortestFlag , ffmpegExperimental , preserveMetadata , preserveMetadataOnMerge , preserveMovData , preserveChapters , movFastStart , avoidNegativeTs , customTagsByFile , paramsByStreamId , detectedFps , willMerge , enableOverwriteOutput , exportConfirmEnabled , mainFileFormatData , mainStreams , exportExtraStreams , areWeCutting , hideAllNotifications , cleanupChoices . cleanupAfterExport , cleanupFilesWithDialog , segmentsOrInverse . selected , t , mergedFileTemplateOrDefault , segmentsToChapters , invertCutSegments , generateMergedFileNames , concatCutSegments , autoDeleteMergedSegments , tryDeleteFiles , nonCopiedExtraStreams , extractStreams , showOsNotification , openCutFinishedDialog, handleExportFailed] ) ;
const onExportPress = useCallback ( async ( ) = > {
const onExportPress = useCallback ( async ( ) = > {
if ( ! filePath ) return ;
if ( ! filePath ) return ;
@ -1200,12 +1200,12 @@ function App() {
: await captureFrameFromTag ( { customOutDir , filePath , time : currentTime , captureFormat , quality : captureFrameQuality , video } ) ;
: await captureFrameFromTag ( { customOutDir , filePath , time : currentTime , captureFormat , quality : captureFrameQuality , video } ) ;
shootConfetti ( ) ;
shootConfetti ( ) ;
if ( ! hideAllNotifications ) open DirToast( { icon : 'success' , filePath : outPath , text : ` ${ i18n . t ( 'Screenshot captured to:' ) } ${ outPath } ` } ) ;
if ( ! hideAllNotifications ) open ExportFinishedDialog( { filePath : outPath , children : ` ${ i18n . t ( 'Screenshot captured to:' ) } ${ outPath } ` } ) ;
} , i18n . t ( 'Failed to capture frame' ) ) ;
} , i18n . t ( 'Failed to capture frame' ) ) ;
} finally {
} finally {
setWorking ( undefined ) ;
setWorking ( undefined ) ;
}
}
} , [ filePath , workingRef , setWorking , withErrorHandling , getRelevantTime , videoRef , usingPreviewFile , captureFrameMethod , captureFrameFromFfmpeg , customOutDir , captureFormat , captureFrameQuality , captureFrameFromTag , hideAllNotifications ]) ;
} , [ filePath , workingRef , setWorking , withErrorHandling , getRelevantTime , videoRef , usingPreviewFile , captureFrameMethod , captureFrameFromFfmpeg , customOutDir , captureFormat , captureFrameQuality , captureFrameFromTag , hideAllNotifications , openExportFinishedDialog ]) ;
const extractSegmentsFramesAsImages = useCallback ( async ( segments : SegmentBase [ ] ) = > {
const extractSegmentsFramesAsImages = useCallback ( async ( segments : SegmentBase [ ] ) = > {
if ( ! filePath || detectedFps == null || workingRef . current || segments . length === 0 ) return ;
if ( ! filePath || detectedFps == null || workingRef . current || segments . length === 0 ) return ;
@ -1242,7 +1242,7 @@ function App() {
}
}
if ( ! hideAllNotifications && lastOutPath != null ) {
if ( ! hideAllNotifications && lastOutPath != null ) {
showOsNotification ( i18n . t ( 'Frames have been extracted' ) ) ;
showOsNotification ( i18n . t ( 'Frames have been extracted' ) ) ;
open DirToast( { icon : 'success' , filePath : lastOutPath , text : i18n.t ( 'Frames extracted to: {{path}}' , { path : outputDir } ) } ) ;
open ExportFinishedDialog( { filePath : lastOutPath , children : i18n.t ( 'Frames extracted to: {{path}}' , { path : outputDir } ) } ) ;
}
}
} catch ( err ) {
} catch ( err ) {
showOsNotification ( i18n . t ( 'Failed to extract frames' ) ) ;
showOsNotification ( i18n . t ( 'Failed to extract frames' ) ) ;
@ -1251,7 +1251,7 @@ function App() {
setWorking ( undefined ) ;
setWorking ( undefined ) ;
setProgress ( undefined ) ;
setProgress ( undefined ) ;
}
}
} , [ filePath , detectedFps , workingRef , getFrameCount , setWorking , hideAllNotifications , captureFramesRange , customOutDir , captureFormat , captureFrameQuality , captureFrameFileNameFormat , showOsNotification , o utputDir, handleError ] ) ;
} , [ filePath , detectedFps , workingRef , getFrameCount , setWorking , hideAllNotifications , captureFramesRange , customOutDir , captureFormat , captureFrameQuality , captureFrameFileNameFormat , showOsNotification , o penExportFinishedDialog, o utputDir, handleError ] ) ;
const extractCurrentSegmentFramesAsImages = useCallback ( ( ) = > {
const extractCurrentSegmentFramesAsImages = useCallback ( ( ) = > {
if ( currentCutSeg != null ) extractSegmentsFramesAsImages ( [ currentCutSeg ] ) ;
if ( currentCutSeg != null ) extractSegmentsFramesAsImages ( [ currentCutSeg ] ) ;
@ -1579,7 +1579,7 @@ function App() {
const [ firstExtractedPath ] = await extractStreams ( { customOutDir , streams : mainCopiedStreams } ) ;
const [ firstExtractedPath ] = await extractStreams ( { customOutDir , streams : mainCopiedStreams } ) ;
if ( ! hideAllNotifications && firstExtractedPath != null ) {
if ( ! hideAllNotifications && firstExtractedPath != null ) {
showOsNotification ( i18n . t ( 'All tracks have been extracted' ) ) ;
showOsNotification ( i18n . t ( 'All tracks have been extracted' ) ) ;
open DirToast( { icon : 'success' , filePath : firstExtractedPath , text : i18n.t ( 'All streams have been extracted as separate files' ) } ) ;
open ExportFinishedDialog( { filePath : firstExtractedPath , children : i18n.t ( 'All streams have been extracted as separate files' ) } ) ;
}
}
} catch ( err ) {
} catch ( err ) {
showOsNotification ( i18n . t ( 'Failed to extract tracks' ) ) ;
showOsNotification ( i18n . t ( 'Failed to extract tracks' ) ) ;
@ -1593,7 +1593,7 @@ function App() {
} finally {
} finally {
setWorking ( undefined ) ;
setWorking ( undefined ) ;
}
}
} , [ confirmDialog , customOutDir , extractStreams , filePath , hideAllNotifications , mainCopiedStreams , setWorking, showOsNotification , t , workingRef ] ) ;
} , [ confirmDialog , customOutDir , extractStreams , filePath , hideAllNotifications , mainCopiedStreams , openExportFinishedDialog, setWorking, showOsNotification , t , workingRef ] ) ;
const askStartTimeOffset = useCallback ( async ( ) = > {
const askStartTimeOffset = useCallback ( async ( ) = > {
@ -2125,7 +2125,7 @@ function App() {
const [ firstExtractedPath ] = await extractStreams ( { customOutDir , streams : mainStreams.filter ( ( s ) = > s . index === index ) } ) ;
const [ firstExtractedPath ] = await extractStreams ( { customOutDir , streams : mainStreams.filter ( ( s ) = > s . index === index ) } ) ;
if ( ! hideAllNotifications && firstExtractedPath != null ) {
if ( ! hideAllNotifications && firstExtractedPath != null ) {
showOsNotification ( i18n . t ( 'Track has been extracted' ) ) ;
showOsNotification ( i18n . t ( 'Track has been extracted' ) ) ;
open DirToast( { icon : 'success' , filePath : firstExtractedPath , text : i18n.t ( 'Track has been extracted' ) } ) ;
open ExportFinishedDialog( { filePath : firstExtractedPath , children : i18n.t ( 'Track has been extracted' ) } ) ;
}
}
} catch ( err ) {
} catch ( err ) {
showOsNotification ( i18n . t ( 'Failed to extract track' ) ) ;
showOsNotification ( i18n . t ( 'Failed to extract track' ) ) ;
@ -2139,7 +2139,7 @@ function App() {
} finally {
} finally {
setWorking ( undefined ) ;
setWorking ( undefined ) ;
}
}
} , [ customOutDir , extractStreams , filePath , hideAllNotifications , mainStreams , setWorking, showOsNotification , workingRef ] ) ;
} , [ customOutDir , extractStreams , filePath , hideAllNotifications , mainStreams , openExportFinishedDialog, setWorking, showOsNotification , workingRef ] ) ;
const batchFilePaths = useMemo ( ( ) = > batchFiles . map ( ( f ) = > f . path ) , [ batchFiles ] ) ;
const batchFilePaths = useMemo ( ( ) = > batchFiles . map ( ( f ) = > f . path ) , [ batchFiles ] ) ;