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

如何创建语音机器人音频曲目队列?AudioTrack与loadItem新版本适配问题

Discord语音机器人音频队列实现(适配新版本JDA/Lavaplayer)

核心问题梳理

你的代码存在几个关键问题,导致队列功能无法正常工作:

  • 重复创建AudioPlayerManager实例:每次调用createAudioPlayerManager()都会生成新实例,浪费资源且无法共享缓存
  • loadItem用法错误:新版本Lavaplayer需要通过回调处理加载结果,不能直接强转返回值
  • 缺少队列存储结构:没有维护待播放的曲目队列
  • 变量名错误:addTrack方法里将参数videoUrl误写为url

修正后的完整实现

1. 封装音乐管理器(含队列)

import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventListener;
import net.dv8tion.jda.api.managers.AudioManager;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class GuildMusicManager {
    public final AudioPlayer audioPlayer;
    public final BlockingQueue<AudioTrack> queue;
    private final AudioPlayerSendHandler sendHandler;

    public GuildMusicManager(AudioPlayerManager manager) {
        this.audioPlayer = manager.createPlayer();
        this.queue = new LinkedBlockingQueue<>();
        this.sendHandler = new AudioPlayerSendHandler(audioPlayer);
        
        // 绑定轨道结束监听器,自动播放下一曲
        this.audioPlayer.addListener((AudioEventListener) event -> {
            if (event instanceof com.sedmelluq.discord.lavaplayer.player.event.TrackEndEvent trackEndEvent) {
                if (trackEndEvent.getEndReason().mayStartNext) {
                    playNextTrack();
                }
            }
        });
    }

    public AudioPlayerSendHandler getSendHandler() {
        return sendHandler;
    }

    // 播放队列下一曲
    private void playNextTrack() {
        AudioTrack nextTrack = queue.poll();
        if (nextTrack != null) {
            audioPlayer.playTrack(nextTrack);
        }
    }

    // 添加曲目到队列
    public void addTrack(AudioTrack track) {
        // 当前无播放曲目则直接播放,否则加入队列
        if (!audioPlayer.startTrack(track, true)) {
            queue.offer(track);
        }
    }
}

2. 全局播放器管理与工具方法

import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import net.dv8tion.jda.api.entities.VoiceChannel;

// 全局单例播放器管理器,仅初始化一次
private static final DefaultAudioPlayerManager PLAYER_MANAGER = initPlayerManager();

private static DefaultAudioPlayerManager initPlayerManager() {
    DefaultAudioPlayerManager manager = new DefaultAudioPlayerManager();
    manager.setFrameBufferDuration(5000);
    // 注册远程音频源(含YouTube)
    AudioSourceManagers.registerRemoteSources(manager);
    // 可选:注册本地文件源
    AudioSourceManagers.registerLocalSource(manager);
    return manager;
}

// 为指定语音频道创建音乐管理器
public static GuildMusicManager getMusicManager(VoiceChannel channel) {
    AudioManager audioManager = channel.getGuild().getAudioManager();
    GuildMusicManager musicManager = new GuildMusicManager(PLAYER_MANAGER);
    
    audioManager.setSendingHandler(musicManager.getSendHandler());
    audioManager.openAudioConnection(channel);
    
    return musicManager;
}

// 加载YouTube链接并添加到队列
public static void loadTrack(String url, GuildMusicManager musicManager) {
    PLAYER_MANAGER.loadItemOrdered(musicManager, url, new com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler() {
        @Override
        public void trackLoaded(AudioTrack track) {
            musicManager.addTrack(track);
        }

        @Override
        public void playlistLoaded(AudioPlaylist playlist) {
            // 加载整个播放列表的所有曲目
            playlist.getTracks().forEach(musicManager::addTrack);
        }

        @Override
        public void noMatches() {
            System.out.println("未找到匹配音频:" + url);
        }

        @Override
        public void loadFailed(FriendlyException exception) {
            System.out.println("音频加载失败:" + exception.getMessage());
        }
    });
}

3. 使用示例(命令处理中调用)

// 假设在命令事件处理方法中:
VoiceChannel userVoiceChannel = event.getMember().getVoiceState().getChannel();
if (userVoiceChannel == null) {
    event.reply("请先加入语音频道!").queue();
    return;
}

GuildMusicManager musicManager = getMusicManager(userVoiceChannel);
String youtubeUrl = "https://www.youtube.com/watch?v=xxx"; // 用户输入的链接
loadTrack(youtubeUrl, musicManager);
event.reply("已添加曲目到播放队列!").queue();

关键注意事项

  • 复用AudioPlayerManager:全局仅初始化一次,避免重复创建资源和丢失缓存
  • 使用loadItemOrdered:替代旧版loadItem,确保请求按顺序处理并绑定到对应服务器的音乐管理器
  • 线程安全队列:用BlockingQueue保证多线程环境下的队列操作安全
  • 自动续播逻辑:通过TrackEndEvent监听器,在上一曲播放完毕后自动启动下一曲
  • 错误处理:在AudioLoadResultHandler中处理加载失败、无匹配等异常场景

内容的提问来源于stack exchange,提问作者user20894127

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:45:00