Stack Exchange
FFmpeg is Not Working on my Electron Angular app
This previously working FFMPEG implementation, is no longer successful on my updated Angular 14 Electron 19 project. The getAudiVolume will exit with this error: " An error occurred while analysing: Output format null is not available"
IMPLEMENTING NORMALISATION
The component method
downloadYoutube(id: string){
  const filename = id+'.mp4';
  const filepath = Config.homePath? Config.homePath + '/' + filename : filename;
  const stream =  ytdl(id).pipe(fs.createWriteStream(filepath));
      stream.on('finish', (data) => {
        console.log(stream);
        this.mediaService.getAudioVolumes(stream).subscribe((d) => {console.log('done', d);});
      });
}

The service
import ffmpeg from 'fluent-ffmpeg-corrected';
import * as ffmpegBin from 'ffmpeg-static-electron';
import FS from 'fs';
import {Readable} from 'stream';
import {Config} from "../shared/config";

export interface IVolumes {
meanVolume: string;
maxVolume: string;
}

@Injectable()
export class MediaService {
  ffmpegPath = ffmpegBin.path
  .replace('app.asar/bin', 'app.asar.unpacked/' + this.binPath)
  .replace('node_modules/electron/dist/Electron.app/Contents/Resources/electron.asar/renderer/bin', this.binPath)
  .replace('browser/javascript', 'mac/x64') // TODO: platform hardcoded!
  ;

getAudioVolumes(stream: Readable | FS.WriteStream): Observable<IVolumes> {
  if (!Config.isElectron){
  this.ffmpegPath= 'node_modules/ffmpeg-static-electron/bin/mac/x64/ffmpeg';
  }
  ffmpeg.setFfmpegPath(this.ffmpegPath);
  
      return new Observable((observer: NextObserver<IVolumes>) => {
        const that = this;
        ffmpeg(stream)
          .withAudioFilter('volumedetect')
          .addOption('-f', 'null')
          .audioBitrate(128)
  
          .on('progress', function(progress) {
            console.log('Normalising Processing: ' + progress.percent + '% done');
          })
  
          .on('error', function(err) {
            console.log('An error occurred while analysing: ' + err.message);
            observer.error('DBs are not accessible');
          })
  
          .on('end', (stdout: any, stderr: string) => {
            const max = that.parseVolume(stderr, 'max_volume:');
            const mean = that.parseVolume(stderr, 'mean_volume:');
            console.log('volume analysis done, MeanDB is ', mean);
            observer.next({meanVolume: mean, maxVolume: max});
            observer.complete();
          })
          .save('/dev/null');
      });
}

TRY IT:

Fork https://github.com/arcovoltaico/angular-electron
Check out on the branch yt-dl
run npm install on the root and also inside app/
We need to hack the following node modules package.json by adding :


fluent-ffmpeg-corrected
"browser": { "fs": false, "child_process": false },
isexe:
"browser": { "fs": false}


Build the app by npm run electron:build
Execute the app (releases/xxx) and click the Download Kraftwerk Button, you will see the error  An error occurred while analysing: Output format null is not available


Have you ensured that the server/local machine  has the FFmpeg executable files already present ?

Hi, the point is not to need to install it but to have it embedded in the Electron app. I already achieved it on a previous Electron and Angular version project, with the same code.

After some debug I realize that the problem was in the utils.js file on the fluent-ffmpeg library, when it compares the instanceof Buffer it always says false because the Buffer created is a browserified Buffer instead of a strict node Buffer.
Why was this the error?, well, when you try to use the ffmpeg commands the library will check that the format passed (null in this case) is one of the available ones, it does this by running ffmpeg -formats wich returns, you guessed it, a Buffer (as any other ffmpeg command), so the validation was always saying that the format you passed was not "valid".
How you can fix this?, you can manually edit the node_modules by hand, changing the condition -maybe- to str.buffer instanceof ArrayBuffer. But a cleaner solution might be adding the Buffer as a pollyfil in the webpack config, like this:

Install buffer as a dev dependency npm install buffer -D
Add the following code on the angular.webpack.js in the plugins section

const webpack = require('webpack');
...
        config.plugins = [
        ...config.plugins,
        new webpack.ProvidePlugin({
          Buffer: ['buffer', 'Buffer'],
        }),
        new NodePolyfillPlugin({
              excludeAliases: ["console"]
        })
    ];

Not sure if it works on all cases but with that you can do the build command and check that the error is gone.
Another thing:

I realize that you are passing a WriteStream to ffmpeg which is odd as it should be a ReadStream, so maybe you will need to change that line to something like

  ffmpeg(FS.createReadStream((stream as any).path)

Hope it helps!

Thanks so much. I have committed your fixes. Unfortunately, when clicking on the first download button, I still get an error: "Cannot read properties of undefined (reading 'isStream'".  What is missing?  The goal is to get the analysed volume dB printed on the console and no errors. Cheers

I will take a look, but are you passing the stream as a readable stream (or file)? ffmpeg(FS.createReadStream(stream.path) or ffmpeg(stream.path) instead of just ffmpeg(stream)?

The problem is on BasicDownlad I can not pipe to CreateReadStream instead  of to  CreateWriteStream. But the stream.path fixed it, as I commit it now :-)

Yes!, because the stream is already finished the ffmpeg cant do much, so passing the path as a file should work!. Does it work now?

I'm cleaning up the project and come back to you ASAP, but I feel optimistic :-)

I think the ffmpeg is solved. Would you want to check the next error? I can set another bounty if you consider it.  Cheers ! https://stackoverflow.com/questions/74753891/why-ytdl-does-not-work-inside-an-observable

Good news!, yes please so we can close this bounty. I will also look into the other problem
