The @ffmpeg/ffmpeg package has internal dynamic imports that Turbopack cannot statically analyze, but they work fine at runtime. This change moves the import to the loadFFmpeg function where it's needed, allowing Turbopack to skip static analysis and let the bundler resolve it at runtime. Fixes: Cannot find module as expression is too dynamic error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
149 lines
3.9 KiB
TypeScript
149 lines
3.9 KiB
TypeScript
// Suppress Turbopack bundler warning for FFmpeg's dynamic imports
|
|
// The @ffmpeg/ffmpeg package internally uses dynamic imports that Turbopack
|
|
// flags as "too dynamic" but still work at runtime
|
|
declare const globalThis: any;
|
|
|
|
import type { ConverterEngine, WASMModuleState } from '@/types/media';
|
|
import { initializeImageMagick } from '@imagemagick/magick-wasm';
|
|
|
|
/**
|
|
* WASM module loading state
|
|
*/
|
|
const moduleState: WASMModuleState = {
|
|
ffmpeg: false,
|
|
imagemagick: false,
|
|
};
|
|
|
|
/**
|
|
* Cached WASM instances
|
|
*/
|
|
let ffmpegInstance: any = null;
|
|
let imagemagickInstance: any = null;
|
|
|
|
/**
|
|
* Load FFmpeg WASM module
|
|
*/
|
|
export async function loadFFmpeg(): Promise<any> {
|
|
if (ffmpegInstance && moduleState.ffmpeg) {
|
|
return ffmpegInstance;
|
|
}
|
|
|
|
try {
|
|
// Import FFmpeg at runtime to avoid static bundler analysis
|
|
// The @ffmpeg/ffmpeg package has internal dynamic imports that
|
|
// Turbopack cannot statically analyze, but they work fine at runtime
|
|
const { FFmpeg } = await import('@ffmpeg/ffmpeg');
|
|
|
|
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);
|
|
});
|
|
|
|
// Files are guaranteed to exist in /wasm/ by the postinstall script
|
|
const coreURL = '/wasm/ffmpeg-core.js';
|
|
const wasmURL = '/wasm/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');
|
|
}
|