Implement multithreaded downloading (Downloader class)
This commit is contained in:
parent
2f7bb13dc6
commit
c8810ca271
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package moe.nekojimi.chords;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jimj316
|
||||
*/
|
||||
public class Downloader implements Consumer<Song>
|
||||
{
|
||||
// private final Queue<Song> downloadQueue = new LinkedBlockingDeque<>();
|
||||
private final LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
|
||||
private final ThreadPoolExecutor exec = new ThreadPoolExecutor(1, 4, 30, TimeUnit.SECONDS, workQueue);
|
||||
private Consumer<Song> next;
|
||||
private BiConsumer<Song, Exception> messageHandler;
|
||||
|
||||
public Downloader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Song song)
|
||||
{
|
||||
exec.submit(() ->
|
||||
{
|
||||
download(song);
|
||||
});
|
||||
}
|
||||
|
||||
public void setNext(Consumer<Song> next)
|
||||
{
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
private void download(Song song)
|
||||
{
|
||||
try
|
||||
{
|
||||
messageHandler.accept(song, null);
|
||||
String cmd = "/usr/bin/youtube-dl -x --audio-format=wav --no-playlist --write-info-json " + song.getUrl().toString();
|
||||
System.out.println("Running command: " + cmd);
|
||||
// Process exec = Runtime.getRuntime().exec().split(" "));
|
||||
Process exec = new ProcessBuilder(cmd.split(" ")).redirectOutput(ProcessBuilder.Redirect.PIPE).start();
|
||||
boolean done = exec.waitFor(30, TimeUnit.SECONDS);
|
||||
if (!done)
|
||||
{
|
||||
exec.destroyForcibly();
|
||||
throw new RuntimeException("Took too long to download, giving up.");
|
||||
}
|
||||
InputStream in = exec.getInputStream();
|
||||
String output = new String(in.readAllBytes(), Charset.defaultCharset());
|
||||
System.out.println(output);
|
||||
Matcher matcher = Pattern.compile("Destination: (.*\\.wav)").matcher(output);
|
||||
if (matcher.find())
|
||||
song.setLocation(new File(matcher.group(1)));
|
||||
else if (exec.exitValue() != 0)
|
||||
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue());
|
||||
// return true;
|
||||
|
||||
if (next != null)
|
||||
next.accept(song);
|
||||
messageHandler.accept(song, null);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex);
|
||||
if (messageHandler != null)
|
||||
messageHandler.accept(song, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public BiConsumer<Song, Exception> getMessageHandler()
|
||||
{
|
||||
return messageHandler;
|
||||
}
|
||||
|
||||
public void setMessageHandler(BiConsumer<Song, Exception> messageHandler)
|
||||
{
|
||||
this.messageHandler = messageHandler;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,18 +5,9 @@
|
|||
*/
|
||||
package moe.nekojimi.chords;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
|
@ -35,6 +26,10 @@ import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
|||
public class Main extends ListenerAdapter
|
||||
{
|
||||
|
||||
private MusicHandler musicHandler;
|
||||
private Downloader downloader;
|
||||
private JDA jda;
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
|
@ -59,9 +54,35 @@ public class Main extends ListenerAdapter
|
|||
// Set activity (like "playing Something")
|
||||
builder.setActivity(Activity.playing("music!"));
|
||||
|
||||
builder.addEventListeners(new Main());
|
||||
final Main listener = new Main();
|
||||
|
||||
builder.addEventListeners(listener);
|
||||
|
||||
JDA jda = builder.build();
|
||||
listener.setJda(jda);
|
||||
}
|
||||
|
||||
public Main()
|
||||
{
|
||||
downloader = new Downloader();
|
||||
downloader.setMessageHandler((Song song, Exception ex) ->
|
||||
{
|
||||
TextChannel channel = jda.getTextChannelById(song.getRequestedIn());
|
||||
if (channel != null)
|
||||
if (ex == null)
|
||||
if (song.getLocation() != null)
|
||||
channel.sendMessage("Finished downloading track for " + song.getRequestedBy() + ", added to queue!").queue();
|
||||
else
|
||||
channel.sendMessage("Now downloading track for " + song.getRequestedBy() + " ...").queue();
|
||||
else
|
||||
channel.sendMessage("Failed to download track for " + song.getRequestedBy() + "! Reason: " + ex.getMessage()).queue();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void setJda(JDA jda)
|
||||
{
|
||||
this.jda = jda;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,12 +138,13 @@ public class Main extends ListenerAdapter
|
|||
Member member = event.getMember(); // Member is the context of the user for the specific guild, containing voice state and roles
|
||||
GuildVoiceState voiceState = member.getVoiceState(); // Check the current voice state of the user
|
||||
VoiceChannel channel = voiceState.getChannel(); // Use the channel the user is currently connected to
|
||||
if (channel != null)
|
||||
{
|
||||
connectTo(channel); // Join the channel of the user
|
||||
onConnecting(channel, event.getChannel()); // Tell the user about our success
|
||||
} else
|
||||
onUnknownChannel(event.getChannel(), "your voice channel"); // Tell the user about our failure
|
||||
// if (channel != null)
|
||||
// {
|
||||
// connectTo(channel); // Join the channel of the user
|
||||
// onConnecting(channel, event.getChannel()); // Tell the user about our success
|
||||
// } else
|
||||
// onUnknownChannel(event.getChannel(), "your voice channel"); // Tell the user about our failure
|
||||
onJoinCommand(event, member.getGuild(), channel.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,23 +185,22 @@ public class Main extends ListenerAdapter
|
|||
try
|
||||
{
|
||||
Song song = new Song(new URL(arg));
|
||||
event.getChannel().sendMessage("Downloading ...").queue();
|
||||
boolean ok = downloadSong(song);
|
||||
if (ok)
|
||||
{
|
||||
musicHandler.addSong(song);
|
||||
if (musicHandler.isPlaying())
|
||||
musicHandler.setPlaying(true);
|
||||
event.getChannel().sendMessage("Downloaded and added to queue!").queue();
|
||||
}
|
||||
song.setRequestedBy(event.getAuthor().getId());
|
||||
song.setRequestedIn(event.getChannel().getId());
|
||||
// event.getChannel().sendMessage("Downloading ...").queue();
|
||||
downloader.accept(song);
|
||||
// boolean ok = downloadSong(song);
|
||||
// if (ok)
|
||||
// {
|
||||
// musicHandler.addSong(song);
|
||||
// if (musicHandler.isPlaying())
|
||||
// musicHandler.setPlaying(true);
|
||||
// event.getChannel().sendMessage("Downloaded and added to queue!").queue();
|
||||
// }
|
||||
|
||||
} catch (MalformedURLException ex)
|
||||
{
|
||||
event.getChannel().sendMessage("That's not a valid URL you idiot! " + ex.getMessage()).queue();
|
||||
} catch (IOException | InterruptedException | RuntimeException ex)
|
||||
{
|
||||
event.getChannel().sendMessage("Failed to download! Reason: " + ex.getMessage()).queue();
|
||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,27 +267,6 @@ public class Main extends ListenerAdapter
|
|||
event.getChannel().sendMessage(help).queue();
|
||||
}
|
||||
|
||||
private boolean downloadSong(Song song) throws IOException, RuntimeException, InterruptedException
|
||||
{
|
||||
String cmd = "/usr/bin/youtube-dl -x --audio-format=wav --no-playlist --write-info-json " + song.getUrl().toString();
|
||||
System.out.println("Running command: " + cmd);
|
||||
// Process exec = Runtime.getRuntime().exec().split(" "));
|
||||
Process exec = new ProcessBuilder(cmd.split(" ")).redirectOutput(ProcessBuilder.Redirect.PIPE).start();
|
||||
exec.waitFor(10, TimeUnit.SECONDS);
|
||||
InputStream in = exec.getInputStream();
|
||||
String output = new String(in.readAllBytes(), Charset.defaultCharset());
|
||||
System.out.println(output);
|
||||
Matcher matcher = Pattern.compile("Destination: (.*\\.wav)").matcher(output);
|
||||
if (matcher.find())
|
||||
song.setLocation(new File(matcher.group(1)));
|
||||
else
|
||||
if (exec.exitValue() != 0)
|
||||
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue());
|
||||
return true;
|
||||
// String destination = matcher.group(1);
|
||||
// return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform user about successful connection.
|
||||
*
|
||||
|
@ -306,6 +306,7 @@ public class Main extends ListenerAdapter
|
|||
// Get an audio manager for this guild, this will be created upon first use for each guild
|
||||
AudioManager audioManager = guild.getAudioManager();
|
||||
musicHandler = new MusicHandler();
|
||||
downloader.setNext(musicHandler);
|
||||
// Create our Send/Receive handler for the audio connection
|
||||
// EchoHandler handler = new EchoHandler();
|
||||
|
||||
|
@ -317,6 +318,5 @@ public class Main extends ListenerAdapter
|
|||
// Connect to the voice channel
|
||||
audioManager.openAudioConnection(channel);
|
||||
}
|
||||
private MusicHandler musicHandler;
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.nio.ByteBuffer;
|
|||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
@ -23,7 +24,7 @@ import net.dv8tion.jda.api.audio.AudioSendHandler;
|
|||
*
|
||||
* @author jimj316
|
||||
*/
|
||||
public class MusicHandler implements AudioSendHandler, Closeable
|
||||
public class MusicHandler implements AudioSendHandler, Closeable, Consumer<Song>
|
||||
{
|
||||
|
||||
private final LinkedList<Song> songQueue = new LinkedList<>();
|
||||
|
@ -204,4 +205,11 @@ public class MusicHandler implements AudioSendHandler, Closeable
|
|||
din.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Song t)
|
||||
{
|
||||
addSong(t);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package moe.nekojimi.chords;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
|
@ -19,8 +20,15 @@ public class Song
|
|||
private final URL url;
|
||||
private File location = null;
|
||||
|
||||
public Song(URL url)
|
||||
private String requestedBy;
|
||||
private String requestedIn;
|
||||
|
||||
public Song(URL url) throws MalformedURLException
|
||||
{
|
||||
if (url.toString().equalsIgnoreCase("https://soundcloud.com/user-185855194/fart-with-extra-reverb")
|
||||
|| url.toString().equalsIgnoreCase("https://www.youtube.com/watch?v=hr7GyFM7pX4"))
|
||||
this.url = new URL("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
|
||||
else
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
@ -66,6 +74,26 @@ public class Song
|
|||
location = null;
|
||||
}
|
||||
|
||||
public String getRequestedBy()
|
||||
{
|
||||
return requestedBy;
|
||||
}
|
||||
|
||||
public void setRequestedBy(String requestedBy)
|
||||
{
|
||||
this.requestedBy = requestedBy;
|
||||
}
|
||||
|
||||
public String getRequestedIn()
|
||||
{
|
||||
return requestedIn;
|
||||
}
|
||||
|
||||
public void setRequestedIn(String requestedIn)
|
||||
{
|
||||
this.requestedIn = requestedIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue