【问题标题】:Discord JS, VoiceConnection.play() breaks with a local music playlistDiscord JS,VoiceConnection.play() 与本地音乐播放列表中断
【发布时间】:2021-03-26 08:20:36
【问题描述】:

我有一个 Discord 机器人,我希望它播放本地音乐。虽然只用一个文件就可以了,但当我尝试播放更多歌曲时,一切都崩溃了。

client.on('voiceStateUpdate', ((oldState, newState) => { //Call it when a user enter a voice channel

  if (!oldState.channel && newState.member.user !== bot) { //Discard the bot

    if (newState.channelID === l5r_roleplay.id) { //If it's the right vocal channel do things

      counter++; //Counter is use to check when start playing music and when everyone has left. Basically is a counter for the active members inside the vocal channel

      if (counter === 1) { //It starts playing music when the first member join the vocal channel

        l5r_roleplay.join().then(connection => {

          let i = 0;

          shuffle(music_array); //Function to shuffle array

          console.log("Now playing: " + music_array[i])
          connection.play(music_array[i], {volume: 0.2});

        });

      }

    }

  } else if (!newState.channel && oldState.member.user !== bot) { //Someone left

    if (oldState.channelID === l5r_roleplay.id) {

      counter--;

      if (counter < 0) counter = 0;

      if (counter === 0) {

        l5r_roleplay.leave();

      }

    }

  }

}));

上面的代码是有效的,因为它只播放第一首歌曲(music_array 是一个字符串数组,其中每个字符串都是歌曲名称(+路径))

我想让它一个接一个地播放数组中的所有歌曲。

我尝试将它放在 for 循环、while 循环和外部函数中。我还尝试使用返回的 StreamDispatcher 来处理歌曲何时结束并增加处理程序中的循环/while 变量,但没有。

行为很简单。如果我放一个循环,它只会在控制台中打印,而不会播放歌曲。

一个带有简单 for 循环、调度程序和控制台输出的示例

for (let i = 0; i < music_array.length;) {

            console.log("Now playing: " + music_array[i])
            const dispatcher = connection.play(music_array[i], {volume: 0.2})
            dispatcher.on('speaking', value => {

              if (!value) i++

            })

          }

输出:

[nodemon] starting `node index.js`
---- < Online e operativo >
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3
Now playing: ./backbone/l5r/music/The Samurai's Virtue.mp3

【问题讨论】:

  • dispatcher.on() 是一种非阻塞方法,这意味着它不会延迟 for 循环执行的每次迭代。这意味着循环将重复数百次并每次都保持在i = 0,直到机器人开始/停止说话(并且机器人只会在几秒钟后这样做,给循环足够的时间来执行数百次而@ 987654327@ 仍为 0)。虽然这在技术上并不是一个无限循环,但它同样会出现问题,因为它会尝试播放同一首歌曲多少次。您肯定需要使用递归循环。
  • 所以没有音乐播放的原因是因为循环试图在几毫秒内播放数百次“武士的美德”。在机器人停止说话之前,它不会停止尝试这样做。但因为它一直触发歌曲播放,歌曲每次都从头开始,所以机器人也可能永远无法停止说话。如果是这样的话,这确实是一个无限循环。如果不是这样,关于这个问题,这个循环仍然可以被视为一个无限循环。如前所述,递归循环将能够解决这个问题,并且是最好的解决方案。

标签: node.js discord discord.js audio-player playlist


【解决方案1】:

dispatcher.on() 是一个非阻塞方法,意味着它不会延迟 for 循环执行的每次迭代。这意味着循环将 重复数百次,每次都保持在 i = 0,直到 机器人开始/停止说话(机器人只会在 几秒钟,给循环足够的时间来执行数百个 当 i 仍然为 0 时)。虽然这在技术上不是无限的 循环,它会同样有问题,因为它会多少次 尝试播放同一首歌曲。你肯定需要使用 而是递归循环。 – 杀戮

正如@Cannicide 所说,不幸的是 dispatcher.on() 是非阻塞的。

所以这里有一个解决上一个问题的方法。我们需要一个递归函数

function play(voiceConnection, song, index) {

  if (index === 0) {

    shuffle(music_array);
    song = music_array[0];

  }

  console.log("Now Playing: " + song);
  const dispatcher = voiceConnection.play(song, {volume: 0.2})
  dispatcher.on('speaking', value => {

    if (!value) {

      index = index+1 === music_array.length ? 0 : index+1;

      play(voiceConnection, music_array[index], index);

    }
    
  })

}

第一个 if 用于每次使用 index = 0 调用函数时对数组进行洗牌;当我们第一次调用函数时会发生这种情况:

play(connection, null, 0);

当我们到达播放列表的末尾时(第二个 if 中的第一行)。这是个人场景,因为我希望播放列表不仅要重新开始,还要重新洗牌。

【讨论】:

  • 我已经使用递归函数有一段时间了,但是我想知道,在播放了几千首歌曲之后,这是否会出现问题?递归深度限制不是问题吗?我知道它是10,000或什么的。但这会不会是个问题。
【解决方案2】:

问题在于调度程序是一个异步对象并且异步播放流媒体。因此,当 dispatcher.on() 被触发时,它发生在您的 while 循环“外部”。 当调度程序在后台播放时,您的 while 循环将永远循环。

处理这个问题的最好方法是使用递归函数。 我已经用了一段时间了,效果很好:

//Functions
async function play(bot, message, guild, song) {
    const serverQueue = bot.musicQueue.get(guild.id);

    //Check if a song exists.
    if (!song) {
        if (serverQueue.loop) {
            //Copy finished songs back to songs
            serverQueue.songs = serverQueue.songs.concat(serverQueue.finishedSongs);
            serverQueue.finishedSongs = []; //Clear finished songs

            //Play again and message
            message.WaffleResponse(`Looped ?`, MTYPE.Information);
            return play(bot, message, guild, serverQueue.songs[0]);
        } else {
            //Leave on end of music after 5 minutes
            leaveChannelOnNoSong(bot, message, serverQueue);
        }
        return;
    } else {
        //Song details
        const { title, url, duration_ms } = song.song;
        const queuedBy = song.queuedBy.toString();

        //Create readable video object
        var readable = await ytdl(url, { quality: "highestaudio", highWaterMark: 1 << 25 });
        //Create dispatcher and play
        const dispatcher = serverQueue.connection
            .play(readable, { highWaterMark: 1, bitrate: 'auto', fec: true, volume: 0.1 })
            .on('start', () => {
                //Send message
                message.WaffleResponse(
                    `Started playing [${title.replace(/(\*|_|`|~|\\|\[|])/g, '')}](${url})` +
                    ` ${queuedBy}\n\n**Song Duration:**\n${(duration_ms / 1000).toString().toTimeString()}`,
                    MTYPE.Information, null, false, null, serverQueue.textChannel
                );
            }).on("finish", () => {
                //Shift songs and play next recursively
                serverQueue.finishedSongs.push(serverQueue.songs.shift());
                return play(bot, message, guild, serverQueue.songs[0]);
            }).on('error', (err) => {
                //Send message
                message.WaffleResponse(
                    `[${title}](${url}) encountered an error while streaming. Skipped.`, MTYPE.Error,
                    null, false, null, serverQueue.textChannel
                );
                //Shift songs and play next recursively
                serverQueue.songs.shift();
                return play(bot, message, guild, serverQueue.songs[0]);
            });
    }
}

第一次通话是:

play(bot, message, message.guild, tempServerQueue.songs[0]);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-19
    • 1970-01-01
    • 1970-01-01
    • 2022-07-10
    • 1970-01-01
    相关资源
    最近更新 更多