From 08e1cac3a0505a9840899896893c4bc3bc7a0d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Wed, 25 Feb 2026 10:44:49 +0100 Subject: [PATCH] feat: streamline WASM loading with local priority and cleanup UI --- .gitignore | 3 ++ components/media/FileConverter.tsx | 8 +-- lib/media/wasm/wasmLoader.ts | 82 +++++++++++++++--------------- 3 files changed, 44 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index fd3dbb5..2840e4e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ yarn-error.log* # vercel .vercel +# wasm binaries +/public/wasm/* + # typescript *.tsbuildinfo next-env.d.ts diff --git a/components/media/FileConverter.tsx b/components/media/FileConverter.tsx index 88dc4dc..5a7c579 100644 --- a/components/media/FileConverter.tsx +++ b/components/media/FileConverter.tsx @@ -375,15 +375,9 @@ export function FileConverter() { const completedCount = conversionJobs.filter(job => job.status === 'completed').length; return ( -
+
{/* Header */} - - File Converter - - Convert videos, audio, and images directly in your browser using WebAssembly - - {/* File upload */} { + 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 */ @@ -25,21 +52,19 @@ export async function loadFFmpeg(): Promise { try { const { FFmpeg } = await import('@ffmpeg/ffmpeg'); - const { toBlobURL } = await import('@ffmpeg/util'); - ffmpegInstance = new FFmpeg(); - // Load core and dependencies - const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd'; - ffmpegInstance.on('log', ({ message }) => { console.log('[FFmpeg]', message); }); - await ffmpegInstance.load({ - coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'), - wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'), - }); + 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'); @@ -62,42 +87,15 @@ export async function loadImageMagick(): Promise { try { const { initializeImageMagick } = await import('@imagemagick/magick-wasm'); - // Initialize ImageMagick with WASM file from public directory - // In production (static export), this will be served from /wasm/magick.wasm - const wasmUrl = '/wasm/magick.wasm'; + const localWasmUrl = '/wasm/magick.wasm'; + const cdnUrl = 'https://unpkg.com/@imagemagick/magick-wasm@0.0.38/dist/magick.wasm'; - console.log('[ImageMagick] Attempting to load WASM from:', wasmUrl); - - // Test fetch the WASM file first to debug - try { - const response = await fetch(wasmUrl); - console.log('[ImageMagick] WASM fetch response:', { - ok: response.ok, - status: response.status, - contentType: response.headers.get('content-type'), - contentLength: response.headers.get('content-length'), - }); - - 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'); - - if (arrayBuffer.byteLength === 0) { - throw new Error('WASM file is empty'); - } - - // Now initialize with the buffer directly - await initializeImageMagick(arrayBuffer); - } catch (fetchError) { - console.error('[ImageMagick] Failed to fetch WASM:', fetchError); - throw fetchError; - } + 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');