|
|
@ -6,8 +6,12 @@ |
|
|
|
package moe.nekojimi.chords; |
|
|
|
package moe.nekojimi.chords; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.io.File; |
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
|
|
|
|
import java.nio.file.Files; |
|
|
|
|
|
|
|
import java.nio.file.attribute.FileAttribute; |
|
|
|
|
|
|
|
import java.nio.file.attribute.PosixFilePermission; |
|
|
|
import java.util.LinkedList; |
|
|
|
import java.util.LinkedList; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Queue; |
|
|
|
import java.util.Queue; |
|
|
@ -20,6 +24,9 @@ import java.util.logging.Level; |
|
|
|
import java.util.logging.Logger; |
|
|
|
import java.util.logging.Logger; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
|
|
|
import javax.json.Json; |
|
|
|
|
|
|
|
import javax.json.JsonObject; |
|
|
|
|
|
|
|
import javax.json.JsonReader; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* |
|
|
|
* |
|
|
@ -33,6 +40,8 @@ public class Downloader implements Consumer<Song> |
|
|
|
private Consumer<Song> next; |
|
|
|
private Consumer<Song> next; |
|
|
|
private BiConsumer<Song, Exception> messageHandler; |
|
|
|
private BiConsumer<Song, Exception> messageHandler; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private File downloadDir = null; |
|
|
|
|
|
|
|
|
|
|
|
public Downloader() |
|
|
|
public Downloader() |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
@ -42,6 +51,7 @@ public class Downloader implements Consumer<Song> |
|
|
|
public void accept(Song song) |
|
|
|
public void accept(Song song) |
|
|
|
{ |
|
|
|
{ |
|
|
|
downloadQueue.add(song); |
|
|
|
downloadQueue.add(song); |
|
|
|
|
|
|
|
getInfo(song); |
|
|
|
exec.submit(new Runnable() |
|
|
|
exec.submit(new Runnable() |
|
|
|
{ |
|
|
|
{ |
|
|
|
@Override |
|
|
|
@Override |
|
|
@ -57,29 +67,49 @@ public class Downloader implements Consumer<Song> |
|
|
|
this.next = next; |
|
|
|
this.next = next; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void download(Song song) |
|
|
|
private void getInfo(Song song) |
|
|
|
{ |
|
|
|
{ |
|
|
|
try |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
messageHandler.accept(song, null); |
|
|
|
String cmd = "/usr/bin/youtube-dl --skip-download --print-json " + song.getUrl().toString(); |
|
|
|
String cmd = "/usr/bin/youtube-dl -x -f=worstaudio/worst --audio-format=wav --no-playlist --write-info-json " + song.getUrl().toString(); |
|
|
|
Process exec = runCommand(cmd, 5); |
|
|
|
System.out.println("Running command: " + cmd); |
|
|
|
InputStream input = exec.getInputStream(); |
|
|
|
// Process exec = Runtime.getRuntime().exec().split(" "));
|
|
|
|
JsonReader reader = Json.createReader(input); |
|
|
|
Process exec = new ProcessBuilder(cmd.split(" ")).redirectOutput(ProcessBuilder.Redirect.PIPE).start(); |
|
|
|
JsonObject object = reader.readObject(); |
|
|
|
boolean done = exec.waitFor(300, TimeUnit.SECONDS); |
|
|
|
if (song.getTitle() == null) |
|
|
|
if (!done) |
|
|
|
song.setTitle(object.getString("title", null)); |
|
|
|
|
|
|
|
if (song.getArtist() == null) |
|
|
|
|
|
|
|
song.setArtist(object.getString("uploader", null)); |
|
|
|
|
|
|
|
} catch (Exception ex) |
|
|
|
{ |
|
|
|
{ |
|
|
|
exec.destroyForcibly(); |
|
|
|
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex); |
|
|
|
throw new RuntimeException("Took too long to download, giving up."); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private File getDownloadDir() throws IOException |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (downloadDir == null || !downloadDir.exists() || !downloadDir.canWrite()) |
|
|
|
|
|
|
|
downloadDir = Files.createTempDirectory("chords").toFile(); |
|
|
|
|
|
|
|
return downloadDir; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void download(Song song) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messageHandler.accept(song, null); |
|
|
|
|
|
|
|
String cmd = "/usr/bin/youtube-dl -x -f=worstaudio/worst --audio-format=wav --no-playlist -o " + getDownloadDir().getAbsolutePath() + "/%(title)s.%(ext)s " + song.getUrl().toString(); |
|
|
|
|
|
|
|
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()); |
|
|
|
System.out.println(output); |
|
|
|
System.out.println(output); |
|
|
|
Matcher matcher = Pattern.compile("Destination: (.*\\.wav)").matcher(output); |
|
|
|
Matcher matcher = Pattern.compile("Destination: (.*\\.wav)").matcher(output); |
|
|
|
if (matcher.find()) |
|
|
|
if (matcher.find()) |
|
|
|
song.setLocation(new File(matcher.group(1))); |
|
|
|
song.setLocation(new File(matcher.group(1))); |
|
|
|
else if (exec.exitValue() != 0) |
|
|
|
else if (exec.exitValue() != 0) |
|
|
|
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue()); |
|
|
|
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue() + ", output:\n" + error); |
|
|
|
// return true;
|
|
|
|
// return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (next != null) |
|
|
|
if (next != null) |
|
|
@ -95,6 +125,20 @@ public class Downloader implements Consumer<Song> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Process runCommand(String cmd, int timeoutSecs) throws RuntimeException, IOException, InterruptedException |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
System.out.println("Running command: " + cmd); |
|
|
|
|
|
|
|
// Process exec = Runtime.getRuntime().exec().split(" "));
|
|
|
|
|
|
|
|
Process exec = new ProcessBuilder(cmd.split(" ")).redirectOutput(ProcessBuilder.Redirect.PIPE).redirectError(ProcessBuilder.Redirect.PIPE).start(); |
|
|
|
|
|
|
|
boolean done = exec.waitFor(timeoutSecs, TimeUnit.SECONDS); |
|
|
|
|
|
|
|
if (!done) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
exec.destroyForcibly(); |
|
|
|
|
|
|
|
throw new RuntimeException("Took too long, giving up."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return exec; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public BiConsumer<Song, Exception> getMessageHandler() |
|
|
|
public BiConsumer<Song, Exception> getMessageHandler() |
|
|
|
{ |
|
|
|
{ |
|
|
|
return messageHandler; |
|
|
|
return messageHandler; |
|
|
|