You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 16:54:01