3

I'm having a bit of an issue and I'd really appreciate it if I could get some insights.

What I am trying to do is to add an album cover to the mp3 file that will be downloaded from the front-end.

Context

I'm downloading a video stream from YouTube and converting it to mp3 using fluent-ffmpeg.
To get the video I use the ytdl npm module.

I then pipe this stream to the front-end.

What I've found

fluent-ffmpeg offers either pipe() or saveToFile().

What I figured is that when I use the saveToFile() function and actually save my stream into an mp3 file, it works, I do get the album cover.

But when I pipe the stream to front-end or even into a file, the song is saved properly into a file but without the album cover.

Here is my code

Back-end (NodeJS)

let video = ytdl(`http://youtube.com/watch?v=${videoId}`, {
  filter: (format) => format.container === 'mp4' && format.audioEncoding,
  quality: 'lowest'
});

let stream = new FFmpeg()
  .input(video)
  .addInput(`https://i.ytimg.com/vi/${videoId}/default.jpg`)
  .outputOptions([
      '-map 0:1',
      '-map 1:0',
      '-c copy',
      '-c:a libmp3lame',
      '-id3v2_version 3',
      '-metadata:s:v title="Album cover"',
      '-metadata:s:v comment="Cover (front)"'
  ])
  .format('mp3');

And then piping it to my front-end.

stream.pipe(res);
stream
  .on('end', () => {
    console.log('******* Stream end *******');
    res.end.bind(res);
  })
  .on('error', (err) => {
    console.log('ERR', err);
    res.status(500).end.bind(res);
  });

Front-end (React)

axios.get(url)
  .then(res => {
    axios(`${url}/download`, {
      method: 'GET',
      responseType: 'blob'
    })
      .then(stream => {
        const file = new Blob(
          [stream.data],
          { type: 'audio/mpeg' });
        //Build a URL from the file
        const fileURL = URL.createObjectURL(file);
      })
      .catch(err => {
        console.log('ERROR', err);
      });
    })
    .catch(err => {
      console.log('ERROR', err);
    });

1 Answers1

0

Unfortunately, it seems there is no possible solution to complete this task with streams. I've researched a lot but found only an explanation of why we can't do this with FFmpeg and piping stream. njoyard wrote the following:

Actually this problem is not specific to Windows. Most formats write stream information (duration, bitrate, keyframe position...) at the beginning of the file, and thus ffmpeg can only write this information when its output is seekable (because it has to finish processing streams to the end before knowing what to write). Pipes are not seekable, so you won't get this information when using an output pipe.

As for your note about the output format, ffmpeg determines the output format from the output file extension, which is not possible with pipes; that's why you have to specify the output format explicitly.

Here is a link to find it by yourself: https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/159

So, the only solution I see is saving file with saveToFile() method and attaching it to response.