From fdbd8937f5cd6c1adfb139a6a7ab24ddd0d321dd Mon Sep 17 00:00:00 2001 From: Nekojimi Date: Wed, 8 Nov 2023 20:06:43 +0000 Subject: [PATCH] Change pipeline to use pull architecture. --- .../java/moe/nekojimi/chords/Downloader.java | 175 +++++++++------ .../moe/nekojimi/chords/MusicHandler.java | 43 ++-- .../moe/nekojimi/chords/QueueManager.java | 118 ++++++---- .../java/moe/nekojimi/chords/QueueThing.java | 208 ++++++++++++++++++ src/main/java/moe/nekojimi/chords/Track.java | 18 +- .../moe/nekojimi/chords/TrackRequest.java | 57 ++++- .../nekojimi/chords/commands/HelpCommand.java | 11 +- .../chords/commands/QueueCommand.java | 24 +- .../nekojimi/chords/commands/SkipCommand.java | 2 +- 9 files changed, 529 insertions(+), 127 deletions(-) create mode 100644 src/main/java/moe/nekojimi/chords/QueueThing.java diff --git a/src/main/java/moe/nekojimi/chords/Downloader.java b/src/main/java/moe/nekojimi/chords/Downloader.java index dc3fb3a..b531fb5 100644 --- a/src/main/java/moe/nekojimi/chords/Downloader.java +++ b/src/main/java/moe/nekojimi/chords/Downloader.java @@ -14,7 +14,6 @@ import java.nio.file.Files; import java.util.*; import java.util.concurrent.*; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -24,14 +23,13 @@ import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; -import moe.nekojimi.chords.Downloader.DownloadTask; import net.dv8tion.jda.api.audio.AudioSendHandler; /** * * @author jimj316 */ -public class Downloader implements Consumer +public class Downloader extends QueueThing { private static final int DOWNLOAD_TIMEOUT = 300; @@ -45,7 +43,16 @@ public class Downloader implements Consumer private static final Pattern ETA_PATTERN = Pattern.compile("\\[download\\].*?ETA\\s+(\\d{1,2}:\\d{2})"); private static final Pattern DOWNLOAD_ITEM_PATTERN = Pattern.compile("\\[download\\] Downloading item (\\d+) of (\\d+)"); - private final List downloadQueue = new LinkedList<>(); + public static final String[] INFO_TITLE_KEYS = + { + "track", "title", "fulltitle" + }; + public static final String[] INFO_ARTIST_KEYS = + { + "artist", "channel", "uploader" + }; + + private final List downloadingTracks = new LinkedList<>(); private final LinkedBlockingDeque workQueue = new LinkedBlockingDeque<>(); private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue); private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1); @@ -58,34 +65,45 @@ public class Downloader implements Consumer public Downloader() { - + super(new LinkedList<>()); } +// +// @Override +// public void accept(TrackRequest request) +// { + // if all tracks of the request are already downloaded, just skip +// if (!request.getTracks().isEmpty() && request.getTracks().stream().allMatch((t) -> t.isDownloaded())) +// { +// for (Track track : request.getTracks()) +// getDestination().accept(track); +// return; +// } + +// inputQueue.add(task); + +// } @Override - public void accept(DownloadTask task) + protected boolean completePromise(Promise promise) { - // if all tracks of the request are already downloaded, just skip - if (!task.request.getTracks().isEmpty() && task.request.getTracks().stream().allMatch((t) -> t.isDownloaded())) - { - for (Track track : task.request.getTracks()) - task.getDestination().accept(track); - return; - } + TrackRequest request = promise.getInput(); - downloadQueue.add(task); - getInfo(task.request); - // TODO: get info should also use the thread pool executor.submit(() -> { + downloadingTracks.add(request); try { -// getFormats(track); - download(task); + + getInfo(request); + download(promise); } catch (Exception ex) { ex.printStackTrace(); } + downloadingTracks.remove(request); }); + + return true; } private List sortFormats(Collection input) @@ -217,7 +235,7 @@ public class Downloader implements Consumer Scanner sc = new Scanner(input); while (sc.hasNextLine()) { - Track track = new Track(request.getUrl()); + Track track = new Track(request); track.setNumber(trackNumber); trackNumber++; request.addTrack(track); @@ -225,10 +243,40 @@ public class Downloader implements Consumer String line = sc.nextLine(); JsonReader reader = Json.createReader(new StringReader(line)); JsonObject object = reader.readObject(); + + // look for metadata if (track.getTitle() == null) - track.setTitle(object.getString("title", null)); + { + for (String key : INFO_TITLE_KEYS) + { + if (object.containsKey(key) && !object.getString(key).isBlank()) + { + track.setTitle(object.getString(key)); + break; + } + } + } if (track.getArtist() == null) - track.setArtist(object.getString("uploader", null)); + { + for (String key : INFO_ARTIST_KEYS) + { + if (object.containsKey(key) && !object.getString(key).isBlank()) + { + track.setArtist(object.getString(key)); + break; + } + } + } + if (track.getTitle().contains("-")) + { + String[] split = track.getTitle().split("-", 2); + track.setArtist(split[0]); + track.setTitle(split[1]); + } + if (track.getArtist().contains(" - Topic")) + { + track.setArtist(track.getArtist().replace(" - Topic", "")); + } JsonArray formatsJSON = object.getJsonArray("formats"); if (formatsJSON != null) @@ -264,7 +312,7 @@ public class Downloader implements Consumer // if there's less tracks in the request than expected, fill the array while (idx >= request.getTracks().size()) { - Track track = new Track(request.getUrl()); + Track track = new Track(request); track.setNumber(trackNumber); trackNumber++; request.addTrack(track); @@ -272,10 +320,12 @@ public class Downloader implements Consumer return request.getTracks().get(idx); } - private void download(DownloadTask task) + private void download(Promise promise) throws InterruptedException, ExecutionException { + TrackRequest request = promise.getInput(); Set uniqueFormats = new HashSet<>(); - for (Track track : task.request.getTracks()) + + for (Track track : request.getTracks()) { uniqueFormats.addAll(track.getFormats()); } @@ -290,7 +340,7 @@ public class Downloader implements Consumer try { - messageHandler.accept(task.request, null); + messageHandler.accept(request, null); String cmd = Chords.getSettings().getYtdlCommand() + " -x" + " -f " + formatCodes + "worstaudio/bestaudio/worst/best" @@ -298,7 +348,7 @@ public class Downloader implements Consumer + " --no-playlist" + " --extractor-args youtube:player_client=android" + " -o " + getDownloadDir().getAbsolutePath() + "/%(title)s.%(ext)s " - + task.request.getUrl().toString(); + + request.getUrl().toString(); Process exec = runCommand(cmd, DOWNLOAD_TIMEOUT); InputStream in = exec.getInputStream(); @@ -321,47 +371,40 @@ public class Downloader implements Consumer Matcher progMatcher = PROGRESS_PATTERN.matcher(line); if (progMatcher.find()) { - getTrackFromRequest(task.request, downloadIdx).setProgress(Double.parseDouble(progMatcher.group(1))); - messageHandler.accept(task.request, null); + getTrackFromRequest(request, downloadIdx).setProgress(Double.parseDouble(progMatcher.group(1))); + messageHandler.accept(request, null); } Matcher destMatcher = DESTINATION_PATTERN.matcher(line); if (destMatcher.find()) { - Track track = getTrackFromRequest(task.request, downloadIdx); + Track track = getTrackFromRequest(request, downloadIdx); track.setLocation(new File(destMatcher.group(1))); // this is currently our criteria for completion; submit the track and move on - if (task.getDestination() != null) - task.getDestination().accept(track); + promise.complete(track); track.setProgress(100.0); - messageHandler.accept(task.request, null); + messageHandler.accept(request, null); downloadIdx++; } } - // String output = new String(in.readAllBytes(), Charset.defaultCharset()); String error = new String(exec.getErrorStream().readAllBytes(), Charset.defaultCharset()); - // System.out.println(output); if (exec.exitValue() != 0) throw new RuntimeException("youtube-dl failed with error " + exec.exitValue() + ", output:\n" + error); -// task.request.setProgress(100); - // return true; - - downloadQueue.remove(task); - messageHandler.accept(task.request, null); + messageHandler.accept(request, null); } catch (Exception ex) { Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex); if (messageHandler != null) - messageHandler.accept(task.request, ex); - downloadQueue.remove(task); + messageHandler.accept(request, ex); + //downloadQueue.remove(task); } } @@ -397,33 +440,39 @@ public class Downloader implements Consumer this.messageHandler = messageHandler; } - public List getDownloadQueue() + public List getDownloadQueue() { - return downloadQueue; + return new ArrayList<>(inputQueue); } - public static class DownloadTask - { - - private final TrackRequest request; - private final Consumer destination; - - public DownloadTask(TrackRequest request, Consumer destination) - { - this.request = request; - this.destination = destination; - } - - public TrackRequest getTrack() - { - return request; - } - public Consumer getDestination() - { - return destination; - } +// public static class DownloadTask +// { +// +// private final TrackRequest request; +// private final Consumer destination; +// +// public DownloadTask(TrackRequest request, Consumer destination) +// { +// this.request = request; +// this.destination = destination; +// } +// +// public TrackRequest getTrack() +// { +// return request; +// } +// +// public Consumer getDestination() +// { +// return destination; +// } +// +// } + public List getDownloadingTracks() + { + return downloadingTracks; } } diff --git a/src/main/java/moe/nekojimi/chords/MusicHandler.java b/src/main/java/moe/nekojimi/chords/MusicHandler.java index 56efc07..908fab3 100644 --- a/src/main/java/moe/nekojimi/chords/MusicHandler.java +++ b/src/main/java/moe/nekojimi/chords/MusicHandler.java @@ -19,9 +19,10 @@ import net.dv8tion.jda.api.audio.AudioSendHandler; * * @author jimj316 */ -public class MusicHandler implements AudioSendHandler, Closeable +public class MusicHandler implements AudioSendHandler, Closeable, Consumer { +// private QueueThing queueManager; private QueueManager queueManager; // private final LinkedList trackQueue = new LinkedList<>(); // private final Queue queue = new ConcurrentLinkedQueue<>(); @@ -59,28 +60,29 @@ public class MusicHandler implements AudioSendHandler, Closeable } } - void setQueueManager(QueueManager manager) + @Override + public void accept(Track t) { - queueManager = manager; + play(t); } - public void playNext() + void setQueueManager(QueueManager manager) { - nextTrack(true); + queueManager = manager; } + public void playOver(Track track) { } - - private boolean nextTrack() + public boolean play(Track track) { - return nextTrack(false); + return play(track, false); } - public boolean nextTrack(boolean immediate) + public boolean play(Track track, boolean immediate) { if (immediate) { @@ -96,12 +98,11 @@ public class MusicHandler implements AudioSendHandler, Closeable currentTrack.delete(); currentTrack = null; } - currentTrack = queueManager.nextTrackNeeded(); + currentTrack = track; if (nowPlayingConsumer != null) nowPlayingConsumer.accept(currentTrack); if (currentTrack == null) { - System.out.println("End of queue."); debugOut.flush(); return false; } @@ -121,6 +122,13 @@ public class MusicHandler implements AudioSendHandler, Closeable return false; } + public boolean requestTrack() + { + List> request = queueManager.request(1, this); + // Queuemanager will syncronously attempt to call play() + return !playingTracks.isEmpty(); + } + public boolean isPlaying() { return !playingTracks.isEmpty(); @@ -134,7 +142,7 @@ public class MusicHandler implements AudioSendHandler, Closeable public void setShouldPlay(boolean shouldPlay) { if (!this.shouldPlay && shouldPlay) - nextTrack(); + requestTrack(); this.shouldPlay = shouldPlay; } @@ -181,7 +189,7 @@ public class MusicHandler implements AudioSendHandler, Closeable ret.put(mixBuffers(mixes)); if (outOfInput) { - boolean foundNext = nextTrack(); + boolean foundNext = requestTrack(); if (!foundNext) break; } @@ -244,4 +252,13 @@ public class MusicHandler implements AudioSendHandler, Closeable return ret; } + public boolean skipTrack() + { + if (!isPlaying()) + return false; + playingTracks.clear(); + requestTrack(); + return true; + } + } diff --git a/src/main/java/moe/nekojimi/chords/QueueManager.java b/src/main/java/moe/nekojimi/chords/QueueManager.java index 613a1e6..87fdc72 100644 --- a/src/main/java/moe/nekojimi/chords/QueueManager.java +++ b/src/main/java/moe/nekojimi/chords/QueueManager.java @@ -16,8 +16,7 @@ */ package moe.nekojimi.chords; -import java.util.Comparator; -import java.util.LinkedList; +import java.util.List; import java.util.PriorityQueue; import java.util.Queue; import java.util.function.Consumer; @@ -26,61 +25,96 @@ import java.util.function.Consumer; * * @author jimj316 */ -public class QueueManager implements Consumer +public class QueueManager extends QueueThing { + private final int QUEUE_TARGET_SIZE = 5; + private Track restartingTrack = null; - private final PriorityQueue jukeboxQueue = new PriorityQueue<>(); +// private final PriorityQueue jukeboxQueue = new PriorityQueue<>(); + private QueueThing trackSource; private Playlist playlist; private MusicHandler handler; public QueueManager() { + super(new PriorityQueue()); + queueTargetSize = QUEUE_TARGET_SIZE; // jukeboxQueue = new LinkedList<>(); } @Override - public void accept(Track t) + protected void notifyNewInput() { - jukeboxQueue.add(t); + super.notifyNewInput(); + +// if (inputQueue.size() < QUEUE_TARGET_SIZE) +// demandInput(QUEUE_TARGET_SIZE - inputQueue.size()); if (!handler.isPlaying() || handler.getCurrentTrack() == null) - handler.playNext(); + handler.requestTrack(); + } - if (handler.isPlaying() != (handler.getCurrentTrack() == null)) - System.err.println("WARNING: handler isPlaying violates contract! Something dumb going on!"); + @Override + protected boolean completePromise(Promise request) + { + final Track input = request.getInput(); + if (input != null) + { + request.complete(input); + return true; + } + return false; } - /** - * Called by the music handler when the current track has ended, or if - * playNext is called with nothing playing. - * - * @return the next track to play, or null to stop playing. - */ - public Track nextTrackNeeded() + @Override + public List> request(int count, Consumer destination) { - // if we're restarting the current track: store, clear, and return it if (restartingTrack != null) { - Track ret = restartingTrack; + Promise ret = new Promise<>(restartingTrack, destination); restartingTrack = null; - return ret; - } - // if there's anything in the queue, play that first - if (!jukeboxQueue.isEmpty()) - { - return jukeboxQueue.poll(); + return List.of(ret); } - // otherwise if there's a playlist, shuffle from that - else if (playlist != null) - { - return playlist.getNextTrack(); - } - // otherwise stop playing - else - return null; + + List> ret = super.request(count, destination); + if (inputQueue.size() < QUEUE_TARGET_SIZE) + demandInput(QUEUE_TARGET_SIZE - inputQueue.size()); + return ret; } + /** + * Called by the music handler when the current track has ended, or if + * playNext is called with nothing playing. + * + * @return the next track to play, or null to stop playing. + */ +// public Track nextTrackNeeded() +// { +// Track ret; +// // if we're restarting the current track: store, clear, and return it +// if (restartingTrack != null) +// { +// ret = restartingTrack; +// restartingTrack = null; +// } +// // if there's anything in the queue, play that first +// else if (!jukeboxQueue.isEmpty()) +// { +// ret = jukeboxQueue.poll(); +// } +// // otherwise if there's a playlist, shuffle from that +// else if (playlist != null) +// { +// ret = playlist.getNextTrack(); +// } +// // otherwise stop playing +// else +// ret = null; +// +// return ret; +// } + public MusicHandler getHandler() { return handler; @@ -89,7 +123,7 @@ public class QueueManager implements Consumer public void addTrack(Track track) { System.out.println("Track added to queue: " + track.getLocation().getAbsolutePath()); - jukeboxQueue.add(track); + inputQueue.add(track); } @@ -97,7 +131,7 @@ public class QueueManager implements Consumer { try { - return jukeboxQueue.remove((Track) jukeboxQueue.toArray()[i]); + return inputQueue.remove((Track) inputQueue.toArray()[i]); } catch (ArrayIndexOutOfBoundsException ex) { return false; @@ -106,7 +140,7 @@ public class QueueManager implements Consumer public boolean removeTrack(Track track) { - return jukeboxQueue.remove(track); + return inputQueue.remove(track); } public void setHandler(MusicHandler handler) @@ -117,7 +151,7 @@ public class QueueManager implements Consumer public Queue getJukeboxQueue() { - return jukeboxQueue; + return inputQueue; } public Playlist getPlaylist() @@ -135,9 +169,19 @@ public class QueueManager implements Consumer restartingTrack = handler.getCurrentTrack(); if (restartingTrack != null) { - handler.playNext(); + handler.requestTrack(); return true; } else return false; } + + public QueueThing getTrackSource() + { + return trackSource; + } + + public void setTrackSource(QueueThing trackSource) + { + this.trackSource = trackSource; + } } diff --git a/src/main/java/moe/nekojimi/chords/QueueThing.java b/src/main/java/moe/nekojimi/chords/QueueThing.java new file mode 100644 index 0000000..24f9ca2 --- /dev/null +++ b/src/main/java/moe/nekojimi/chords/QueueThing.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2023 jimj316 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package moe.nekojimi.chords; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.function.Consumer; + +/** + * + * @author jimj316 + */ +public abstract class QueueThing implements Consumer +{ + // TODO: better name + protected final Queue inputQueue; + protected final List> sources = new ArrayList<>(); + protected final List> sinks = new ArrayList<>(); + protected final Queue> pendingPromises = new LinkedList<>(); + + protected int queueTargetSize = 0; + + protected QueueThing(Queue inputQueue) + { + this.inputQueue = inputQueue; + } + + @Override + public void accept(I t) + { + + // if we've got pending promises, fullfill them, otherwise just put it in the queue and notify the sinks + if (pendingPromises.isEmpty()) + { + inputQueue.add(t); + notifyNewInput(); + } else + { + Promise promise = pendingPromises.poll(); + promise.setInput(t); + handlePromise(promise); + } + } + + /** + * Called to notify downstream modules that there's input upstream - + * consider requesting it to pull it through + */ + protected void notifyNewInput() + { + if (inputQueue.size() < queueTargetSize) + demandInput(queueTargetSize - inputQueue.size()); + + for (QueueThing sink : sinks) + { + sink.notifyNewInput(); + } + } + + public List> request(int count, Consumer destination) + { + List> ret = new ArrayList<>(); + int demands = 0; + for (int i = 0; i < count; i++) + { + I input = null; + if (!inputQueue.isEmpty()) + { + input = inputQueue.poll(); + } + + Promise promise = new Promise<>(input, destination); + + boolean ok = handlePromise(promise); + + if (ok) + { + // we got a promise of output so we can tell the client + ret.add(promise); + } else + { + // we need to get more input from sources + demands++; + } + } + + demands += queueTargetSize - inputQueue.size(); + + if (demands > 0) + { + // try to get more input from sources + int sourcePromises = demandInput(demands); + // each promise of input we get represents a promise of output we can give + for (int i = 0; i < sourcePromises; i++) + { + Promise myPromise = new Promise<>(destination); + pendingPromises.add(myPromise); + ret.add(myPromise); + } + } + return ret; + } + + private boolean handlePromise(Promise promise) + { + boolean ok; + ok = completePromise(promise); + return ok; + } + + protected abstract boolean completePromise(Promise request); + + /** + * Requests from sources a certain about of input, to be provided later. + * + * @param count the number of input items to request. + * @return a number Promises of input items. May be more or less than count. + */ + protected int demandInput(int count) + { + int ret = 0; + for (QueueThing source : sources) + { + List> promises = (List>) source.request(count - ret, this); + ret += promises.size(); + if (ret >= count) + break; + } + return ret; + } + + public void addSource(QueueThing source) + { + sources.add(source); + source.addSink(this); + } + + public void removeSource(QueueThing source) + { + sources.remove(source); + source.removeSink(this); + } + + private void addSink(QueueThing sink) + { + sinks.add(sink); + } + + private void removeSink(QueueThing sink) + { + sinks.remove(sink); + } + + public static class Promise + { + + private I input; + private final Consumer output; + + public Promise(I input, Consumer output) + { + this.input = input; + this.output = output; + } + + public Promise(Consumer output) + { + this.output = output; + } + + public void setInput(I input) + { + this.input = input; + } + + public void complete(O out) + { + output.accept(out); + } + + public I getInput() + { + return input; + } + + } +// protected void submit(O output) +// { +// if (nextStage != null) +// nextStage.accept(output); +// } +} diff --git a/src/main/java/moe/nekojimi/chords/Track.java b/src/main/java/moe/nekojimi/chords/Track.java index 46a7944..ae9465f 100644 --- a/src/main/java/moe/nekojimi/chords/Track.java +++ b/src/main/java/moe/nekojimi/chords/Track.java @@ -33,9 +33,18 @@ public class Track implements Comparable private double progress = -1; private double eta = -1; - public Track(URL url) + private final TrackRequest request; + + public Track(URL url, TrackRequest request) { this.url = url; + this.request = request; + } + + public Track(TrackRequest request) + { + this.request = request; + this.url = request.getUrl(); } public YamlMapping toYaml() @@ -59,7 +68,7 @@ public class Track implements Comparable public static Track fromYaml(YamlMapping map) throws MalformedURLException { - Track track = new Track(new URL(map.string("url"))); + Track track = new Track(new URL(map.string("url")), null); track.setArtist(map.string("artist")); track.setTitle(map.string("title")); track.setLocation(new File(map.string("location"))); @@ -144,6 +153,11 @@ public class Track implements Comparable this.kept = kept; } + public TrackRequest getRequest() + { + return request; + } + @Override public String toString() { diff --git a/src/main/java/moe/nekojimi/chords/TrackRequest.java b/src/main/java/moe/nekojimi/chords/TrackRequest.java index 1ec54db..07a4bfb 100644 --- a/src/main/java/moe/nekojimi/chords/TrackRequest.java +++ b/src/main/java/moe/nekojimi/chords/TrackRequest.java @@ -16,9 +16,11 @@ */ package moe.nekojimi.chords; +import com.amihaiemil.eoyaml.YamlMapping; import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import moe.nekojimi.chords.commands.Invocation; import moe.nekojimi.musicsearcher.Result; @@ -26,7 +28,7 @@ import moe.nekojimi.musicsearcher.Result; * * @author jimj316 */ -public class TrackRequest +public class TrackRequest implements Comparable { private Invocation invocation; @@ -42,6 +44,19 @@ public class TrackRequest private String requestedBy; private String requestedIn; + private double priority = 1.0; + + public YamlMapping toYAML() + { + throw new UnsupportedOperationException("Not supported yet."); + } + + public static TrackRequest fromYAML(YamlMapping yaml) + { + TrackRequest ret = new TrackRequest(); + + return ret; + } public List getSearchResults() { @@ -159,4 +174,44 @@ public class TrackRequest return (trackName + " " + requestName).trim(); } + @Override + public int hashCode() + { + int hash = 7; + hash = 83 * hash + Objects.hashCode(this.url); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final TrackRequest other = (TrackRequest) obj; + if (!Objects.equals(this.url, other.url)) + return false; + return true; + } + + + @Override + public int compareTo(TrackRequest o) + { + return -Double.compare(priority, o.priority); // backwards so higher priority comes first + } + + public double getPriority() + { + return priority; + } + + public void setPriority(double priority) + { + this.priority = priority; + } + } diff --git a/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java b/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java index bec4fc9..68543ca 100644 --- a/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java +++ b/src/main/java/moe/nekojimi/chords/commands/HelpCommand.java @@ -16,6 +16,8 @@ */ package moe.nekojimi.chords.commands; +import java.util.Map; +import java.util.Map.Entry; import moe.nekojimi.chords.Chords; public class HelpCommand extends Command @@ -38,10 +40,11 @@ public class HelpCommand extends Command + "!remove - Remove the track at position from the queue.\n" + "!skip - Skip the current track and play the next one.\n" + "!restart - Try playing the current track again in case it goes wrong.\n"; -// for (String key: commands.keySet()) -// { -// help += "!" + key + ":" -// } + final Map commands = bot.getCommands(); + for (Entry e : commands.entrySet()) + { + help += "!" + e.getKey() + " ".repeat(10 - e.getKey().length()) + "- " + e.getValue().synopsis(); + } invocation.respond(help); } diff --git a/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java b/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java index 911100e..e5b177d 100644 --- a/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java +++ b/src/main/java/moe/nekojimi/chords/commands/QueueCommand.java @@ -21,6 +21,7 @@ import java.util.Queue; import moe.nekojimi.chords.Downloader; import moe.nekojimi.chords.Chords; import moe.nekojimi.chords.Track; +import moe.nekojimi.chords.TrackRequest; public class QueueCommand extends Command { @@ -48,23 +49,34 @@ public class QueueCommand extends Command message += "__Ready to play:__\n"; for (Track track : trackQueue) { - message += ":bread: **" + (i) + ":** " + track + "\n"; + message += ":bread: **" + i + ":** " + track + "\n"; i++; } } - final List downloadQueue = bot.getDownloader().getDownloadQueue(); - if (!downloadQueue.isEmpty()) + final List downloading = bot.getDownloader().getDownloadingTracks(); + if (!downloading.isEmpty()) { message += "__Downloading:__\n"; - for (Downloader.DownloadTask task : downloadQueue) + for (TrackRequest request : downloading) + { + message += ":satellite: **" + (i) + ":** " + request + "\n"; + i++; + } + } + + final List downloadQueue = bot.getDownloader().getDownloadQueue(); + if (!downloadQueue.isEmpty()) + { + message += "__In queue for download:__\n"; + for (TrackRequest request : downloadQueue) { - message += ":inbox_tray: **" + (i) + ":** " + task.getTrack() + "\n"; + message += ":inbox_tray: **" + (i) + ":** " + request + "\n"; i++; } } - if (downloadQueue.isEmpty() && trackQueue.isEmpty()) + if (downloading.isEmpty() && trackQueue.isEmpty() && downloadQueue.isEmpty()) message += ":mailbox_with_no_mail: The track queue is empty."; // :inbox_tray: invocation.respond(message); diff --git a/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java b/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java index 835d8fb..f380a9e 100644 --- a/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java +++ b/src/main/java/moe/nekojimi/chords/commands/SkipCommand.java @@ -29,7 +29,7 @@ public class SkipCommand extends Command @Override public void call(Invocation invocation) { - boolean ok = bot.getMusicHandler().nextTrack(true); + boolean ok = bot.getMusicHandler().skipTrack(); if (ok) invocation.respond("Skipped to next track!"); else