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 { 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 { 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 { 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'); }