@@ -69,6 +69,7 @@ export interface MediaProcessor {
69
69
getMetadata ( ) : Promise < MediaMetadata > ;
70
70
convert ( options : ConversionOptions ) : Promise < Uint8Array > ;
71
71
dispose ( ) : void ;
72
+ isReady ( ) : boolean ;
72
73
}
73
74
74
75
// Formats that Mediabunny typically supports well
@@ -202,16 +203,66 @@ async function loadFFmpegWasm() {
202
203
class MediabunnyProcessor implements MediaProcessor {
203
204
private input : MediabunnyInput | null = null ;
204
205
private file : File | null = null ;
206
+ private mediabunny : any = null ;
207
+
208
+ isReady ( ) : boolean {
209
+ return (
210
+ this . input !== null && this . file !== null && this . mediabunny !== null
211
+ ) ;
212
+ }
205
213
206
214
async loadFile ( file : File ) : Promise < void > {
207
- const mediabunny = await loadMediabunny ( ) ;
208
- const { Input, BlobSource, ALL_FORMATS } = mediabunny ;
215
+ console . debug ( "MediabunnyProcessor: Starting loadFile" , {
216
+ fileName : file . name ,
217
+ fileSize : file . size ,
218
+ fileType : file . type ,
219
+ } ) ;
209
220
210
- this . file = file ;
211
- this . input = new Input ( {
212
- source : new BlobSource ( file ) ,
213
- formats : ALL_FORMATS ,
221
+ this . mediabunny = await loadMediabunny ( ) ;
222
+ const { Input, BlobSource, ALL_FORMATS } = this . mediabunny ;
223
+
224
+ console . debug ( "MediabunnyProcessor: Mediabunny loaded successfully" , {
225
+ hasInput : ! ! Input ,
226
+ hasBlobSource : ! ! BlobSource ,
227
+ hasFormats : ! ! ALL_FORMATS ,
214
228
} ) ;
229
+
230
+ this . file = file ;
231
+
232
+ try {
233
+ const blobSource = new BlobSource ( file ) ;
234
+ console . debug ( "MediabunnyProcessor: BlobSource created" ) ;
235
+
236
+ this . input = new Input ( {
237
+ source : blobSource ,
238
+ formats : ALL_FORMATS ,
239
+ } ) ;
240
+
241
+ console . debug ( "MediabunnyProcessor: Input created" , {
242
+ inputType : typeof this . input ,
243
+ inputConstructor : this . input ?. constructor ?. name ,
244
+ hasInput : ! ! this . input ,
245
+ } ) ;
246
+
247
+ // Validate that the input was created properly
248
+ if ( ! this . input || typeof this . input !== "object" ) {
249
+ throw new Error ( "Failed to create valid Input object" ) ;
250
+ }
251
+
252
+ // Additional validation - check if input has expected methods
253
+ if (
254
+ typeof this . input . getVideoTracks !== "function" ||
255
+ typeof this . input . getAudioTracks !== "function"
256
+ ) {
257
+ throw new Error ( "Input object missing expected methods" ) ;
258
+ }
259
+ } catch ( error ) {
260
+ console . error ( "MediabunnyProcessor: Failed to create input" , error ) ;
261
+ this . input = null ;
262
+ throw new Error (
263
+ `Failed to create media input: ${ error instanceof Error ? error . message : String ( error ) } ` ,
264
+ ) ;
265
+ }
215
266
}
216
267
217
268
async getMetadata ( ) : Promise < MediaMetadata > {
@@ -248,15 +299,40 @@ class MediabunnyProcessor implements MediaProcessor {
248
299
}
249
300
250
301
async convert ( options : ConversionOptions ) : Promise < Uint8Array > {
302
+ console . debug ( "MediabunnyProcessor: Starting convert" , {
303
+ outputFormat : options . outputFormat ,
304
+ hasInput : ! ! this . input ,
305
+ inputType : typeof this . input ,
306
+ inputConstructor : this . input ?. constructor ?. name ,
307
+ } ) ;
308
+
251
309
if ( ! this . input ) {
252
310
throw new Error ( "No file loaded" ) ;
253
311
}
254
312
255
- const mediabunny = await loadMediabunny ( ) ;
256
- const { Output, Conversion, BufferTarget } = mediabunny ;
313
+ if ( ! this . mediabunny ) {
314
+ throw new Error ( "Mediabunny not initialized" ) ;
315
+ }
316
+
317
+ // Additional validation to ensure input is proper
318
+ if ( typeof this . input !== "object" || this . input === null ) {
319
+ throw new Error (
320
+ "Invalid input object - media file may not be properly loaded" ,
321
+ ) ;
322
+ }
323
+ const { Output, Conversion, BufferTarget } = this . mediabunny ;
324
+
325
+ console . debug ( "MediabunnyProcessor: Mediabunny classes available" , {
326
+ hasOutput : ! ! Output ,
327
+ hasConversion : ! ! Conversion ,
328
+ hasBufferTarget : ! ! BufferTarget ,
329
+ } ) ;
257
330
258
331
// Get appropriate output format
259
- const outputFormat = this . getOutputFormat ( options . outputFormat , mediabunny ) ;
332
+ const outputFormat = this . getOutputFormat (
333
+ options . outputFormat ,
334
+ this . mediabunny ,
335
+ ) ;
260
336
261
337
const target = new BufferTarget ( ) ;
262
338
const output = new Output ( {
@@ -271,7 +347,36 @@ class MediabunnyProcessor implements MediaProcessor {
271
347
output . setVideoSize ( options . width , options . height ) ;
272
348
if ( options . frameRate ) output . setVideoFrameRate ( options . frameRate ) ;
273
349
274
- const conversion = new Conversion ( this . input , output ) ;
350
+ // Final validation before creating Conversion
351
+ if ( ! this . input || this . input === null || this . input === undefined ) {
352
+ throw new Error ( "Input is null or undefined" ) ;
353
+ }
354
+
355
+ // Check if input has the expected structure for mediabunny
356
+ if ( typeof this . input !== "object" ) {
357
+ throw new Error ( `Input is not an object, got: ${ typeof this . input } ` ) ;
358
+ }
359
+
360
+ // Create conversion with proper error handling
361
+ let conversion : any ;
362
+ try {
363
+ console . debug ( "MediabunnyProcessor: Creating Conversion with" , {
364
+ inputConstructor : this . input . constructor ?. name ,
365
+ outputConstructor : output . constructor ?. name ,
366
+ } ) ;
367
+ conversion = new Conversion ( this . input , output ) ;
368
+ } catch ( conversionError ) {
369
+ console . error ( "MediabunnyProcessor: Conversion creation failed" , {
370
+ error : conversionError ,
371
+ inputType : typeof this . input ,
372
+ inputConstructor : this . input ?. constructor ?. name ,
373
+ outputType : typeof output ,
374
+ outputConstructor : output ?. constructor ?. name ,
375
+ } ) ;
376
+ throw new Error (
377
+ `Failed to create conversion: ${ conversionError instanceof Error ? conversionError . message : String ( conversionError ) } . Input type: ${ typeof this . input } , Input constructor: ${ this . input ?. constructor ?. name } ` ,
378
+ ) ;
379
+ }
275
380
276
381
// Handle progress updates
277
382
if ( options . onProgress ) {
@@ -336,6 +441,7 @@ class MediabunnyProcessor implements MediaProcessor {
336
441
dispose ( ) : void {
337
442
this . input = null ;
338
443
this . file = null ;
444
+ this . mediabunny = null ;
339
445
}
340
446
}
341
447
@@ -345,6 +451,10 @@ class FFmpegProcessor implements MediaProcessor {
345
451
private file : File | null = null ;
346
452
private metadata : MediaMetadata | null = null ;
347
453
454
+ isReady ( ) : boolean {
455
+ return this . ffmpeg !== null && this . file !== null ;
456
+ }
457
+
348
458
async loadFile ( file : File ) : Promise < void > {
349
459
const { FFmpeg, fetchFile, toBlobURL } = await loadFFmpegWasm ( ) ;
350
460
@@ -588,6 +698,12 @@ export async function createMediaProcessor(
588
698
try {
589
699
const processor = new MediabunnyProcessor ( ) ;
590
700
await processor . loadFile ( file ) ;
701
+
702
+ // Validate processor is properly initialized
703
+ if ( ! processor . isReady ( ) ) {
704
+ throw new Error ( "MediabunnyProcessor not properly initialized" ) ;
705
+ }
706
+
591
707
return processor ;
592
708
} catch ( error ) {
593
709
console . warn ( "Mediabunny failed, falling back to FFmpeg:" , error ) ;
@@ -624,6 +740,55 @@ export async function convertMedia(
624
740
}
625
741
}
626
742
743
+ // Debug helper function for troubleshooting media processing issues
744
+ export async function debugMediaFile ( file : File ) : Promise < {
745
+ fileInfo : {
746
+ name : string ;
747
+ size : number ;
748
+ type : string ;
749
+ } ;
750
+ mediabunnyAvailable : boolean ;
751
+ mediabunnyError ?: string ;
752
+ processorCreated : boolean ;
753
+ processorError ?: string ;
754
+ } > {
755
+ const result = {
756
+ fileInfo : {
757
+ name : file . name ,
758
+ size : file . size ,
759
+ type : file . type ,
760
+ } ,
761
+ mediabunnyAvailable : false ,
762
+ processorCreated : false ,
763
+ } as any ;
764
+
765
+ // Test mediabunny availability
766
+ try {
767
+ const mediabunny = await loadMediabunny ( ) ;
768
+ result . mediabunnyAvailable = true ;
769
+ console . log ( "Mediabunny loaded successfully:" , {
770
+ hasInput : ! ! mediabunny . Input ,
771
+ hasBlobSource : ! ! mediabunny . BlobSource ,
772
+ hasFormats : ! ! mediabunny . ALL_FORMATS ,
773
+ } ) ;
774
+ } catch ( error ) {
775
+ result . mediabunnyError =
776
+ error instanceof Error ? error . message : String ( error ) ;
777
+ }
778
+
779
+ // Test processor creation
780
+ try {
781
+ const processor = await createMediaProcessor ( file ) ;
782
+ result . processorCreated = processor . isReady ( ) ;
783
+ processor . dispose ( ) ;
784
+ } catch ( error ) {
785
+ result . processorError =
786
+ error instanceof Error ? error . message : String ( error ) ;
787
+ }
788
+
789
+ return result ;
790
+ }
791
+
627
792
// Check if a format combination is supported
628
793
export function isFormatSupported (
629
794
inputFormat : string ,
0 commit comments