Split queue management in its own class and use a double-ended queue instead

This commit is contained in:
anthorob committed 2022-03-26 18:13:56 -04:00
1 parent ef038c5b5b
commit 7bfadb05f7
24 files changed
+446 -264

No files matched your search

@@ -6,7 +6,10 @@ package netneutrality.command;
* https://discord.gg/CdFcyQ * https://discord.gg/CdFcyQ
*/ */
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.TextChannel;
import netneutrality.core.GuildManager; import netneutrality.core.GuildManager;
import netneutrality.utils.UserFunction; import netneutrality.utils.UserFunction;
@@ -9,9 +9,9 @@ package netneutrality.command.basic;
import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.MessageBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import netneutrality.command.AbstractCommand; import netneutrality.command.AbstractCommand;
import netneutrality.core.CommandManager;
import netneutrality.command.CommandType; import netneutrality.command.CommandType;
import netneutrality.command.CommandUsage; import netneutrality.command.CommandUsage;
import netneutrality.core.CommandManager;
import netneutrality.core.context.CommandContext; import netneutrality.core.context.CommandContext;
import netneutrality.utils.MessageFunction; import netneutrality.utils.MessageFunction;
import netneutrality.utils.UserFunction; import netneutrality.utils.UserFunction;
@@ -41,7 +41,7 @@ public class ConfigureCommand extends AbstractCommand {
@Override @Override
public CommandUsage getUsage() { public CommandUsage getUsage() {
return CommandUsage.create(this, return CommandUsage.create(this,
new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"musicrole", "music-channel", "musicvolume", "debug"}, "") new CommandUsage.Argument(CommandUsage.Argument.Type.REQUIRED, new String[] {"music-role", "music-channel", "music-volume", "music-topic", "debug"}, "")
); );
} }
@@ -63,7 +63,7 @@ public class ConfigureCommand extends AbstractCommand {
if (args.length > 0) { if (args.length > 0) {
// Role for music commands // Role for music commands
if (context.getArg(0).equals("musicrole")) { if (context.getArg(0).equals("music-role")) {
if (args.length == 1) { if (args.length == 1) {
String roleDb = guildManager.getSetting(GuildManager.Settings.MUSIC_ROLE); String roleDb = guildManager.getSetting(GuildManager.Settings.MUSIC_ROLE);
Role role = null; Role role = null;
@@ -72,7 +72,7 @@ public class ConfigureCommand extends AbstractCommand {
} }
context.reply("Current role needed to use music commands (default is everyone): %s", (role == null ? "Everyone" : role.getAsMention())); context.reply("Current role needed to use music commands (default is everyone): %s", (role == null ? "Everyone" : role.getAsMention()));
context.reply("Reset role using 'conf musicrole reset' or set it using 'conf musicrole @'"); context.reply("Reset role using 'conf music-role reset' or set it using 'conf musicrole @'");
} }
if (args.length == 2) { if (args.length == 2) {
@@ -129,12 +129,12 @@ public class ConfigureCommand extends AbstractCommand {
} }
// Default volume for music player // Default volume for music player
if (context.getArg(0).equals("musicvolume")) { if (context.getArg(0).equals("music-volume")) {
if (args.length == 1) { if (args.length == 1) {
int volume = Integer.parseInt(guildManager.getSetting(GuildManager.Settings.MUSIC_VOLUME)); int volume = Integer.parseInt(guildManager.getSetting(GuildManager.Settings.MUSIC_VOLUME));
context.reply("Default volume for music player: " + volume); context.reply("Default volume for music player: %d", volume);
context.reply("Set using 'conf musicvolume <value between 0 and 100>'"); context.reply("Set using 'conf music-volume <value between 0 and 100>'");
} }
if (args.length == 2) { if (args.length == 2) {
@@ -155,6 +155,28 @@ public class ConfigureCommand extends AbstractCommand {
} }
} }
// Music topic change
if (context.getArg(0).equals("music-topic")) {
if (args.length == 1) {
boolean enabled = "1".equals(guildManager.getSetting(GuildManager.Settings.MUSIC_TOPIC));
context.reply("Set currently playing track in text channel topic: %s", (enabled ? "enabled" : "disabled"));
context.reply("Use 'conf music-topic 0/1' to toggle between states (0 = disabled, 1 = enabled).");
}
if (args.length == 2) {
String str = args[1];
boolean enabled = "1".equals(guildManager.getSetting(GuildManager.Settings.MUSIC_TOPIC));
if (str.equals("0") || str.equals("1")) {
guildManager.setSettingValue(GuildManager.Settings.MUSIC_TOPIC, str);
enabled = "1".equals(str);
}
context.reply("Set currently playing track in text channel topic is now %s for this guild", (enabled ? "enabled" : "disabled"));
}
}
// Debug // Debug
if (context.getArg(0).equals("debug")) { if (context.getArg(0).equals("debug")) {
if (args.length == 1) { if (args.length == 1) {
@@ -59,10 +59,6 @@ public class BlacklistCommand extends AbstractCommand {
String[] args = context.getArgumentsAsArray(); String[] args = context.getArgumentsAsArray();
DBBlacklist controller = DBController.get("blacklist", DBBlacklist.class); DBBlacklist controller = DBController.get("blacklist", DBBlacklist.class);
if (controller == null) {
}
if (args.length > 0 && args.length < 3) { if (args.length > 0 && args.length < 3) {
String word = (args.length == 2 ? args[1] : ""); String word = (args.length == 2 ? args[1] : "");
@@ -7,7 +7,6 @@ package netneutrality.command.music;
* https://discord.gg/CdFcyQ * https://discord.gg/CdFcyQ
*/ */
import net.dv8tion.jda.api.entities.Member;
import netneutrality.command.AbstractMusicCommand; import netneutrality.command.AbstractMusicCommand;
import netneutrality.command.CommandUsage; import netneutrality.command.CommandUsage;
import netneutrality.core.context.CommandContext; import netneutrality.core.context.CommandContext;
@@ -44,13 +43,10 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
Member member = context.getGuildUser();
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
if (player.getCurrentlyPlaying() != null) { if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) {
context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, member)); context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, context.getGuildUser()));
} else { } else {
context.reply("Player is not playing any tracks right now"); context.reply("Player is not playing any tracks right now");
} }
@@ -17,6 +17,11 @@ import netneutrality.utils.MusicUtils;
public class DeleteQueueCommand extends AbstractMusicCommand { public class DeleteQueueCommand extends AbstractMusicCommand {
@Override
public boolean needAdmin() {
return true;
}
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return true; return true;
@@ -47,10 +52,8 @@ public class DeleteQueueCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
if (!MusicUtils.checkIfDj(player, context)) if (!MusicUtils.checkIfConnected(player, context))
return; return;
if (context.getArgumentsAsArray().length == 0) { if (context.getArgumentsAsArray().length == 0) {
@@ -60,12 +63,12 @@ public class DeleteQueueCommand extends AbstractMusicCommand {
if (context.getArgumentsAsArray().length > 0) { if (context.getArgumentsAsArray().length > 0) {
int queueSize = player.getScheduler().getQueue().size(); int queueSize = player.getQueueSize();
if (context.getArg(0).equals("all")) { if (context.getArg(0).equals("all")) {
if (queueSize > 0) { if (queueSize > 0) {
player.leave(); player.leave();
player.getScheduler().getQueue().clear(); player.getScheduler().getQueueManager().getQueue().clear();
context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : ""))); context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : "")));
} else { } else {
context.reply("Queue is empty!"); context.reply("Queue is empty!");
@@ -47,9 +47,9 @@ public class LeaveCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
player.leave(); if (MusicUtils.checkIfConnected(player, context)) {
player.leave();
}
} }
} }
@@ -12,6 +12,8 @@ import netneutrality.command.CommandUsage;
import netneutrality.core.context.CommandContext; import netneutrality.core.context.CommandContext;
import netneutrality.music.MusicManager; import netneutrality.music.MusicManager;
import java.util.concurrent.TimeUnit;
public class PlayCommand extends AbstractMusicCommand { public class PlayCommand extends AbstractMusicCommand {
@Override @Override
@@ -45,12 +47,12 @@ public class PlayCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
context.getMessage().delete().queue(); context.getMessage().delete().queueAfter(1, TimeUnit.SECONDS);
if (context.getArgumentsAsArray().length > 0) { if (context.getArgs().length > 0) {
player.play(context.getMessageText(), context, false, false); player.play(context.getMessageText(), context, false, false);
} else { } else {
//TODO: resume queue
context.reply(getUsage()); context.reply(getUsage());
} }
@@ -43,19 +43,23 @@ public class SkipCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context)) if (!MusicUtils.checkIfConnected(player, context))
return; return;
// Combined fonctionality of vote skip if (UserFunction.isAdmin(context.getGuildUser())) {
//TODO: clean ifs if (context.getArgs().length > 0) {
if (context.getArgumentsAsArray().length > 0 && UserFunction.isAdmin(context.getGuildUser())) { try {
int skips = Integer.parseInt(context.getArg(0)); int skips = Integer.parseInt(context.getArg(0));
player.skip(skips); player.skip(skips);
} else { } catch (NumberFormatException e) {
if (UserFunction.isAdmin(context.getGuildUser())) context.reply("Please enter how many track(s) to skip");
}
} else {
player.skip(1); player.skip(1);
else }
player.voteSkip(context); } else {
player.voteSkip(context);
} }
} }
@@ -12,7 +12,6 @@ import netneutrality.command.CommandUsage;
import netneutrality.core.context.CommandContext; import netneutrality.core.context.CommandContext;
import netneutrality.music.MusicManager; import netneutrality.music.MusicManager;
import netneutrality.utils.MusicUtils; import netneutrality.utils.MusicUtils;
import netneutrality.utils.UserFunction;
public class StopCommand extends AbstractMusicCommand { public class StopCommand extends AbstractMusicCommand {
@@ -38,17 +37,15 @@ public class StopCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return "Stop the music player, emptying the queue in the process"; return "Stop the music player, keeping the current queue.";
} }
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
if (UserFunction.isAdmin(context.getGuildUser()) || context.getGuildUser().getId().equals(player.getRequesterOfCurrentTrack().getId())) { if (MusicUtils.checkIfConnected(player, context) && MusicUtils.isDj(player, context)) {
player.leave(); player.stopTrack();
} }
} }
} }
@@ -7,7 +7,6 @@ package netneutrality.command.music;
* https://discord.gg/CdFcyQ * https://discord.gg/CdFcyQ
*/ */
import net.dv8tion.jda.api.entities.Member;
import netneutrality.command.AbstractMusicCommand; import netneutrality.command.AbstractMusicCommand;
import netneutrality.command.CommandUsage; import netneutrality.command.CommandUsage;
import netneutrality.core.context.CommandContext; import netneutrality.core.context.CommandContext;
@@ -48,16 +47,16 @@ public class ViewQueueCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
Member member = context.getGuildUser();
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
int queueSize = player.getScheduler().getQueue().size(); if (MusicUtils.checkIfConnected(player, context)) {
if (queueSize > 0) { int queueSize = player.getQueueSize();
context.reply(MessageFunction.buildViewQueueMessage(player, member, true)); if (queueSize > 0) {
} else { context.reply(MessageFunction.buildViewQueueMessage(player, context.getGuildUser(), true));
context.reply("Queue is empty!"); } else {
context.reply("Queue is empty!");
}
} }
} }
} }
@@ -10,7 +10,6 @@ package netneutrality.command.music;
import netneutrality.command.AbstractMusicCommand; import netneutrality.command.AbstractMusicCommand;
import netneutrality.command.CommandUsage; import netneutrality.command.CommandUsage;
import netneutrality.core.context.CommandContext; import netneutrality.core.context.CommandContext;
import netneutrality.exceptions.CommandErrorException;
import netneutrality.music.MusicManager; import netneutrality.music.MusicManager;
import netneutrality.utils.MusicUtils; import netneutrality.utils.MusicUtils;
@@ -44,24 +43,20 @@ public class VolumeCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
if (!MusicUtils.checkIfDj(player, context)) if (MusicUtils.checkIfConnected(player, context) && MusicUtils.isDj(player, context)) {
return; try {
int volume = Integer.parseInt(context.getArg(0));
try { if (volume > 0 && volume <= 100) {
int volume = Integer.parseInt(context.getArg(0)); player.setVolume(volume);
context.reply("Volume set to %s", volume);
} else
throw new NumberFormatException();
if (volume > 0 && volume <= 100) { } catch (NumberFormatException e) {
player.setVolume(volume); context.reply("Please enter a value from 0 to 100");
context.reply("Volume set to %s", volume); }
} else
throw new NumberFormatException();
} catch (NumberFormatException e) {
context.reply("Please enter a value from 0 to 100");
} }
} }
} }
@@ -42,9 +42,9 @@ public class VoteSkipCommand extends AbstractMusicCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
return;
player.voteSkip(context); if (MusicUtils.checkIfConnected(player, context)) {
player.voteSkip(context);
}
} }
} }
@@ -38,6 +38,7 @@ public class GuildManager {
MUSIC_ROLE("MUSIC_ROLE", ""), MUSIC_ROLE("MUSIC_ROLE", ""),
MUSIC_CHANNEL("MUSIC_CHANNEL", ""), MUSIC_CHANNEL("MUSIC_CHANNEL", ""),
MUSIC_VOLUME("MUSIC_VOLUME", "30"), MUSIC_VOLUME("MUSIC_VOLUME", "30"),
MUSIC_TOPIC("MUSIC_TOPIC", "0"),
BLACKLIST_CHANNELS("BLACKLIST_EXCLUDE_CHANNELS", ""); BLACKLIST_CHANNELS("BLACKLIST_EXCLUDE_CHANNELS", "");
private final String name; private final String name;
@@ -67,6 +67,10 @@ public class CommandContext {
return getArgumentsAsArray()[index]; return getArgumentsAsArray()[index];
} }
public String[] getArgs() {
return getArgumentsAsArray();
}
public String[] getArgumentsAsArray() { public String[] getArgumentsAsArray() {
String msg = message.getContentDisplay().trim(); String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim();
@@ -180,6 +184,7 @@ public class CommandContext {
try { try {
if (command.needAdmin() && !UserFunction.isAdmin(member)) { if (command.needAdmin() && !UserFunction.isAdmin(member)) {
reply("You are not powerful enough, scrub");
return; return;
} }
@@ -56,7 +56,7 @@ public class MusicManager {
this.guild = guild; this.guild = guild;
this.playerManager = new DefaultAudioPlayerManager(); this.playerManager = new DefaultAudioPlayerManager();
this.player = playerManager.createPlayer(); this.player = playerManager.createPlayer();
this.scheduler = new TrackScheduler(player); this.scheduler = new TrackScheduler(player, guild);
this.baseVolume = Integer.parseInt(GuildManager.getManager(guild).getSetting(GuildManager.Settings.MUSIC_VOLUME)); this.baseVolume = Integer.parseInt(GuildManager.getManager(guild).getSetting(GuildManager.Settings.MUSIC_VOLUME));
AudioSourceManagers.registerRemoteSources(playerManager); AudioSourceManagers.registerRemoteSources(playerManager);
@@ -105,8 +105,12 @@ public class MusicManager {
return player.getPlayingTrack(); return player.getPlayingTrack();
} }
public synchronized boolean isCurrentlyPlaying() {
return player.getPlayingTrack() != null;
}
public synchronized Member getRequesterOfCurrentTrack() { public synchronized Member getRequesterOfCurrentTrack() {
TrackScheduler.TrackInfo info = scheduler.getQueue().peek(); QueueManager.TrackInfo info = scheduler.getQueueManager().getQueue().peek();
if (info == null) if (info == null)
return null; return null;
@@ -124,7 +128,7 @@ public class MusicManager {
guild.getAudioManager().closeAudioConnection(); guild.getAudioManager().closeAudioConnection();
} }
scheduler.resetTopic(); scheduler.getTopicManager().detachTextChannel();
if (autoDisconnectThread != null) { if (autoDisconnectThread != null) {
boolean cancel = autoDisconnectThread.cancel(true); boolean cancel = autoDisconnectThread.cancel(true);
@@ -138,7 +142,7 @@ public class MusicManager {
for (MusicManager mm : managers.values()) { for (MusicManager mm : managers.values()) {
mm.leave(); mm.leave();
mm.player.destroy(); mm.player.destroy();
mm.scheduler.getQueue().clear(); mm.scheduler.getQueueManager().getQueue().clear();
} }
} }
@@ -157,12 +161,14 @@ public class MusicManager {
} }
if (!member.getVoiceState().inVoiceChannel()) { if (!member.getVoiceState().inVoiceChannel()) {
throw new CommandErrorException("You are not in a voice channel", context, null); context.reply("You are not in a voice channel");
return;
} }
VoiceChannel vChannel = member.getVoiceState().getChannel(); VoiceChannel vChannel = member.getVoiceState().getChannel();
if (needToBeConnected && !isConnectedTo(vChannel)) { if (needToBeConnected && !isConnectedTo(vChannel)) {
throw new CommandErrorException("Not in your voice channel", context, null); context.reply("The player is not in your voice channel");
return;
} }
if (!isConnectedTo(vChannel)) { if (!isConnectedTo(vChannel)) {
@@ -170,7 +176,7 @@ public class MusicManager {
} }
String identifier = SourceFinder.getIdentifier(url); String identifier = SourceFinder.getIdentifier(url);
scheduler.setTextChannel(channel); scheduler.getTopicManager().attachTextChannel(channel);
if (identifier != null) { if (identifier != null) {
@@ -179,12 +185,11 @@ public class MusicManager {
@Override @Override
public void trackLoaded(AudioTrack audioTrack) { public void trackLoaded(AudioTrack audioTrack) {
logger.debug("Found results for identifier: {}", identifier); logger.debug("Found results for identifier: {}", identifier);
scheduler.getQueueManager().queue(audioTrack, member);
if (!silent) { if (!silent) {
channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(audioTrack, member, identifier)).queue(); channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(audioTrack, member, identifier)).queue();
} }
scheduler.queue(audioTrack, member);
} }
@Override @Override
@@ -192,20 +197,22 @@ public class MusicManager {
if (identifier.startsWith("ytsearch:")) { if (identifier.startsWith("ytsearch:")) {
AudioTrack track = audioPlaylist.getTracks().get(0); AudioTrack track = audioPlaylist.getTracks().get(0);
logger.debug("Found results for identifier: {}", identifier); logger.debug("Found results for identifier: {}", identifier);
scheduler.getQueueManager().queue(track, member);
if (!silent) if (!silent) {
channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(track, member, String.format("https://youtu.be/%s", track.getIdentifier()))).queue(); channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(track, member, String.format("https://youtu.be/%s", track.getIdentifier()))).queue();
}
scheduler.queue(track, member);
} else { } else {
logger.debug("Found results for playlist identifier: {}", identifier); logger.debug("Found results for playlist identifier: {}", identifier);
if (!silent) if (!silent) {
channel.sendMessageEmbeds(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, member, identifier)).queue(); channel.sendMessageEmbeds(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, member, identifier)).queue();
}
for (AudioTrack track : audioPlaylist.getTracks()) { for (AudioTrack track : audioPlaylist.getTracks()) {
synchronized (scheduler.getQueue()) { synchronized (scheduler.getQueueManager().getQueue()) {
scheduler.queue(track, member); scheduler.getQueueManager().queue(track, member);
} }
} }
} }
@@ -232,7 +239,7 @@ public class MusicManager {
if (num == 1) { if (num == 1) {
skip(1); skip(1);
} else { } else {
scheduler.removeFromQueue(num); scheduler.getQueueManager().removeFromQueue(num);
} }
} }
@@ -241,23 +248,23 @@ public class MusicManager {
} }
public synchronized void skip(int nbTrack) { public synchronized void skip(int nbTrack) {
if (!scheduler.getQueue().isEmpty()) { if (!scheduler.getQueueManager().getQueue().isEmpty()) {
if (nbTrack >= scheduler.getQueue().size()) { if (nbTrack >= scheduler.getQueueManager().getQueue().size()) {
nbTrack = 1; nbTrack = 1;
} }
scheduler.skipAndPlay(nbTrack); scheduler.getQueueManager().skipAndPlay(nbTrack);
} else { } else {
leave(); leave();
} }
} }
public synchronized void voteSkip(CommandContext context) { public synchronized void voteSkip(CommandContext context) {
Queue<TrackScheduler.TrackInfo> queue = scheduler.getQueue(); Queue<QueueManager.TrackInfo> queue = scheduler.getQueueManager().getQueue();
VoiceChannel voiceChannel = guild.getAudioManager().getConnectedChannel(); VoiceChannel voiceChannel = guild.getAudioManager().getConnectedChannel();
TrackScheduler.TrackInfo info = queue.peek(); QueueManager.TrackInfo info = queue.peek();
if (getCurrentlyPlaying() == null || voiceChannel == null) { if (!isCurrentlyPlaying() || voiceChannel == null) {
return; return;
} }
@@ -290,15 +297,15 @@ public class MusicManager {
player.setVolume(volume); player.setVolume(volume);
} }
public int getQueueSize () {
synchronized (scheduler.getQueueManager().getQueue()) {
return scheduler.getQueueManager().getQueue().size();
}
}
public TrackScheduler getScheduler() { public TrackScheduler getScheduler() {
return scheduler; return scheduler;
} }
public synchronized ArrayList<AudioTrack> getAllQueuedTrack() {
ArrayList<AudioTrack> tracks = new ArrayList<>();
Queue<TrackScheduler.TrackInfo> queue = scheduler.getQueue();
queue.forEach(trackInfo -> tracks.add(trackInfo.getTrack()));
return tracks;
}
} }
@@ -0,0 +1,187 @@
package netneutrality.music;/*
* Copyright (c) anthorob
*
* https://xtremcommunity.com/
* https://discord.gg/CdFcyQ
*/
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
public class QueueManager {
static class TrackInfo {
private final AudioTrack track;
private final Member member;
private final Set<String> skips;
public TrackInfo(AudioTrack track, Member member) {
this.track = track;
this.member = member;
this.skips = new HashSet<>();
}
public AudioTrack getTrack() {
return track;
}
public Set<String> getSkips() {
return skips;
}
public void toggleSkip(Member member) {
if (!hasSkip(member))
skips.add(member.getId());
else
skips.remove(member.getId());
}
public boolean hasSkip(Member member) {
return skips.contains(member.getId());
}
public Member getRequester() {
return member;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrackInfo trackInfo = (TrackInfo) o;
return track.equals(trackInfo.track);
}
@Override
public int hashCode() {
return Objects.hash(track);
}
@Override
public String toString() {
return "TrackInfo{" +
"track=" + track.getInfo().title +
", member=" + member.getEffectiveName() +
", skips=" + skips +
'}';
}
}
private final AudioPlayer player;
private final Guild guild;
private final Deque<TrackInfo> queue;
public QueueManager(AudioPlayer player, Guild guild) {
this.player = player;
this.guild = guild;
this.queue = new LinkedBlockingDeque<>();
}
public Queue<TrackInfo> getQueue() {
synchronized (queue) {
return queue;
}
}
public Guild getGuild() {
return guild;
}
public ArrayList<AudioTrack> getAllQueuedTrack() {
synchronized (queue) {
ArrayList<AudioTrack> tracks = new ArrayList<>();
queue.forEach(trackInfo -> tracks.add(trackInfo.getTrack()));
return tracks;
}
}
public void queue(AudioTrack track, Member member) {
TrackInfo info = new TrackInfo(track, member);
synchronized (queue) {
if (queue.contains(info)) {
return;
}
if (player.getPlayingTrack() == null && !queue.isEmpty()) {
queue.addFirst(info);
}
if ((player.getPlayingTrack() == null && queue.isEmpty()) || player.getPlayingTrack() != null) {
queue.add(info);
}
if (player.getPlayingTrack() == null) {
player.startTrack(queue.peek().getTrack(), false);
}
}
}
public void nextTrack(AudioPlayer player) {
synchronized (queue) {
TrackInfo last = queue.poll();
if (queue.peek() != null) {
player.startTrack(queue.peek().getTrack(), false);
} else {
player.stopTrack();
}
}
}
public void skipAndPlay(int nb) {
if (nb == 0)
return;
synchronized (queue) {
for (int i = 0; i < nb; i++) {
TrackInfo last = queue.poll();
}
TrackInfo current = queue.peek();
if (current != null) {
player.startTrack(current.getTrack(), false);
} else {
player.stopTrack();
}
}
}
public void shuffle() {
synchronized (queue) {
if (queue.size() > 2) {
TrackInfo current = queue.poll();
ArrayList<TrackInfo> newQueue = new ArrayList<>(queue);
newQueue.remove(0);
Collections.shuffle(newQueue);
queue.clear();
queue.add(current);
queue.addAll(newQueue);
}
}
}
protected void removeFromQueue(int index) {
synchronized (queue) {
ArrayList<TrackInfo> newQueue = new ArrayList<>(queue);
newQueue.remove(index - 1);
queue.clear();
queue.addAll(newQueue);
}
}
}
@@ -10,36 +10,37 @@ package netneutrality.music;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
enum SourceEnum {
Youtube,
Soundcloud,
Bandcamp,
Vimeo,
YTSearch,
Nothing
}
public class SourceFinder { public class SourceFinder {
public enum AvailableSource {
Youtube,
Soundcloud,
Bandcamp,
Vimeo,
YTSearch,
Nothing
}
private static final Pattern youtubePattern = Pattern.compile("^(?:https?://)?(?:(?:www\\.)?)?(?:youtube\\.com|youtu\\.be)/.*?(?:embed|e|v|watch.*?v=)?/?([-_a-z0-9]{10,})?(?:&?index=\\d+)?(?>(?:playlist\\?|&)?list=([^#\\\\&?]{12,}))?", Pattern.CASE_INSENSITIVE); private static final Pattern youtubePattern = Pattern.compile("^(?:https?://)?(?:(?:www\\.)?)?(?:youtube\\.com|youtu\\.be)/.*?(?:embed|e|v|watch.*?v=)?/?([-_a-z0-9]{10,})?(?:&?index=\\d+)?(?>(?:playlist\\?|&)?list=([^#\\\\&?]{12,}))?", Pattern.CASE_INSENSITIVE);
private static final Pattern soundcloudPattern = Pattern.compile("^(?:https?://)?(?:www\\.)?(?:m\\.)?soundcloud\\.com/([a-zA-Z0-9-_]+)/([a-zA-Z0-9-_]+)(?:\\\\?.*|)$", Pattern.CASE_INSENSITIVE); private static final Pattern soundcloudPattern = Pattern.compile("^(?:https?://)?(?:www\\.)?(?:m\\.)?soundcloud\\.com/([a-zA-Z0-9-_]+)/([a-zA-Z0-9-_]+)(?:\\\\?.*|)$", Pattern.CASE_INSENSITIVE);
private static Matcher BandcampMatcher; private static Matcher BandcampMatcher;
private static Matcher vimeoMatcher; private static Matcher vimeoMatcher;
public static SourceEnum getSource(String url) { public static AvailableSource getSource(String url) {
Matcher youtubeMatcher = youtubePattern.matcher(url); Matcher youtubeMatcher = youtubePattern.matcher(url);
Matcher soundcloudMatcher = soundcloudPattern.matcher(url); Matcher soundcloudMatcher = soundcloudPattern.matcher(url);
if (youtubeMatcher.find()) if (youtubeMatcher.find())
return SourceEnum.Youtube; return AvailableSource.Youtube;
if (soundcloudMatcher.find()) if (soundcloudMatcher.find())
return SourceEnum.Soundcloud; return AvailableSource.Soundcloud;
if (!(url.startsWith("http://") || url.startsWith("https://"))) if (!(url.startsWith("http://") || url.startsWith("https://")))
return SourceEnum.YTSearch; return AvailableSource.YTSearch;
return SourceEnum.Nothing; return AvailableSource.Nothing;
} }
public static String getIdentifier(String url) { public static String getIdentifier(String url) {
@@ -0,0 +1,74 @@
package netneutrality.music;/*
* Copyright (c) anthorob
*
* https://xtremcommunity.com/
* https://discord.gg/CdFcyQ
*/
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.TextChannel;
import netneutrality.core.GuildManager;
import netneutrality.thread.ThreadManager;
import org.apache.logging.log4j.LogManager;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TopicManager {
private final ScheduledExecutorService service;
private final Guild guild;
private TextChannel channel;
private String originalTopic;
public TopicManager(Guild guild) {
this.guild = guild;
this.service = (ScheduledExecutorService) ThreadManager.getExecutor("topic-management", ThreadManager.ExecutorType.SCHEDULED);
}
public Guild getGuild() {
return guild;
}
public TextChannel getTextChannel() {
return channel;
}
public String getOriginalTopic() {
return originalTopic;
}
public synchronized void attachTextChannel(TextChannel channel) {
this.channel = channel;
if (originalTopic == null) {
String current = channel.getTopic();
originalTopic = (current == null ? "" : current);
}
}
public synchronized void detachTextChannel() {
updateTopic(null);
this.channel = null;
this.originalTopic = null;
}
public synchronized void updateTopic(AudioTrack track) {
if (isEnabled() && channel != null) {
if (track == null) {
LogManager.getLogger(TopicManager.class).debug("Resetting topic for channel [{}] in guild [{}]", channel.getId(), guild.getId());
channel.getManager().setTopic(originalTopic).queueAfter(3, TimeUnit.SECONDS, service);
} else {
channel.getManager().setTopic(":speaker: Now playing: " + track.getInfo().title).queueAfter(3, TimeUnit.SECONDS, service);
}
}
}
private boolean isEnabled() {
return "1".equals(GuildManager.getManager(guild).getSetting(GuildManager.Settings.MUSIC_TOPIC));
}
}
@@ -12,142 +12,35 @@ import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.TextChannel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
public class TrackScheduler extends AudioEventAdapter { public class TrackScheduler extends AudioEventAdapter {
static class TrackInfo {
private final AudioTrack track;
private final Member member;
private final Set<String> skips;
public TrackInfo(AudioTrack track, Member member) {
this.track = track;
this.member = member;
this.skips = new HashSet<>();
}
public AudioTrack getTrack() {
return track;
}
public Set<String> getSkips() {
return skips;
}
public void toggleSkip(Member member) {
if (!hasSkip(member))
skips.add(member.getId());
else
skips.remove(member.getId());
}
public boolean hasSkip(Member member) {
return skips.contains(member.getId());
}
public Member getRequester() {
return member;
}
}
private final static Logger logger = LoggerFactory.getLogger(TrackScheduler.class); private final static Logger logger = LoggerFactory.getLogger(TrackScheduler.class);
private final QueueManager queueManager;
private final TopicManager topicManager;
private final AudioPlayer player; private final AudioPlayer player;
private final Queue<TrackInfo> queue; private final Guild guild;
private TextChannel channel;
private String originalTopic;
public TrackScheduler(AudioPlayer player) { public TrackScheduler(AudioPlayer player, Guild guild) {
this.player = player; this.player = player;
this.queue = new LinkedBlockingQueue<>(); this.guild = guild;
this.queueManager = new QueueManager(player, guild);
this.topicManager = new TopicManager(guild);
} }
public synchronized void queue(AudioTrack track, Member member) { public QueueManager getQueueManager() {
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If return queueManager;
// something is playing, it returns false and does nothing. In that case the player was already playing so this
// track goes to the queue instead.
queue.offer(new TrackInfo(track, member));
if (player.getPlayingTrack() == null) {
player.startTrack(track, true);
}
} }
public synchronized String getOriginalTopic() { public TopicManager getTopicManager() {
return originalTopic; return topicManager;
} }
public TextChannel getTextChannel() { public AudioPlayer getPlayer() {
return channel; return player;
}
public synchronized void setTextChannel(TextChannel channel) {
this.channel = channel;
if (originalTopic == null) {
originalTopic = channel.getTopic();
}
}
public synchronized void resetTextChannel() {
this.channel = null;
}
public synchronized Queue<TrackInfo> getQueue() {
return queue;
}
public synchronized void resetTopic() {
if (channel != null && (originalTopic != null && !originalTopic.isEmpty())) {
//channel.getManager().setTopic(topic).queue();
}
originalTopic = null;
}
public synchronized void nextTrack(AudioPlayer player) {
TrackInfo last = queue.poll();
if (queue.peek() != null) {
player.startTrack(queue.peek().getTrack(), false);
} else
player.stopTrack();
}
public synchronized void skipAndPlay(int nb) {
if (nb == 0)
return;
for (int i = 0; i < nb; i++) {
TrackInfo last = queue.poll();
}
TrackInfo current = queue.peek();
if (current != null) {
player.startTrack(current.getTrack(), false);
} else {
player.stopTrack();
}
}
protected synchronized void removeFromQueue(int index) {
ArrayList<TrackInfo> newQueue = new ArrayList<>(queue);
newQueue.remove(index - 1);
queue.clear();
queue.addAll(newQueue);
} }
@Override @Override
@@ -162,30 +55,25 @@ public class TrackScheduler extends AudioEventAdapter {
@Override @Override
public void onTrackStart(AudioPlayer player, AudioTrack track) { public void onTrackStart(AudioPlayer player, AudioTrack track) {
logger.debug("Starting track on guild {}: {}", channel.getGuild().getId(), track.getIdentifier()); logger.debug("Starting track on guild {}: {}", guild.getId(), track.getIdentifier());
// A track started playing topicManager.updateTopic(track);
//channel.getManager().setTopic(":speaker: Now playing : " + track.getInfo().title).queue();
} }
@Override @Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
resetTopic();
// check if mayStartNext // check if mayStartNext
if (endReason == AudioTrackEndReason.FINISHED || endReason == AudioTrackEndReason.LOAD_FAILED) { if (endReason == AudioTrackEndReason.FINISHED || endReason == AudioTrackEndReason.LOAD_FAILED) {
//logger.debug("Queue size before next: " + queue.size()); queueManager.nextTrack(player);
nextTrack(player);
} }
if (endReason == AudioTrackEndReason.STOPPED) { if (endReason == AudioTrackEndReason.STOPPED) {
synchronized (queue) { synchronized (queueManager.getQueue()) {
queue.poll(); topicManager.updateTopic(null);
queueManager.getQueue().poll();
} }
} }
//logger.debug("Queue size after next: " + queue.size());
// endReason == FINISHED: A track finished or died by an exception (mayStartNext = true). // endReason == FINISHED: A track finished or died by an exception (mayStartNext = true).
// endReason == LOAD_FAILED: Loading of a track failed (mayStartNext = true). // endReason == LOAD_FAILED: Loading of a track failed (mayStartNext = true).
// endReason == STOPPED: The player was stopped. // endReason == STOPPED: The player was stopped.
@@ -196,13 +84,13 @@ public class TrackScheduler extends AudioEventAdapter {
@Override @Override
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) { public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
resetTopic(); topicManager.updateTopic(null);
channel.sendMessage("Error while playing track: " + track.getInfo().title).queue(); topicManager.getTextChannel().sendMessage("Error while playing track: " + track.getInfo().title).queue();
} }
@Override @Override
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) { public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
resetTopic(); topicManager.updateTopic(null);
nextTrack(player); queueManager.nextTrack(player);
} }
} }
@@ -26,15 +26,10 @@ public class MusicPlayerAutoDisconnectThread implements Runnable {
Guild guild = manager.getGuild(); Guild guild = manager.getGuild();
VoiceChannel channel = guild.getAudioManager().getConnectedChannel(); VoiceChannel channel = guild.getAudioManager().getConnectedChannel();
if (manager.getCurrentlyPlaying() == null && manager.getScheduler().getQueue().isEmpty()) {
manager.leave();
return;
}
if (channel != null) { if (channel != null) {
List<Member> members = channel.getMembers().stream().filter(member -> !member.getId().equals(NetNeutrality.getSelf().getId())).toList(); List<Member> members = channel.getMembers().stream().filter(member -> !member.getId().equals(NetNeutrality.getSelf().getId())).toList();
if (members.size() == 0) if (members.size() == 0 || manager.getScheduler().getQueueManager().getQueue().isEmpty())
manager.leave(); manager.leave();
} }
} }
@@ -107,7 +107,7 @@ public class MessageFunction {
} }
public static MessageEmbed buildViewQueueMessage(MusicManager manager, Member user, boolean currentlyPlaying) { public static MessageEmbed buildViewQueueMessage(MusicManager manager, Member user, boolean currentlyPlaying) {
ArrayList<AudioTrack> tracks = manager.getAllQueuedTrack(); ArrayList<AudioTrack> tracks = manager.getScheduler().getQueueManager().getAllQueuedTrack();
AudioTrack current = manager.getCurrentlyPlaying(); AudioTrack current = manager.getCurrentlyPlaying();
EmbedBuilder message = new EmbedBuilder(); EmbedBuilder message = new EmbedBuilder();
@@ -22,12 +22,12 @@ public class MusicUtils {
return false; return false;
} }
VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel();
if (!manager.isConnected()) { if (!manager.isConnected()) {
context.reply("You are not in a voice channel"); context.reply("You are not in a voice channel");
return false; return false;
} }
VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel();
if (!manager.isConnectedTo(vChannel)) { if (!manager.isConnectedTo(vChannel)) {
context.reply("You are not in a voice channel"); context.reply("You are not in a voice channel");
return false; return false;
@@ -36,13 +36,14 @@ public class MusicUtils {
return true; return true;
} }
public static boolean checkIfDj(MusicManager manager, CommandContext context) { public static boolean isDj(MusicManager manager, CommandContext context) {
if (manager.getCurrentlyPlaying() != null) { if (manager.isCurrentlyPlaying()) {
if (!UserFunction.isAdmin(context.getGuildUser()) || !manager.getRequesterOfCurrentTrack().getId().equals(context.getGuildUser().getId())) { if (UserFunction.isAdmin(context.getGuildUser()) || manager.getRequesterOfCurrentTrack().getId().equals(context.getGuildUser().getId())) {
return true;
} else {
context.reply("You are not powerful enough, scrub"); context.reply("You are not powerful enough, scrub");
return false; return false;
} else }
return true;
} }
return false; return false;
+6
View File
@@ -24,6 +24,12 @@ logger.hibernate.level = info
logger.hibernate.additivity = false logger.hibernate.additivity = false
logger.hibernate.appenderRef.rolling.ref = LogToRollingFile logger.hibernate.appenderRef.rolling.ref = LogToRollingFile
logger.jda.name = net.dv8tion.jda
logger.jda.level = info
logger.jda.additivity = false
logger.jda.appenderRef.rolling.ref = LogToRollingFile
logger.jda.appenderRef.console.ref = consoleLogger
logger.app.name = netneutrality logger.app.name = netneutrality
logger.app.level = debug logger.app.level = debug
logger.app.additivity = false logger.app.additivity = false