The 'Cannot find module as expression is too dynamic' error occurs at runtime when FFmpeg tries to dynamically load the core module. Using CDN URLs bypasses this bundler issue entirely since absolute URLs don't require bundler analysis. Switched to jsdelivr CDN for FFmpeg core and WASM files - this is a proven approach used by many projects. Fixes: wasmLoader.ts runtime error on media conversion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
142 lines
3.8 KiB
TypeScript
142 lines
3.8 KiB
TypeScript
import type { ConverterEngine, WASMModuleState } from '@/types/media';
|
|
import type { FFmpeg as FFmpegType } from '@ffmpeg/ffmpeg';
|
|
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
|
import { initializeImageMagick } from '@imagemagick/magick-wasm';
|
|
|
|
/**
|
|
* WASM module loading state
|
|
*/
|
|
const moduleState: WASMModuleState = {
|
|
ffmpeg: false,
|
|
imagemagick: false,
|
|
};
|
|
|
|
/**
|
|
* Cached WASM instances
|
|
*/
|
|
let ffmpegInstance: FFmpegType | null = null;
|
|
let imagemagickInstance: any = null;
|
|
|
|
/**
|
|
* Load FFmpeg WASM module
|
|
*/
|
|
export async function loadFFmpeg(): Promise<FFmpegType> {
|
|
if (ffmpegInstance && moduleState.ffmpeg) {
|
|
return ffmpegInstance;
|
|
}
|
|
|
|
try {
|
|
if (!FFmpeg) {
|
|
throw new Error('FFmpeg class not available');
|
|
}
|
|
|
|
ffmpegInstance = new FFmpeg();
|
|
|
|
if (!ffmpegInstance) {
|
|
throw new Error('Failed to create FFmpeg instance');
|
|
}
|
|
|
|
ffmpegInstance.on('log', ({ message }: { message: string }) => {
|
|
console.log('[FFmpeg]', message);
|
|
});
|
|
|
|
// Use CDN URLs for FFmpeg core - avoids "dynamic require" bundler issues
|
|
// with local file paths that FFmpeg's internal code cannot resolve at runtime
|
|
const coreURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/esm/ffmpeg-core.js';
|
|
const wasmURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/esm/ffmpeg-core.wasm';
|
|
|
|
await ffmpegInstance.load({ coreURL, wasmURL });
|
|
|
|
moduleState.ffmpeg = true;
|
|
console.log('[FFmpeg] Loaded successfully from local assets');
|
|
|
|
return ffmpegInstance;
|
|
} catch (error) {
|
|
console.error('[FFmpeg] Failed to load:', error);
|
|
throw new Error('Failed to load FFmpeg WASM module');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load ImageMagick WASM module
|
|
*/
|
|
export async function loadImageMagick(): Promise<any> {
|
|
if (imagemagickInstance && moduleState.imagemagick) {
|
|
return imagemagickInstance;
|
|
}
|
|
|
|
try {
|
|
// File is guaranteed to exist in /wasm/ by the postinstall script
|
|
const wasmUrl = '/wasm/magick.wasm';
|
|
|
|
console.log('[ImageMagick] Loading local WASM:', wasmUrl);
|
|
|
|
const response = await fetch(wasmUrl);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch WASM file: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
console.log('[ImageMagick] WASM file size:', arrayBuffer.byteLength, 'bytes');
|
|
|
|
await initializeImageMagick(arrayBuffer);
|
|
|
|
// Store the module for later use
|
|
imagemagickInstance = { initialized: true };
|
|
moduleState.imagemagick = true;
|
|
console.log('[ImageMagick] Loaded and initialized successfully from local asset');
|
|
|
|
return imagemagickInstance;
|
|
} catch (error) {
|
|
console.error('[ImageMagick] Failed to load:', error);
|
|
throw new Error(`Failed to load ImageMagick WASM module: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get loaded module state
|
|
*/
|
|
export function getModuleState(): WASMModuleState {
|
|
return { ...moduleState };
|
|
}
|
|
|
|
/**
|
|
* Check if a specific module is loaded
|
|
*/
|
|
export function isModuleLoaded(engine: ConverterEngine): boolean {
|
|
return moduleState[engine];
|
|
}
|
|
|
|
/**
|
|
* Load appropriate WASM module for converter engine
|
|
*/
|
|
export async function loadModule(engine: ConverterEngine): Promise<any> {
|
|
switch (engine) {
|
|
case 'ffmpeg':
|
|
return loadFFmpeg();
|
|
case 'imagemagick':
|
|
return loadImageMagick();
|
|
default:
|
|
throw new Error(`Unknown converter engine: ${engine}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unload all WASM modules and free memory
|
|
*/
|
|
export function unloadAll(): void {
|
|
if (ffmpegInstance) {
|
|
// FFmpeg doesn't have an explicit unload method
|
|
// Just null the instance
|
|
ffmpegInstance = null;
|
|
moduleState.ffmpeg = false;
|
|
}
|
|
|
|
if (imagemagickInstance) {
|
|
imagemagickInstance = null;
|
|
moduleState.imagemagick = false;
|
|
}
|
|
|
|
console.log('All WASM modules unloaded');
|
|
}
|