如何创建语音机器人音频曲目队列?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
相关产品推荐
相关产品推荐

