ffmpeg.wasm icon indicating copy to clipboard operation
ffmpeg.wasm copied to clipboard

Unable to import and worker.js error

Open ralyodio opened this issue 1 year ago • 8 comments

import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile } from '@ffmpeg/util';

ralyodio avatar Aug 01 '24 11:08 ralyodio

So I realized it has to run client side only. I'm using svelte. Here's what I did:

	let FFmpeg;
	let fetchFile;

	onMount(async () => {
		fetchFile = (await import('@ffmpeg/util')).fetchFile;
		FFmpeg = (await import('@ffmpeg/ffmpeg')).FFmpeg;
		ffmpeg = new FFmpeg({ log: true });
	});


The problem is something is requesting /worker.js which obviously isn't on my root static folder...so it just gets a 404

Where is this worker.js file coming from and why is it not loading from the package?

ralyodio avatar Aug 01 '24 12:08 ralyodio

@ralyodio Did you ever find a solution to this? I'm experiencing the same worker.js issue...

kamiri-charles avatar Dec 20 '24 13:12 kamiri-charles

@kamiri-charles Did you figure the issue?... Having same issue v0.12.13, Nextjs 15

nemerem-x avatar Dec 31 '24 07:12 nemerem-x

@nemerem-x I have kinda understood what's causing the issue. The problem (which has actually been stated in other issues in this repo directly and indirectly) is that the package does not provide the worker.js file. I think this is due to the package deployment failing because of some tests (check deployment logs). I came up with a workaround but it is specific to my use case so I'm not sure if it'll be of help. I created worker.js file as a static asset and included it in my public folder. With some help from chatgpt, I added some code for the worker file. As of now, I have run into some other problems with this but I'm still resolving them. Other solutions I can think of are:

  1. Clone this repo, resolve the package build issue and then use it in your project. (Currently trying this...)

  2. Find a similar library that can provide the same functionality. (If I find one, I'll update on this discussion)

  3. Await for issue resolution and package update (Not really hopeful on this one)

Thanks and happy new year 🙂

kamiri-charles avatar Dec 31 '24 16:12 kamiri-charles

@nemerem-x I have kinda understood what's causing the issue. The problem (which has actually been stated in other issues in this repo directly and indirectly) is that the package does not provide the worker.js file. I think this is due to the package deployment failing because of some tests (check deployment logs). I came up with a workaround but it is specific to my use case so I'm not sure if it'll be of help. I created worker.js file as a static asset and included it in my public folder. With some help from chatgpt, I added some code for the worker file. As of now, I have run into some other problems with this but I'm still resolving them.

Other solutions I can think of are:

  1. Clone this repo, resolve the package build issue and then use it in your project. (Currently trying this...)

  2. Find a similar library that can provide the same functionality. (If I find one, I'll update on this discussion)

  3. Await for issue resolution and package update (Not really hopeful on this one)

Thanks and happy new year 🙂

I had to use React instead of Next.js and it worked and now stuck at adding subtitle to a video.

Thanks and happy new year.

nemerem-x avatar Dec 31 '24 16:12 nemerem-x

@ralyodio @nemerem-x

I'm not sure if your issue is exactly the same as mine, but I encountered a similar problem using React with Vite. In my development environment (localhost), everything worked fine, but after deployment, I faced issues related to worker.js. (I apologize for not having any screenshots or error logs, as I didn't record them at the time.)

As @kamiri-charles mentioned, the problem occurs because the worker.js file is not provided. However, this might be related to our build process. I suspect that by properly configuring the build environment, the worker can be included correctly.

FFmpeg's load Method

Upon examining FFmpeg's load method, I found that it initializes the web worker as follows:

/**
 * Loads ffmpeg-core inside web worker. It is required to call this method first
 * as it initializes WebAssembly and other essential variables.
 *
 * @category FFmpeg
 * @returns `true` if ffmpeg core is loaded for the first time.
 */
load = ({ classWorkerURL, ...config } = {}, { signal } = {}) => {
    if (!this.#worker) {
        this.#worker = classWorkerURL ?
            new Worker(new URL(classWorkerURL, import.meta.url), {
                type: "module",
            }) :
            // We need to duplicate the code here to enable webpack
            // to bundle worker.js here.
            new Worker(new URL("./worker.js", import.meta.url), {
                type: "module",
            }); // result: @fs/:local-path-of-your-project/node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js
        this.#registerHandlers();
    }
    return this.#send({
        type: FFMessageType.LOAD,
        data: config,
    }, undefined, signal);
};

It constructs the URL using import.meta.url. Locally, if classWorkerURL is not specified, it generates a URL pointing to ./worker.js based on the project's local path.

The Problem

As a result, after deployment, when the client-side code attempts to load FFmpeg by calling load, it requests worker.js from a path relative to your website's URL. If no additional steps are taken, this request will fail because the worker.js file isn't available at that location.

My Solution

To resolve this issue, I took the following steps:

  1. Copy worker.js to the Build Output Directory: I manually copied the /node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js file into the directory where my build output is stored. This ensures that worker.js is available in the deployed environment.
  • Or, it might be a good idea to set it to include the worker file when build time.
  1. Update Worker URL Configuration: I adjusted the URLs used to load FFmpeg's core and worker files to correctly reference their locations in both development and production environments.

Here is the TypeScript code snippet I used:

 const baseURL = 'https://unpkg.com/@ffmpeg/[email protected]/dist/esm';

 const coreUrl = await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript');
 const wasmUrl = await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm');
 const classWorkerUrl = import.meta.env.PROD
      ? await toBlobURL(`/path-of-my-service-assets/worker.js`, 'text/javascript')
      : undefined // when undefined, /node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js will be loaded

Explanation

  • baseURL: Points to the CDN location of FFmpeg's core files.
  • coreUrl & wasmUrl: Convert the FFmpeg core JavaScript and WebAssembly files into Blob URLs for loading.
  • classWorkerUrl: In production (import.meta.env.PROD), it points to the copied worker.js file within the build assets in my assets(I try to redirect the path to S3 path on Nginx). In development, it resolves to undefined, allowing FFmpeg to load the worker from the local node_modules directory.
    • When classWorkerUrl is set, Nginx is configured to redirect requests for the worker.js file to a file server such as Amazon S3. This ensures that the worker.js file is correctly served from a reliable and scalable storage solution in the production environment.

Summary

By copying the worker.js file into the build output directory and correctly configuring the URLs, I ensured that the web worker is properly loaded in both development and production environments. This approach resolved the issues I faced with FFmpeg after deployment.

I wrote this in Korean and translated and summarized it into English using ChatGPT.

dmstmdrbs avatar Feb 07 '25 06:02 dmstmdrbs

Hi @dmstmdrbs @kamiri-charles ,

thank you very much for your explanation and for your use of the fffmpeg worker: I tried to go in the same direction as you, but I think I have a slightly different situation which makes it even more difficult for me to reach the solution.

I try to explain myself (to you and hopefully to others) as best I can:

Initial situation. I have a repo that makes use of ffmpeg and the resulting build will be used as a package in another repo.

Building the build of the child repo. Like you, I copied the worker.js file locally, to have it static in the final dist and at the same time I build the repo using the classWorkerURL attribute pointing to my static.

Problem. The problem I have, however, is that this repo builds with Vite, while the one that then uses it, builds with Webpack. This condition brings me two problems, one at runTime and one at buildTime:

buildTime problem.

  • Webpack fails to do the build correctly because, reading this part of the build code:
Image

it doesn't know how to handle that "/assets/worker-CgGQRANT.js" and generates this error:

Image

even though I will never, on balance, go through that point in the code using the classWorkerURL attribute. So this is my first error at buildTime.

runTime problem. if, cunningly, I delete those lines from the build of my repo and thus force a proper rebuild of the parent repo, once I launch the application, I get a cors error, because compared to my locahost, for him the file is located elsewhere and not internally in the repo.

Image

I have tried many different ways, but it seems to me that the only one left is to clone fffmpeg and change the use made of the worker and the load function, which I would very much like to avoid. 

 I hope I have made myself clear and I hope this entry gets as far as possible.

Thanks

Paolino95 avatar Jul 18 '25 15:07 Paolino95

I'm having an issue similar to @Paolino95 where during build time, vite is trying to handle the new Worker(new URL("./worker.js", import.meta.url), {type: "module" }) line of code, when I can't and won't use that. I feel like a library should never really be using import.meta.url. I may be wrong, but I think this library should just require passing a URL parameter to load and let the consumer dictate on how that URL is constructed

LordZardeck avatar Nov 17 '25 20:11 LordZardeck