@@ -34,11 +34,11 @@ export const useVideoChunkManager = (): {
3434 zipProcessingComplete : any
3535 zipProcessingProgress : any
3636 zipProcessingMessage : any
37- formatDate : ( date : Date ) => string
3837 fetchChunkGroups : ( ) => Promise < void >
3938 deleteChunkGroup : ( group : ChunkGroup ) => Promise < void >
4039 deleteAllChunks : ( ) => Promise < void >
4140 downloadChunkGroup : ( group : ChunkGroup ) => Promise < void >
41+ processChunkGroup : ( group : ChunkGroup ) => Promise < void >
4242 openVideoChunksFolder : ( ) => Promise < void >
4343 processVideoChunksZip : ( onComplete ?: ( ) => Promise < void > ) => Promise < void >
4444 processAnotherZip : ( ) => void
@@ -59,25 +59,6 @@ export const useVideoChunkManager = (): {
5959 const zipProcessingProgress = ref ( 0 )
6060 const zipProcessingMessage = ref ( '' )
6161
62- /**
63- * Format date for display
64- * @param {Date } date
65- * @returns {string } Formatted date string
66- */
67- const formatDate = ( date : Date ) : string => {
68- if ( date . getTime ( ) === 0 ) {
69- return 'Unknown creation datetime'
70- }
71- return new Intl . DateTimeFormat ( 'en-US' , {
72- year : 'numeric' ,
73- month : 'short' ,
74- day : 'numeric' ,
75- hour : '2-digit' ,
76- minute : '2-digit' ,
77- second : '2-digit' ,
78- } ) . format ( date )
79- }
80-
8162 /**
8263 * Get timestamp for a chunk
8364 * @param {string } key
@@ -445,6 +426,93 @@ export const useVideoChunkManager = (): {
445426 }
446427 }
447428
429+ /**
430+ * Process a chunk group using the live video processor
431+ * @param {ChunkGroup } group
432+ * @returns {Promise<void> } Promise that resolves when processing is complete
433+ */
434+ const processChunkGroup = async ( group : ChunkGroup ) : Promise < void > => {
435+ if ( ! isElectron ( ) || ! window . electronAPI ) {
436+ openSnackbar ( { message : 'Manual processing is only available in Electron' , variant : 'error' } )
437+ return
438+ }
439+
440+ try {
441+ isProcessingChunks . value = true
442+
443+ // Generate filename based on the chunk group
444+ const timeString = group . firstChunkDate . toLocaleDateString ( 'en-US' , {
445+ month : 'short' ,
446+ day : '2-digit' ,
447+ year : 'numeric' ,
448+ hour : '2-digit' ,
449+ minute : '2-digit' ,
450+ second : '2-digit' ,
451+ } )
452+ const fileName = group . fileName || `Cockpit (${ timeString } ) #${ group . hash } `
453+
454+ // Get the chunk file paths from the group's chunks array, sorted by chunk number
455+ const sortedChunks = group . chunks . sort ( ( a , b ) => {
456+ const aNum = parseInt ( a . key . split ( '_' ) . pop ( ) || '0' , 10 )
457+ const bNum = parseInt ( b . key . split ( '_' ) . pop ( ) || '0' , 10 )
458+ return aNum - bNum
459+ } )
460+
461+ if ( sortedChunks . length === 0 ) {
462+ throw new Error ( 'No chunk files found for processing' )
463+ }
464+
465+ // Read first chunk using the electron storage API to get the blob
466+ const firstChunkBlob = ( await videoStore . tempVideoStorage . getItem ( sortedChunks [ 0 ] . key ) ) as Blob
467+ if ( ! firstChunkBlob ) {
468+ throw new Error ( 'Failed to read first chunk' )
469+ }
470+
471+ // Start FFmpeg streaming process with first chunk
472+ const { id : processId , outputPath } = await window . electronAPI . startVideoRecording (
473+ firstChunkBlob ,
474+ group . hash ,
475+ fileName ,
476+ false // Don't keep chunk backup since we're processing existing chunks
477+ )
478+
479+ // Stream remaining chunks to FFmpeg in order
480+ for ( let i = 1 ; i < sortedChunks . length ; i ++ ) {
481+ const chunkBlob = ( await videoStore . tempVideoStorage . getItem ( sortedChunks [ i ] . key ) ) as Blob
482+ if ( ! chunkBlob ) {
483+ console . warn ( `Failed to read chunk ${ i } , skipping` )
484+ continue
485+ }
486+
487+ await window . electronAPI . appendChunkToVideoRecording ( processId , chunkBlob , i )
488+ }
489+
490+ // Finalize the streaming process
491+ await window . electronAPI . finalizeVideoRecording ( processId )
492+
493+ // Find and copy telemetry file if it exists
494+ try {
495+ const videoKeys = await videoStore . videoStorage . keys ( )
496+ const assFile = videoKeys . find ( ( key ) => key . includes ( group . hash ) && key . endsWith ( '.ass' ) )
497+ if ( assFile ) {
498+ await window . electronAPI . copyTelemetryFile ( assFile , outputPath )
499+ }
500+ } catch ( error ) {
501+ console . warn ( 'Failed to copy telemetry file:' , error )
502+ }
503+
504+ openSnackbar ( {
505+ message : `Video processed successfully! Output: ${ outputPath } ` ,
506+ variant : 'success' ,
507+ } )
508+ } catch ( error ) {
509+ const msg = `Failed to process video chunks: ${ error instanceof Error ? error . message : 'Unknown error' } `
510+ openSnackbar ( { message : msg , variant : 'error' } )
511+ } finally {
512+ isProcessingChunks . value = false
513+ }
514+ }
515+
448516 /**
449517 * Reset ZIP processing state
450518 */
@@ -466,11 +534,11 @@ export const useVideoChunkManager = (): {
466534 zipProcessingMessage,
467535
468536 // Methods
469- formatDate,
470537 fetchChunkGroups,
471538 deleteChunkGroup,
472539 deleteAllChunks,
473540 downloadChunkGroup,
541+ processChunkGroup,
474542 openVideoChunksFolder,
475543 processVideoChunksZip,
476544 processAnotherZip,
0 commit comments