Change pipeline to use pull architecture.

master
Nekojimi 6 months ago
parent b41f5c97a3
commit fdbd8937f5
  1. 175
      src/main/java/moe/nekojimi/chords/Downloader.java
  2. 43
      src/main/java/moe/nekojimi/chords/MusicHandler.java
  3. 118
      src/main/java/moe/nekojimi/chords/QueueManager.java
  4. 208
      src/main/java/moe/nekojimi/chords/QueueThing.java
  5. 18
      src/main/java/moe/nekojimi/chords/Track.java
  6. 57
      src/main/java/moe/nekojimi/chords/TrackRequest.java
  7. 11
      src/main/java/moe/nekojimi/chords/commands/HelpCommand.java
  8. 24
      src/main/java/moe/nekojimi/chords/commands/QueueCommand.java
  9. 2
      src/main/java/moe/nekojimi/chords/commands/SkipCommand.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<DownloadTask>
public class Downloader extends QueueThing<TrackRequest, Track>
{
private static final int DOWNLOAD_TIMEOUT = 300;
@ -45,7 +43,16 @@ public class Downloader implements Consumer<DownloadTask>
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<DownloadTask> 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<TrackRequest> downloadingTracks = new LinkedList<>();
private final LinkedBlockingDeque<Runnable> 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<DownloadTask>
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<TrackRequest, Track> 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<Format> sortFormats(Collection<Format> input)
@ -217,7 +235,7 @@ public class Downloader implements Consumer<DownloadTask>
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<DownloadTask>
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<DownloadTask>
// 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<DownloadTask>
return request.getTracks().get(idx);
}
private void download(DownloadTask task)
private void download(Promise<TrackRequest, Track> promise) throws InterruptedException, ExecutionException
{
TrackRequest request = promise.getInput();
Set<Format> 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<DownloadTask>
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<DownloadTask>
+ " --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<DownloadTask>
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<DownloadTask>
this.messageHandler = messageHandler;
}
public List<DownloadTask> getDownloadQueue()
public List<TrackRequest> getDownloadQueue()
{
return downloadQueue;
return new ArrayList<>(inputQueue);
}
public static class DownloadTask
{
private final TrackRequest request;
private final Consumer<Track> destination;
public DownloadTask(TrackRequest request, Consumer<Track> destination)
{
this.request = request;
this.destination = destination;
}
public TrackRequest getTrack()
{
return request;
}
public Consumer<Track> getDestination()
{
return destination;
}
// public static class DownloadTask
// {
//
// private final TrackRequest request;
// private final Consumer<Track> destination;
//
// public DownloadTask(TrackRequest request, Consumer<Track> destination)
// {
// this.request = request;
// this.destination = destination;
// }
//
// public TrackRequest getTrack()
// {
// return request;
// }
//
// public Consumer<Track> getDestination()
// {
// return destination;
// }
//
// }
public List<TrackRequest> getDownloadingTracks()
{
return downloadingTracks;
}
}

@ -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<Track>
{
// private QueueThing<?, Track> queueManager;
private QueueManager queueManager;
// private final LinkedList<Track> trackQueue = new LinkedList<>();
// private final Queue<byte[]> 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<QueueThing.Promise<Track, Track>> 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;
}
}

@ -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<Track>
public class QueueManager extends QueueThing<Track, Track>
{
private final int QUEUE_TARGET_SIZE = 5;
private Track restartingTrack = null;
private final PriorityQueue<Track> jukeboxQueue = new PriorityQueue<>();
// private final PriorityQueue<Track> jukeboxQueue = new PriorityQueue<>();
private QueueThing<?, Track> trackSource;
private Playlist playlist;
private MusicHandler handler;
public QueueManager()
{
super(new PriorityQueue<Track>());
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<Track, Track> 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<Promise<Track, Track>> request(int count, Consumer<Track> 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<Promise<Track, Track>> 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<Track>
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<Track>
{
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<Track>
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<Track>
public Queue<Track> getJukeboxQueue()
{
return jukeboxQueue;
return inputQueue;
}
public Playlist getPlaylist()
@ -135,9 +169,19 @@ public class QueueManager implements Consumer<Track>
restartingTrack = handler.getCurrentTrack();
if (restartingTrack != null)
{
handler.playNext();
handler.requestTrack();
return true;
} else
return false;
}
public QueueThing<?, Track> getTrackSource()
{
return trackSource;
}
public void setTrackSource(QueueThing<?, Track> trackSource)
{
this.trackSource = trackSource;
}
}

@ -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 <http://www.gnu.org/licenses/>.
*/
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<I, O> implements Consumer<I>
{
// TODO: better name
protected final Queue<I> inputQueue;
protected final List<QueueThing<?, I>> sources = new ArrayList<>();
protected final List<QueueThing<O, ?>> sinks = new ArrayList<>();
protected final Queue<Promise<I, O>> pendingPromises = new LinkedList<>();
protected int queueTargetSize = 0;
protected QueueThing(Queue<I> 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<I, O> 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<O, ?> sink : sinks)
{
sink.notifyNewInput();
}
}
public List<Promise<I, O>> request(int count, Consumer<O> destination)
{
List<Promise<I, O>> ret = new ArrayList<>();
int demands = 0;
for (int i = 0; i < count; i++)
{
I input = null;
if (!inputQueue.isEmpty())
{
input = inputQueue.poll();
}
Promise<I, O> 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<I, O> myPromise = new Promise<>(destination);
pendingPromises.add(myPromise);
ret.add(myPromise);
}
}
return ret;
}
private boolean handlePromise(Promise<I, O> promise)
{
boolean ok;
ok = completePromise(promise);
return ok;
}
protected abstract boolean completePromise(Promise<I, O> 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<?, I> source : sources)
{
List<Promise<?, I>> promises = (List<Promise<?, I>>) source.request(count - ret, this);
ret += promises.size();
if (ret >= count)
break;
}
return ret;
}
public void addSource(QueueThing<?, I> source)
{
sources.add(source);
source.addSink(this);
}
public void removeSource(QueueThing<?, I> source)
{
sources.remove(source);
source.removeSink(this);
}
private void addSink(QueueThing<O, ?> sink)
{
sinks.add(sink);
}
private void removeSink(QueueThing<O, ?> sink)
{
sinks.remove(sink);
}
public static class Promise<I, O>
{
private I input;
private final Consumer<O> output;
public Promise(I input, Consumer<O> output)
{
this.input = input;
this.output = output;
}
public Promise(Consumer<O> 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);
// }
}

@ -33,9 +33,18 @@ public class Track implements Comparable<Track>
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<Track>
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<Track>
this.kept = kept;
}
public TrackRequest getRequest()
{
return request;
}
@Override
public String toString()
{

@ -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<TrackRequest>
{
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<Result> 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;
}
}

@ -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 <Index> - Remove the track at position <Index> 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<String, Command> commands = bot.getCommands();
for (Entry<String, Command> e : commands.entrySet())
{
help += "!" + e.getKey() + " ".repeat(10 - e.getKey().length()) + "- " + e.getValue().synopsis();
}
invocation.respond(help);
}

@ -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<Downloader.DownloadTask> downloadQueue = bot.getDownloader().getDownloadQueue();
if (!downloadQueue.isEmpty())
final List<TrackRequest> 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<TrackRequest> 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);

@ -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

Loading…
Cancel
Save