在开始阅读下一个之前等待每个 playStream() 完成

Wait for each playStream() to finish before starting to read the next one

我有一个 .mp3 格式的音乐库,存储在 Google 驱动器文件夹中,其中有一堆音乐文件,我希望能够一个接一个地播放。我能够单独读取和流式传输每个文件,但是当我尝试 "queue" 文件夹中的所有文件并一个接一个地播放它们时,它不会等到一个流(歌曲)播放完毕播放下一首,而是立即开始下一首,这会导致整个文件夹中只播放最后一首歌曲。我假设我必须弄乱我在 discord.js 开发之前做过的 async/await,或者我不熟悉的 Promises 和 Promise.all()。这是代码的相关部分。

var folderId = "'the-folder-id'";
drive.files.list({
    q: folderId + " in parents", // to get all the files in the folder
    fields: 'files(id)'
}, (err, res) => {
    if (err) throw err;
    const files = res.data.files;
    files.map(file => {
        drive.files.get({
            fileId: file.id,
            alt: 'media'
        },
        { responseType: "stream" },
        (err, { data }) => {
            message.member.voiceChannel.join().then(connection => {
                const dispatcher = connection.playStream(data); // doesn't wait for this to finish to play the next stream (song)
            }).catch(err => console.log(err));
        });
    });
});

请注意,我有一个让机器人离开频道的命令,所以我的代码中没有任何 voiceChannel.leave() 是正常的,因为我不希望它在歌曲播放后立即离开玩完了

欢迎任何建议,提前致谢!

  • 您想通过从 Google 驱动器中的特定文件夹下载多个 MP3 文件来播放它们。
  • 您已经可以在语音通道播放MP3数据并使用DriveAPI。
  • 您想使用 discord.js 和带有 Node.js 的 googleapis 来实现此目的。

如果我的理解是正确的,这个答案怎么样?请将此视为几个可能的答案之一。

修改点:

在这个回答中,googleapis下载的MP3文件被转换为流并放入语音频道discord.js。

修改后的脚本:

var folderId = "'the-folder-id'";
drive.files.list(
  {
    q: folderId + " in parents", // to get all the files in the folder
    fields: "files(id)"
  },
  (err, res) => {
    if (err) throw err;
    const files = res.data.files;
    Promise.all(
      files.map(file => {
        return new Promise((resolve, reject) => {
          drive.files.get(
            {
              fileId: file.id,
              alt: "media"
            },
            { responseType: "stream" },
            (err, { data }) => {
              if (err) {
                reject(err);
                return;
              }
              let buf = [];
              data.on("data", function(e) {
                buf.push(e);
              });
              data.on("end", function() {
                const buffer = Buffer.concat(buf);
                resolve(buffer);
              });
            }
          );
        });
      })
    )
      .then(e => {
        const stream = require("stream");
        let bufferStream = new stream.PassThrough();
        bufferStream.end(Buffer.concat(e));
        message.member.voiceChannel
          .join()
          .then(connection => {
            const dispatcher = connection.playStream(bufferStream);
            dispatcher.on("end", () => {
              // do something
              console.log("end");
            });
          })
          .catch(e => console.log(e));
      })
      .catch(e => console.log(e));
  }
);
  • 在此示例脚本中,完成所有 MP3 文件后,end 会显示在控制台中。

参考文献:

如果我误解了您的问题并且这不是您想要的方向,我深表歉意。

编辑:

在下面的示例脚本中,Google 驱动器上特定文件夹中的所有文件都将下载每个文件并与流一起播放。

示例脚本:

var folderId = "'the-folder-id'";
drive.files.list(
  {
    q: folderId + " in parents",
    fields: "files(id,name)"
  },
  (err, res) => {
    if (err) throw err;
    const channel = message.member.voiceChannel;
    channel
      .join()
      .then(connection => playFiles(drive, channel, connection, res.data.files))
      .catch(e => console.log(e));
  }
);
  • playFiles()的函数是从上面的脚本中调用的。
播放文件()的功能
function playFiles(drive, channel, connection, files) {
  if (files.length == 0) {
    channel.leave();
    return;
  }
  drive.files.get(
    {
      fileId: files[0].id,
      alt: "media"
    },
    { responseType: "stream" },
    (err, { data }) => {
      if (err) throw new Error(err);
      console.log(files[0]); // Here, you can see the current playing file at console.
      connection
        .playStream(data)
        .on("end", () => {
          files.shift();
          playFiles(drive, channel, connection, files);
        })
        .on("error", err => console.log(err));
    }
  );
}
  • 在这种情况下,channel.leave() 很重要。我确认如果不使用它,在下次播放时,可能会出现无法从第 2 文件听到声音的情况。请注意这一点。