Read format info from Youtube JSON response.

jda-v5
Nekojimi 2 years ago
parent ca3ebd712b
commit 4e654d2d18
  1. 57
      src/main/java/moe/nekojimi/chords/Downloader.java
  2. 57
      src/main/java/moe/nekojimi/chords/Format.java
  3. 2
      src/main/java/moe/nekojimi/chords/Main.java

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

@ -9,6 +9,7 @@ import com.amihaiemil.eoyaml.Yaml;
import com.amihaiemil.eoyaml.YamlMapping; import com.amihaiemil.eoyaml.YamlMapping;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.json.JsonObject;
/** /**
* *
@ -18,12 +19,15 @@ class Format
{ {
private static final Pattern SIZE_PATTERN = Pattern.compile("\\b([0-9]+\\.?[0-9]*)([kkMmGg])i?[bB]\\b"); private static final Pattern SIZE_PATTERN = Pattern.compile("\\b([0-9]+\\.?[0-9]*)([kkMmGg])i?[bB]\\b");
private static final Pattern BITRATE_PATTERN = Pattern.compile("\\b([0-9]+)k(b(ps?))?\\b"); private static final Pattern SAMPLE_RATE_PATTERN = Pattern.compile("\\b([0-9]+)k(b(ps?))?\\b");
private final String code; private final String code;
private final String extension; private final String extension;
private final String resolution; private final String resolution;
private final String note; private final String note;
private long size = -1;
private int samplerate = -1;
private boolean audioOnly = false;
public Format(String code, String extension, String resolution, String note) public Format(String code, String extension, String resolution, String note)
{ {
@ -33,6 +37,28 @@ class Format
this.note = note; this.note = note;
} }
/**
* Read format info from a youtube-dl JSON response.
*
* @param object the JSON object from youtube-dl.
* @return a new Format object if the JSON is valid, or null if not.
*/
public static Format fromJSON(JsonObject object)
{
String code = object.getString("format_id");
String ext = object.getString("ext");
// String res = object.getString()
String res = object.getString("format");
String note = "";
Format ret = new Format(code, ext, res, note);
int asr = object.getInt("asr", -1);
int size = object.getInt("filesize", -1);
ret.setSampleRate(asr);
ret.setSize(size);
return ret;
}
public static Format fromYaml(YamlMapping yaml) public static Format fromYaml(YamlMapping yaml)
{ {
Format format = new Format( Format format = new Format(
@ -55,11 +81,21 @@ class Format
public boolean isAudioOnly() public boolean isAudioOnly()
{ {
return resolution.trim().toLowerCase().contains("audio only"); if (audioOnly)
return true;
return resolution.trim().toLowerCase().contains("audio only") || note.trim().toLowerCase().contains("tiny");
}
public void setAudioOnly(boolean audioOnly)
{
this.audioOnly = audioOnly;
} }
public long getSize() public long getSize()
{ {
if (size != -1)
return size;
// try to find eg. "1.32MiB" inside note // try to find eg. "1.32MiB" inside note
Matcher matcher = SIZE_PATTERN.matcher(note); Matcher matcher = SIZE_PATTERN.matcher(note);
if (matcher.find()) if (matcher.find())
@ -86,10 +122,13 @@ class Format
return -1; return -1;
} }
public int getBitrate() public int getSampleRate()
{ {
if (samplerate != -1)
return samplerate;
// try to find eg. "51k" inside note // try to find eg. "51k" inside note
Matcher matcher = BITRATE_PATTERN.matcher(note); Matcher matcher = SAMPLE_RATE_PATTERN.matcher(note);
if (matcher.find()) if (matcher.find())
{ {
return Integer.parseInt(matcher.group(1)) * 1000; return Integer.parseInt(matcher.group(1)) * 1000;
@ -97,6 +136,16 @@ class Format
return -1; return -1;
} }
public void setSampleRate(int samplerate)
{
this.samplerate = samplerate;
}
public void setSize(long size)
{
this.size = size;
}
public String getCode() public String getCode()
{ {
return code; return code;

@ -118,7 +118,7 @@ public final class Main extends ListenerAdapter
String formatDetails = ""; String formatDetails = "";
if (format != null) if (format != null)
{ {
final int bitrate = format.getBitrate() / 1000; final int bitrate = format.getSampleRate() / 1000;
final long size = format.getSize(); final long size = format.getSize();
String sizeFmt = (size <= 0 ? "?.??" : String.format("%.2f", size / (1024.0 * 1024.0))) + "MiB"; String sizeFmt = (size <= 0 ? "?.??" : String.format("%.2f", size / (1024.0 * 1024.0))) + "MiB";
String bitFmt = (bitrate <= 0 ? "??" : bitrate) + "k"; String bitFmt = (bitrate <= 0 ? "??" : bitrate) + "k";

Loading…
Cancel
Save