我正在创建一个 Java 音乐机器人。我遇到了一个问题,机器人加入了我的频道,但它没有开始播放音乐。我正在使用 JDA 和 lavaplayer 来运行机器人。它可以找到曲目,甚至调用 AudioLoadResultHandler 类中的 .trackLoaded 函数。我在我的 Windows 电脑上运行 java 19。
我的 Maven 依赖项:
<repositories>
<repository>
<id>dv8tion</id>
<name>m2-dv8tion</name>
<url>https://m2.dv8tion.net/releases</url>
</repository>
</repositories>
<dependencies>
<!-- Discord Bot - JDA-->
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.0.0-beta.21</version>
</dependency>
<!-- Music library - Lavaplayer -->
<dependency>
<groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId>
<version>1.3.78</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- Injection - Guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
我的 PlayerManager 类:
public class PlayerManager {
private Map<Long, GuildMusicManager> guildMusicManagers = new HashMap<>();
private AudioPlayerManager audioPlayerManager = new DefaultAudioPlayerManager();
public PlayerManager() {
registerRemoteSources(audioPlayerManager);
registerLocalSource(audioPlayerManager);
}
public GuildMusicManager getGuildMusicManager(Guild guild) {
return guildMusicManagers.computeIfAbsent(guild.getIdLong(), (guildId) -> {
GuildMusicManager musicManager = new GuildMusicManager(audioPlayerManager, guild);
guild.getAudioManager().setSendingHandler(musicManager.getAudioForwarder());
return musicManager;
});
}
public void play(Guild guild, String trackURL) {
GuildMusicManager guildMusicManager = getGuildMusicManager(guild);
audioPlayerManager.loadItemOrdered(guildMusicManager, trackURL, new AudioLoadResultHandler() {
@Override
public void trackLoaded(AudioTrack audioTrack) {
guildMusicManager.getTrackScheduler().queue(audioTrack);
}
@Override
public void playlistLoaded(AudioPlaylist audioPlaylist) {
guildMusicManager.getTrackScheduler().queue(audioPlaylist.getTracks().get(0));
}
@Override
public void noMatches() {
System.out.println("not matches");
}
@Override
public void loadFailed(FriendlyException e) {
System.out.println("load failed");
}
});
}
我的 GuildMusicManager 类:
@Getter
public class GuildMusicManager {
private TrackScheduler trackScheduler;
private AudioForwarder audioForwarder;
public GuildMusicManager(AudioPlayerManager audioPlayerManager, Guild guild) {
AudioPlayer audioPlayer = audioPlayerManager.createPlayer();
trackScheduler = new TrackScheduler(audioPlayer, guild);
audioPlayer.addListener(trackScheduler);
audioForwarder = new AudioForwarder(audioPlayer);
}
}
我的 TrackScheduler 课程:
@Getter
public class TrackScheduler extends AudioEventAdapter {
private AudioPlayer audioPlayer;
private BlockingQueue<AudioTrack> queue = new LinkedBlockingQueue<>();
private Guild guild;
TrackScheduler(AudioPlayer audioPlayer, Guild guild) {
this.audioPlayer = audioPlayer;
this.guild = guild;
}
@Override
public void onTrackStart(AudioPlayer player, AudioTrack track) {
EmbedBuilder builder = new EmbedBuilder();
builder.setColor(MAGENTA);
AudioTrackInfo info = track.getInfo();
builder.setTitle("Jetzt läuft: " + info.title);
long sekunden = info.length/1000;
long minuten = sekunden/60;
long stunden = minuten/60;
minuten %= 60;
sekunden %= 60;
String url = info.uri;
builder.addField(info.author, "[" + info.title +"](" + url + ")", false);
builder.addField("Länge", info.isStream ? ":red_circle: Stream" : (stunden > 0 ? stunden + "h " : "") + minuten + "min " + sekunden + "s", true);
map.get(guild.getIdLong()).sendMessageEmbeds(builder.build()).queue();
}
@Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
audioPlayer.startTrack(queue.poll(), false);
newSingleThreadExecutor().execute(() -> {
try {
sleep(60000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(isNull(player.getPlayingTrack())) {
if(nonNull(guild.getAudioManager().getConnectedChannel())) {
guild.getAudioManager().closeAudioConnection();
}
}
});
}
public void queue(AudioTrack audioTrack) {
if (!audioPlayer.startTrack(audioTrack, true)) {
queue.offer(audioTrack);
}
}
}
我的音频转发器类:
public class AudioForwarder implements AudioSendHandler {
private final AudioPlayer audioPlayer;
private final ByteBuffer byteBuffer = allocate(1024);
private final MutableAudioFrame audioFrame = new MutableAudioFrame();
public AudioForwarder(AudioPlayer audioPlayer) {
this.audioPlayer = audioPlayer;
this.audioFrame.setBuffer(byteBuffer);
}
@Override
public boolean canProvide() {
return audioPlayer.provide(audioFrame);
}
@Nullable
@Override
public ByteBuffer provide20MsAudio() {
return byteBuffer.flip();
}
@Override
public boolean isOpus() {
return true;
}
}
我的播放命令:
@CommandBase.Command(name = "play", description = "Wähle ein Lied, das abgespielt werden soll.", hasOptions = true)
public class PlayCommand extends CommandBase {
@Inject
private JavaBot javaBot;
public static HashMap<Long, TextChannel> map = new HashMap<>();
public PlayCommand(@NotNull Command command) {
super(command);
}
@Override
public void execute(Member member, TextChannel textChannel, List<OptionMapping> options, SlashCommandInteractionEvent event) {
GuildVoiceState guildVoiceState = member.getVoiceState();
if (isNull(guildVoiceState) || isNull(guildVoiceState.getChannel()) || isNull(guildVoiceState.getChannel().asVoiceChannel())) {
event.reply("Du musst in einem Sprachkanal sein.").queue(); //TODO Replace with embed (EmbedBuilder builder = new EmbedBuilder())
return;
}
VoiceChannel voiceChannel = guildVoiceState.getChannel().asVoiceChannel();
PlayerManager playerManager = javaBot.getPlayerManager();
AudioManager manager = voiceChannel.getGuild().getAudioManager();
manager.openAudioConnection(voiceChannel);
String url = options.get(0).getAsString();
if (!url.startsWith("http")) {
url = "ytsearch:" + url + " audio";
}
event.reply("Suche nach dem Titel...").queue(); //TODO Replace with embed (EmbedBuilder builder = new EmbedBuilder())
playerManager.play(event.getGuild(), url);
map.put(voiceChannel.getGuild().getIdLong(), textChannel);
}
@Override
public void autoComplete(String optionName, CommandAutoCompleteInteractionEvent event) {
List<String> options = of("Achterbahn wise guys", "Ich trink uso was trinkst denn du so", "for the night pop smoke"); //TODO add logic to get famous songs
if (optionName.equalsIgnoreCase("song")) {
List<net.dv8tion.jda.api.interactions.commands.Command.Choice> returnChoices = options.stream()
.filter(option -> option.toLowerCase().startsWith(event.getFocusedOption().getValue().toLowerCase()))
.map(option -> new net.dv8tion.jda.api.interactions.commands.Command.Choice(option, option))
.collect(toList());
event.replyChoices(returnChoices).queue();
}
}
@Override
public List<OptionData> getOptions() {
return of(new OptionData(STRING, "song", "Titel oder URL vom Lied", true, true));
}
}
正如您从我的 pom.xml 文件中看到的,我正在运行最新版本的 JDA 和 Lavaplayer。我的机器人在我的不和谐服务器上也拥有尽可能高的权限。该机器人几周前也能工作,但如果没有做任何大的改变,它就不再工作了。正如我所说,我正在运行 Java 19。如果您需要更多信息,请随时询问。 谢谢您的帮助!
我终于找到了解决问题的方法。 Lavaplayer 似乎已经过时了,这就是为什么你应该使用 fork。我使用以下 Lavaplayer 分支:https://github.com/lavalink-devs/lavaplayer 目前最新的官方版本也有一个bug,这就是为什么你应该使用以下maven导入:
<repository>
<id>arbjergDev-snapshots</id>
<name>Lavalink Repository</name>
<url>https://maven.lavalink.dev/snapshots</url>
</repository>
<dependency>
<groupId>dev.arbjerg</groupId>
<artifactId>lavaplayer</artifactId>
<version>0eaeee195f0315b2617587aa3537fa202df07ddc-SNAPSHOT</version>
</dependency>
您可以在本期中了解为什么要使用此导入:https://github.com/lavalink-devs/lavaplayer/issues/90
它对我有用,我希望它也对你有用。