@@ -52,26 +52,31 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
52
52
constructor ( options ?: { timeout ?: number } ) {
53
53
super ( ) ;
54
54
this . timeout = options ?. timeout ;
55
+ dbg ( `Creating new MinimalFfmpegCommand with timeout: ${ this . timeout || 'none' } ` ) ;
55
56
}
56
57
57
58
// Input/Output management
58
59
input ( file : string ) : this {
60
+ dbg ( `Setting input file: ${ file } ` ) ;
59
61
this . inputFile = file ;
60
62
return this ;
61
63
}
62
64
63
65
output ( file : string ) : this {
66
+ dbg ( `Setting output file: ${ file } ` ) ;
64
67
this . outputFile = file ;
65
68
return this ;
66
69
}
67
70
68
71
// FfmpegCommandBuilder interface implementation
69
72
seekInput ( startTime : number | string ) : FfmpegCommandBuilder {
73
+ dbg ( `Adding seek input: ${ startTime } ` ) ;
70
74
this . args . push ( "-ss" , String ( startTime ) ) ;
71
75
return this ;
72
76
}
73
77
74
78
duration ( duration : number | string ) : FfmpegCommandBuilder {
79
+ dbg ( `Adding duration: ${ duration } ` ) ;
75
80
this . args . push ( "-t" , String ( duration ) ) ;
76
81
return this ;
77
82
}
@@ -113,10 +118,12 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
113
118
114
119
audioFilters ( filters : string | string [ ] ) : FfmpegCommandBuilder {
115
120
const filterStr = Array . isArray ( filters ) ? filters . join ( "," ) : filters ;
121
+ dbg ( `Adding audio filters: ${ filterStr } ` ) ;
116
122
// Check if we already have audio filters
117
123
const afIndex = this . args . findIndex ( ( arg , i ) => arg === "-af" && i < this . args . length - 1 ) ;
118
124
if ( afIndex >= 0 ) {
119
125
// Append to existing audio filter
126
+ dbg ( `Appending to existing audio filter: ${ this . args [ afIndex + 1 ] } -> ${ this . args [ afIndex + 1 ] } ,${ filterStr } ` ) ;
120
127
this . args [ afIndex + 1 ] += `,${ filterStr } ` ;
121
128
} else {
122
129
this . args . push ( "-af" , filterStr ) ;
@@ -145,10 +152,12 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
145
152
146
153
videoFilters ( filters : string | string [ ] ) : FfmpegCommandBuilder {
147
154
const filterStr = Array . isArray ( filters ) ? filters . join ( "," ) : filters ;
155
+ dbg ( `Adding video filters: ${ filterStr } ` ) ;
148
156
// Check if we already have video filters
149
157
const vfIndex = this . args . findIndex ( ( arg , i ) => arg === "-vf" && i < this . args . length - 1 ) ;
150
158
if ( vfIndex >= 0 ) {
151
159
// Append to existing video filter
160
+ dbg ( `Appending to existing video filter: ${ this . args [ vfIndex + 1 ] } -> ${ this . args [ vfIndex + 1 ] } ,${ filterStr } ` ) ;
152
161
this . args [ vfIndex + 1 ] += `,${ filterStr } ` ;
153
162
} else {
154
163
this . args . push ( "-vf" , filterStr ) ;
@@ -219,6 +228,7 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
219
228
ffprobe ( callback : ( err : Error | null , data ?: any ) => void ) : void {
220
229
const args = [ "-v" , "quiet" , "-print_format" , "json" , "-show_format" , "-show_streams" , this . inputFile ] ;
221
230
231
+ dbg ( `Running ffprobe with args: ${ args . join ( " " ) } ` ) ;
222
232
const child = spawn ( "ffprobe" , args ) ;
223
233
let stdout = "" ;
224
234
let stderr = "" ;
@@ -228,23 +238,30 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
228
238
} ) ;
229
239
230
240
child . stderr . on ( "data" , ( data ) => {
231
- stderr += data . toString ( ) ;
241
+ const errorOutput = data . toString ( ) ;
242
+ stderr += errorOutput ;
243
+ dbg ( `ffprobe stderr: ${ errorOutput . trim ( ) } ` ) ;
232
244
} ) ;
233
245
234
246
child . on ( "close" , ( code ) => {
247
+ dbg ( `ffprobe process exited with code: ${ code } ` ) ;
235
248
if ( code === 0 ) {
236
249
try {
237
250
const data = JSON . parse ( stdout ) ;
251
+ dbg ( `ffprobe successfully parsed JSON output with ${ data . streams ?. length || 0 } streams` ) ;
238
252
callback ( null , data ) ;
239
253
} catch ( err ) {
254
+ dbg ( `ffprobe JSON parse error: ${ err . message } ` ) ;
240
255
callback ( new Error ( `Failed to parse ffprobe output: ${ err . message } ` ) ) ;
241
256
}
242
257
} else {
258
+ dbg ( `ffprobe failed with stderr: ${ stderr } ` ) ;
243
259
callback ( new Error ( `ffprobe failed with code ${ code } : ${ stderr } ` ) ) ;
244
260
}
245
261
} ) ;
246
262
247
263
child . on ( "error" , ( err ) => {
264
+ dbg ( `ffprobe process error: ${ err . message } ` ) ;
248
265
callback ( err ) ;
249
266
} ) ;
250
267
}
@@ -269,6 +286,7 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
269
286
270
287
child . stdout . on ( "data" , ( data ) => {
271
288
// FFmpeg typically outputs progress to stderr, not stdout
289
+ dbg ( `ffmpeg stdout: ${ data . toString ( ) . trim ( ) } ` ) ;
272
290
} ) ;
273
291
274
292
child . stderr . on ( "data" , ( data ) => {
@@ -280,6 +298,7 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
280
298
const audioMatch = output . match ( / S t r e a m # \d + : \d + .* A u d i o : / ) ;
281
299
const videoMatch = output . match ( / S t r e a m # \d + : \d + .* V i d e o : / ) ;
282
300
if ( audioMatch || videoMatch ) {
301
+ dbg ( `Detected streams - audio: ${ ! ! audioMatch } , video: ${ ! ! videoMatch } ` ) ;
283
302
this . emit ( "codeData" , {
284
303
audio : ! ! audioMatch ,
285
304
video : ! ! videoMatch
@@ -288,27 +307,37 @@ class MinimalFfmpegCommand extends EventEmitter implements FfmpegCommandBuilder
288
307
} ) ;
289
308
290
309
child . on ( "close" , ( code ) => {
310
+ dbg ( `ffmpeg process exited with code: ${ code } ` ) ;
291
311
if ( code === 0 ) {
292
312
// Emit filenames event if output file contains wildcards
293
313
if ( this . outputFile && this . outputFile . includes ( MinimalFfmpegCommand . WILD_CARD ) ) {
314
+ dbg ( `Output contains wildcard, filenames will be handled by end event listener` ) ;
294
315
// The actual filename detection will be handled in the end event listener
295
316
// in runFfmpegCommandUncached function
296
317
} else if ( this . outputFile ) {
297
318
// For single file outputs, emit the filename
298
- this . emit ( "filenames" , [ basename ( this . outputFile ) ] ) ;
319
+ const filename = basename ( this . outputFile ) ;
320
+ dbg ( `Emitting single filename: ${ filename } ` ) ;
321
+ this . emit ( "filenames" , [ filename ] ) ;
299
322
}
323
+ dbg ( `Emitting end event` ) ;
300
324
this . emit ( "end" ) ;
301
325
} else {
302
- this . emit ( "error" , new Error ( `FFmpeg process exited with code ${ code } : ${ stderr } ` ) ) ;
326
+ const errorMsg = `FFmpeg process exited with code ${ code } : ${ stderr } ` ;
327
+ dbg ( `FFmpeg error: ${ errorMsg } ` ) ;
328
+ this . emit ( "error" , new Error ( errorMsg ) ) ;
303
329
}
304
330
} ) ;
305
331
306
332
child . on ( "error" , ( err ) => {
333
+ dbg ( `ffmpeg process error: ${ err . message } ` ) ;
307
334
this . emit ( "error" , err ) ;
308
335
} ) ;
309
336
310
337
if ( this . timeout ) {
338
+ dbg ( `Setting timeout for ${ this . timeout } ms` ) ;
311
339
setTimeout ( ( ) => {
340
+ dbg ( `FFmpeg process timed out, killing with SIGTERM` ) ;
312
341
child . kill ( "SIGTERM" ) ;
313
342
this . emit ( "error" , new Error ( `FFmpeg process timed out after ${ this . timeout } ms` ) ) ;
314
343
} , this . timeout ) ;
@@ -336,6 +365,7 @@ interface FFmpegCommandResult {
336
365
}
337
366
338
367
export async function ffmpegCommand ( options ?: { timeout ?: number } ) {
368
+ dbg ( `Creating ffmpeg command with options: ${ JSON . stringify ( options || { } ) } ` ) ;
339
369
return new MinimalFfmpegCommand ( options ) ;
340
370
}
341
371
@@ -354,16 +384,21 @@ async function computeHashFolder(
354
384
}
355
385
356
386
async function resolveInput ( filename : string | WorkspaceFile , folder : string ) : Promise < string > {
387
+ dbg ( `Resolving input: ${ typeof filename === 'string' ? filename : 'WorkspaceFile object' } ` ) ;
357
388
if ( typeof filename === "object" ) {
358
389
if ( filename . content && filename . encoding === "base64" ) {
359
390
const bytes = fromBase64 ( filename . content ) ;
360
391
const mime = await fileTypeFromBuffer ( bytes ) ;
361
- filename = join ( folder , "input." + mime . ext ) ;
362
- await writeFile ( filename , bytes ) ;
392
+ const resolvedFilename = join ( folder , "input." + mime . ext ) ;
393
+ dbg ( `Converting base64 WorkspaceFile to: ${ resolvedFilename } ` ) ;
394
+ await writeFile ( resolvedFilename , bytes ) ;
395
+ return resolvedFilename ;
363
396
} else {
364
- filename = filename . filename ;
397
+ dbg ( `Using filename from WorkspaceFile: ${ filename . filename } ` ) ;
398
+ return filename . filename ;
365
399
}
366
400
}
401
+ dbg ( `Using string filename directly: ${ filename } ` ) ;
367
402
return filename ;
368
403
}
369
404
@@ -697,21 +732,30 @@ async function runFfmpegCommandUncached(
697
732
) : Promise < FFmpegCommandResult > {
698
733
return await new Promise ( async ( resolve , reject ) => {
699
734
const r : FFmpegCommandResult = { filenames : [ ] , data : [ ] } ;
700
- const end = ( ) => resolve ( r ) ;
735
+ const end = ( ) => {
736
+ dbg ( `Command execution completed with ${ r . filenames . length } filenames and ${ r . data . length } data items` ) ;
737
+ resolve ( r ) ;
738
+ } ;
701
739
702
740
let output : string ;
703
741
cmd . input ( input ) ;
704
742
if ( options . size ) {
743
+ dbg ( `Applying size option: ${ options . size } ` ) ;
705
744
cmd . size ( options . size ) ;
706
745
}
707
746
if ( options . inputOptions ) {
708
- cmd . inputOptions ( ...arrayify ( options . inputOptions ) ) ;
747
+ const inputOpts = arrayify ( options . inputOptions ) ;
748
+ dbg ( `Applying input options: ${ inputOpts . join ( ' ' ) } ` ) ;
749
+ cmd . inputOptions ( ...inputOpts ) ;
709
750
}
710
751
if ( options . outputOptions ) {
711
- cmd . outputOption ( ...arrayify ( options . outputOptions ) ) ;
752
+ const outputOpts = arrayify ( options . outputOptions ) ;
753
+ dbg ( `Applying output options: ${ outputOpts . join ( ' ' ) } ` ) ;
754
+ cmd . outputOption ( ...outputOpts ) ;
712
755
}
713
756
dbg ( `adding filenames listener` ) ;
714
757
cmd . addListener ( "filenames" , ( fns : string [ ] ) => {
758
+ dbg ( `Received filenames event: ${ fns . join ( ', ' ) } ` ) ;
715
759
r . filenames . push ( ...fns . map ( ( f ) => join ( folder , f ) ) ) ;
716
760
} ) ;
717
761
cmd . addListener ( "codeData" , ( data ) => {
@@ -721,35 +765,42 @@ async function runFfmpegCommandUncached(
721
765
dbg ( `processing wildcard output: ${ output } ` ) ;
722
766
if ( output ?. includes ( WILD_CARD ) ) {
723
767
const [ prefix , suffix ] = output . split ( WILD_CARD , 2 ) ;
768
+ dbg ( `Looking for wildcard files with prefix '${ prefix } ' and suffix '${ suffix } ' in ${ folder } ` ) ;
724
769
const files = await readdir ( folder ) ;
725
770
const gen = files . filter ( ( f ) => f . startsWith ( prefix ) && f . endsWith ( suffix ) ) ;
771
+ dbg ( `Found ${ gen . length } wildcard files: ${ gen . join ( ', ' ) } ` ) ;
726
772
r . filenames . push ( ...gen . map ( ( f ) => join ( folder , f ) ) ) ;
727
773
}
728
774
end ( ) ;
729
775
} ) ;
730
776
cmd . addListener ( "error" , ( err ) => {
731
- dbg ( `ffmpeg command encountered an error` ) ;
777
+ dbg ( `ffmpeg command encountered an error: ${ err . message } ` ) ;
732
778
reject ( err ) ;
733
779
} ) ;
734
780
try {
781
+ dbg ( `Calling renderer function` ) ;
735
782
const rendering = await renderer ( cmd , {
736
783
input,
737
784
dir : folder ,
738
785
} ) ;
739
786
if ( typeof rendering === "string" ) {
740
787
output = rendering . replace ( / \* / g, WILD_CARD ) ;
741
788
const fo = join ( folder , basename ( output ) ) ;
789
+ dbg ( `Renderer returned string output: ${ rendering } -> ${ fo } ` ) ;
742
790
cmd . output ( fo ) ;
743
791
cmd . run ( ) ;
744
792
if ( ! output . includes ( WILD_CARD ) ) {
793
+ dbg ( `Non-wildcard output, adding to filenames immediately: ${ fo } ` ) ;
745
794
r . filenames . push ( fo ) ;
746
795
}
747
796
} else if ( typeof rendering === "object" ) {
797
+ dbg ( `Renderer returned object data, resolving immediately` ) ;
748
798
r . data . push ( rendering ) ;
749
799
cmd . removeListener ( "end" , end ) ;
750
800
resolve ( r ) ;
751
801
}
752
802
} catch ( err ) {
803
+ dbg ( `Renderer function threw error: ${ err . message } ` ) ;
753
804
reject ( err ) ;
754
805
}
755
806
} ) ;
0 commit comments