|
|
@ -26,22 +26,23 @@ import java.util.stream.Collectors; |
|
|
|
import javax.json.Json; |
|
|
|
import javax.json.Json; |
|
|
|
import javax.json.JsonObject; |
|
|
|
import javax.json.JsonObject; |
|
|
|
import javax.json.JsonReader; |
|
|
|
import javax.json.JsonReader; |
|
|
|
|
|
|
|
import moe.nekojimi.chords.Downloader.DownloadTask; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* |
|
|
|
* |
|
|
|
* @author jimj316 |
|
|
|
* @author jimj316 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class Downloader implements Consumer<Song> |
|
|
|
public class Downloader implements Consumer<DownloadTask> |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
private static final int BITRATE_TARGET = 64_000; |
|
|
|
private static final int BITRATE_TARGET = 64_000; |
|
|
|
private static final Pattern FORMAT_PATTERN = Pattern.compile("^([\\w]+)\\s+([\\w]+)\\s+(\\w+ ?\\w*)\\s+(.*)$"); |
|
|
|
private static final Pattern FORMAT_PATTERN = Pattern.compile("^([\\w]+)\\s+([\\w]+)\\s+(\\w+ ?\\w*)\\s+(.*)$"); |
|
|
|
public static final Pattern DESTINATION_PATTERN = Pattern.compile("Destination: (.*\\.wav)"); |
|
|
|
public static final Pattern DESTINATION_PATTERN = Pattern.compile("Destination: (.*\\.wav)"); |
|
|
|
|
|
|
|
|
|
|
|
private final List<Song> downloadQueue = new LinkedList<>(); |
|
|
|
private final List<DownloadTask> downloadQueue = new LinkedList<>(); |
|
|
|
private final LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>(); |
|
|
|
private final LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>(); |
|
|
|
private final ThreadPoolExecutor exec = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue); |
|
|
|
private final ThreadPoolExecutor exec = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue); |
|
|
|
private Consumer<Song> next; |
|
|
|
// private Consumer<Song> next;
|
|
|
|
private BiConsumer<Song, Exception> messageHandler; |
|
|
|
private BiConsumer<Song, Exception> messageHandler; |
|
|
|
|
|
|
|
|
|
|
|
private File downloadDir = null; |
|
|
|
private File downloadDir = null; |
|
|
@ -52,32 +53,30 @@ public class Downloader implements Consumer<Song> |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void accept(Song song) |
|
|
|
public void accept(DownloadTask task) |
|
|
|
{ |
|
|
|
{ |
|
|
|
downloadQueue.add(song); |
|
|
|
// if already downloaded, just skip
|
|
|
|
getInfo(song); |
|
|
|
if (task.getSong().isDownloaded()) |
|
|
|
exec.submit(new Runnable() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
@Override |
|
|
|
task.getDestination().accept(task.getSong()); |
|
|
|
public void run() |
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
downloadQueue.add(task); |
|
|
|
|
|
|
|
getInfo(task.getSong()); |
|
|
|
|
|
|
|
exec.submit(() -> |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
try |
|
|
|
getFormats(task.getSong()); |
|
|
|
{ |
|
|
|
download(task); |
|
|
|
getFormats(song); |
|
|
|
} catch (Exception ex) |
|
|
|
download(song); |
|
|
|
{ |
|
|
|
} catch (Exception ex) |
|
|
|
ex.printStackTrace(); |
|
|
|
{ |
|
|
|
|
|
|
|
ex.printStackTrace(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setNext(Consumer<Song> next) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
this.next = next; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void chooseFormats(Song song) |
|
|
|
private void chooseFormats(Song song) |
|
|
|
{ |
|
|
|
{ |
|
|
|
List<Format> formats = song.getFormats(); |
|
|
|
List<Format> formats = song.getFormats(); |
|
|
@ -195,8 +194,9 @@ public class Downloader implements Consumer<Song> |
|
|
|
return downloadDir; |
|
|
|
return downloadDir; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void download(Song song) |
|
|
|
private void download(DownloadTask task) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Song song = task.song; |
|
|
|
chooseFormats(song); |
|
|
|
chooseFormats(song); |
|
|
|
String formatCodes = ""; |
|
|
|
String formatCodes = ""; |
|
|
|
final List<Format> formats = song.getFormats(); |
|
|
|
final List<Format> formats = song.getFormats(); |
|
|
@ -225,16 +225,16 @@ public class Downloader implements Consumer<Song> |
|
|
|
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); |
|
|
|
// return true;
|
|
|
|
// return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (next != null) |
|
|
|
if (task.getDestination() != null) |
|
|
|
next.accept(song); |
|
|
|
task.getDestination().accept(song); |
|
|
|
downloadQueue.remove(song); |
|
|
|
downloadQueue.remove(task); |
|
|
|
messageHandler.accept(song, null); |
|
|
|
messageHandler.accept(song, 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(song, ex); |
|
|
|
messageHandler.accept(song, ex); |
|
|
|
downloadQueue.remove(song); |
|
|
|
downloadQueue.remove(task); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -262,9 +262,33 @@ public class Downloader implements Consumer<Song> |
|
|
|
this.messageHandler = messageHandler; |
|
|
|
this.messageHandler = messageHandler; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public List<Song> getDownloadQueue() |
|
|
|
public List<DownloadTask> getDownloadQueue() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return downloadQueue; |
|
|
|
return downloadQueue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static class DownloadTask |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Song song; |
|
|
|
|
|
|
|
private final Consumer<Song> destination; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public DownloadTask(Song song, Consumer<Song> destination) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
this.song = song; |
|
|
|
|
|
|
|
this.destination = destination; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Song getSong() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return song; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Consumer<Song> getDestination() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return destination; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|