|
|
@ -49,8 +49,8 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
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); |
|
|
|
// private Consumer<Song> next;
|
|
|
|
// private Consumer<Track> next;
|
|
|
|
private BiConsumer<SongRequest, Exception> messageHandler; |
|
|
|
private BiConsumer<TrackRequest, Exception> messageHandler; |
|
|
|
|
|
|
|
|
|
|
|
private File downloadDir = null; |
|
|
|
private File downloadDir = null; |
|
|
|
|
|
|
|
|
|
|
@ -64,11 +64,11 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void accept(DownloadTask task) |
|
|
|
public void accept(DownloadTask task) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// if all songs of the request are already downloaded, just skip
|
|
|
|
// if all tracks of the request are already downloaded, just skip
|
|
|
|
if (!task.request.getSongs().isEmpty() && task.request.getSongs().stream().allMatch((t) -> t.isDownloaded())) |
|
|
|
if (!task.request.getTracks().isEmpty() && task.request.getTracks().stream().allMatch((t) -> t.isDownloaded())) |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (Song song : task.request.getSongs()) |
|
|
|
for (Track track : task.request.getTracks()) |
|
|
|
task.getDestination().accept(song); |
|
|
|
task.getDestination().accept(track); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -79,7 +79,7 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
{ |
|
|
|
{ |
|
|
|
try |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
// getFormats(song);
|
|
|
|
// getFormats(track);
|
|
|
|
download(task); |
|
|
|
download(task); |
|
|
|
} catch (Exception ex) |
|
|
|
} catch (Exception ex) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -129,13 +129,13 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
return formats; |
|
|
|
return formats; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void getFormats(Song song) |
|
|
|
private void getFormats(Track track) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!song.getFormats().isEmpty()) |
|
|
|
if (!track.getFormats().isEmpty()) |
|
|
|
return; |
|
|
|
return; |
|
|
|
try |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
String cmd = Chords.getSettings().getYtdlCommand() + " --skip-download -F " + song.getUrl().toString(); |
|
|
|
String cmd = Chords.getSettings().getYtdlCommand() + " --skip-download -F " + track.getUrl().toString(); |
|
|
|
Process exec = runCommand(cmd, FORMAT_TIMEOUT); |
|
|
|
Process exec = runCommand(cmd, FORMAT_TIMEOUT); |
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
String output = new String(input.readAllBytes(), Charset.defaultCharset()); |
|
|
|
String output = new String(input.readAllBytes(), Charset.defaultCharset()); |
|
|
@ -195,7 +195,7 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
format.setSampleRate(Util.parseSampleRate(split[bitrateCol])); |
|
|
|
format.setSampleRate(Util.parseSampleRate(split[bitrateCol])); |
|
|
|
formats.add(format); |
|
|
|
formats.add(format); |
|
|
|
} |
|
|
|
} |
|
|
|
song.setFormats(formats); |
|
|
|
track.setFormats(formats); |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception ex) |
|
|
|
} catch (Exception ex) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -204,31 +204,31 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private List<Song> getInfo(SongRequest request) |
|
|
|
private List<Track> getInfo(TrackRequest request) |
|
|
|
{ |
|
|
|
{ |
|
|
|
List<Song> ret = new ArrayList<>(); |
|
|
|
List<Track> ret = new ArrayList<>(); |
|
|
|
try |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
String cmd = Chords.getSettings().getYtdlCommand() + " --skip-download --print-json " + request.getUrl().toString(); |
|
|
|
String cmd = Chords.getSettings().getYtdlCommand() + " --skip-download --print-json " + request.getUrl().toString(); |
|
|
|
Process exec = runCommand(cmd, INFO_TIMEOUT); |
|
|
|
Process exec = runCommand(cmd, INFO_TIMEOUT); |
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
|
|
|
|
|
|
|
|
// read each line as JSON, turn each into a song object
|
|
|
|
// read each line as JSON, turn each into a track object
|
|
|
|
Scanner sc = new Scanner(input); |
|
|
|
Scanner sc = new Scanner(input); |
|
|
|
while (sc.hasNextLine()) |
|
|
|
while (sc.hasNextLine()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Song song = new Song(request.getUrl()); |
|
|
|
Track track = new Track(request.getUrl()); |
|
|
|
song.setNumber(trackNumber); |
|
|
|
track.setNumber(trackNumber); |
|
|
|
trackNumber++; |
|
|
|
trackNumber++; |
|
|
|
request.addSong(song); |
|
|
|
request.addTrack(track); |
|
|
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
if (song.getTitle() == null) |
|
|
|
if (track.getTitle() == null) |
|
|
|
song.setTitle(object.getString("title", null)); |
|
|
|
track.setTitle(object.getString("title", null)); |
|
|
|
if (song.getArtist() == null) |
|
|
|
if (track.getArtist() == null) |
|
|
|
song.setArtist(object.getString("uploader", null)); |
|
|
|
track.setArtist(object.getString("uploader", null)); |
|
|
|
|
|
|
|
|
|
|
|
JsonArray formatsJSON = object.getJsonArray("formats"); |
|
|
|
JsonArray formatsJSON = object.getJsonArray("formats"); |
|
|
|
if (formatsJSON != null) |
|
|
|
if (formatsJSON != null) |
|
|
@ -240,10 +240,10 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
if (format != null) |
|
|
|
if (format != null) |
|
|
|
formats.add(format); |
|
|
|
formats.add(format); |
|
|
|
} |
|
|
|
} |
|
|
|
song.setFormats(formats); |
|
|
|
track.setFormats(formats); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ret.add(song); |
|
|
|
ret.add(track); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (Exception ex) |
|
|
|
} catch (Exception ex) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -259,25 +259,25 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
return downloadDir; |
|
|
|
return downloadDir; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Song getSongFromRequest(SongRequest request, int idx) |
|
|
|
private Track getTrackFromRequest(TrackRequest request, int idx) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// if there's less songs in the request than expected, fill the array
|
|
|
|
// if there's less tracks in the request than expected, fill the array
|
|
|
|
while (idx >= request.getSongs().size()) |
|
|
|
while (idx >= request.getTracks().size()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Song song = new Song(request.getUrl()); |
|
|
|
Track track = new Track(request.getUrl()); |
|
|
|
song.setNumber(trackNumber); |
|
|
|
track.setNumber(trackNumber); |
|
|
|
trackNumber++; |
|
|
|
trackNumber++; |
|
|
|
request.addSong(song); |
|
|
|
request.addTrack(track); |
|
|
|
} |
|
|
|
} |
|
|
|
return request.getSongs().get(idx); |
|
|
|
return request.getTracks().get(idx); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void download(DownloadTask task) |
|
|
|
private void download(DownloadTask task) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Set<Format> uniqueFormats = new HashSet<>(); |
|
|
|
Set<Format> uniqueFormats = new HashSet<>(); |
|
|
|
for (Song song : task.request.getSongs()) |
|
|
|
for (Track track : task.request.getTracks()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
uniqueFormats.addAll(song.getFormats()); |
|
|
|
uniqueFormats.addAll(track.getFormats()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
List<Format> sortedFormats = sortFormats(uniqueFormats); |
|
|
|
List<Format> sortedFormats = sortFormats(uniqueFormats); |
|
|
@ -313,7 +313,7 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
if (itemMatcher.find()) |
|
|
|
if (itemMatcher.find()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int idx = Integer.parseInt(itemMatcher.group(1)) - 1; |
|
|
|
int idx = Integer.parseInt(itemMatcher.group(1)) - 1; |
|
|
|
int total = Integer.parseInt(cmd); |
|
|
|
// int total = Integer.parseInt(itemMatcher.group(2));
|
|
|
|
|
|
|
|
|
|
|
|
downloadIdx = idx; |
|
|
|
downloadIdx = idx; |
|
|
|
} |
|
|
|
} |
|
|
@ -321,20 +321,24 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
Matcher progMatcher = PROGRESS_PATTERN.matcher(line); |
|
|
|
Matcher progMatcher = PROGRESS_PATTERN.matcher(line); |
|
|
|
if (progMatcher.find()) |
|
|
|
if (progMatcher.find()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
getSongFromRequest(task.request, downloadIdx).setProgress(Double.parseDouble(progMatcher.group(1))); |
|
|
|
getTrackFromRequest(task.request, downloadIdx).setProgress(Double.parseDouble(progMatcher.group(1))); |
|
|
|
messageHandler.accept(task.request, null); |
|
|
|
messageHandler.accept(task.request, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Matcher destMatcher = DESTINATION_PATTERN.matcher(line); |
|
|
|
Matcher destMatcher = DESTINATION_PATTERN.matcher(line); |
|
|
|
if (destMatcher.find()) |
|
|
|
if (destMatcher.find()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Song song = getSongFromRequest(task.request, downloadIdx); |
|
|
|
Track track = getTrackFromRequest(task.request, downloadIdx); |
|
|
|
|
|
|
|
|
|
|
|
song.setLocation(new File(destMatcher.group(1))); |
|
|
|
track.setLocation(new File(destMatcher.group(1))); |
|
|
|
|
|
|
|
|
|
|
|
// this is currently our criteria for completion; submit the song and move on
|
|
|
|
// this is currently our criteria for completion; submit the track and move on
|
|
|
|
if (task.getDestination() != null) |
|
|
|
if (task.getDestination() != null) |
|
|
|
task.getDestination().accept(song); |
|
|
|
task.getDestination().accept(track); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
track.setProgress(100.0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messageHandler.accept(task.request, null); |
|
|
|
|
|
|
|
|
|
|
|
downloadIdx++; |
|
|
|
downloadIdx++; |
|
|
|
} |
|
|
|
} |
|
|
@ -383,12 +387,12 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
return exec; |
|
|
|
return exec; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public BiConsumer<SongRequest, Exception> getMessageHandler() |
|
|
|
public BiConsumer<TrackRequest, Exception> getMessageHandler() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return messageHandler; |
|
|
|
return messageHandler; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setMessageHandler(BiConsumer<SongRequest, Exception> messageHandler) |
|
|
|
public void setMessageHandler(BiConsumer<TrackRequest, Exception> messageHandler) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.messageHandler = messageHandler; |
|
|
|
this.messageHandler = messageHandler; |
|
|
|
} |
|
|
|
} |
|
|
@ -401,21 +405,21 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
public static class DownloadTask |
|
|
|
public static class DownloadTask |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
private final SongRequest request; |
|
|
|
private final TrackRequest request; |
|
|
|
private final Consumer<Song> destination; |
|
|
|
private final Consumer<Track> destination; |
|
|
|
|
|
|
|
|
|
|
|
public DownloadTask(SongRequest request, Consumer<Song> destination) |
|
|
|
public DownloadTask(TrackRequest request, Consumer<Track> destination) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.request = request; |
|
|
|
this.request = request; |
|
|
|
this.destination = destination; |
|
|
|
this.destination = destination; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public SongRequest getSong() |
|
|
|
public TrackRequest getTrack() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return request; |
|
|
|
return request; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Consumer<Song> getDestination() |
|
|
|
public Consumer<Track> getDestination() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return destination; |
|
|
|
return destination; |
|
|
|
} |
|
|
|
} |
|
|
|