import type { FFmpeg } from '@ffmpeg/ffmpeg'; import type { ConverterEngine, WASMModuleState } from '@/types/media'; /** * WASM module loading state */ const moduleState: WASMModuleState = { ffmpeg: false, imagemagick: false, }; /** * Cached WASM instances */ let ffmpegInstance: FFmpeg | null = null; let imagemagickInstance: any = null; /** * Helper to fetch a file with local priority and CDN fallback */ async function fetchWithFallback(localPath: string, cdnUrl: string): Promise { try { const response = await fetch(localPath); if (!response.ok) throw new Error(`Local fetch failed: ${response.status}`); console.log(`[WASM] Loaded from local: ${localPath}`); return await response.arrayBuffer(); } catch (e) { console.warn(`[WASM] Local load failed for ${localPath}, falling back to CDN:`, e); const response = await fetch(cdnUrl); if (!response.ok) throw new Error(`CDN fetch failed: ${response.status}`); console.log(`[WASM] Loaded from CDN: ${cdnUrl}`); return await response.arrayBuffer(); } } /** * Helper to create a blob URL from a fallback fetch */ async function getBlobUrl(localPath: string, cdnUrl: string, mimeType: string): Promise { const buffer = await fetchWithFallback(localPath, cdnUrl); const blob = new Blob([buffer], { type: mimeType }); return URL.createObjectURL(blob); } /** * Load FFmpeg WASM module */ export async function loadFFmpeg(): Promise { if (ffmpegInstance && moduleState.ffmpeg) { return ffmpegInstance; } try { const { FFmpeg } = await import('@ffmpeg/ffmpeg'); ffmpegInstance = new FFmpeg(); ffmpegInstance.on('log', ({ message }) => { console.log('[FFmpeg]', message); }); const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd'; // Load core and dependencies with local priority const coreURL = await getBlobUrl('/wasm/ffmpeg-core.js', `${baseURL}/ffmpeg-core.js`, 'text/javascript'); const wasmURL = await getBlobUrl('/wasm/ffmpeg-core.wasm', `${baseURL}/ffmpeg-core.wasm`, 'application/wasm'); await ffmpegInstance.load({ coreURL, wasmURL }); moduleState.ffmpeg = true; console.log('FFmpeg loaded successfully'); return ffmpegInstance; } catch (error) { console.error('Failed to load FFmpeg:', 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 { const { initializeImageMagick } = await import('@imagemagick/magick-wasm'); const localWasmUrl = '/wasm/magick.wasm'; const cdnUrl = 'https://unpkg.com/@imagemagick/magick-wasm@0.0.38/dist/magick.wasm'; const arrayBuffer = await fetchWithFallback(localWasmUrl, cdnUrl); console.log('[ImageMagick] WASM file size:', arrayBuffer.byteLength, 'bytes'); await initializeImageMagick(arrayBuffer); const ImageMagick = await import('@imagemagick/magick-wasm'); imagemagickInstance = ImageMagick; moduleState.imagemagick = true; console.log('[ImageMagick] Loaded and initialized successfully'); 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'); }