This creates a blob url outside of the worker for for the core .js and .wasm files so they aren't downloaded on each run.
87 lines
2.6 KiB
JavaScript
87 lines
2.6 KiB
JavaScript
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
|
|
|
let ffmpeg = null;
|
|
let loadingPromise = null;
|
|
|
|
async function loadFFmpeg(loadOptions = {}) {
|
|
if (loadingPromise) return loadingPromise;
|
|
|
|
loadingPromise = (async () => {
|
|
ffmpeg = new FFmpeg();
|
|
|
|
ffmpeg.on('log', ({ message }) => {
|
|
self.postMessage({ type: 'log', message });
|
|
});
|
|
|
|
ffmpeg.on('progress', ({ progress, time }) => {
|
|
self.postMessage({
|
|
type: 'progress',
|
|
stage: 'encoding',
|
|
progress: progress * 100,
|
|
time,
|
|
});
|
|
});
|
|
|
|
self.postMessage({ type: 'progress', stage: 'loading', message: 'Loading FFmpeg...' });
|
|
|
|
await ffmpeg.load(loadOptions);
|
|
})();
|
|
|
|
return loadingPromise;
|
|
}
|
|
|
|
self.onmessage = async (e) => {
|
|
const {
|
|
audioData,
|
|
args = [],
|
|
output = {
|
|
name: 'output',
|
|
mime: 'application/octet-stream',
|
|
},
|
|
encodeStartMessage = 'Encoding...',
|
|
encodeEndMessage = 'Finalizing...',
|
|
loadOptions = {},
|
|
} = e.data;
|
|
|
|
try {
|
|
console.log(loadOptions);
|
|
await loadFFmpeg(loadOptions);
|
|
|
|
self.postMessage({ type: 'progress', stage: 'encoding', message: encodeStartMessage });
|
|
|
|
try {
|
|
// Write input file to FFmpeg virtual filesystem
|
|
await ffmpeg.writeFile('input', new Uint8Array(audioData));
|
|
|
|
const ffmpegArgs = ['-i', 'input', ...args, output.name];
|
|
|
|
// Log the exact FFmpeg command being run for debugging.
|
|
self.postMessage({ type: 'log', message: `Running with args: ${ffmpegArgs.join(' ')}` });
|
|
|
|
// Run FFMPEG with the provided arguments.
|
|
await ffmpeg.exec(ffmpegArgs);
|
|
|
|
self.postMessage({ type: 'progress', stage: 'finalizing', message: encodeEndMessage });
|
|
|
|
// Read output file - use Uint8Array directly to avoid extra bytes from ArrayBuffer
|
|
const data = await ffmpeg.readFile(output.name);
|
|
const outputBlob = new Blob([data], { type: output.mime });
|
|
|
|
self.postMessage({ type: 'complete', blob: outputBlob });
|
|
} finally {
|
|
// Always cleanup virtual filesystem files
|
|
try {
|
|
await ffmpeg.deleteFile('input');
|
|
} catch {
|
|
// File may not exist if writeFile failed
|
|
}
|
|
try {
|
|
await ffmpeg.deleteFile(output.name);
|
|
} catch {
|
|
// File may not exist if exec failed
|
|
}
|
|
}
|
|
} catch (error) {
|
|
self.postMessage({ type: 'error', message: error.message });
|
|
}
|
|
};
|