Split queue management in its own class and use a double-ended queue instead
This commit is contained in:
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
|
||||
*/
|
||||
|
||||
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.utils.UserFunction;
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ package netneutrality.command.basic;
|
||||
import net.dv8tion.jda.api.MessageBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import netneutrality.command.AbstractCommand;
|
||||
import netneutrality.core.CommandManager;
|
||||
import netneutrality.command.CommandType;
|
||||
import netneutrality.command.CommandUsage;
|
||||
import netneutrality.core.CommandManager;
|
||||
import netneutrality.core.context.CommandContext;
|
||||
import netneutrality.utils.MessageFunction;
|
||||
import netneutrality.utils.UserFunction;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ConfigureCommand extends AbstractCommand {
|
||||
@Override
|
||||
public CommandUsage getUsage() {
|
||||
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) {
|
||||
|
||||
// Role for music commands
|
||||
if (context.getArg(0).equals("musicrole")) {
|
||||
if (context.getArg(0).equals("music-role")) {
|
||||
if (args.length == 1) {
|
||||
String roleDb = guildManager.getSetting(GuildManager.Settings.MUSIC_ROLE);
|
||||
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("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) {
|
||||
@@ -129,12 +129,12 @@ public class ConfigureCommand extends AbstractCommand {
|
||||
}
|
||||
|
||||
// Default volume for music player
|
||||
if (context.getArg(0).equals("musicvolume")) {
|
||||
if (context.getArg(0).equals("music-volume")) {
|
||||
if (args.length == 1) {
|
||||
int volume = Integer.parseInt(guildManager.getSetting(GuildManager.Settings.MUSIC_VOLUME));
|
||||
|
||||
context.reply("Default volume for music player: " + volume);
|
||||
context.reply("Set using 'conf musicvolume <value between 0 and 100>'");
|
||||
context.reply("Default volume for music player: %d", volume);
|
||||
context.reply("Set using 'conf music-volume <value between 0 and 100>'");
|
||||
}
|
||||
|
||||
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
|
||||
if (context.getArg(0).equals("debug")) {
|
||||
if (args.length == 1) {
|
||||
|
||||
@@ -59,10 +59,6 @@ public class BlacklistCommand extends AbstractCommand {
|
||||
String[] args = context.getArgumentsAsArray();
|
||||
DBBlacklist controller = DBController.get("blacklist", DBBlacklist.class);
|
||||
|
||||
if (controller == null) {
|
||||
|
||||
}
|
||||
|
||||
if (args.length > 0 && args.length < 3) {
|
||||
String word = (args.length == 2 ? args[1] : "");
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ package netneutrality.command.music;
|
||||
* https://discord.gg/CdFcyQ
|
||||
*/
|
||||
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import netneutrality.command.AbstractMusicCommand;
|
||||
import netneutrality.command.CommandUsage;
|
||||
import netneutrality.core.context.CommandContext;
|
||||
@@ -44,13 +43,10 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
Member member = context.getGuildUser();
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
if (player.getCurrentlyPlaying() != null) {
|
||||
context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, member));
|
||||
if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) {
|
||||
context.reply(MessageFunction.buildCurrentlyPlayingMessage(player, context.getGuildUser()));
|
||||
} else {
|
||||
context.reply("Player is not playing any tracks right now");
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@ import netneutrality.utils.MusicUtils;
|
||||
|
||||
public class DeleteQueueCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
@@ -47,10 +52,8 @@ public class DeleteQueueCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
if (!MusicUtils.checkIfDj(player, context))
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
if (context.getArgumentsAsArray().length == 0) {
|
||||
@@ -60,12 +63,12 @@ public class DeleteQueueCommand extends AbstractMusicCommand {
|
||||
|
||||
if (context.getArgumentsAsArray().length > 0) {
|
||||
|
||||
int queueSize = player.getScheduler().getQueue().size();
|
||||
int queueSize = player.getQueueSize();
|
||||
if (context.getArg(0).equals("all")) {
|
||||
|
||||
if (queueSize > 0) {
|
||||
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" : "")));
|
||||
} else {
|
||||
context.reply("Queue is empty!");
|
||||
|
||||
@@ -47,9 +47,9 @@ public class LeaveCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
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.music.MusicManager;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PlayCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
@@ -45,12 +47,12 @@ public class PlayCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
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);
|
||||
|
||||
} else {
|
||||
//TODO: resume queue
|
||||
context.reply(getUsage());
|
||||
}
|
||||
|
||||
|
||||
@@ -43,19 +43,23 @@ public class SkipCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
// Combined fonctionality of vote skip
|
||||
//TODO: clean ifs
|
||||
if (context.getArgumentsAsArray().length > 0 && UserFunction.isAdmin(context.getGuildUser())) {
|
||||
int skips = Integer.parseInt(context.getArg(0));
|
||||
player.skip(skips);
|
||||
} else {
|
||||
if (UserFunction.isAdmin(context.getGuildUser()))
|
||||
if (UserFunction.isAdmin(context.getGuildUser())) {
|
||||
if (context.getArgs().length > 0) {
|
||||
try {
|
||||
int skips = Integer.parseInt(context.getArg(0));
|
||||
player.skip(skips);
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter how many track(s) to skip");
|
||||
}
|
||||
} else {
|
||||
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.music.MusicManager;
|
||||
import netneutrality.utils.MusicUtils;
|
||||
import netneutrality.utils.UserFunction;
|
||||
|
||||
public class StopCommand extends AbstractMusicCommand {
|
||||
|
||||
@@ -38,17 +37,15 @@ public class StopCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Stop the music player, emptying the queue in the process";
|
||||
return "Stop the music player, keeping the current queue.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
if (UserFunction.isAdmin(context.getGuildUser()) || context.getGuildUser().getId().equals(player.getRequesterOfCurrentTrack().getId())) {
|
||||
player.leave();
|
||||
if (MusicUtils.checkIfConnected(player, context) && MusicUtils.isDj(player, context)) {
|
||||
player.stopTrack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ package netneutrality.command.music;
|
||||
* https://discord.gg/CdFcyQ
|
||||
*/
|
||||
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import netneutrality.command.AbstractMusicCommand;
|
||||
import netneutrality.command.CommandUsage;
|
||||
import netneutrality.core.context.CommandContext;
|
||||
@@ -48,16 +47,16 @@ public class ViewQueueCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
Member member = context.getGuildUser();
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
int queueSize = player.getScheduler().getQueue().size();
|
||||
if (queueSize > 0) {
|
||||
context.reply(MessageFunction.buildViewQueueMessage(player, member, true));
|
||||
} else {
|
||||
context.reply("Queue is empty!");
|
||||
if (MusicUtils.checkIfConnected(player, context)) {
|
||||
int queueSize = player.getQueueSize();
|
||||
if (queueSize > 0) {
|
||||
context.reply(MessageFunction.buildViewQueueMessage(player, context.getGuildUser(), true));
|
||||
} else {
|
||||
context.reply("Queue is empty!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ package netneutrality.command.music;
|
||||
import netneutrality.command.AbstractMusicCommand;
|
||||
import netneutrality.command.CommandUsage;
|
||||
import netneutrality.core.context.CommandContext;
|
||||
import netneutrality.exceptions.CommandErrorException;
|
||||
import netneutrality.music.MusicManager;
|
||||
import netneutrality.utils.MusicUtils;
|
||||
|
||||
@@ -44,24 +43,20 @@ public class VolumeCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return;
|
||||
|
||||
if (!MusicUtils.checkIfDj(player, context))
|
||||
return;
|
||||
if (MusicUtils.checkIfConnected(player, context) && MusicUtils.isDj(player, context)) {
|
||||
try {
|
||||
int volume = Integer.parseInt(context.getArg(0));
|
||||
|
||||
try {
|
||||
int volume = Integer.parseInt(context.getArg(0));
|
||||
if (volume > 0 && volume <= 100) {
|
||||
player.setVolume(volume);
|
||||
context.reply("Volume set to %s", volume);
|
||||
} else
|
||||
throw new NumberFormatException();
|
||||
|
||||
if (volume > 0 && volume <= 100) {
|
||||
player.setVolume(volume);
|
||||
context.reply("Volume set to %s", volume);
|
||||
} else
|
||||
throw new NumberFormatException();
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter a value from 0 to 100");
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter a value from 0 to 100");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,9 @@ public class VoteSkipCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
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_CHANNEL("MUSIC_CHANNEL", ""),
|
||||
MUSIC_VOLUME("MUSIC_VOLUME", "30"),
|
||||
MUSIC_TOPIC("MUSIC_TOPIC", "0"),
|
||||
BLACKLIST_CHANNELS("BLACKLIST_EXCLUDE_CHANNELS", "");
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -67,6 +67,10 @@ public class CommandContext {
|
||||
return getArgumentsAsArray()[index];
|
||||
}
|
||||
|
||||
public String[] getArgs() {
|
||||
return getArgumentsAsArray();
|
||||
}
|
||||
|
||||
public String[] getArgumentsAsArray() {
|
||||
String msg = message.getContentDisplay().trim();
|
||||
msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim();
|
||||
@@ -180,6 +184,7 @@ public class CommandContext {
|
||||
|
||||
try {
|
||||
if (command.needAdmin() && !UserFunction.isAdmin(member)) {
|
||||
reply("You are not powerful enough, scrub");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class MusicManager {
|
||||
this.guild = guild;
|
||||
this.playerManager = new DefaultAudioPlayerManager();
|
||||
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));
|
||||
|
||||
AudioSourceManagers.registerRemoteSources(playerManager);
|
||||
@@ -105,8 +105,12 @@ public class MusicManager {
|
||||
return player.getPlayingTrack();
|
||||
}
|
||||
|
||||
public synchronized boolean isCurrentlyPlaying() {
|
||||
return player.getPlayingTrack() != null;
|
||||
}
|
||||
|
||||
public synchronized Member getRequesterOfCurrentTrack() {
|
||||
TrackScheduler.TrackInfo info = scheduler.getQueue().peek();
|
||||
QueueManager.TrackInfo info = scheduler.getQueueManager().getQueue().peek();
|
||||
|
||||
if (info == null)
|
||||
return null;
|
||||
@@ -124,7 +128,7 @@ public class MusicManager {
|
||||
guild.getAudioManager().closeAudioConnection();
|
||||
}
|
||||
|
||||
scheduler.resetTopic();
|
||||
scheduler.getTopicManager().detachTextChannel();
|
||||
|
||||
if (autoDisconnectThread != null) {
|
||||
boolean cancel = autoDisconnectThread.cancel(true);
|
||||
@@ -138,7 +142,7 @@ public class MusicManager {
|
||||
for (MusicManager mm : managers.values()) {
|
||||
mm.leave();
|
||||
mm.player.destroy();
|
||||
mm.scheduler.getQueue().clear();
|
||||
mm.scheduler.getQueueManager().getQueue().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,12 +161,14 @@ public class MusicManager {
|
||||
}
|
||||
|
||||
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();
|
||||
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)) {
|
||||
@@ -170,7 +176,7 @@ public class MusicManager {
|
||||
}
|
||||
|
||||
String identifier = SourceFinder.getIdentifier(url);
|
||||
scheduler.setTextChannel(channel);
|
||||
scheduler.getTopicManager().attachTextChannel(channel);
|
||||
|
||||
if (identifier != null) {
|
||||
|
||||
@@ -179,12 +185,11 @@ public class MusicManager {
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack audioTrack) {
|
||||
logger.debug("Found results for identifier: {}", identifier);
|
||||
scheduler.getQueueManager().queue(audioTrack, member);
|
||||
|
||||
if (!silent) {
|
||||
channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(audioTrack, member, identifier)).queue();
|
||||
}
|
||||
|
||||
scheduler.queue(audioTrack, member);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,20 +197,22 @@ public class MusicManager {
|
||||
if (identifier.startsWith("ytsearch:")) {
|
||||
AudioTrack track = audioPlaylist.getTracks().get(0);
|
||||
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();
|
||||
}
|
||||
|
||||
scheduler.queue(track, member);
|
||||
} else {
|
||||
logger.debug("Found results for playlist identifier: {}", identifier);
|
||||
|
||||
if (!silent)
|
||||
if (!silent) {
|
||||
channel.sendMessageEmbeds(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, member, identifier)).queue();
|
||||
}
|
||||
|
||||
for (AudioTrack track : audioPlaylist.getTracks()) {
|
||||
synchronized (scheduler.getQueue()) {
|
||||
scheduler.queue(track, member);
|
||||
synchronized (scheduler.getQueueManager().getQueue()) {
|
||||
scheduler.getQueueManager().queue(track, member);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +239,7 @@ public class MusicManager {
|
||||
if (num == 1) {
|
||||
skip(1);
|
||||
} else {
|
||||
scheduler.removeFromQueue(num);
|
||||
scheduler.getQueueManager().removeFromQueue(num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,23 +248,23 @@ public class MusicManager {
|
||||
}
|
||||
|
||||
public synchronized void skip(int nbTrack) {
|
||||
if (!scheduler.getQueue().isEmpty()) {
|
||||
if (nbTrack >= scheduler.getQueue().size()) {
|
||||
if (!scheduler.getQueueManager().getQueue().isEmpty()) {
|
||||
if (nbTrack >= scheduler.getQueueManager().getQueue().size()) {
|
||||
nbTrack = 1;
|
||||
}
|
||||
|
||||
scheduler.skipAndPlay(nbTrack);
|
||||
scheduler.getQueueManager().skipAndPlay(nbTrack);
|
||||
} else {
|
||||
leave();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void voteSkip(CommandContext context) {
|
||||
Queue<TrackScheduler.TrackInfo> queue = scheduler.getQueue();
|
||||
Queue<QueueManager.TrackInfo> queue = scheduler.getQueueManager().getQueue();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -290,15 +297,15 @@ public class MusicManager {
|
||||
player.setVolume(volume);
|
||||
}
|
||||
|
||||
public int getQueueSize () {
|
||||
synchronized (scheduler.getQueueManager().getQueue()) {
|
||||
return scheduler.getQueueManager().getQueue().size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TrackScheduler getScheduler() {
|
||||
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.Pattern;
|
||||
|
||||
enum SourceEnum {
|
||||
Youtube,
|
||||
Soundcloud,
|
||||
Bandcamp,
|
||||
Vimeo,
|
||||
YTSearch,
|
||||
Nothing
|
||||
|
||||
}
|
||||
|
||||
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 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 vimeoMatcher;
|
||||
|
||||
public static SourceEnum getSource(String url) {
|
||||
public static AvailableSource getSource(String url) {
|
||||
Matcher youtubeMatcher = youtubePattern.matcher(url);
|
||||
Matcher soundcloudMatcher = soundcloudPattern.matcher(url);
|
||||
|
||||
if (youtubeMatcher.find())
|
||||
return SourceEnum.Youtube;
|
||||
return AvailableSource.Youtube;
|
||||
|
||||
if (soundcloudMatcher.find())
|
||||
return SourceEnum.Soundcloud;
|
||||
return AvailableSource.Soundcloud;
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.slf4j.Logger;
|
||||
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 {
|
||||
|
||||
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 QueueManager queueManager;
|
||||
private final TopicManager topicManager;
|
||||
private final AudioPlayer player;
|
||||
private final Queue<TrackInfo> queue;
|
||||
private TextChannel channel;
|
||||
private String originalTopic;
|
||||
private final Guild guild;
|
||||
|
||||
public TrackScheduler(AudioPlayer player) {
|
||||
public TrackScheduler(AudioPlayer player, Guild guild) {
|
||||
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) {
|
||||
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If
|
||||
// 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 QueueManager getQueueManager() {
|
||||
return queueManager;
|
||||
}
|
||||
|
||||
public synchronized String getOriginalTopic() {
|
||||
return originalTopic;
|
||||
public TopicManager getTopicManager() {
|
||||
return topicManager;
|
||||
}
|
||||
|
||||
public TextChannel getTextChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
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);
|
||||
public AudioPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,30 +55,25 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||
|
||||
@Override
|
||||
public void onTrackStart(AudioPlayer player, AudioTrack track) {
|
||||
logger.debug("Starting track on guild {}: {}", channel.getGuild().getId(), track.getIdentifier());
|
||||
// A track started playing
|
||||
//channel.getManager().setTopic(":speaker: Now playing : " + track.getInfo().title).queue();
|
||||
logger.debug("Starting track on guild {}: {}", guild.getId(), track.getIdentifier());
|
||||
topicManager.updateTopic(track);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||
resetTopic();
|
||||
|
||||
// check if mayStartNext
|
||||
if (endReason == AudioTrackEndReason.FINISHED || endReason == AudioTrackEndReason.LOAD_FAILED) {
|
||||
//logger.debug("Queue size before next: " + queue.size());
|
||||
nextTrack(player);
|
||||
queueManager.nextTrack(player);
|
||||
|
||||
}
|
||||
|
||||
if (endReason == AudioTrackEndReason.STOPPED) {
|
||||
synchronized (queue) {
|
||||
queue.poll();
|
||||
synchronized (queueManager.getQueue()) {
|
||||
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 == LOAD_FAILED: Loading of a track failed (mayStartNext = true).
|
||||
// endReason == STOPPED: The player was stopped.
|
||||
@@ -196,13 +84,13 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||
|
||||
@Override
|
||||
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
|
||||
resetTopic();
|
||||
channel.sendMessage("Error while playing track: " + track.getInfo().title).queue();
|
||||
topicManager.updateTopic(null);
|
||||
topicManager.getTextChannel().sendMessage("Error while playing track: " + track.getInfo().title).queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
|
||||
resetTopic();
|
||||
nextTrack(player);
|
||||
topicManager.updateTopic(null);
|
||||
queueManager.nextTrack(player);
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,10 @@ public class MusicPlayerAutoDisconnectThread implements Runnable {
|
||||
Guild guild = manager.getGuild();
|
||||
VoiceChannel channel = guild.getAudioManager().getConnectedChannel();
|
||||
|
||||
if (manager.getCurrentlyPlaying() == null && manager.getScheduler().getQueue().isEmpty()) {
|
||||
manager.leave();
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel != null) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class MessageFunction {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
|
||||
@@ -22,12 +22,12 @@ public class MusicUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel();
|
||||
if (!manager.isConnected()) {
|
||||
context.reply("You are not in a voice channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
VoiceChannel vChannel = context.getGuildUser().getVoiceState().getChannel();
|
||||
if (!manager.isConnectedTo(vChannel)) {
|
||||
context.reply("You are not in a voice channel");
|
||||
return false;
|
||||
@@ -36,13 +36,14 @@ public class MusicUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkIfDj(MusicManager manager, CommandContext context) {
|
||||
if (manager.getCurrentlyPlaying() != null) {
|
||||
if (!UserFunction.isAdmin(context.getGuildUser()) || !manager.getRequesterOfCurrentTrack().getId().equals(context.getGuildUser().getId())) {
|
||||
public static boolean isDj(MusicManager manager, CommandContext context) {
|
||||
if (manager.isCurrentlyPlaying()) {
|
||||
if (UserFunction.isAdmin(context.getGuildUser()) || manager.getRequesterOfCurrentTrack().getId().equals(context.getGuildUser().getId())) {
|
||||
return true;
|
||||
} else {
|
||||
context.reply("You are not powerful enough, scrub");
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in new issue
Block a user