Change pipeline to use pull architecture.
This commit is contained in:
parent
b41f5c97a3
commit
fdbd8937f5
|
@ -14,7 +14,6 @@ import java.nio.file.Files;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
@ -24,14 +23,13 @@ import javax.json.Json;
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
import javax.json.JsonReader;
|
import javax.json.JsonReader;
|
||||||
import moe.nekojimi.chords.Downloader.DownloadTask;
|
|
||||||
import net.dv8tion.jda.api.audio.AudioSendHandler;
|
import net.dv8tion.jda.api.audio.AudioSendHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author jimj316
|
* @author jimj316
|
||||||
*/
|
*/
|
||||||
public class Downloader implements Consumer<DownloadTask>
|
public class Downloader extends QueueThing<TrackRequest, Track>
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final int DOWNLOAD_TIMEOUT = 300;
|
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 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 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 LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
|
||||||
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue);
|
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue);
|
||||||
private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
|
private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
|
||||||
|
@ -58,34 +65,45 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
|
|
||||||
public Downloader()
|
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
|
@Override
|
||||||
public void accept(DownloadTask task)
|
protected boolean completePromise(Promise<TrackRequest, Track> promise)
|
||||||
{
|
{
|
||||||
// if all tracks of the request are already downloaded, just skip
|
TrackRequest request = promise.getInput();
|
||||||
if (!task.request.getTracks().isEmpty() && task.request.getTracks().stream().allMatch((t) -> t.isDownloaded()))
|
|
||||||
{
|
|
||||||
for (Track track : task.request.getTracks())
|
|
||||||
task.getDestination().accept(track);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadQueue.add(task);
|
|
||||||
getInfo(task.request);
|
|
||||||
// TODO: get info should also use the thread pool
|
|
||||||
executor.submit(() ->
|
executor.submit(() ->
|
||||||
{
|
{
|
||||||
|
downloadingTracks.add(request);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// getFormats(track);
|
|
||||||
download(task);
|
getInfo(request);
|
||||||
|
download(promise);
|
||||||
} catch (Exception ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
downloadingTracks.remove(request);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Format> sortFormats(Collection<Format> input)
|
private List<Format> sortFormats(Collection<Format> input)
|
||||||
|
@ -217,7 +235,7 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
Scanner sc = new Scanner(input);
|
Scanner sc = new Scanner(input);
|
||||||
while (sc.hasNextLine())
|
while (sc.hasNextLine())
|
||||||
{
|
{
|
||||||
Track track = new Track(request.getUrl());
|
Track track = new Track(request);
|
||||||
track.setNumber(trackNumber);
|
track.setNumber(trackNumber);
|
||||||
trackNumber++;
|
trackNumber++;
|
||||||
request.addTrack(track);
|
request.addTrack(track);
|
||||||
|
@ -225,10 +243,40 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
String line = sc.nextLine();
|
String line = sc.nextLine();
|
||||||
JsonReader reader = Json.createReader(new StringReader(line));
|
JsonReader reader = Json.createReader(new StringReader(line));
|
||||||
JsonObject object = reader.readObject();
|
JsonObject object = reader.readObject();
|
||||||
|
|
||||||
|
// look for metadata
|
||||||
if (track.getTitle() == null)
|
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)
|
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");
|
JsonArray formatsJSON = object.getJsonArray("formats");
|
||||||
if (formatsJSON != null)
|
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
|
// if there's less tracks in the request than expected, fill the array
|
||||||
while (idx >= request.getTracks().size())
|
while (idx >= request.getTracks().size())
|
||||||
{
|
{
|
||||||
Track track = new Track(request.getUrl());
|
Track track = new Track(request);
|
||||||
track.setNumber(trackNumber);
|
track.setNumber(trackNumber);
|
||||||
trackNumber++;
|
trackNumber++;
|
||||||
request.addTrack(track);
|
request.addTrack(track);
|
||||||
|
@ -272,10 +320,12 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
return request.getTracks().get(idx);
|
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<>();
|
Set<Format> uniqueFormats = new HashSet<>();
|
||||||
for (Track track : task.request.getTracks())
|
|
||||||
|
for (Track track : request.getTracks())
|
||||||
{
|
{
|
||||||
uniqueFormats.addAll(track.getFormats());
|
uniqueFormats.addAll(track.getFormats());
|
||||||
}
|
}
|
||||||
|
@ -290,7 +340,7 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
messageHandler.accept(task.request, null);
|
messageHandler.accept(request, null);
|
||||||
String cmd = Chords.getSettings().getYtdlCommand()
|
String cmd = Chords.getSettings().getYtdlCommand()
|
||||||
+ " -x"
|
+ " -x"
|
||||||
+ " -f " + formatCodes + "worstaudio/bestaudio/worst/best"
|
+ " -f " + formatCodes + "worstaudio/bestaudio/worst/best"
|
||||||
|
@ -298,7 +348,7 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
+ " --no-playlist"
|
+ " --no-playlist"
|
||||||
+ " --extractor-args youtube:player_client=android"
|
+ " --extractor-args youtube:player_client=android"
|
||||||
+ " -o " + getDownloadDir().getAbsolutePath() + "/%(title)s.%(ext)s "
|
+ " -o " + getDownloadDir().getAbsolutePath() + "/%(title)s.%(ext)s "
|
||||||
+ task.request.getUrl().toString();
|
+ request.getUrl().toString();
|
||||||
|
|
||||||
Process exec = runCommand(cmd, DOWNLOAD_TIMEOUT);
|
Process exec = runCommand(cmd, DOWNLOAD_TIMEOUT);
|
||||||
InputStream in = exec.getInputStream();
|
InputStream in = exec.getInputStream();
|
||||||
|
@ -321,47 +371,40 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
Matcher progMatcher = PROGRESS_PATTERN.matcher(line);
|
Matcher progMatcher = PROGRESS_PATTERN.matcher(line);
|
||||||
if (progMatcher.find())
|
if (progMatcher.find())
|
||||||
{
|
{
|
||||||
getTrackFromRequest(task.request, downloadIdx).setProgress(Double.parseDouble(progMatcher.group(1)));
|
getTrackFromRequest(request, downloadIdx).setProgress(Double.parseDouble(progMatcher.group(1)));
|
||||||
messageHandler.accept(task.request, null);
|
messageHandler.accept(request, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher destMatcher = DESTINATION_PATTERN.matcher(line);
|
Matcher destMatcher = DESTINATION_PATTERN.matcher(line);
|
||||||
if (destMatcher.find())
|
if (destMatcher.find())
|
||||||
{
|
{
|
||||||
Track track = getTrackFromRequest(task.request, downloadIdx);
|
Track track = getTrackFromRequest(request, downloadIdx);
|
||||||
|
|
||||||
track.setLocation(new File(destMatcher.group(1)));
|
track.setLocation(new File(destMatcher.group(1)));
|
||||||
|
|
||||||
// this is currently our criteria for completion; submit the track and move on
|
// this is currently our criteria for completion; submit the track and move on
|
||||||
if (task.getDestination() != null)
|
promise.complete(track);
|
||||||
task.getDestination().accept(track);
|
|
||||||
|
|
||||||
track.setProgress(100.0);
|
track.setProgress(100.0);
|
||||||
|
|
||||||
messageHandler.accept(task.request, null);
|
messageHandler.accept(request, null);
|
||||||
|
|
||||||
downloadIdx++;
|
downloadIdx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// String output = new String(in.readAllBytes(), Charset.defaultCharset());
|
|
||||||
String error = new String(exec.getErrorStream().readAllBytes(), Charset.defaultCharset());
|
String error = new String(exec.getErrorStream().readAllBytes(), Charset.defaultCharset());
|
||||||
// System.out.println(output);
|
|
||||||
|
|
||||||
if (exec.exitValue() != 0)
|
if (exec.exitValue() != 0)
|
||||||
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue() + ", output:\n" + error);
|
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue() + ", output:\n" + error);
|
||||||
|
|
||||||
// task.request.setProgress(100);
|
messageHandler.accept(request, null);
|
||||||
// return true;
|
|
||||||
|
|
||||||
downloadQueue.remove(task);
|
|
||||||
messageHandler.accept(task.request, null);
|
|
||||||
} catch (Exception ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex);
|
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
if (messageHandler != null)
|
if (messageHandler != null)
|
||||||
messageHandler.accept(task.request, ex);
|
messageHandler.accept(request, ex);
|
||||||
downloadQueue.remove(task);
|
//downloadQueue.remove(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,33 +440,39 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DownloadTask> getDownloadQueue()
|
public List<TrackRequest> getDownloadQueue()
|
||||||
{
|
{
|
||||||
return downloadQueue;
|
return new ArrayList<>(inputQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DownloadTask
|
|
||||||
|
// 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;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,10 @@ import net.dv8tion.jda.api.audio.AudioSendHandler;
|
||||||
*
|
*
|
||||||
* @author jimj316
|
* @author jimj316
|
||||||
*/
|
*/
|
||||||
public class MusicHandler implements AudioSendHandler, Closeable
|
public class MusicHandler implements AudioSendHandler, Closeable, Consumer<Track>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// private QueueThing<?, Track> queueManager;
|
||||||
private QueueManager queueManager;
|
private QueueManager queueManager;
|
||||||
// private final LinkedList<Track> trackQueue = new LinkedList<>();
|
// private final LinkedList<Track> trackQueue = new LinkedList<>();
|
||||||
// private final Queue<byte[]> queue = new ConcurrentLinkedQueue<>();
|
// private final Queue<byte[]> queue = new ConcurrentLinkedQueue<>();
|
||||||
|
@ -59,28 +60,29 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Track t)
|
||||||
|
{
|
||||||
|
play(t);
|
||||||
|
}
|
||||||
|
|
||||||
void setQueueManager(QueueManager manager)
|
void setQueueManager(QueueManager manager)
|
||||||
{
|
{
|
||||||
queueManager = manager;
|
queueManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playNext()
|
|
||||||
{
|
|
||||||
nextTrack(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void playOver(Track track)
|
public void playOver(Track track)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean play(Track track)
|
||||||
private boolean nextTrack()
|
|
||||||
{
|
{
|
||||||
return nextTrack(false);
|
return play(track, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean nextTrack(boolean immediate)
|
public boolean play(Track track, boolean immediate)
|
||||||
{
|
{
|
||||||
if (immediate)
|
if (immediate)
|
||||||
{
|
{
|
||||||
|
@ -96,12 +98,11 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
||||||
currentTrack.delete();
|
currentTrack.delete();
|
||||||
currentTrack = null;
|
currentTrack = null;
|
||||||
}
|
}
|
||||||
currentTrack = queueManager.nextTrackNeeded();
|
currentTrack = track;
|
||||||
if (nowPlayingConsumer != null)
|
if (nowPlayingConsumer != null)
|
||||||
nowPlayingConsumer.accept(currentTrack);
|
nowPlayingConsumer.accept(currentTrack);
|
||||||
if (currentTrack == null)
|
if (currentTrack == null)
|
||||||
{
|
{
|
||||||
System.out.println("End of queue.");
|
|
||||||
debugOut.flush();
|
debugOut.flush();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -121,6 +122,13 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
||||||
return false;
|
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()
|
public boolean isPlaying()
|
||||||
{
|
{
|
||||||
return !playingTracks.isEmpty();
|
return !playingTracks.isEmpty();
|
||||||
|
@ -134,7 +142,7 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
||||||
public void setShouldPlay(boolean shouldPlay)
|
public void setShouldPlay(boolean shouldPlay)
|
||||||
{
|
{
|
||||||
if (!this.shouldPlay && shouldPlay)
|
if (!this.shouldPlay && shouldPlay)
|
||||||
nextTrack();
|
requestTrack();
|
||||||
this.shouldPlay = shouldPlay;
|
this.shouldPlay = shouldPlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +189,7 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
||||||
ret.put(mixBuffers(mixes));
|
ret.put(mixBuffers(mixes));
|
||||||
if (outOfInput)
|
if (outOfInput)
|
||||||
{
|
{
|
||||||
boolean foundNext = nextTrack();
|
boolean foundNext = requestTrack();
|
||||||
if (!foundNext)
|
if (!foundNext)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -244,4 +252,13 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean skipTrack()
|
||||||
|
{
|
||||||
|
if (!isPlaying())
|
||||||
|
return false;
|
||||||
|
playingTracks.clear();
|
||||||
|
requestTrack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package moe.nekojimi.chords;
|
package moe.nekojimi.chords;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.List;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -26,29 +25,62 @@ import java.util.function.Consumer;
|
||||||
*
|
*
|
||||||
* @author jimj316
|
* @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 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 Playlist playlist;
|
||||||
private MusicHandler handler;
|
private MusicHandler handler;
|
||||||
|
|
||||||
public QueueManager()
|
public QueueManager()
|
||||||
{
|
{
|
||||||
|
super(new PriorityQueue<Track>());
|
||||||
|
queueTargetSize = QUEUE_TARGET_SIZE;
|
||||||
// jukeboxQueue = new LinkedList<>();
|
// jukeboxQueue = new LinkedList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)
|
if (!handler.isPlaying() || handler.getCurrentTrack() == null)
|
||||||
handler.playNext();
|
handler.requestTrack();
|
||||||
|
}
|
||||||
|
|
||||||
if (handler.isPlaying() != (handler.getCurrentTrack() == null))
|
@Override
|
||||||
System.err.println("WARNING: handler isPlaying violates contract! Something dumb going on!");
|
protected boolean completePromise(Promise<Track, Track> request)
|
||||||
|
{
|
||||||
|
final Track input = request.getInput();
|
||||||
|
if (input != null)
|
||||||
|
{
|
||||||
|
request.complete(input);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Promise<Track, Track>> request(int count, Consumer<Track> destination)
|
||||||
|
{
|
||||||
|
if (restartingTrack != null)
|
||||||
|
{
|
||||||
|
Promise ret = new Promise<>(restartingTrack, destination);
|
||||||
|
restartingTrack = null;
|
||||||
|
return List.of(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Promise<Track, Track>> ret = super.request(count, destination);
|
||||||
|
if (inputQueue.size() < QUEUE_TARGET_SIZE)
|
||||||
|
demandInput(QUEUE_TARGET_SIZE - inputQueue.size());
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,29 +89,31 @@ public class QueueManager implements Consumer<Track>
|
||||||
*
|
*
|
||||||
* @return the next track to play, or null to stop playing.
|
* @return the next track to play, or null to stop playing.
|
||||||
*/
|
*/
|
||||||
public Track nextTrackNeeded()
|
// public Track nextTrackNeeded()
|
||||||
{
|
// {
|
||||||
// if we're restarting the current track: store, clear, and return it
|
// Track ret;
|
||||||
if (restartingTrack != null)
|
// // if we're restarting the current track: store, clear, and return it
|
||||||
{
|
// if (restartingTrack != null)
|
||||||
Track ret = restartingTrack;
|
// {
|
||||||
restartingTrack = null;
|
// ret = restartingTrack;
|
||||||
return ret;
|
// restartingTrack = null;
|
||||||
}
|
// }
|
||||||
// if there's anything in the queue, play that first
|
// // if there's anything in the queue, play that first
|
||||||
if (!jukeboxQueue.isEmpty())
|
// else if (!jukeboxQueue.isEmpty())
|
||||||
{
|
// {
|
||||||
return jukeboxQueue.poll();
|
// ret = jukeboxQueue.poll();
|
||||||
}
|
// }
|
||||||
// otherwise if there's a playlist, shuffle from that
|
// // otherwise if there's a playlist, shuffle from that
|
||||||
else if (playlist != null)
|
// else if (playlist != null)
|
||||||
{
|
// {
|
||||||
return playlist.getNextTrack();
|
// ret = playlist.getNextTrack();
|
||||||
}
|
// }
|
||||||
// otherwise stop playing
|
// // otherwise stop playing
|
||||||
else
|
// else
|
||||||
return null;
|
// ret = null;
|
||||||
}
|
//
|
||||||
|
// return ret;
|
||||||
|
// }
|
||||||
|
|
||||||
public MusicHandler getHandler()
|
public MusicHandler getHandler()
|
||||||
{
|
{
|
||||||
|
@ -89,7 +123,7 @@ public class QueueManager implements Consumer<Track>
|
||||||
public void addTrack(Track track)
|
public void addTrack(Track track)
|
||||||
{
|
{
|
||||||
System.out.println("Track added to queue: " + track.getLocation().getAbsolutePath());
|
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
|
try
|
||||||
{
|
{
|
||||||
return jukeboxQueue.remove((Track) jukeboxQueue.toArray()[i]);
|
return inputQueue.remove((Track) inputQueue.toArray()[i]);
|
||||||
} catch (ArrayIndexOutOfBoundsException ex)
|
} catch (ArrayIndexOutOfBoundsException ex)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -106,7 +140,7 @@ public class QueueManager implements Consumer<Track>
|
||||||
|
|
||||||
public boolean removeTrack(Track track)
|
public boolean removeTrack(Track track)
|
||||||
{
|
{
|
||||||
return jukeboxQueue.remove(track);
|
return inputQueue.remove(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHandler(MusicHandler handler)
|
public void setHandler(MusicHandler handler)
|
||||||
|
@ -117,7 +151,7 @@ public class QueueManager implements Consumer<Track>
|
||||||
|
|
||||||
public Queue<Track> getJukeboxQueue()
|
public Queue<Track> getJukeboxQueue()
|
||||||
{
|
{
|
||||||
return jukeboxQueue;
|
return inputQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Playlist getPlaylist()
|
public Playlist getPlaylist()
|
||||||
|
@ -135,9 +169,19 @@ public class QueueManager implements Consumer<Track>
|
||||||
restartingTrack = handler.getCurrentTrack();
|
restartingTrack = handler.getCurrentTrack();
|
||||||
if (restartingTrack != null)
|
if (restartingTrack != null)
|
||||||
{
|
{
|
||||||
handler.playNext();
|
handler.requestTrack();
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
return false;
|
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 progress = -1;
|
||||||
private double eta = -1;
|
private double eta = -1;
|
||||||
|
|
||||||
public Track(URL url)
|
private final TrackRequest request;
|
||||||
|
|
||||||
|
public Track(URL url, TrackRequest request)
|
||||||
{
|
{
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Track(TrackRequest request)
|
||||||
|
{
|
||||||
|
this.request = request;
|
||||||
|
this.url = request.getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
public YamlMapping toYaml()
|
public YamlMapping toYaml()
|
||||||
|
@ -59,7 +68,7 @@ public class Track implements Comparable<Track>
|
||||||
|
|
||||||
public static Track fromYaml(YamlMapping map) throws MalformedURLException
|
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.setArtist(map.string("artist"));
|
||||||
track.setTitle(map.string("title"));
|
track.setTitle(map.string("title"));
|
||||||
track.setLocation(new File(map.string("location")));
|
track.setLocation(new File(map.string("location")));
|
||||||
|
@ -144,6 +153,11 @@ public class Track implements Comparable<Track>
|
||||||
this.kept = kept;
|
this.kept = kept;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TrackRequest getRequest()
|
||||||
|
{
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package moe.nekojimi.chords;
|
package moe.nekojimi.chords;
|
||||||
|
|
||||||
|
import com.amihaiemil.eoyaml.YamlMapping;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import moe.nekojimi.chords.commands.Invocation;
|
import moe.nekojimi.chords.commands.Invocation;
|
||||||
import moe.nekojimi.musicsearcher.Result;
|
import moe.nekojimi.musicsearcher.Result;
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ import moe.nekojimi.musicsearcher.Result;
|
||||||
*
|
*
|
||||||
* @author jimj316
|
* @author jimj316
|
||||||
*/
|
*/
|
||||||
public class TrackRequest
|
public class TrackRequest implements Comparable<TrackRequest>
|
||||||
{
|
{
|
||||||
private Invocation invocation;
|
private Invocation invocation;
|
||||||
|
|
||||||
|
@ -42,6 +44,19 @@ public class TrackRequest
|
||||||
private String requestedBy;
|
private String requestedBy;
|
||||||
private String requestedIn;
|
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()
|
public List<Result> getSearchResults()
|
||||||
{
|
{
|
||||||
|
@ -159,4 +174,44 @@ public class TrackRequest
|
||||||
return (trackName + " " + requestName).trim();
|
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;
|
package moe.nekojimi.chords.commands;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import moe.nekojimi.chords.Chords;
|
import moe.nekojimi.chords.Chords;
|
||||||
|
|
||||||
public class HelpCommand extends Command
|
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"
|
+ "!remove <Index> - Remove the track at position <Index> from the queue.\n"
|
||||||
+ "!skip - Skip the current track and play the next one.\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";
|
+ "!restart - Try playing the current track again in case it goes wrong.\n";
|
||||||
// for (String key: commands.keySet())
|
final Map<String, Command> commands = bot.getCommands();
|
||||||
// {
|
for (Entry<String, Command> e : commands.entrySet())
|
||||||
// help += "!" + key + ":"
|
{
|
||||||
// }
|
help += "!" + e.getKey() + " ".repeat(10 - e.getKey().length()) + "- " + e.getValue().synopsis();
|
||||||
|
}
|
||||||
invocation.respond(help);
|
invocation.respond(help);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Queue;
|
||||||
import moe.nekojimi.chords.Downloader;
|
import moe.nekojimi.chords.Downloader;
|
||||||
import moe.nekojimi.chords.Chords;
|
import moe.nekojimi.chords.Chords;
|
||||||
import moe.nekojimi.chords.Track;
|
import moe.nekojimi.chords.Track;
|
||||||
|
import moe.nekojimi.chords.TrackRequest;
|
||||||
|
|
||||||
public class QueueCommand extends Command
|
public class QueueCommand extends Command
|
||||||
{
|
{
|
||||||
|
@ -48,23 +49,34 @@ public class QueueCommand extends Command
|
||||||
message += "__Ready to play:__\n";
|
message += "__Ready to play:__\n";
|
||||||
for (Track track : trackQueue)
|
for (Track track : trackQueue)
|
||||||
{
|
{
|
||||||
message += ":bread: **" + (i) + ":** " + track + "\n";
|
message += ":bread: **" + i + ":** " + track + "\n";
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Downloader.DownloadTask> downloadQueue = bot.getDownloader().getDownloadQueue();
|
final List<TrackRequest> downloading = bot.getDownloader().getDownloadingTracks();
|
||||||
if (!downloadQueue.isEmpty())
|
if (!downloading.isEmpty())
|
||||||
{
|
{
|
||||||
message += "__Downloading:__\n";
|
message += "__Downloading:__\n";
|
||||||
for (Downloader.DownloadTask task : downloadQueue)
|
for (TrackRequest request : downloading)
|
||||||
{
|
{
|
||||||
message += ":inbox_tray: **" + (i) + ":** " + task.getTrack() + "\n";
|
message += ":satellite: **" + (i) + ":** " + request + "\n";
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadQueue.isEmpty() && trackQueue.isEmpty())
|
final List<TrackRequest> downloadQueue = bot.getDownloader().getDownloadQueue();
|
||||||
|
if (!downloadQueue.isEmpty())
|
||||||
|
{
|
||||||
|
message += "__In queue for download:__\n";
|
||||||
|
for (TrackRequest request : downloadQueue)
|
||||||
|
{
|
||||||
|
message += ":inbox_tray: **" + (i) + ":** " + request + "\n";
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloading.isEmpty() && trackQueue.isEmpty() && downloadQueue.isEmpty())
|
||||||
message += ":mailbox_with_no_mail: The track queue is empty.";
|
message += ":mailbox_with_no_mail: The track queue is empty.";
|
||||||
// :inbox_tray:
|
// :inbox_tray:
|
||||||
invocation.respond(message);
|
invocation.respond(message);
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class SkipCommand extends Command
|
||||||
@Override
|
@Override
|
||||||
public void call(Invocation invocation)
|
public void call(Invocation invocation)
|
||||||
{
|
{
|
||||||
boolean ok = bot.getMusicHandler().nextTrack(true);
|
boolean ok = bot.getMusicHandler().skipTrack();
|
||||||
if (ok)
|
if (ok)
|
||||||
invocation.respond("Skipped to next track!");
|
invocation.respond("Skipped to next track!");
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue