Compare commits
No commits in common. "ae7fe8cc7b14cf0d3b45fb00a8c32347de6326cb" and "ca3ebd712bb24bdb7a18ddc83f7c9ba054c158bd" have entirely different histories.
ae7fe8cc7b
...
ca3ebd712b
|
@ -24,7 +24,6 @@ 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;
|
||||||
|
@ -36,17 +35,13 @@ 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 executor = 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<SongRequest, Exception> messageHandler;
|
private BiConsumer<SongRequest, Exception> messageHandler;
|
||||||
|
|
||||||
|
@ -70,7 +65,7 @@ public class Downloader implements Consumer<DownloadTask>
|
||||||
|
|
||||||
downloadQueue.add(task);
|
downloadQueue.add(task);
|
||||||
getInfo(song);
|
getInfo(song);
|
||||||
executor.submit(() ->
|
exec.submit(() ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -88,25 +83,30 @@ 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.getSampleRate() == b.getSampleRate())
|
if (a.getBitrate() == b.getBitrate())
|
||||||
comp = 0;
|
comp = 0;
|
||||||
else if (a.getSampleRate() <= 0)
|
else if (a.getBitrate() <= 0)
|
||||||
comp = 1;
|
comp = 1;
|
||||||
else if (b.getSampleRate() <= 0)
|
else if (b.getBitrate() <= 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.getSampleRate() - BITRATE_TARGET);
|
int aDist = Math.abs(a.getBitrate() - BITRATE_TARGET);
|
||||||
int bDist = Math.abs(b.getSampleRate() - BITRATE_TARGET);
|
int bDist = Math.abs(b.getBitrate() - 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,26 +114,30 @@ 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;
|
||||||
else if (a.getSize() <= 0)
|
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, FORMAT_TIMEOUT);
|
Process exec = runCommand(cmd, 5);
|
||||||
InputStream input = exec.getInputStream();
|
InputStream input = exec.getInputStream();
|
||||||
String output = new String(input.readAllBytes(), Charset.defaultCharset());
|
String output = new String(input.readAllBytes(), Charset.defaultCharset());
|
||||||
|
|
||||||
|
@ -153,6 +157,10 @@ 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)
|
||||||
{
|
{
|
||||||
|
@ -166,7 +174,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, INFO_TIMEOUT);
|
Process exec = runCommand(cmd, 5);
|
||||||
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();
|
||||||
|
@ -174,19 +182,6 @@ 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);
|
||||||
|
@ -219,7 +214,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, DOWNLOAD_TIMEOUT);
|
Process exec = runCommand(cmd, 300);
|
||||||
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,7 +9,6 @@ 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -19,15 +18,12 @@ 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 SAMPLE_RATE_PATTERN = Pattern.compile("\\b([0-9]+)k(b(ps?))?\\b");
|
private static final Pattern BITRATE_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)
|
||||||
{
|
{
|
||||||
|
@ -37,28 +33,6 @@ 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(
|
||||||
|
@ -81,21 +55,11 @@ class Format
|
||||||
|
|
||||||
public boolean isAudioOnly()
|
public boolean isAudioOnly()
|
||||||
{
|
{
|
||||||
if (audioOnly)
|
return resolution.trim().toLowerCase().contains("audio only");
|
||||||
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())
|
||||||
|
@ -122,13 +86,10 @@ class Format
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSampleRate()
|
public int getBitrate()
|
||||||
{
|
{
|
||||||
if (samplerate != -1)
|
|
||||||
return samplerate;
|
|
||||||
|
|
||||||
// try to find eg. "51k" inside note
|
// try to find eg. "51k" inside note
|
||||||
Matcher matcher = SAMPLE_RATE_PATTERN.matcher(note);
|
Matcher matcher = BITRATE_PATTERN.matcher(note);
|
||||||
if (matcher.find())
|
if (matcher.find())
|
||||||
{
|
{
|
||||||
return Integer.parseInt(matcher.group(1)) * 1000;
|
return Integer.parseInt(matcher.group(1)) * 1000;
|
||||||
|
@ -136,16 +97,6 @@ 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.getSampleRate() / 1000;
|
final int bitrate = format.getBitrate() / 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";
|
||||||
|
|
|
@ -26,8 +26,6 @@ import java.util.function.Consumer;
|
||||||
*/
|
*/
|
||||||
public class QueueManager implements Consumer<Song>
|
public class QueueManager implements Consumer<Song>
|
||||||
{
|
{
|
||||||
|
|
||||||
private Song restartingSong = null;
|
|
||||||
private final Queue<Song> jukeboxQueue;
|
private final Queue<Song> jukeboxQueue;
|
||||||
private Playlist playlist;
|
private Playlist playlist;
|
||||||
private MusicHandler handler;
|
private MusicHandler handler;
|
||||||
|
@ -54,13 +52,6 @@ public class QueueManager implements Consumer<Song>
|
||||||
*/
|
*/
|
||||||
public Song nextSongNeeded()
|
public Song nextSongNeeded()
|
||||||
{
|
{
|
||||||
// if we're restarting the current song: store, clear, and return it
|
|
||||||
if (restartingSong != null)
|
|
||||||
{
|
|
||||||
Song ret = restartingSong;
|
|
||||||
restartingSong = null;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
// if there's anything in the queue, play that first
|
// if there's anything in the queue, play that first
|
||||||
if (!jukeboxQueue.isEmpty())
|
if (!jukeboxQueue.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -127,12 +118,6 @@ public class QueueManager implements Consumer<Song>
|
||||||
|
|
||||||
public boolean restartSong()
|
public boolean restartSong()
|
||||||
{
|
{
|
||||||
restartingSong = handler.getCurrentSong();
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
if (restartingSong != null)
|
|
||||||
{
|
|
||||||
handler.playNext();
|
|
||||||
return true;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue