Open andykais opened 4 years ago
@jeromewu - I could try to export it to deno. Would you mind assigning this issue to me?
No problem, this is great! Also invite you to the organisation, so you can create new repos if required.
Any update on this? I have created the following snippet in deno
import ffmpegCore from 'https://cdn.skypack.dev/@ffmpeg/core';
import { createFFmpeg, fetchFile } from 'https://cdn.skypack.dev/@ffmpeg/ffmpeg';
const ffmpeg = createFFmpeg({ log: true });
await ffmpeg.load();
ffmpeg.FS('writeFile', 'test.avi', await fetchFile('/Users/andrew.kaiser/Downloads/yt-dlp/youtube/Hermitcraft Season 9 - Creeper Conveyor Belts - #16.webm'));
await ffmpeg.run('-i', 'test.avi', 'test.mp4');
await Deno.writeFile('./test.mp4', ffmpeg.FS('readFile', 'test.mp4'))
which gives the following error
[info] use ffmpeg.wasm v0.11.5
[info] load ffmpeg-core
[info] loading ffmpeg-core
error: Uncaught (in promise) ReferenceError: document is not defined
const script = document.createElement("script");
^
at https://cdn.skypack.dev/-/@ffmpeg/ffmpeg@v0.11.5-u3mWpbueYOGaLpBMFMgb/dist=es2019,mode=imports/optimized/@ffmpeg/ffmpeg.js:400:22
at new Promise (<anonymous>)
at getCreateFFmpegCore (https://cdn.skypack.dev/-/@ffmpeg/ffmpeg@v0.11.5-u3mWpbueYOGaLpBMFMgb/dist=es2019,mode=imports/optimized/@ffmpeg/ffmpeg.js:399:12)
at async Object.load (https://cdn.skypack.dev/-/@ffmpeg/ffmpeg@v0.11.5-u3mWpbueYOGaLpBMFMgb/dist=es2019,mode=imports/optimized/@ffmpeg/ffmpeg.js:557:11)
at async file:///Users/andrew/Code/scratchwork/deno-ffmpeg/mod.ts:6:1
if I had to guess, ffmpeg is detecting that we are in the browser using typeof window !== undefined
somewhere, and assuming that document
exists
@andykais I went ahead and sorted out the errors to get it working on deno. You need to have deno make a vendor folder for this hack to work -- after placing the script in a file such as "ffmpeg.ts", do:
deno vendor ./ffmpeg.ts
After that is successful, run the script with an import map:
deno run --import-map=vendor/import_map.json ./ffmpeg.ts
Make sure to modify the folders / files as per what you want.
ffmpeg.ts
// needed to avoid downloading .wasm bin again
import { existsSync } from "https://deno.land/std/fs/mod.ts";
// need these imports for the vendor gen
import * as a from 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.worker.js';
import * as b from 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js';
import * as FFmpeg from 'https://cdn.skypack.dev/pin/@ffmpeg/ffmpeg@v0.11.5-u3mWpbueYOGaLpBMFMgb/mode=imports/optimized/@ffmpeg/ffmpeg.js';
const { createFFmpeg, fetchFile } = FFmpeg;
// folder where files exist for processing, relative to current module dir
let workingDir = import.meta.resolve("../test");
// folder where the core worker js is
let corePath = import.meta.resolve("https://unpkg.com/@ffmpeg/core@0.11.0/dist/").replace("file://", "");
let wasmPath = corePath + "ffmpeg-core.wasm";
if (existsSync(wasmPath)) {
// we are good
} else {
console.log(`Downloading ffmpeg-core.wasm to ${wasmPath}`);
let b = await fetch("https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.wasm")
let d = await (await b.blob()).arrayBuffer();
await Deno.writeFile(wasmPath, d);
console.log(`Download complete`);
}
// worker shim for Deno worker compatibility
let DenoWorker = Worker;
window.Worker = function Worker(script) {
return new DenoWorker(new URL(script, import.meta.url).href, {
deno: true,
type: 'module',
});
}
// we need to mod the worker file if it hasn't been modded already
let workerPath = corePath + "ffmpeg-core.worker.js";
let data = Deno.readTextFileSync(workerPath);
let newData = data.replace(/this\./gi, "self.");
if (newData != data) {
// original file was unmodified; add the rest of the replacement and save it
// modify importscripts to work with deno
newData = newData.replace(/importScripts\(/gi, "self.importScripts(");
// early return to prevent error
newData = newData.replace("createFFmpegCore(Module).then", "return;createFFmpegCore(Module).then")
// import scripts replacement
newData = newData + `
self.importScripts = () => {
let corePath = 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/';
let file = corePath + "core-dyn.js"
import(file).then((r) => {
r.default.createFFmpegCore(Module).then(function(instance) {
Module = instance;
postMessage({
"cmd": "loaded"
})
})
});
}
`
Deno.writeTextFileSync(workerPath, newData);
}
// document shim needed for ffmpeg-wasm
window.document = {
handler: null,
createElement: (e) => {
return {
removeEventListener: (ev, handler) => {},
src: "",
addEventListener: (ev, handler) => {
console.log(ev);
document.handler = handler;
}
}
},
getElementsByTagName: (tag) => {
return [
{
appendChild: (script) => {
let file = corePath + "core-dyn.js"
fetch(script.src)
.then((response) => response.blob())
.then((data) => {
data.arrayBuffer()
.then((b) => {
const encoder = new TextEncoder();
// export fix
let append = encoder.encode(
`
var src = {
createFFmpegCore,
};
export default src;
`
);
let tmp = new Uint8Array(b.byteLength + append.byteLength);
tmp.set(new Uint8Array(b), 0);
tmp.set(new Uint8Array(append), b.byteLength);
Deno.writeFile(file, tmp);
})
.then(async (r) => {
import(file)
.then((r) => {
const { createFFmpegCore } = r.default;
window.createFFmpegCore = createFFmpegCore;
document.handler();
});
});
});
}
}
]
}
};
const ffmpeg = createFFmpeg({
log: true,
corePath: corePath + "ffmpeg-core.js"
});
await ffmpeg.load();
let f = await fetch(workingDir + '/resources/images/20220906044647.mp4');
f = await f.blob();
f = await fetchFile(f);
ffmpeg.FS('writeFile', 'test.avi', f);
await ffmpeg.run('-i', 'test.avi', 'test.mp4');
await Deno.writeFile('./test.mp4', ffmpeg.FS('readFile', 'test.mp4'));
Enjoy!
wow! Thanks for putting this together. Obviously this is very hacky though. It seems like we just need replace browser script loading with dynamic imports. Perhaps we could replace it in both browser contexts and deno contexts, is there a reason we are relying on script loading? Also just summarizing, it seems like there isnt any deno feature missing, just that we are using browser document apis within a deno context
You'll have to play with it and get the result you want, I just wanted to see if it'd work at all without much modification and it does. That code contains all the mods I needed to make it usable standalone with deno & using existing packages. I'm sure it wouldn't be much work to make it usable in both browser and deno contexts, given that relatively small set of hacks needed -- and given it's using the ffmpeg browser js packages instead of the node packages.
Hey there! I was wondering if there were any progress on this. Ty!
me too....is it working in deno 1.35.0 yet?
@ralyodio No, currently if I run this in 1.35.0:
deno run --allow-read=. --allow-write=. --allow-net main.js
// main.js
await import("https://unpkg.com/@ffmpeg/ffmpeg@0.12.1/dist/umd/ffmpeg.js");
await import("https://unpkg.com/@ffmpeg/util@0.12.0/dist/umd/index.js");
const { FFmpeg } = FFmpegWASM;
const { fetchFile } = FFmpegUtil;
const ffmpeg = new FFmpeg();
ffmpeg.on("log", ({ message }) => {
console.log(message);
});
await ffmpeg.load({
coreURL: `https://unpkg.com/@ffmpeg/core@0.12.1/dist/umd/ffmpeg-core.js`,
wasmURL: `https://unpkg.com/@ffmpeg/core@0.12.1/dist/umd/ffmpeg-core.wasm`,
});
await ffmpeg.writeFile("input.avi", await fetchFile('./input.avi'));
await ffmpeg.exec(['-i', 'input.avi', 'output.mp4']);
const data = await ffmpeg.readFile('output.mp4');
// let outputBlob = new Blob([data.buffer], {type: 'video/mp4'});
await Deno.writeFile('output.mp4', data);
I get this error:
error: Uncaught (in promise) Error: Automatic publicPath is not supported in this browser
at https://unpkg.com/@ffmpeg/ffmpeg@0.12.1/dist/umd/ffmpeg.js:1:982
at https://unpkg.com/@ffmpeg/ffmpeg@0.12.1/dist/umd/ffmpeg.js:1:1124
at https://unpkg.com/@ffmpeg/ffmpeg@0.12.1/dist/umd/ffmpeg.js:1:3364
at https://unpkg.com/@ffmpeg/ffmpeg@0.12.1/dist/umd/ffmpeg.js:1:197
at https://unpkg.com/@ffmpeg/ffmpeg@0.12.1/dist/umd/ffmpeg.js:1:201
@survirtual I just copy-pasted your script, ran the vendoring command, and then ran the script as instructed, and got this error:
error: Uncaught TypeError: Cannot set properties of undefined (setting 'alert')
at file:///<working folder path>/vendor/unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.worker.js:1:353
Looks like all the import URLs are pinned (other than deno.land/std/fs
which I pinned to @0.156.0
since that was the latest at the time of your comment) so I'm not sure what has changed there. I also tried with deno upgrade --version 1.25.3
which would have been the latest Deno version at the time of your comment.
Is your feature request related to a problem? Please describe. I have created a called ffmpeg-templates in deno which uses ffmpeg on the command line.
Describe the solution you'd like It would be fantastic if I could bundle ffmpeg into this library rather than asking users to install an external dependency
Describe alternatives you've considered The current implementation is the alternative, use the system's ffmpeg and ask developers to install it.
Additional context Deno strives for parity with browsers, including webassembly support https://deno.land/manual@v1.5.2/getting_started/webassembly.
denoify is a package that assists in porting npm modules to deno