|
|
|
@ -24,6 +24,7 @@ import java.util.regex.Matcher; |
|
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
import javax.json.Json; |
|
|
|
|
import javax.json.JsonArray; |
|
|
|
|
import javax.json.JsonObject; |
|
|
|
|
import javax.json.JsonReader; |
|
|
|
|
import moe.nekojimi.chords.Downloader.DownloadTask; |
|
|
|
@ -35,13 +36,17 @@ import moe.nekojimi.chords.Downloader.DownloadTask; |
|
|
|
|
public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
private static final int DOWNLOAD_TIMEOUT = 300; |
|
|
|
|
private static final int INFO_TIMEOUT = 10; |
|
|
|
|
private static final int FORMAT_TIMEOUT = 5; |
|
|
|
|
|
|
|
|
|
private static final int BITRATE_TARGET = 64_000; |
|
|
|
|
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)"); |
|
|
|
|
|
|
|
|
|
private final List<DownloadTask> downloadQueue = new LinkedList<>(); |
|
|
|
|
private final LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>(); |
|
|
|
|
private final ThreadPoolExecutor exec = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue); |
|
|
|
|
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, workQueue); |
|
|
|
|
// private Consumer<Song> next;
|
|
|
|
|
private BiConsumer<SongRequest, Exception> messageHandler; |
|
|
|
|
|
|
|
|
@ -65,7 +70,7 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
|
|
|
|
|
downloadQueue.add(task); |
|
|
|
|
getInfo(song); |
|
|
|
|
exec.submit(() -> |
|
|
|
|
executor.submit(() -> |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
@ -83,30 +88,25 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
List<Format> formats = song.getFormats(); |
|
|
|
|
if (formats.isEmpty()) |
|
|
|
|
return; |
|
|
|
|
// System.out.println("Choosing from " + formats.size() + " formats:");
|
|
|
|
|
// System.out.println(formats);
|
|
|
|
|
formats.sort((Format a, Format b) -> |
|
|
|
|
{ |
|
|
|
|
// audio only preferred to video
|
|
|
|
|
// System.out.println("sort entered; a=" + a.toString() + " b=" + b.toString());
|
|
|
|
|
int comp = 0; |
|
|
|
|
comp = Boolean.compare(a.isAudioOnly(), b.isAudioOnly()); |
|
|
|
|
// System.out.println("\tCompared on audio only: " + comp);
|
|
|
|
|
if (comp == 0) |
|
|
|
|
{ |
|
|
|
|
// known preferred to unknown
|
|
|
|
|
if (a.getBitrate() == b.getBitrate()) |
|
|
|
|
if (a.getSampleRate() == b.getSampleRate()) |
|
|
|
|
comp = 0; |
|
|
|
|
else if (a.getBitrate() <= 0) |
|
|
|
|
else if (a.getSampleRate() <= 0) |
|
|
|
|
comp = 1; |
|
|
|
|
else if (b.getBitrate() <= 0) |
|
|
|
|
else if (b.getSampleRate() <= 0) |
|
|
|
|
comp = -1; |
|
|
|
|
else // closer to the target bitrate is best
|
|
|
|
|
{ |
|
|
|
|
int aDist = Math.abs(a.getBitrate() - BITRATE_TARGET); |
|
|
|
|
int bDist = Math.abs(b.getBitrate() - BITRATE_TARGET); |
|
|
|
|
int aDist = Math.abs(a.getSampleRate() - BITRATE_TARGET); |
|
|
|
|
int bDist = Math.abs(b.getSampleRate() - BITRATE_TARGET); |
|
|
|
|
comp = Integer.compare(bDist, aDist); |
|
|
|
|
// System.out.println("\tCompared on bitrate distance: " + comp);
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (comp == 0) |
|
|
|
@ -114,30 +114,26 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
// known preferred to unknown
|
|
|
|
|
if (a.getSize() == b.getSize()) |
|
|
|
|
comp = 0; |
|
|
|
|
if (a.getSize() <= 0) |
|
|
|
|
else if (a.getSize() <= 0) |
|
|
|
|
comp = 1; |
|
|
|
|
else if (b.getSize() <= 0) |
|
|
|
|
comp = -1; |
|
|
|
|
else // smaller is better
|
|
|
|
|
{ |
|
|
|
|
comp = Long.compare(b.getSize(), a.getSize()); |
|
|
|
|
// System.out.println("\tCompared on filesize: " + comp);
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// System.out.println("\tOverall: " + comp);
|
|
|
|
|
return -comp; |
|
|
|
|
}); |
|
|
|
|
song.setFormats(formats); |
|
|
|
|
// System.out.println("Sorting done! Formats:" + formats);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void getFormats(Song song) |
|
|
|
|
{ |
|
|
|
|
//
|
|
|
|
|
if (!song.getFormats().isEmpty()) |
|
|
|
|
return; |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
String cmd = "/usr/bin/youtube-dl --skip-download -F " + song.getUrl().toString(); |
|
|
|
|
Process exec = runCommand(cmd, 5); |
|
|
|
|
Process exec = runCommand(cmd, FORMAT_TIMEOUT); |
|
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
|
String output = new String(input.readAllBytes(), Charset.defaultCharset()); |
|
|
|
|
|
|
|
|
@ -157,10 +153,6 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
formats.add(new Format(split[0], split[1], split[2], split[3])); |
|
|
|
|
} |
|
|
|
|
song.setFormats(formats); |
|
|
|
|
// Matcher matcher = FORMAT_PATTERN.matcher(output);
|
|
|
|
|
// while (matcher.find())
|
|
|
|
|
// formats.add(new Format(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4)));
|
|
|
|
|
// return formats;
|
|
|
|
|
|
|
|
|
|
} catch (Exception ex) |
|
|
|
|
{ |
|
|
|
@ -174,7 +166,7 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
String cmd = "/usr/bin/youtube-dl --skip-download --print-json " + song.getUrl().toString(); |
|
|
|
|
Process exec = runCommand(cmd, 5); |
|
|
|
|
Process exec = runCommand(cmd, INFO_TIMEOUT); |
|
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
|
JsonReader reader = Json.createReader(input); |
|
|
|
|
JsonObject object = reader.readObject(); |
|
|
|
@ -182,6 +174,19 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
song.setTitle(object.getString("title", null)); |
|
|
|
|
if (song.getArtist() == null) |
|
|
|
|
song.setArtist(object.getString("uploader", null)); |
|
|
|
|
|
|
|
|
|
JsonArray formatsJSON = object.getJsonArray("formats"); |
|
|
|
|
if (formatsJSON != null) |
|
|
|
|
{ |
|
|
|
|
List<Format> formats = new ArrayList<>(); |
|
|
|
|
for (JsonObject formatJson : formatsJSON.getValuesAs(JsonObject.class)) |
|
|
|
|
{ |
|
|
|
|
Format format = Format.fromJSON(formatJson); |
|
|
|
|
if (format != null) |
|
|
|
|
formats.add(format); |
|
|
|
|
} |
|
|
|
|
song.setFormats(formats); |
|
|
|
|
} |
|
|
|
|
} catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex); |
|
|
|
@ -214,7 +219,7 @@ public class Downloader implements Consumer<DownloadTask> |
|
|
|
|
+ " -o " + getDownloadDir().getAbsolutePath() + "/%(title)s.%(ext)s " |
|
|
|
|
+ song.getUrl().toString(); |
|
|
|
|
|
|
|
|
|
Process exec = runCommand(cmd, 300); |
|
|
|
|
Process exec = runCommand(cmd, DOWNLOAD_TIMEOUT); |
|
|
|
|
InputStream in = exec.getInputStream(); |
|
|
|
|
String output = new String(in.readAllBytes(), Charset.defaultCharset()); |
|
|
|
|
String error = new String(exec.getErrorStream().readAllBytes(), Charset.defaultCharset()); |
|
|
|
|