Discord.js音乐机器人player的finish/end事件无法触发问题求助
问题根因
- 你用的
@discordjs/voice是Discord.js v14+的语音模块,该模块的AudioPlayer本身不存在finish/end事件,且play()方法返回值不是事件发射器,你直接在play()后绑定事件的写法本身就不生效。 - 之前监听
Idle状态不生效,是因为没有绑定在正确的player实例上,也没有做状态变更的前后对比判断。 videoPlayer方法里重复创建了两次音频资源,属于冗余逻辑,可能引发未知播放问题。- 没有处理音频流和播放器的错误事件,播放过程中如果出现加载失败的情况会直接崩溃。
- v14版本已经废弃了旧版的
dispatcher相关逻辑,不要参考v12/v13的旧教程写法。
修复方案
1. 先确认导入必要的枚举
const { AudioPlayerStatus } = require('@discordjs/voice');
2. 修改player实例的绑定逻辑
在你创建player实例的位置,直接绑定全局状态监听,一次绑定终身生效,不要每次播放都重复绑定:
const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Pause } }); // 绑定状态变化监听,处理切歌逻辑 player.on('stateChange', (oldState, newState) => { // 仅当从播放状态切换到闲置状态时,才判定为歌曲播放完成 if (oldState.status === AudioPlayerStatus.Playing && newState.status === AudioPlayerStatus.Idle) { const serverQueue = queue.get(message.guild.id); if(!serverQueue) return; // 移除已播放完成的歌曲 serverQueue.songs.shift(); // 播放下一首 videoPlayer(message.guild, serverQueue.songs[0]); } }); // 绑定播放器错误监听 player.on('error', error => { console.error('播放器出错:', error); const serverQueue = queue.get(message.guild.id); if(serverQueue) { serverQueue.textChannel.send(`当前歌曲播放出错,已自动跳过`); serverQueue.songs.shift(); videoPlayer(message.guild, serverQueue.songs[0]); } }); queueConstructor.player = player;
3. 重写videoPlayer方法
移除错误的事件绑定和冗余逻辑,新增流错误处理:
const videoPlayer = async (guild, song) => { const songQueue = queue.get(guild.id); if (!song) { songQueue.player.stop(); songQueue.connection.destroy(); queue.delete(guild.id); return songQueue.textChannel.send({ content: "队列已空,已退出语音频道" }); } try { const stream = ytdl(song.url, { filter: "audioonly", highWaterMark: 1<<25, // 配置32MB缓存减少卡顿 quality: 'highestaudio' }); // 监听音频流加载错误 stream.on('error', err => { console.error('音频流加载失败:', err); songQueue.textChannel.send(`当前歌曲资源加载失败,已自动跳过`); songQueue.songs.shift(); videoPlayer(guild, songQueue.songs[0]); }); const audioResource = createAudioResource(stream); songQueue.player.play(audioResource); // 发送当前播放提示 songQueue.textChannel.send({ content: `Now playing: ${song.title}` }); } catch (err) { console.error('播放逻辑出错:', err); songQueue.textChannel.send(`播放出错,跳过当前歌曲`); songQueue.songs.shift(); videoPlayer(guild, songQueue.songs[0]); } }
其他优化建议
- 队列构造器可以新增
playing状态锁,避免重复触发播放逻辑 skip命令直接调用player.stop()即可触发Idle状态自动切歌,不需要额外写切歌逻辑- 可以增加队列长度限制,防止恶意刷队列导致内存溢出
- 官方原版
ytdl-core适配性差容易出现403错误,建议替换为@distube/ytdl-core使用
内容的提问来源于stack exchange,提问作者Karlo
相关产品推荐
相关产品推荐

