diff --git a/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java b/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java index d2b9b571a..4a71a565b 100644 --- a/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java +++ b/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java @@ -115,6 +115,7 @@ private static void startBot() new RemoveCmd(bot), new SearchCmd(bot), new SCSearchCmd(bot), + new SeekCmd(bot), new ShuffleCmd(bot), new SkipCmd(bot), @@ -122,7 +123,6 @@ private static void startBot() new ForceskipCmd(bot), new MoveTrackCmd(bot), new PauseCmd(bot), - new PlaynextCmd(bot), new RepeatCmd(bot), new SkiptoCmd(bot), new StopCmd(bot), diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java b/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java index b2ca7179d..f7fbc856f 100644 --- a/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java +++ b/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java @@ -51,6 +51,9 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler public final static String STOP_EMOJI = "\u23F9"; // ⏹ private final FairQueue queue = new FairQueue<>(); + private final FairQueue backgroundQueue = new FairQueue<>(); + private AudioTrack latestActiveBackgroundTrack = null; + private Long latestActiveBackgroundTrackPosition = 0L; private final List defaultQueue = new LinkedList<>(); private final Set votes = new HashSet<>(); @@ -67,42 +70,67 @@ protected AudioHandler(PlayerManager manager, Guild guild, AudioPlayer player) this.guildId = guild.getIdLong(); } - public int addTrackToFront(QueuedTrack qtrack) + public int addTrack(QueuedTrack qtrack, boolean background, Integer position) { - if(audioPlayer.getPlayingTrack()==null) + boolean playingBackground = audioPlayer.getPlayingTrack()==latestActiveBackgroundTrack; + boolean playNow = position != null && position < 0; + if(audioPlayer.getPlayingTrack()==null || (playingBackground && (!background || playNow)) || (!background && playNow)) { - audioPlayer.playTrack(qtrack.getTrack()); + AudioTrack track = qtrack.getTrack(); + if (background) latestActiveBackgroundTrack = track; + else if (latestActiveBackgroundTrack != null) latestActiveBackgroundTrackPosition = latestActiveBackgroundTrack.getPosition(); + audioPlayer.playTrack(track); return -1; } - else + else if (background) { - queue.addAt(0, qtrack); - return 0; + if (position == null) return backgroundQueue.add(qtrack); + else { + if (position < 0) position = 0; + return backgroundQueue.addAt(position, qtrack); + } + } + else + { + if (position == null) return queue.add(qtrack); + return queue.addAt(position, qtrack); } } - public int addTrack(QueuedTrack qtrack) + public int addTrack(QueuedTrack qtrack, boolean background) { - if(audioPlayer.getPlayingTrack()==null) - { - audioPlayer.playTrack(qtrack.getTrack()); - return -1; + return addTrack(qtrack, background, null); + } + + public void skipCurrentTrack() { + if (audioPlayer.getPlayingTrack() == latestActiveBackgroundTrack) { + latestActiveBackgroundTrack = null; } - else - return queue.add(qtrack); + audioPlayer.stopTrack(); } public FairQueue getQueue() { return queue; } + + public FairQueue getBackgroundQueue() + { + return backgroundQueue; + } + + public boolean playingFromBackgroundQueue() + { + return audioPlayer.getPlayingTrack() != null && latestActiveBackgroundTrack != null && audioPlayer.getPlayingTrack() == latestActiveBackgroundTrack; + } public void stopAndClear() { queue.clear(); + backgroundQueue.clear(); + latestActiveBackgroundTrack = null; defaultQueue.clear(); audioPlayer.stopTrack(); - //current = null; } public boolean isMusicPlaying(JDA jda) @@ -128,6 +156,25 @@ public RequestMetadata getRequestMetadata() return rm == null ? RequestMetadata.EMPTY : rm; } + public boolean playFromBackground() + { + if (latestActiveBackgroundTrack != null) { + latestActiveBackgroundTrack = latestActiveBackgroundTrack.makeClone(); + latestActiveBackgroundTrack.setPosition(latestActiveBackgroundTrackPosition); + audioPlayer.playTrack(latestActiveBackgroundTrack); + return true; + } + + if (!backgroundQueue.isEmpty()) + { + latestActiveBackgroundTrack = backgroundQueue.remove(0).getTrack(); + audioPlayer.playTrack(latestActiveBackgroundTrack); + return true; + } + + return false; + } + public boolean playFromDefault() { if(!defaultQueue.isEmpty()) @@ -142,7 +189,7 @@ public boolean playFromDefault() Playlist pl = manager.getBot().getPlaylistLoader().getPlaylist(settings.getDefaultPlaylist()); if(pl==null || pl.getItems().isEmpty()) return false; - pl.loadTracks(manager, (at) -> + pl.loadTracks(manager, (at, i) -> { if(audioPlayer.getPlayingTrack()==null) audioPlayer.playTrack(at); @@ -165,15 +212,22 @@ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason if(endReason==AudioTrackEndReason.FINISHED && repeatMode != RepeatMode.OFF) { QueuedTrack clone = new QueuedTrack(track.makeClone(), track.getUserData(RequestMetadata.class)); + FairQueue trackQueue = (track == latestActiveBackgroundTrack) ? backgroundQueue : queue; if(repeatMode == RepeatMode.ALL) - queue.add(clone); + trackQueue.add(clone); else - queue.addAt(0, clone); + trackQueue.addAt(0, clone); + } + + if (track == latestActiveBackgroundTrack && endReason != AudioTrackEndReason.REPLACED) { + // when background track is replaced by a queue track, we keep it around. Otherwise, this track is over. + latestActiveBackgroundTrack = null; + // note that REPLACED is *also* used when the track is skipped, but skipTrack() makes sure we don't keep it around in that case. } - if(queue.isEmpty()) + if(queue.isEmpty() && endReason!=AudioTrackEndReason.REPLACED) { - if(!playFromDefault()) + if(!playFromBackground() && !playFromDefault()) { manager.getBot().getNowplayingHandler().onTrackUpdate(guildId, null, this); if(!manager.getBot().getConfig().getStay()) @@ -183,7 +237,7 @@ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason player.setPaused(false); } } - else + else if (endReason.mayStartNext) { QueuedTrack qt = queue.pull(); player.playTrack(qt.getTrack()); @@ -206,7 +260,7 @@ public Message getNowPlaying(JDA jda) Guild guild = guild(jda); AudioTrack track = audioPlayer.getPlayingTrack(); MessageBuilder mb = new MessageBuilder(); - mb.append(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing in "+guild.getSelfMember().getVoiceState().getChannel().getAsMention()+"...**")); + mb.append(FormatUtil.filter(manager.getBot().getConfig().getSuccess()+" **Now Playing from "+(playingFromBackgroundQueue() ? "Background Queue" : "Queue")+" in "+guild.getSelfMember().getVoiceState().getChannel().getAsMention()+"...**")); EmbedBuilder eb = new EmbedBuilder(); eb.setColor(guild.getSelfMember().getColor()); RequestMetadata rm = getRequestMetadata(); diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceRemoveCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceRemoveCmd.java index 0d25f73af..b50990396 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceRemoveCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceRemoveCmd.java @@ -38,7 +38,7 @@ public ForceRemoveCmd(Bot bot) { super(bot); this.name = "forceremove"; - this.help = "removes all entries by a user from the queue"; + this.help = "removes all entries by a user from the queue and the background queue"; this.arguments = ""; this.aliases = bot.getConfig().getAliases(this.name); this.beListening = false; @@ -56,9 +56,9 @@ public void doCommand(CommandEvent event) } AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler(); - if (handler.getQueue().isEmpty()) + if (handler.getQueue().isEmpty() && handler.getBackgroundQueue().isEmpty()) { - event.reply(bot.getError(event)+"There is nothing in the queue!"); + event.reply(bot.getError(event)+"There is nothing in the queues!"); return; } @@ -107,13 +107,14 @@ else if(found.size()>1) private void removeAllEntries(User target, CommandEvent event) { int count = ((AudioHandler) event.getGuild().getAudioManager().getSendingHandler()).getQueue().removeAll(target.getIdLong()); - if (count == 0) + int backgroundCount = ((AudioHandler) event.getGuild().getAudioManager().getSendingHandler()).getBackgroundQueue().removeAll(target.getIdLong()); + if (count == 0 && backgroundCount == 0) { - event.reply(bot.getWarning(event)+"**"+target.getName()+"** doesn't have any songs in the queue!"); + event.reply(bot.getWarning(event)+"**"+target.getName()+"** doesn't have any songs in the queues!"); } else { - event.reply(bot.getSuccess(event)+"Successfully removed `"+count+"` entries from **"+target.getName()+"**#"+target.getDiscriminator()+"."); + event.reply(bot.getSuccess(event)+"Successfully removed `"+(count+backgroundCount)+"` entries from **"+target.getName()+"**#"+target.getDiscriminator()+"."); } } } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceskipCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceskipCmd.java index a902d8ea0..a93c01f52 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceskipCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceskipCmd.java @@ -43,6 +43,6 @@ public void doCommand(CommandEvent event) RequestMetadata rm = handler.getRequestMetadata(); event.reply(bot.getSuccess(event)+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title +"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)")); - handler.getPlayer().stopTrack(); + handler.skipCurrentTrack(); } } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/MoveTrackCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/MoveTrackCmd.java index 816f65461..95f9b3448 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/MoveTrackCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/MoveTrackCmd.java @@ -18,7 +18,7 @@ public MoveTrackCmd(Bot bot) { super(bot); this.name = "movetrack"; - this.help = "move a track in the current queue to a different position"; + this.help = "move a track in the currently playing queue to a different position"; this.arguments = " "; this.aliases = bot.getConfig().getAliases(this.name); this.bePlaying = true; @@ -57,16 +57,17 @@ public void doCommand(CommandEvent event) // Validate that from and to are available AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler(); - FairQueue queue = handler.getQueue(); + FairQueue queue = (handler.playingFromBackgroundQueue()) ? handler.getBackgroundQueue() : handler.getQueue(); + String queueName = (handler.playingFromBackgroundQueue()) ? "the background queue" : "the queue"; if (isUnavailablePosition(queue, from)) { - String reply = String.format("`%d` is not a valid position in the queue!", from); + String reply = String.format("`%d` is not a valid position in "+queueName+"!", from); event.reply(bot.getError(event)+reply); return; } if (isUnavailablePosition(queue, to)) { - String reply = String.format("`%d` is not a valid position in the queue!", to); + String reply = String.format("`%d` is not a valid position in "+queueName+"!", to); event.reply(bot.getError(event)+reply); return; } @@ -74,7 +75,7 @@ public void doCommand(CommandEvent event) // Move the track QueuedTrack track = queue.moveItem(from - 1, to - 1); String trackTitle = track.getTrack().getInfo().title; - String reply = String.format("Moved **%s** from position `%d` to `%d`.", trackTitle, from, to); + String reply = String.format("Moved **%s** from position `%d` to `%d` in "+queueName+".", trackTitle, from, to); event.reply(bot.getSuccess(event)+reply); } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java deleted file mode 100644 index f906f9b89..000000000 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2018 John Grosh . - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jagrosh.jmusicbot.commands.dj; - -import com.jagrosh.jdautilities.command.CommandEvent; -import com.jagrosh.jmusicbot.Bot; -import com.jagrosh.jmusicbot.audio.AudioHandler; -import com.jagrosh.jmusicbot.audio.QueuedTrack; -import com.jagrosh.jmusicbot.commands.DJCommand; -import com.jagrosh.jmusicbot.utils.FormatUtil; -import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; -import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; -import com.sedmelluq.discord.lavaplayer.track.AudioTrack; -import net.dv8tion.jda.api.entities.Message; - -/** - * - * @author John Grosh (john.a.grosh@gmail.com) - */ -public class PlaynextCmd extends DJCommand -{ - - public PlaynextCmd(Bot bot) - { - super(bot); - this.name = "playnext"; - this.arguments = ""; - this.help = "plays a single song next"; - this.aliases = bot.getConfig().getAliases(this.name); - this.beListening = true; - this.bePlaying = false; - } - - @Override - public void doCommand(CommandEvent event) - { - if(event.getArgs().isEmpty() && event.getMessage().getAttachments().isEmpty()) - { - event.reply(bot.getWarning(event)+"Please include a song title or URL!"); - return; - } - String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">") - ? event.getArgs().substring(1,event.getArgs().length()-1) - : event.getArgs().isEmpty() ? event.getMessage().getAttachments().get(0).getUrl() : event.getArgs(); - event.reply(bot.getLoading(event)+" Loading... `["+args+"]`", m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false))); - } - - private class ResultHandler implements AudioLoadResultHandler - { - private final Message m; - private final CommandEvent event; - private final boolean ytsearch; - - private ResultHandler(Message m, CommandEvent event, boolean ytsearch) - { - this.m = m; - this.event = event; - this.ytsearch = ytsearch; - } - - private void loadSingle(AudioTrack track) - { - if(bot.getConfig().isTooLong(track)) - { - m.editMessage(FormatUtil.filter(bot.getWarning(event)+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `" - +FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue(); - return; - } - AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - int pos = handler.addTrackToFront(new QueuedTrack(track, event.getAuthor()))+1; - String addMsg = FormatUtil.filter(bot.getSuccess(event)+" Added **"+track.getInfo().title - +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos)); - m.editMessage(addMsg).queue(); - } - - @Override - public void trackLoaded(AudioTrack track) - { - loadSingle(track); - } - - @Override - public void playlistLoaded(AudioPlaylist playlist) - { - AudioTrack single; - if(playlist.getTracks().size()==1 || playlist.isSearchResult()) - single = playlist.getSelectedTrack()==null ? playlist.getTracks().get(0) : playlist.getSelectedTrack(); - else if (playlist.getSelectedTrack()!=null) - single = playlist.getSelectedTrack(); - else - single = playlist.getTracks().get(0); - loadSingle(single); - } - - @Override - public void noMatches() - { - if(ytsearch) - m.editMessage(FormatUtil.filter(bot.getWarning(event)+" No results found for `"+event.getArgs()+"`.")).queue(); - else - bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true)); - } - - @Override - public void loadFailed(FriendlyException throwable) - { - if(throwable.severity==FriendlyException.Severity.COMMON) - m.editMessage(bot.getError(event)+" Error loading: "+throwable.getMessage()).queue(); - else - m.editMessage(bot.getError(event)+" Error loading track.").queue(); - } - } -} diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/RepeatCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/RepeatCmd.java index a21d8605f..8ca006bc4 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/RepeatCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/RepeatCmd.java @@ -31,7 +31,7 @@ public RepeatCmd(Bot bot) { super(bot); this.name = "repeat"; - this.help = "re-adds music to the queue when finished"; + this.help = "enables re-adding music to the queue when finished"; this.arguments = "[off|all|single]"; this.aliases = bot.getConfig().getAliases(this.name); this.guildOnly = true; diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/SkiptoCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/SkiptoCmd.java index fe196c4cb..c15f2e57d 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/SkiptoCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/SkiptoCmd.java @@ -57,6 +57,6 @@ public void doCommand(CommandEvent event) } handler.getQueue().skip(index-1); event.reply(bot.getSuccess(event)+" Skipped to **"+handler.getQueue().get(0).getTrack().getInfo().title+"**"); - handler.getPlayer().stopTrack(); + handler.skipCurrentTrack(); } } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/StopCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/StopCmd.java index a8d968c27..3a0ce644c 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/StopCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/StopCmd.java @@ -30,7 +30,7 @@ public StopCmd(Bot bot) { super(bot); this.name = "stop"; - this.help = "stops the current song and clears the queue"; + this.help = "stops the current song and clears the queue and the background queue"; this.aliases = bot.getConfig().getAliases(this.name); this.bePlaying = false; } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java index 75987210a..52077f43f 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java @@ -37,6 +37,9 @@ import com.jagrosh.jmusicbot.commands.MusicCommand; import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist; import com.jagrosh.jmusicbot.utils.FormatUtil; + +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Message; @@ -58,81 +61,118 @@ public PlayCmd(Bot bot) super(bot); this.bot = bot; this.name = "play"; - this.arguments = ""; - this.help = "plays the provided song"; + this.arguments = getPlayArgsSyntax(null, null, null); + this.help = getPlayHelpMessage(null, null, null); this.aliases = bot.getConfig().getAliases(this.name); this.beListening = true; this.bePlaying = false; - this.children = new Command[]{new PlaylistCmd(bot)}; + this.children = getPlayChildren(null, null, null); } @Override - public void doCommand(CommandEvent event) + public void doCommand(CommandEvent event) { - if(event.getArgs().isEmpty() && event.getMessage().getAttachments().isEmpty()) + doPlay(bot, event, null, null, null); + } + + private void doPlay(Bot bot, CommandEvent event, Integer position, Boolean background_undefaulted, Boolean localPlaylist) { + boolean background = background_undefaulted == null ? false : background_undefaulted; + if (localPlaylist != null && localPlaylist) { + if(event.getArgs().isEmpty()) + { + event.reply(bot.getError(event)+" Please include a playlist name."); + return; + } + Playlist playlist = bot.getPlaylistLoader().getPlaylist(event.getArgs()); + if(playlist==null) + { + event.reply(bot.getError(event)+"I could not find `"+event.getArgs()+".txt` in the Playlists folder."); + return; + } + event.getChannel().sendMessage(bot.getLoading(event)+" Loading playlist **"+event.getArgs()+"**... ("+playlist.getItems().size()+" items)").queue(m -> + { + AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); + playlist.loadTracks(bot.getPlayerManager(), (at, i)->handler.addTrack(new QueuedTrack(at, event.getAuthor()), background, position == null ? null : position + i), () -> { + StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty() + ? bot.getWarning(event)+" No tracks were loaded!" + : bot.getSuccess(event)+" Loaded **"+playlist.getTracks().size()+"** tracks into the "+(position != null ? "front of the " : "")+(background ? "background " : "")+"queue!"); + if(!playlist.getErrors().isEmpty()) + builder.append("\nThe following tracks failed to load:"); + playlist.getErrors().forEach(err -> builder.append("\n`[").append(err.getIndex()+1).append("]` **").append(err.getItem()).append("**: ").append(err.getReason())); + String str = builder.toString(); + if(str.length()>2000) + str = str.substring(0,1994)+" (...)"; + m.editMessage(FormatUtil.filter(str)).queue(); + }); + }); + } + else { - AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - if(handler.getPlayer().getPlayingTrack()!=null && handler.getPlayer().isPaused()) + if(event.getArgs().isEmpty() && event.getMessage().getAttachments().isEmpty()) { - if(DJCommand.checkDJPermission(event)) + AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); + if(handler.getPlayer().getPlayingTrack()!=null && handler.getPlayer().isPaused()) { - handler.getPlayer().setPaused(false); - event.reply(bot.getSuccess(event)+"Resumed **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**."); + if(DJCommand.checkDJPermission(event)) + { + handler.getPlayer().setPaused(false); + event.reply(bot.getSuccess(event)+"Resumed **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**."); + } + else + event.reply(bot.getError(event)+"Only DJs can unpause the player!"); + return; } - else - event.reply(bot.getError(event)+"Only DJs can unpause the player!"); + StringBuilder builder = new StringBuilder(bot.getWarning(event)+" Play Commands:\n"); + builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ` - plays the first result from Youtube"); + builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ` - plays the provided song, playlist, or stream"); + for(Command cmd: children) + builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName()).append(" ").append(cmd.getArguments()).append("` - ").append(cmd.getHelp()); + event.reply(builder.toString()); return; } - StringBuilder builder = new StringBuilder(bot.getWarning(event)+" Play Commands:\n"); - builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ` - plays the first result from Youtube"); - builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ` - plays the provided song, playlist, or stream"); - for(Command cmd: children) - builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName()).append(" ").append(cmd.getArguments()).append("` - ").append(cmd.getHelp()); - event.reply(builder.toString()); - return; - } - - String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">") - ? event.getArgs().substring(1,event.getArgs().length()-1) - : event.getArgs().isEmpty() ? event.getMessage().getAttachments().get(0).getUrl() : event.getArgs(); - - LoggerFactory.getLogger("MusicBot").info("Playing: " + args); - SpotifyUrlData spotifyUrl = SpotifyAPI.tryParseUrl(args); - if (spotifyUrl != null) { - if (bot.getSpotifyAPI() == null) { - event.reply(bot.getError(event)+" Spotify not enabled on this bot; contact the owner."); - } else { - if (spotifyUrl.type == SpotifyUrlData.Type.TRACK) { - try { - SpotifyTrack track = bot.getSpotifyAPI().getTrack(spotifyUrl.id); - event.reply(bot.getLoading(event) +" Loading... `["+track.name+"]`", - m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(track), new SpotifyResultHandler(m, event, track))); - } catch (Exception e) { - LoggerFactory.getLogger("MusicBot").error("Failed to load spotify track from: " + args, e); - event.reply(bot.getError(event)+" Failed to load spotify track... `["+spotifyUrl.id+"]`"); - } + + String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">") + ? event.getArgs().substring(1,event.getArgs().length()-1) + : event.getArgs().isEmpty() ? event.getMessage().getAttachments().get(0).getUrl() : event.getArgs(); + + LoggerFactory.getLogger("MusicBot").info("Playing: " + args); + SpotifyUrlData spotifyUrl = SpotifyAPI.tryParseUrl(args); + if (spotifyUrl != null) { + if (bot.getSpotifyAPI() == null) { + event.reply(bot.getError(event)+" Spotify not enabled on this bot; contact the owner."); } else { - String noun = (spotifyUrl.type == SpotifyUrlData.Type.PLAYLIST) ? "playlist" : "album"; - try { - SpotifyPlaylist playlist = - (spotifyUrl.type == SpotifyUrlData.Type.PLAYLIST) - ? bot.getSpotifyAPI().getPlaylist(spotifyUrl.id) - : bot.getSpotifyAPI().getAlbum(spotifyUrl.id); - - if (playlist.tracks.length == 0) { - event.reply(bot.getError(event) +" Spotify "+noun+" does not have any tracks... `["+playlist.name+"]`"); - } else { - event.reply(bot.getLoading(event) +" Loading... `["+String.format("%s (%d track%s)", playlist.name, playlist.tracks.length, playlist.tracks.length == 1 ? "" : "s")+"]`", - m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[0]), new SpotifyResultHandler(m, event, playlist))); + if (spotifyUrl.type == SpotifyUrlData.Type.TRACK) { + try { + SpotifyTrack track = bot.getSpotifyAPI().getTrack(spotifyUrl.id); + event.reply(bot.getLoading(event) +" Loading... `["+track.name+"]`", + m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(track), new SpotifyResultHandler(m, event, track, position, background))); + } catch (Exception e) { + LoggerFactory.getLogger("MusicBot").error("Failed to load spotify track from: " + args, e); + event.reply(bot.getError(event)+" Failed to load spotify track... `["+spotifyUrl.id+"]`"); + } + } else { + String noun = (spotifyUrl.type == SpotifyUrlData.Type.PLAYLIST) ? "playlist" : "album"; + try { + SpotifyPlaylist playlist = + (spotifyUrl.type == SpotifyUrlData.Type.PLAYLIST) + ? bot.getSpotifyAPI().getPlaylist(spotifyUrl.id) + : bot.getSpotifyAPI().getAlbum(spotifyUrl.id); + + if (playlist.tracks.length == 0) { + event.reply(bot.getError(event) +" Spotify "+noun+" does not have any tracks... `["+playlist.name+"]`"); + } else { + event.reply(bot.getLoading(event) +" Loading... `["+String.format("%s (%d track%s)", playlist.name, playlist.tracks.length, playlist.tracks.length == 1 ? "" : "s")+"]`", + m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[0]), new SpotifyResultHandler(m, event, playlist, position, background))); + } + } catch (Exception e) { + LoggerFactory.getLogger("MusicBot").error("Failed to load spotify "+noun+" from: " + args, e); + event.reply(bot.getError(event)+" Failed to load spotify "+noun+"... `["+spotifyUrl.id+"]`"); } - } catch (Exception e) { - LoggerFactory.getLogger("MusicBot").error("Failed to load spotify "+noun+" from: " + args, e); - event.reply(bot.getError(event)+" Failed to load spotify "+noun+"... `["+spotifyUrl.id+"]`"); } } + } else { + event.reply(bot.getLoading(event)+" Loading... `["+args+"]`", m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false,position,background))); } - } else { - event.reply(bot.getLoading(event)+" Loading... `["+args+"]`", m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false))); } } @@ -185,18 +225,22 @@ private class ResultHandler implements AudioLoadResultHandler protected final Message m; protected final CommandEvent event; protected final boolean ytsearch; + protected final Integer position; + protected final boolean background; protected final FrequentMessageEditAgent frequentEditAgent; - private ResultHandler(Message m, CommandEvent event, boolean ytsearch) + private ResultHandler(Message m, CommandEvent event, boolean ytsearch, Integer position, boolean background) { - this(m, event, ytsearch, new FrequentMessageEditAgent(m)); + this(m, event, ytsearch, position, background, new FrequentMessageEditAgent(m)); } - private ResultHandler(Message m, CommandEvent event, boolean ytsearch, FrequentMessageEditAgent frequentEditAgent) + private ResultHandler(Message m, CommandEvent event, boolean ytsearch, Integer position, boolean background, FrequentMessageEditAgent frequentEditAgent) { this.m = m; this.event = event; this.ytsearch = ytsearch; + this.position = position; + this.background = background; this.frequentEditAgent = frequentEditAgent; } @@ -209,15 +253,15 @@ protected void loadSingle(AudioTrack track, AudioPlaylist playlist) return; } AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1; + int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()), background, position)+1; String addMsg = FormatUtil.filter(bot.getSuccess(event)+" Added **"+track.getInfo().title - +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos)); + +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos<=0?"to begin playing"+(background ? " in the background" : ""):" to the "+(background ? "background " : "")+"queue at position "+pos)); if(playlist==null || !event.getSelfMember().hasPermission(event.getTextChannel(), Permission.MESSAGE_ADD_REACTION)) frequentEditAgent.queueEditMessage(addMsg); else { new ButtonMenu.Builder() - .setText(addMsg+"\n"+bot.getWarning(event)+" This track has a playlist of **"+playlist.getTracks().size()+"** tracks attached. Select "+LOAD+" to load playlist.") + .setText(addMsg+"\n"+bot.getWarning(event)+" This track has a playlist of **"+playlist.getTracks().size()+"** tracks attached. Select "+LOAD+" to add the rest to the "+(background ? "background " : "")+" queue.") .setChoices(LOAD, CANCEL) .setEventWaiter(bot.getWaiter()) .setTimeout(30, TimeUnit.SECONDS) @@ -237,14 +281,16 @@ protected void loadSingle(AudioTrack track, AudioPlaylist playlist) protected int loadPlaylist(AudioPlaylist playlist, AudioTrack exclude) { int[] count = {0}; - playlist.getTracks().stream().forEach((track) -> { + List tracks = playlist.getTracks(); + for (int i = 0; i < tracks.size(); i++) { + AudioTrack track = tracks.get(i); if(!bot.getConfig().isTooLong(track) && !track.equals(exclude)) { AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - handler.addTrack(new QueuedTrack(track, event.getAuthor())); + handler.addTrack(new QueuedTrack(track, event.getAuthor()), background, position == null ? null : position + i); count[0]++; } - }); + } return count[0]; } @@ -297,7 +343,7 @@ public void noMatches() if(ytsearch) frequentEditAgent.queueEditMessage(FormatUtil.filter(bot.getWarning(event)+" No results found for `"+event.getArgs()+"`.")); else - bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true)); + bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true,position,background)); } @Override @@ -322,17 +368,17 @@ private class SpotifyResultHandler extends ResultHandler { private final int prevErrors; private final FrequentMessageEditAgent frequentEditAgent; - private SpotifyResultHandler(Message m, CommandEvent event, SpotifyTrack track){ - this(m, event, new SpotifyPlaylist(track.name, new SpotifyTrack[] { track })); + private SpotifyResultHandler(Message m, CommandEvent event, SpotifyTrack track, Integer position, boolean background){ + this(m, event, new SpotifyPlaylist(track.name, new SpotifyTrack[] { track }), position, background); } - private SpotifyResultHandler(Message m, CommandEvent event, SpotifyPlaylist playlist) { - this(m, event, playlist, 0, "", 0, new FrequentMessageEditAgent(m)); + private SpotifyResultHandler(Message m, CommandEvent event, SpotifyPlaylist playlist, Integer position, boolean background) { + this(m, event, playlist, 0, "", 0, position, background, new FrequentMessageEditAgent(m)); } - private SpotifyResultHandler(Message m, CommandEvent event, SpotifyPlaylist playlist, int iTrack, String prevMessageText, int prevErrors, FrequentMessageEditAgent frequentEditAgent) + private SpotifyResultHandler(Message m, CommandEvent event, SpotifyPlaylist playlist, int iTrack, String prevMessageText, int prevErrors, Integer position, boolean background, FrequentMessageEditAgent frequentEditAgent) { - super(m, event, true); + super(m, event, true, position, background); this.playlist = playlist; this.iTrack = iTrack; this.prevMessageText = prevMessageText; @@ -354,15 +400,15 @@ protected void loadSingle(AudioTrack track, AudioPlaylist UNUSED) return; } AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1; + int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()), background, (position == null ? null : position + iTrack))+1; String addMsg = FormatUtil.filter(bot.getSuccess(event)+" Added **"+track.getInfo().title - +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos)); + +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos<=0?"to begin playing"+(background ? " in the background" : ""):" to the "+(background ? "background " : "")+"queue at position "+pos)); if(playlist.tracks.length == 1 || !event.getSelfMember().hasPermission(event.getTextChannel(), Permission.MESSAGE_ADD_REACTION)) frequentEditAgent.queueEditMessage(addMsg); else { new ButtonMenu.Builder() - .setText(addMsg+"\n"+bot.getWarning(event)+" [**"+playlist.name+"**] has **"+playlist.tracks.length+"** tracks attached. Select "+LOAD+" to add the rest to the queue.") + .setText(addMsg+"\n"+bot.getWarning(event)+" [**"+playlist.name+"**] has **"+playlist.tracks.length+"** tracks attached. Select "+LOAD+" to add the rest to the "+(background ? "background " : "")+" queue.") .setChoices(LOAD, CANCEL) .setEventWaiter(bot.getWaiter()) .setTimeout(30, TimeUnit.SECONDS) @@ -371,7 +417,7 @@ protected void loadSingle(AudioTrack track, AudioPlaylist UNUSED) if(re.getName().equals(LOAD)) { String newMessageText = addMsg+"\n"+bot.getSuccess(event)+" Loading **1/"+(playlist.tracks.length - 1)+"** additional tracks..."; frequentEditAgent.queueEditMessage(newMessageText); - bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[1]), new SpotifyResultHandler(m,event,playlist,1,newMessageText,prevErrors, frequentEditAgent)); + bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[1]), new SpotifyResultHandler(m,event,playlist,1,newMessageText,prevErrors,position,background,frequentEditAgent)); } else frequentEditAgent.queueEditMessage(addMsg); @@ -399,7 +445,7 @@ else if(bot.getConfig().isTooLong(track)) frequentEditAgent.queueEditMessage(newMessageText); } else { AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - handler.addTrack(new QueuedTrack(track, event.getAuthor())); + handler.addTrack(new QueuedTrack(track, event.getAuthor()), background, (position == null ? null : position + iTrack)); } String[] messageParts = newMessageText.split("\n"); @@ -413,7 +459,7 @@ else if(bot.getConfig().isTooLong(track)) // update second line ("loading tracks...") newMessageText = firstLine+"\n"+(bot.getSuccess(event)+" Loading **" + (iTrack + 1) + "/"+(playlist.tracks.length - 1)+"** additional tracks...")+(remainingParts.length == 0 ? "" : "\n"+String.join("\n", remainingParts)); frequentEditAgent.queueEditMessage(newMessageText); - bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[iTrack + 1]), new SpotifyResultHandler(m,event,playlist,iTrack + 1,newMessageText,newErrors,frequentEditAgent)); + bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[iTrack + 1]), new SpotifyResultHandler(m,event,playlist,iTrack + 1,newMessageText,newErrors,position,background,frequentEditAgent)); } else { // all done! finalize message // remove the second line ("loading tracks...") but keep everything else @@ -455,7 +501,7 @@ public void noMatches() if(ytsearch) loadSingle(null, null); else - bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[iTrack]), new SpotifyResultHandler(m,event,playlist,iTrack + 1,prevMessageText,prevErrors,frequentEditAgent)); + bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+getYoutubeQueryOfTrack(playlist.tracks[iTrack]), new SpotifyResultHandler(m,event,playlist,iTrack + 1,prevMessageText,prevErrors,position,background,frequentEditAgent)); } @Override @@ -466,22 +512,177 @@ public void loadFailed(FriendlyException throwable) } } + private String getPlayHelpMessage(Integer position, Boolean background, Boolean localPlaylist) { + StringBuilder builder = new StringBuilder(); + builder.append("plays the provided"); + + if (localPlaylist != null && localPlaylist) { + builder.append(" playlist"); + } else { + builder.append(" song"); + } + + if (position == null) {} + else if (position == -1) { + builder.append(" immediately, stopping the current song"); + } else if (position == 0) { + builder.append(" or adds it to the front of the queue"); + } else { + builder.append(" or adds it to the queue at position " + (position + 1)); + } + + if (background == null || !background) {} + else { + builder.append(", and the"); + if (localPlaylist != null && localPlaylist) { + builder.append(" playlist"); + } else { + builder.append(" song"); + } + builder.append(" pauses when non-background songs are queued"); + } + + return builder.toString(); + } + + private String getPlayArgsSyntax(Integer position, Boolean background, Boolean localPlaylist) { + StringBuilder builder = new StringBuilder(); + boolean firstBlood = true; + if (position == null) { + if (!firstBlood) builder.append(" "); + builder.append("['next'|'now']"); + firstBlood = false; + } + if (background == null) { + if (!firstBlood) builder.append(" "); + builder.append("['background']"); + firstBlood = false; + } + if (!firstBlood) builder.append(" "); + if (localPlaylist == null) { + builder.append(">"); + } else if (localPlaylist) { + builder.append(""); + } else { + builder.append(""); + } + return builder.toString(); + } + + private Command[] getPlayChildren(Integer position, Boolean background, Boolean localPlaylist) { + ArrayList children = new ArrayList(); + if (position == null) { + children.add(new NextCmd(bot, background, localPlaylist)); + children.add(new NowCmd(bot, background, localPlaylist)); + } + if (background == null) children.add(new BackgroundCmd(bot, position, localPlaylist)); + if (localPlaylist == null) children.add(new PlaylistCmd(bot, position, background)); + return children.toArray(new Command[0]); + } + + public class NextCmd extends DJCommand + { + private static final int position = 0; + private final Boolean background; + private final Boolean localPlaylist; + + public NextCmd(Bot bot, Boolean background, Boolean localPlaylist) + { + super(bot); + this.background = background; + this.localPlaylist = localPlaylist; + this.name = "next"; + this.arguments = getPlayArgsSyntax(position, background, localPlaylist); + this.help = getPlayHelpMessage(position, background, localPlaylist); + this.beListening = true; + this.bePlaying = false; + this.children = getPlayChildren(position, background, localPlaylist); + } + + @Override + public void doCommand(CommandEvent event) + { + doPlay(bot, event, position, background, localPlaylist); + } + } + + public class NowCmd extends DJCommand + { + private static final int position = -1; + private final Boolean background; + private final Boolean localPlaylist; + + public NowCmd(Bot bot, Boolean background, Boolean localPlaylist) + { + super(bot); + this.background = background; + this.localPlaylist = localPlaylist; + this.name = "now"; + this.arguments = getPlayArgsSyntax(position, background, localPlaylist); + this.help = getPlayHelpMessage(position, background, localPlaylist); + this.beListening = true; + this.bePlaying = false; + this.children = getPlayChildren(position, background, localPlaylist); + } + + @Override + public void doCommand(CommandEvent event) + { + doPlay(bot, event, position, background, localPlaylist); + } + } + + public class BackgroundCmd extends MusicCommand + { + private final Integer position; + private static final boolean background = true; + private final Boolean localPlaylist; + + public BackgroundCmd(Bot bot, Integer position, Boolean localPlaylist) + { + super(bot); + this.position = position; + this.localPlaylist = localPlaylist; + this.name = "background"; + this.aliases = new String[]{"bg", "b", "back"}; + this.arguments = getPlayArgsSyntax(position, background, localPlaylist); + this.help = getPlayHelpMessage(position, background, localPlaylist); + this.beListening = true; + this.bePlaying = false; + this.children = getPlayChildren(position, background, localPlaylist); + } + + @Override + public void doCommand(CommandEvent event) + { + doPlay(bot, event, position, background, localPlaylist); + } + } + public class PlaylistCmd extends MusicCommand { - public PlaylistCmd(Bot bot) + private final Integer position; + private final Boolean background; + private static final boolean localPlaylist = true; + + public PlaylistCmd(Bot bot, Integer position, Boolean background) { super(bot); + this.position = position; + this.background = background; this.name = "playlist"; this.aliases = new String[]{"pl"}; - this.arguments = ""; - this.help = "plays the provided playlist"; + this.arguments = getPlayArgsSyntax(position, background, localPlaylist); + this.help = getPlayHelpMessage(position, background, localPlaylist); this.beListening = true; this.bePlaying = false; + this.children = getPlayChildren(position, background, localPlaylist); } @Override public void doCommand(CommandEvent event) { + boolean background = this.background == null ? false : this.background; if(event.getArgs().isEmpty()) { event.reply(bot.getError(event)+" Please include a playlist name."); @@ -496,10 +697,10 @@ public void doCommand(CommandEvent event) event.getChannel().sendMessage(bot.getLoading(event)+" Loading playlist **"+event.getArgs()+"**... ("+playlist.getItems().size()+" items)").queue(m -> { AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - playlist.loadTracks(bot.getPlayerManager(), (at)->handler.addTrack(new QueuedTrack(at, event.getAuthor())), () -> { + playlist.loadTracks(bot.getPlayerManager(), (at, i)->handler.addTrack(new QueuedTrack(at, event.getAuthor()), background, position == null ? null : position + i), () -> { StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty() ? bot.getWarning(event)+" No tracks were loaded!" - : bot.getSuccess(event)+" Loaded **"+playlist.getTracks().size()+"** tracks!"); + : bot.getSuccess(event)+" Loaded **"+playlist.getTracks().size()+"** tracks into the "+(position != null ? "front of the " : "")+(background ? "background " : "")+"queue!"); if(!playlist.getErrors().isEmpty()) builder.append("\nThe following tracks failed to load:"); playlist.getErrors().forEach(err -> builder.append("\n`[").append(err.getIndex()+1).append("]` **").append(err.getItem()).append("**: ").append(err.getReason())); diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java index fc0333be4..bfb0bb461 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java @@ -17,6 +17,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; + +import com.jagrosh.jdautilities.command.Command; import com.jagrosh.jdautilities.command.CommandEvent; import com.jagrosh.jdautilities.menu.Paginator; import com.jagrosh.jmusicbot.Bot; @@ -27,6 +29,8 @@ import com.jagrosh.jmusicbot.settings.RepeatMode; import com.jagrosh.jmusicbot.settings.Settings; import com.jagrosh.jmusicbot.utils.FormatUtil; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; + import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Message; @@ -44,7 +48,7 @@ public QueueCmd(Bot bot) { super(bot); this.name = "queue"; - this.help = "shows the current queue"; + this.help = "shows the current queues"; this.arguments = "[pagenum]"; this.aliases = bot.getConfig().getAliases(this.name); this.bePlaying = true; @@ -52,7 +56,6 @@ public QueueCmd(Bot bot) builder = new Paginator.Builder() .setColumns(1) .setFinalAction(m -> {try{m.clearReactions().queue();}catch(PermissionException ignore){}}) - .setItemsPerPage(10) .waitOnSinglePage(false) .useNumberedItems(true) .showPageNumbers(true) @@ -64,6 +67,19 @@ public QueueCmd(Bot bot) @Override public void doCommand(CommandEvent event) { + // TEMPORARY -- can only show one queue right now, because JDA-Utilities paginator has bug preventing multiple paginators + AudioHandler ah = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); + if (ah.playingFromBackgroundQueue() || (ah.getQueue().isEmpty() && !ah.getBackgroundQueue().isEmpty())) { + showQueue(event, true, true); + } else { + showQueue(event, false, true); + if (!ah.getBackgroundQueue().isEmpty() || ah.getQueue().isEmpty()) { + showQueue(event, true, false); + } + } + } + + private void showQueue(CommandEvent event, boolean backgroundQueue, boolean paginate) { int pagenum = 1; try { @@ -71,13 +87,14 @@ public void doCommand(CommandEvent event) } catch(NumberFormatException ignore){} AudioHandler ah = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - List list = ah.getQueue().getList(); - if(list.isEmpty()) + List list = (backgroundQueue) ? ah.getBackgroundQueue().getList() : ah.getQueue().getList(); + String queueName = (backgroundQueue) ? "the background queue" : "the queue"; + if(list.isEmpty() && (backgroundQueue == ah.playingFromBackgroundQueue())) { Message nowp = ah.getNowPlaying(event.getJDA()); Message nonowp = ah.getNoMusicPlaying(event.getJDA()); Message built = new MessageBuilder() - .setContent(bot.getWarning(event) + " There is no music in the queue!") + .setContent(bot.getWarning(event) + " There is no music in "+queueName+"!") .setEmbeds((nowp==null ? nonowp : nowp).getEmbeds().get(0)).build(); event.reply(built, m -> { @@ -95,23 +112,30 @@ public void doCommand(CommandEvent event) } Settings settings = event.getClient().getSettingsFor(event.getGuild()); long fintotal = total; - builder.setText((i1,i2) -> getQueueTitle(ah, bot.getSuccess(event), songs.length, fintotal, settings.getRepeatMode())) + if (paginate) { + builder + .setItemsPerPage((ah.getQueue().isEmpty() || ah.getBackgroundQueue().isEmpty()) ? 10 : backgroundQueue ? 2 : 6) + .setText((i1,i2) -> getQueueTitle(ah, backgroundQueue, bot.getSuccess(event), songs.length, fintotal, settings.getRepeatMode())) .setItems(songs) .setUsers(event.getAuthor()) .setColor(event.getSelfMember().getColor()) ; - builder.build().paginate(event.getChannel(), pagenum); + builder.build().paginate(event.getChannel(), pagenum); + } else { + event.reply(getQueueTitle(ah, backgroundQueue, bot.getSuccess(event), songs.length, fintotal, settings.getRepeatMode()) + +"\n(cannot show multiple queues yet -- coming soon!)"); + } } - private String getQueueTitle(AudioHandler ah, String success, int songslength, long total, RepeatMode repeatmode) + private String getQueueTitle(AudioHandler ah, boolean backgroundQueue, String success, int songslength, long total, RepeatMode repeatmode) { StringBuilder sb = new StringBuilder(); - if(ah.getPlayer().getPlayingTrack()!=null) + if(ah.getPlayer().getPlayingTrack()!=null && (backgroundQueue == ah.playingFromBackgroundQueue())) { sb.append(ah.getStatusEmoji()).append(" **") .append(ah.getPlayer().getPlayingTrack().getInfo().title).append("**\n"); } - return FormatUtil.filter(sb.append(success).append(" Current Queue | ").append(songslength) + return FormatUtil.filter(sb.append(success).append(" Current "+(backgroundQueue ? "Background Queue" : "Queue")+" | ").append(songslength) .append(" entries | `").append(FormatUtil.formatTime(total)).append("` ") .append(repeatmode.getEmoji() != null ? "| "+repeatmode.getEmoji() : "").toString()); } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/RemoveCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/RemoveCmd.java index a5f96a931..ed387196b 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/music/RemoveCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/RemoveCmd.java @@ -15,11 +15,13 @@ */ package com.jagrosh.jmusicbot.commands.music; +import com.jagrosh.jdautilities.command.Command; import com.jagrosh.jdautilities.command.CommandEvent; import com.jagrosh.jmusicbot.Bot; import com.jagrosh.jmusicbot.audio.AudioHandler; import com.jagrosh.jmusicbot.audio.QueuedTrack; import com.jagrosh.jmusicbot.commands.MusicCommand; +import com.jagrosh.jmusicbot.queue.FairQueue; import com.jagrosh.jmusicbot.settings.Settings; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.User; @@ -35,28 +37,35 @@ public RemoveCmd(Bot bot) super(bot); this.name = "remove"; this.help = "removes a song from the queue"; - this.arguments = ""; + this.arguments = "[background] "; this.aliases = bot.getConfig().getAliases(this.name); this.beListening = true; this.bePlaying = true; + this.children = new Command[]{new BackgroundCmd(bot)}; } @Override public void doCommand(CommandEvent event) { + doRemove(bot, event, false); + } + + private static void doRemove(Bot bot, CommandEvent event, boolean background) { AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); if(handler.getQueue().isEmpty()) { event.reply(bot.getError(event)+"There is nothing in the queue!"); return; } + FairQueue queue = (background) ? handler.getBackgroundQueue() : handler.getQueue(); + String queueName = (background) ? "the background queue" : "the queue"; if(event.getArgs().equalsIgnoreCase("all")) { - int count = handler.getQueue().removeAll(event.getAuthor().getIdLong()); + int count = queue.removeAll(event.getAuthor().getIdLong()); if(count==0) - event.reply(bot.getWarning(event)+"You don't have any songs in the queue!"); + event.reply(bot.getWarning(event)+"You don't have any songs in "+queueName+"!"); else - event.reply(bot.getSuccess(event)+"Successfully removed your "+count+" entries."); + event.reply(bot.getSuccess(event)+"Successfully removed your "+count+" entries from "+queueName+"."); return; } int pos; @@ -65,24 +74,24 @@ public void doCommand(CommandEvent event) } catch(NumberFormatException e) { pos = 0; } - if(pos<1 || pos>handler.getQueue().size()) + if(pos<1 || pos>queue.size()) { - event.reply(bot.getError(event)+"Position must be a valid integer between 1 and "+handler.getQueue().size()+"!"); + event.reply(bot.getError(event)+"Position must be a valid integer between 1 and "+queue.size()+"!"); return; } Settings settings = event.getClient().getSettingsFor(event.getGuild()); boolean isDJ = event.getMember().hasPermission(Permission.MANAGE_SERVER); if(!isDJ) isDJ = event.getMember().getRoles().contains(settings.getRole(event.getGuild())); - QueuedTrack qt = handler.getQueue().get(pos-1); + QueuedTrack qt = queue.get(pos-1); if(qt.getIdentifier()==event.getAuthor().getIdLong()) { - handler.getQueue().remove(pos-1); - event.reply(bot.getSuccess(event)+"Removed **"+qt.getTrack().getInfo().title+"** from the queue"); + queue.remove(pos-1); + event.reply(bot.getSuccess(event)+"Removed **"+qt.getTrack().getInfo().title+"** from "+queueName); } else if(isDJ) { - handler.getQueue().remove(pos-1); + queue.remove(pos-1); User u; try { u = event.getJDA().getUserById(qt.getIdentifier()); @@ -90,11 +99,32 @@ else if(isDJ) u = null; } event.reply(bot.getSuccess(event)+"Removed **"+qt.getTrack().getInfo().title - +"** from the queue (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")"); + +"** from "+queueName+" (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")"); } else { event.reply(bot.getError(event)+"You cannot remove **"+qt.getTrack().getInfo().title+"** because you didn't add it!"); } } + + public class BackgroundCmd extends MusicCommand + { + private static final boolean background = true; + + public BackgroundCmd(Bot bot) + { + super(bot); + this.name = "background"; + this.aliases = new String[]{"bg", "b", "back"}; + this.help = "removes a song from the background queue"; + this.beListening = true; + this.bePlaying = true; + } + + @Override + public void doCommand(CommandEvent event) + { + doRemove(bot, event, background); + } + } } diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java index d6fe0b328..ad696febe 100644 --- a/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java +++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java @@ -90,7 +90,7 @@ public void trackLoaded(AudioTrack track) return; } AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1; + int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()), false)+1; m.editMessage(FormatUtil.filter(bot.getSuccess(event)+" Added **"+track.getInfo().title +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing" : " to the queue at position "+pos))).queue(); @@ -112,7 +112,7 @@ public void playlistLoaded(AudioPlaylist playlist) return; } AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); - int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1; + int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()), false)+1; event.reply(bot.getSuccess(event)+"Added **" + FormatUtil.filter(track.getInfo().title) + "** (`" + FormatUtil.formatTime(track.getDuration()) + "`) " + (pos==0 ? "to begin playing" : " to the queue at position "+pos)); diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/SeekCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/SeekCmd.java new file mode 100644 index 000000000..7225f99ec --- /dev/null +++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/SeekCmd.java @@ -0,0 +1,359 @@ +package com.jagrosh.jmusicbot.commands.music; + +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; + +import com.jagrosh.jdautilities.command.Command; +import com.jagrosh.jdautilities.command.CommandEvent; +import com.jagrosh.jmusicbot.Bot; +import com.jagrosh.jmusicbot.audio.AudioHandler; +import com.jagrosh.jmusicbot.commands.MusicCommand; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SeekCmd extends MusicCommand { + + private final Bot bot; + + public SeekCmd(Bot bot) + { + super(bot); + this.bot = bot; + this.name = "seek"; + this.arguments = "() | (