将 ytdl-core 流转换为 Nodejs Express 中的 blob

问题描述 投票:0回答:1

我正在开发 YouTube 播放列表音频下载器,但遇到了障碍(见下文)。下载一个视频的音频没有问题;我只是将流传输到响应:

ytdl(info.videoDetails.videoId, {
  quality: "highestaudio",
  filter: "audioonly",
}).pipe(res);

我想做的是发送播放列表中每个视频的音频。我正在考虑将响应作为“多部分/表单数据”发送,其中每个表单段都包含作为 blob 的音频,如本文中所述。

我在将 ytdl-core 的响应转换为 blob 时遇到问题。 我认为我正在转换流本身,而不是音频数据。

我收到的错误是:

C:\Users\blackjacques\youtube-audio-downloader-master\node_modules\delayed-stream\lib\delayed_stream.js:33
source.on('error', function() {});
     ^
TypeError: source.on is not a function
    at DelayedStream.create (C:\Users\blackjacques\youtube-audio-downloader-master\node_modules\delayed-stream\lib\delayed_stream.js:33:10)
    at CombinedStream.append (C:\Users\blackjacques\youtube-audio-downloader-master\node_modules\combined-stream\lib\combined_stream.js:45:37)
    at FormData.append (C:\Users\blackjacques\youtube-audio-downloader-master\node_modules\form-data\lib\form_data.js:75:3)
    at C:\Users\blackjacques\youtube-audio-downloader-master\server.js:169:12
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)

Node.js v22.11.0

我正在使用“@distube/ytdl-core”:“^4.15.1”,“cors”:“^2.8.5”,“express”:“^4.17.1”,“form-data”:“^ 4.0.1”和“ytpl”:“^2.3.0”。

以下是相关的nodejsexpress服务器代码:

getListOfSongsFromPlaylist = async (playlistUrl) => {
  console.log(playlistUrl)
  let playlist = await ytpl(playlistUrl);

  playlist.items = playlist.items.filter(song => song.isPlayable === true).map(song => {
      const title = (song.title.includes(song.author.name)) ? song.title : song.author.name + ' - ' + song.title;

      return {
          id: song.id,
          title: title,
          duration: song.duration
      }
    }
  );

  return playlist;
}

getAudioAsBlob = async (videoId) => {
  return await new Response(
    ytdl(videoId, {
      quality: "highestaudio",
      filter: "audioonly",
    }).blob();
};

app.post("/", async (req, res) => {
  console.log(`Request: ${req.url}`);
  const form = new FormData();

  getListOfSongsFromPlaylist(req.body.url).then(async playlist => {
    console.log('Playlist name: ' + playlist.title);
    console.log('Total songs in playlist: ' + playlist.estimatedItemCount);

    playlist.items.forEach(async song => {
      const audioAsBlob = await getAudioAsBlob(song.id);
      form.append(song.title, audioAsBlob, {
        filename: `${song.title}.mp3`,
        contentType: 'audio/mpeg',
        knownLength: audioAsBlob.size,
      });
    });
  });

  // Set headers for multipart response.
  res.writeHead(200, {
    'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`, // boundary must match the one generated by form-data
    'Content-Length': form.getLengthSync(),
    'Access-Control-Allow-Credentials': 'true',
    'Access-Control-Allow-Origin': '*', 
  });

  // Write the multipart form data response.
  res.write(form.getBuffer());
  res.end();
});

关于如何将 ytdl-core 数据转换为 blob 有什么想法吗?

node.js express blob multipartform-data ytdl
1个回答
0
投票

我终于弄清楚如何将音频流转换为 blob。诀窍是在“data”事件中将数据推送到数组中。我最终将数据数组转换为缓冲区,因为 FormData 不喜欢 blob(这是一个已知问题)。这是在“结束”事件中完成的:

const getAudioAsBuffer = async (videoId) => {
  return new Promise((resolve, reject) => {
    const chunks = [];
    ytdl(videoId, {
      quality: "highestaudio",
      filter: "audioonly",
    }).on('data', (data) => {
      chunks.push(data);
    }).on('error', (err) => {
      console.log("error: " + err.message);
      reject(err.message);
    }).on('end', async () => {
      const arrayBuffer = await new Blob(chunks).arrayBuffer();
      resolve(Buffer.from(arrayBuffer));
    });
  });
};

然后只需将缓冲区附加到表单即可:

const form = new FormData();
const buffer = await getAudioAsBuffer(song.id);
  form.append(song.title, buffer, {
    filename: `${song.title}.mp3`,
    contentType: 'audio/mpeg',
    knownLength: buffer.length,
  });
  // Set headers for multipart response.
  res.writeHead(200, {
      'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`, // boundary must match the one generated by form-data
      'Content-Length': form.getLengthSync(),
      'Access-Control-Allow-Credentials': 'true',
      'Access-Control-Allow-Origin': '*',
  });

  // Write the multipart form data response.
  res.write(form.getBuffer());
  res.end();
© www.soinside.com 2019 - 2024. All rights reserved.