Discord音乐Bot播放完歌曲后清空整个队列而非移除单首已播歌曲问题
问题根因
核心问题是重复绑定了AudioPlayerStatus.Idle事件的监听器。你大概率在每次新增歌曲到队列、或者每次触发播放操作时,都重复执行了player.on(AudioPlayerStatus.Idle, () => { ... })的绑定逻辑,没有做监听器去重处理。
你提供的调试日志可以直接验证这个问题:
- 第一次触发Idle状态时,多个绑定的回调同时执行,第一次
splice(0,1)执行后队列变为song2,song3,后续回调执行splice时,因为你在回调里调用了player.play()让播放器退出了Idle状态,所以没有触发下一轮播放,就出现了连续三次输出song2,song3的情况 - 第二次触发Idle状态时,多个回调再次连续执行,删除song2后队列只剩
song3,同样出现连续三次输出的情况 - 最后一次触发Idle状态时,回调直接把最后一首歌删除,队列变为空
修复方案
只需要保证Idle事件的回调全局只绑定一次即可,不要在每次操作队列的时候重复绑定,参考实现如下:
// 播放器初始化阶段全局只绑定一次Idle事件监听器 const player = createAudioPlayer(); // 全局唯一的队列变量 const queue = []; player.on(AudioPlayerStatus.Idle, () => { // 移除已播放完成的队首歌曲,shift和splice(0,1)效果一致,写法更简洁 queue.shift(); if (queue.length === 0) return; const nextResource = createAudioResource(join('./music/', `${queue[0].toLowerCase()}.mp3`)); player.play(nextResource); }); // 后续加歌、触发播放的逻辑中不要重复绑定上面的Idle事件 function addSongToQueue(songName) { queue.push(songName); // 播放器空闲时直接触发播放 if (player.state.status === AudioPlayerStatus.Idle) { const resource = createAudioResource(join('./music/', `${queue[0].toLowerCase()}.mp3`)); player.play(resource); } }
如果你的业务逻辑无法避免重复绑定的场景,也可以在绑定新监听器前先移除旧监听器,需要先把回调提取为具名函数:
function handlePlayerIdle() { queue.shift(); if (queue.length === 0) return; const nextResource = createAudioResource(join('./music/', `${queue[0].toLowerCase()}.mp3`)); player.play(nextResource); } // 每次绑定前先移除旧的监听器,保证同一时间只有一个监听器生效 player.off(AudioPlayerStatus.Idle, handlePlayerIdle); player.on(AudioPlayerStatus.Idle, handlePlayerIdle);
内容的提问来源于stack exchange,提问作者MegaMix_Craft
相关产品推荐
相关产品推荐

