简体   繁体   中英

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

I have a music library in .mp3 format stored in a Google Drive folder, with a bunch of music files I want to be able to play one after the other. I am able to read and stream each file individually, but when I try to "queue" all the files from the folder and play them one after the other, it won't wait till one stream (song) is done playing to play the next, and instead starts the next one immediately, which results in only the last song being played out of the entire folder. I'd assume I have to mess around with async/await which I have done earlier in discord.js development, or with Promises and Promise.all(), which I am not familiar with. Here's the relevant part of the code.

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));
        });
    });
});

Note that I have a command to make the bot leave the channel, so it's normal that there isn't any voiceChannel.leave() in my code, as I don't want it to leave right after the songs have finished playing.

Any advice is welcome, thanks in advance!

  • You want to play multiple MP3 files by downloading them from the specific folder in Google Drive.
  • You have already been able to play the MP3 data at the voice channel and use Drive API.
  • You want to achieve this using discord.js and googleapis with Node.js.

If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.

Modification points:

In this answer, the MP3 files downloaded by googleapis are converted to the stream and put to the voice channel with discord.js.

Modified script:

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));
  }
);
  • In this sample script, when all MP3 files were finished, end is shown in the console.

References:

If I misunderstood your question and this was not the direction you want, I apologize.

Edit:

In the following sample script, all files in the specific folder on Google Drive are downloaded every one file and that is played with the stream.

Sample script:

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));
  }
);
  • The function of playFiles() is called from above script.
Function of 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)); } ); }
  • In this case, channel.leave() is important. I confirmed that when this is not used, there are the cases that at the next play, the sound cannot be listened from 2nd file. Please be careful this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM