refactor: load WASM from local assets via postinstall script
This commit is contained in:
@@ -15,33 +15,6 @@ const moduleState: WASMModuleState = {
|
|||||||
let ffmpegInstance: FFmpeg | null = null;
|
let ffmpegInstance: FFmpeg | null = null;
|
||||||
let imagemagickInstance: any = null;
|
let imagemagickInstance: any = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to fetch a file with local priority and CDN fallback
|
|
||||||
*/
|
|
||||||
async function fetchWithFallback(localPath: string, cdnUrl: string): Promise<ArrayBuffer> {
|
|
||||||
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<string> {
|
|
||||||
const buffer = await fetchWithFallback(localPath, cdnUrl);
|
|
||||||
const blob = new Blob([buffer], { type: mimeType });
|
|
||||||
return URL.createObjectURL(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load FFmpeg WASM module
|
* Load FFmpeg WASM module
|
||||||
*/
|
*/
|
||||||
@@ -58,20 +31,18 @@ export async function loadFFmpeg(): Promise<FFmpeg> {
|
|||||||
console.log('[FFmpeg]', message);
|
console.log('[FFmpeg]', message);
|
||||||
});
|
});
|
||||||
|
|
||||||
const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd';
|
// Files are guaranteed to exist in /wasm/ by the postinstall script
|
||||||
|
const coreURL = '/wasm/ffmpeg-core.js';
|
||||||
// Load core and dependencies with local priority
|
const wasmURL = '/wasm/ffmpeg-core.wasm';
|
||||||
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 });
|
await ffmpegInstance.load({ coreURL, wasmURL });
|
||||||
|
|
||||||
moduleState.ffmpeg = true;
|
moduleState.ffmpeg = true;
|
||||||
console.log('FFmpeg loaded successfully');
|
console.log('[FFmpeg] Loaded successfully from local assets');
|
||||||
|
|
||||||
return ffmpegInstance;
|
return ffmpegInstance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load FFmpeg:', error);
|
console.error('[FFmpeg] Failed to load:', error);
|
||||||
throw new Error('Failed to load FFmpeg WASM module');
|
throw new Error('Failed to load FFmpeg WASM module');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,10 +58,17 @@ export async function loadImageMagick(): Promise<any> {
|
|||||||
try {
|
try {
|
||||||
const { initializeImageMagick } = await import('@imagemagick/magick-wasm');
|
const { initializeImageMagick } = await import('@imagemagick/magick-wasm');
|
||||||
|
|
||||||
const localWasmUrl = '/wasm/magick.wasm';
|
// File is guaranteed to exist in /wasm/ by the postinstall script
|
||||||
const cdnUrl = 'https://unpkg.com/@imagemagick/magick-wasm@0.0.38/dist/magick.wasm';
|
const wasmUrl = '/wasm/magick.wasm';
|
||||||
|
|
||||||
const arrayBuffer = await fetchWithFallback(localWasmUrl, cdnUrl);
|
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');
|
console.log('[ImageMagick] WASM file size:', arrayBuffer.byteLength, 'bytes');
|
||||||
|
|
||||||
await initializeImageMagick(arrayBuffer);
|
await initializeImageMagick(arrayBuffer);
|
||||||
@@ -98,7 +76,7 @@ export async function loadImageMagick(): Promise<any> {
|
|||||||
const ImageMagick = await import('@imagemagick/magick-wasm');
|
const ImageMagick = await import('@imagemagick/magick-wasm');
|
||||||
imagemagickInstance = ImageMagick;
|
imagemagickInstance = ImageMagick;
|
||||||
moduleState.imagemagick = true;
|
moduleState.imagemagick = true;
|
||||||
console.log('[ImageMagick] Loaded and initialized successfully');
|
console.log('[ImageMagick] Loaded and initialized successfully from local asset');
|
||||||
|
|
||||||
return imagemagickInstance;
|
return imagemagickInstance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint ."
|
"lint": "eslint .",
|
||||||
|
"postinstall": "mkdir -p public/wasm && cp node_modules/.pnpm/@ffmpeg+core@0.12.6/node_modules/@ffmpeg/core/dist/umd/ffmpeg-core.* public/wasm/ && cp node_modules/.pnpm/@imagemagick+magick-wasm@0.0.38/node_modules/@imagemagick/magick-wasm/dist/magick.wasm public/wasm/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ffmpeg/core": "0.12.6",
|
||||||
"@ffmpeg/ffmpeg": "^0.12.15",
|
"@ffmpeg/ffmpeg": "^0.12.15",
|
||||||
"@ffmpeg/util": "^0.12.2",
|
"@ffmpeg/util": "^0.12.2",
|
||||||
"@imagemagick/magick-wasm": "^0.0.38",
|
"@imagemagick/magick-wasm": "^0.0.38",
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@ffmpeg/core':
|
||||||
|
specifier: 0.12.6
|
||||||
|
version: 0.12.6
|
||||||
'@ffmpeg/ffmpeg':
|
'@ffmpeg/ffmpeg':
|
||||||
specifier: ^0.12.15
|
specifier: ^0.12.15
|
||||||
version: 0.12.15
|
version: 0.12.15
|
||||||
@@ -314,6 +317,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@ffmpeg/core@0.12.6':
|
||||||
|
resolution: {integrity: sha512-PrjWBTfGn2WVn9T7wGnzfFwChbqWeZc7tM9vvJZVRadYFUDakfzy7W0LpYC0cvvK0xT82qlBsk38lQhJ/Hps5A==}
|
||||||
|
engines: {node: '>=16.x'}
|
||||||
|
|
||||||
'@ffmpeg/ffmpeg@0.12.15':
|
'@ffmpeg/ffmpeg@0.12.15':
|
||||||
resolution: {integrity: sha512-1C8Obr4GsN3xw+/1Ww6PFM84wSQAGsdoTuTWPOj2OizsRDLT4CXTaVjPhkw6ARyDus1B9X/L2LiXHqYYsGnRFw==}
|
resolution: {integrity: sha512-1C8Obr4GsN3xw+/1Ww6PFM84wSQAGsdoTuTWPOj2OizsRDLT4CXTaVjPhkw6ARyDus1B9X/L2LiXHqYYsGnRFw==}
|
||||||
engines: {node: '>=18.x'}
|
engines: {node: '>=18.x'}
|
||||||
@@ -4223,6 +4230,8 @@ snapshots:
|
|||||||
'@eslint/core': 0.17.0
|
'@eslint/core': 0.17.0
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
|
'@ffmpeg/core@0.12.6': {}
|
||||||
|
|
||||||
'@ffmpeg/ffmpeg@0.12.15':
|
'@ffmpeg/ffmpeg@0.12.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ffmpeg/types': 0.12.4
|
'@ffmpeg/types': 0.12.4
|
||||||
|
|||||||
Reference in New Issue
Block a user