Skip to content

Commit 16a7cad

Browse files
committed
Expand java example to include a queue
1 parent 21247ec commit 16a7cad

File tree

6 files changed

+170
-31
lines changed

6 files changed

+170
-31
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ Alternatively, you can use `Discord4JUtils.leave(gatewayClient, guildId);` as th
130130
The following examples are minimal implementations but show how the library works.
131131
- Java examples
132132
- JDA (simple): [link](src/test/java/JavaJDAExample.java)
133-
- JDA (more real-world like example): [link](testbot/src/main/java/me/duncte123/testbot/Main.java)
133+
- JDA (Bigger example with queue system): [link](testbot/src/main/java/me/duncte123/testbot/Main.java)
134134
- Kotlin examples
135135
- JDA: [link](src/test/kotlin/testScript.kt)
136136
- Discord4J: [link](src/test/kotlin/d4jTestScript.kt)

testbot/src/main/java/me/duncte123/testbot/JDAListener.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import me.duncte123.lyrics.model.Lyrics;
88
import me.duncte123.lyrics.model.TextLyrics;
99
import me.duncte123.lyrics.model.TimedLyrics;
10+
import me.duncte123.testbot.music.AudioLoader;
11+
import me.duncte123.testbot.music.GuildMusicManager;
1012
import net.dv8tion.jda.api.EmbedBuilder;
1113
import net.dv8tion.jda.api.entities.Guild;
1214
import net.dv8tion.jda.api.entities.GuildVoiceState;
@@ -22,11 +24,15 @@
2224
import org.slf4j.Logger;
2325
import org.slf4j.LoggerFactory;
2426

27+
import java.util.HashMap;
28+
import java.util.Map;
29+
2530
public class JDAListener extends ListenerAdapter {
2631
private static final long DUNCTE = 191231307290771456L;
2732

2833
private static final Logger LOG = LoggerFactory.getLogger(JDAListener.class);
2934

35+
public final Map<Long, GuildMusicManager> musicManagers = new HashMap<>();
3036
private final LavalinkClient client;
3137
private final EvalEngine evalEngine;
3238

@@ -117,13 +123,8 @@ public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent even
117123
joinHelper(event);
118124
break;
119125
case "stop":
120-
this.client.getOrCreateLink(guild.getIdLong())
121-
.updatePlayer(
122-
(update) -> update.setTrack(null).setPaused(false)
123-
)
124-
.subscribe((__) -> {
125-
event.reply("Stopped the current track").queue();
126-
});
126+
event.reply("Stopped the current track and clearing the queue").queue();
127+
this.getOrCreateMusicManager(event.getGuild().getIdLong()).stop();
127128
break;
128129
case "leave":
129130
event.getJDA().getDirectAudioController().disconnect(guild);
@@ -207,11 +208,13 @@ public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent even
207208
final String identifier = event.getOption("identifier").getAsString();
208209
final long guildId = guild.getIdLong();
209210
final Link link = this.client.getOrCreateLink(guildId);
211+
final var mngr = this.getOrCreateMusicManager(guildId);
210212

211-
link.loadItem(identifier).subscribe(new AudioLoader(link, event));
213+
link.loadItem(identifier).subscribe(new AudioLoader(event, mngr));
212214

213215
break;
214216
}
217+
// Required plugin: https://github.com/DuncteBot/java-timed-lyrics
215218
case "lyrics": {
216219
final Link link = this.client.getOrCreateLink(guild.getIdLong());
217220
final var node = link.getNode();
@@ -296,6 +299,19 @@ private void parseEvalResult(SlashCommandInteractionEvent event, Object result)
296299
}
297300
}
298301

302+
private GuildMusicManager getOrCreateMusicManager(long guildId) {
303+
synchronized(this) {
304+
var mng = this.musicManagers.get(guildId);
305+
306+
if (mng == null) {
307+
mng = new GuildMusicManager(guildId, this.client);
308+
this.musicManagers.put(guildId, mng);
309+
}
310+
311+
return mng;
312+
}
313+
}
314+
299315
// Makes sure that the bot is in a voice channel!
300316
private void joinHelper(SlashCommandInteractionEvent event) {
301317
final Member member = event.getMember();
@@ -305,6 +321,8 @@ private void joinHelper(SlashCommandInteractionEvent event) {
305321
event.getJDA().getDirectAudioController().connect(memberVoiceState.getChannel());
306322
}
307323

324+
this.getOrCreateMusicManager(member.getGuild().getIdLong());
325+
308326
event.reply("Joining your channel!").queue();
309327
}
310328
}

testbot/src/main/java/me/duncte123/testbot/Main.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
import org.slf4j.LoggerFactory;
1313

1414
import java.util.List;
15+
import java.util.Optional;
1516

1617
public class Main {
1718
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
1819

1920
private static final int SESSION_INVALID = 4006;
2021

22+
private static JDAListener listener;
23+
2124
public static void main(String[] args) throws InterruptedException {
2225
final var token = System.getenv("BOT_TOKEN");
2326
final LavalinkClient client = new LavalinkClient(Helpers.getUserIdFromToken(token));
@@ -27,11 +30,13 @@ public static void main(String[] args) throws InterruptedException {
2730
registerLavalinkListeners(client);
2831
registerLavalinkNodes(client);
2932

33+
listener = new JDAListener(client);
34+
3035
final var jda = JDABuilder.createDefault(token)
3136
.setVoiceDispatchInterceptor(new JDAVoiceUpdateListener(client))
3237
.enableIntents(GatewayIntent.GUILD_VOICE_STATES)
3338
.enableCache(CacheFlag.VOICE_STATE)
34-
.addEventListeners(new JDAListener(client))
39+
.addEventListeners(listener)
3540
.build()
3641
.awaitReady();
3742

@@ -80,7 +85,7 @@ private static void registerLavalinkNodes(LavalinkClient client) {
8085
node.on(TrackStartEvent.class).subscribe((event) -> {
8186
final LavalinkNode node1 = event.getNode();
8287

83-
LOG.info(
88+
LOG.trace(
8489
"{}: track started: {}",
8590
node1.getName(),
8691
event.getTrack().getInfo()
@@ -112,6 +117,18 @@ private static void registerLavalinkListeners(LavalinkClient client) {
112117
);
113118
});
114119

120+
client.on(TrackStartEvent.class).subscribe((event) -> {
121+
Optional.ofNullable(listener.musicManagers.get(event.getGuildId())).ifPresent(
122+
(mng) -> mng.scheduler.onTrackStart(event.getTrack())
123+
);
124+
});
125+
126+
client.on(TrackEndEvent.class).subscribe((event) -> {
127+
Optional.ofNullable(listener.musicManagers.get(event.getGuildId())).ifPresent(
128+
(mng) -> mng.scheduler.onTrackEnd(event.getTrack(), event.getEndReason())
129+
);
130+
});
131+
115132
client.on(EmittedEvent.class).subscribe((event) -> {
116133
if (event instanceof TrackStartEvent) {
117134
LOG.info("Is a track start event!");

testbot/src/main/java/me/duncte123/testbot/AudioLoader.java renamed to testbot/src/main/java/me/duncte123/testbot/music/AudioLoader.java

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
package me.duncte123.testbot;
1+
package me.duncte123.testbot.music;
22

33
import dev.arbjerg.lavalink.client.AbstractAudioLoadResultHandler;
44
import dev.arbjerg.lavalink.client.Link;
55
import dev.arbjerg.lavalink.client.player.*;
6+
import me.duncte123.testbot.MyUserData;
67
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
78
import org.jetbrains.annotations.NotNull;
89

910
import java.util.List;
1011

1112
public class AudioLoader extends AbstractAudioLoadResultHandler {
12-
private final Link link;
1313
private final SlashCommandInteractionEvent event;
14+
private final GuildMusicManager mngr;
1415

15-
public AudioLoader(Link link, SlashCommandInteractionEvent event) {
16-
this.link = link;
16+
public AudioLoader(SlashCommandInteractionEvent event, GuildMusicManager mngr) {
1717
this.event = event;
18+
this.mngr = mngr;
1819
}
1920

2021
@Override
@@ -25,24 +26,21 @@ public void ontrackLoaded(@NotNull TrackLoaded result) {
2526

2627
track.setUserData(userData);
2728

28-
link.createOrUpdatePlayer()
29-
.setTrack(track)
30-
.setVolume(35)
31-
.subscribe((player) -> {
32-
final Track playingTrack = player.getTrack();
33-
final var trackTitle = playingTrack.getInfo().getTitle();
34-
final MyUserData customData = playingTrack.getUserData(MyUserData.class);
29+
this.mngr.scheduler.enqueue(track);
3530

36-
event.getHook().sendMessage("Now playing: " + trackTitle + "\nRequested by: <@" + customData.requester() + '>').queue();
37-
});
31+
final var trackTitle = track.getInfo().getTitle();
32+
33+
event.getHook().sendMessage("Added to queue: " + trackTitle + "\nRequested by: <@" + userData.requester() + '>').queue();
3834
}
3935

4036
@Override
4137
public void onPlaylistLoaded(@NotNull PlaylistLoaded result) {
4238
final int trackCount = result.getTracks().size();
4339
event.getHook()
44-
.sendMessage("This playlist has " + trackCount + " tracks!")
40+
.sendMessage("Added " + trackCount + " tracks to the queue from " + result.getInfo().getName() + "!")
4541
.queue();
42+
43+
this.mngr.scheduler.enqueuePlaylist(result.getTracks());
4644
}
4745

4846
@Override
@@ -56,12 +54,9 @@ public void onSearchResultLoaded(@NotNull SearchResult result) {
5654

5755
final Track firstTrack = tracks.get(0);
5856

59-
// This is a different way of updating the player! Choose your preference!
60-
// This method will also create a player if there is not one in the server yet
61-
link.updatePlayer((update) -> update.setTrack(firstTrack).setVolume(35))
62-
.subscribe((ignored) -> {
63-
event.getHook().sendMessage("Now playing: " + firstTrack.getInfo().getTitle()).queue();
64-
});
57+
event.getHook().sendMessage("Adding to queue: " + firstTrack.getInfo().getTitle()).queue();
58+
59+
this.mngr.scheduler.enqueue(firstTrack);
6560
}
6661

6762
@Override
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package me.duncte123.testbot.music;
2+
3+
import dev.arbjerg.lavalink.client.LavalinkClient;
4+
import dev.arbjerg.lavalink.client.Link;
5+
import dev.arbjerg.lavalink.client.player.LavalinkPlayer;
6+
7+
import java.util.Optional;
8+
9+
public class GuildMusicManager {
10+
public final TrackScheduler scheduler = new TrackScheduler(this);
11+
private final long guildId;
12+
private final LavalinkClient lavalink;
13+
14+
public GuildMusicManager(long guildId, LavalinkClient lavalink) {
15+
this.lavalink = lavalink;
16+
this.guildId = guildId;
17+
}
18+
19+
public void stop() {
20+
this.scheduler.queue.clear();
21+
22+
this.getPlayer().ifPresent(
23+
(player) -> player.setPaused(false)
24+
.setTrack(null)
25+
.subscribe()
26+
);
27+
}
28+
29+
public Optional<Link> getLink() {
30+
return Optional.ofNullable(
31+
this.lavalink.getLinkIfCached(this.guildId)
32+
);
33+
}
34+
35+
public Optional<LavalinkPlayer> getPlayer() {
36+
return this.getLink().map(Link::getCachedPlayer);
37+
}
38+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package me.duncte123.testbot.music;
2+
3+
import dev.arbjerg.lavalink.client.player.Track;
4+
import dev.arbjerg.lavalink.protocol.v4.Message;
5+
6+
import java.util.LinkedList;
7+
import java.util.List;
8+
import java.util.Queue;
9+
10+
public class TrackScheduler {
11+
private final GuildMusicManager guildMusicManager;
12+
public final Queue<Track> queue = new LinkedList<>();
13+
14+
public TrackScheduler(GuildMusicManager guildMusicManager) {
15+
this.guildMusicManager = guildMusicManager;
16+
}
17+
18+
public void enqueue(Track track) {
19+
this.guildMusicManager.getPlayer().ifPresentOrElse(
20+
(player) -> {
21+
if (player.getTrack() == null) {
22+
this.startTrack(track);
23+
} else {
24+
this.queue.offer(track);
25+
}
26+
},
27+
() -> {
28+
this.startTrack(track);
29+
}
30+
);
31+
}
32+
33+
public void enqueuePlaylist(List<Track> tracks) {
34+
this.queue.addAll(tracks);
35+
36+
this.guildMusicManager.getPlayer().ifPresentOrElse(
37+
(player) -> {
38+
if (player.getTrack() == null) {
39+
this.startTrack(this.queue.poll());
40+
}
41+
},
42+
() -> {
43+
this.startTrack(this.queue.poll());
44+
}
45+
);
46+
}
47+
48+
public void onTrackStart(Track track) {
49+
// Your homework: Send a message to the channel somehow, have fun!
50+
System.out.println("Track started: " + track.getInfo().getTitle());
51+
}
52+
53+
public void onTrackEnd(Track lastTrack, Message.EmittedEvent.TrackEndEvent.AudioTrackEndReason endReason) {
54+
if (endReason.getMayStartNext()) {
55+
final var nextTrack = this.queue.poll();
56+
57+
if (nextTrack != null) {
58+
this.startTrack(nextTrack);
59+
}
60+
}
61+
}
62+
63+
private void startTrack(Track track) {
64+
this.guildMusicManager.getLink().ifPresent(
65+
(link) -> link.createOrUpdatePlayer()
66+
.setTrack(track)
67+
.setVolume(35)
68+
.subscribe()
69+
);
70+
}
71+
}

0 commit comments

Comments
 (0)