|
|
@ -5,26 +5,21 @@ |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
package moe.nekojimi.chords; |
|
|
|
package moe.nekojimi.chords; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.Closeable; |
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.io.File; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.net.MalformedURLException; |
|
|
|
import java.net.MalformedURLException; |
|
|
|
import java.net.URL; |
|
|
|
import java.net.URL; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.util.*; |
|
|
|
import java.util.*; |
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue; |
|
|
|
|
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.logging.Level; |
|
|
|
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.security.auth.login.LoginException; |
|
|
|
import javax.security.auth.login.LoginException; |
|
|
|
import javax.sound.sampled.*; |
|
|
|
|
|
|
|
import net.dv8tion.jda.api.JDA; |
|
|
|
import net.dv8tion.jda.api.JDA; |
|
|
|
import net.dv8tion.jda.api.JDABuilder; |
|
|
|
import net.dv8tion.jda.api.JDABuilder; |
|
|
|
import net.dv8tion.jda.api.audio.AudioSendHandler; |
|
|
|
|
|
|
|
import net.dv8tion.jda.api.entities.*; |
|
|
|
import net.dv8tion.jda.api.entities.*; |
|
|
|
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; |
|
|
|
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; |
|
|
|
import net.dv8tion.jda.api.hooks.ListenerAdapter; |
|
|
|
import net.dv8tion.jda.api.hooks.ListenerAdapter; |
|
|
@ -81,16 +76,32 @@ public class Main extends ListenerAdapter |
|
|
|
if (author.isBot()) |
|
|
|
if (author.isBot()) |
|
|
|
return; |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (content.startsWith("!join ")) |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
String arg = content.substring("!join ".length()); |
|
|
|
|
|
|
|
|
|
|
|
String arg = ""; |
|
|
|
|
|
|
|
if (content.contains(" ")) |
|
|
|
|
|
|
|
arg = content.split(" ", 2)[1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (content.startsWith("!join ")) |
|
|
|
onJoinCommand(event, guild, arg); |
|
|
|
onJoinCommand(event, guild, arg); |
|
|
|
} else if (content.equals("!join")) |
|
|
|
else if (content.equals("!join")) |
|
|
|
onJoinCommand(event); |
|
|
|
onJoinCommand(event); |
|
|
|
else if (content.startsWith("!play ")) |
|
|
|
else if (content.startsWith("!play ")) |
|
|
|
{ |
|
|
|
|
|
|
|
String arg = content.substring("!join ".length()); |
|
|
|
|
|
|
|
onPlayCommand(event, guild, arg); |
|
|
|
onPlayCommand(event, guild, arg); |
|
|
|
|
|
|
|
else if (content.startsWith("!queue")) |
|
|
|
|
|
|
|
onQueueCommand(event, guild); |
|
|
|
|
|
|
|
else if (content.startsWith("!remove ")) |
|
|
|
|
|
|
|
onRemoveCommand(event, guild, arg); |
|
|
|
|
|
|
|
else if (content.startsWith("!restart")) |
|
|
|
|
|
|
|
onRestartCommand(event); |
|
|
|
|
|
|
|
else if (content.startsWith("!skip")) |
|
|
|
|
|
|
|
onSkipCommand(event); |
|
|
|
|
|
|
|
else if (content.startsWith("!")) |
|
|
|
|
|
|
|
onHelpCommand(event); |
|
|
|
|
|
|
|
} catch (Exception ex) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
event.getChannel().sendMessage("Error in command! " + ex.getMessage()).queue(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -129,7 +140,6 @@ public class Main extends ListenerAdapter |
|
|
|
boolean isNumber = arg.matches("\\d+"); // This is a regular expression that ensures the input consists of digits
|
|
|
|
boolean isNumber = arg.matches("\\d+"); // This is a regular expression that ensures the input consists of digits
|
|
|
|
VoiceChannel channel = null; |
|
|
|
VoiceChannel channel = null; |
|
|
|
if (isNumber) // The input is an id?
|
|
|
|
if (isNumber) // The input is an id?
|
|
|
|
|
|
|
|
|
|
|
|
channel = guild.getVoiceChannelById(arg); |
|
|
|
channel = guild.getVoiceChannelById(arg); |
|
|
|
if (channel == null) // Then the input must be a name?
|
|
|
|
if (channel == null) // Then the input must be a name?
|
|
|
|
{ |
|
|
|
{ |
|
|
@ -158,6 +168,8 @@ public class Main extends ListenerAdapter |
|
|
|
if (ok) |
|
|
|
if (ok) |
|
|
|
{ |
|
|
|
{ |
|
|
|
musicHandler.addSong(song); |
|
|
|
musicHandler.addSong(song); |
|
|
|
|
|
|
|
if (musicHandler.isPlaying()) |
|
|
|
|
|
|
|
musicHandler.setPlaying(true); |
|
|
|
event.getChannel().sendMessage("Downloaded and added to queue!").queue(); |
|
|
|
event.getChannel().sendMessage("Downloaded and added to queue!").queue(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -171,21 +183,85 @@ public class Main extends ListenerAdapter |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void onRestartCommand(GuildMessageReceivedEvent event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// TODO: this needs to clear the current data queue
|
|
|
|
|
|
|
|
boolean ok = musicHandler.restartSong(); |
|
|
|
|
|
|
|
if (ok) |
|
|
|
|
|
|
|
event.getChannel().sendMessage("Restarted current song!").queue(); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
event.getChannel().sendMessage("Cannot restart!").queue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void onSkipCommand(GuildMessageReceivedEvent event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
boolean ok = musicHandler.nextSong(true); |
|
|
|
|
|
|
|
if (ok) |
|
|
|
|
|
|
|
event.getChannel().sendMessage("Skipped to next song!").queue(); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
event.getChannel().sendMessage("There's no more songs!").queue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void onQueueCommand(GuildMessageReceivedEvent event, Guild guild) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Queue<Song> songQueue = musicHandler.getSongQueue(); |
|
|
|
|
|
|
|
String message = new String(); |
|
|
|
|
|
|
|
int i = 1; |
|
|
|
|
|
|
|
message += "Now playing: " + musicHandler.getCurrentSong() + "\n"; |
|
|
|
|
|
|
|
message += "---\n"; |
|
|
|
|
|
|
|
for (Song song : songQueue) |
|
|
|
|
|
|
|
message += (i) + ": " + song + "\n"; |
|
|
|
|
|
|
|
event.getChannel().sendMessage(message).queue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void onRemoveCommand(GuildMessageReceivedEvent event, Guild guild, String arg) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
try |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int i = Integer.parseInt(arg); |
|
|
|
|
|
|
|
boolean removed = musicHandler.removeSong(i - 1); |
|
|
|
|
|
|
|
final int size = musicHandler.getSongQueue().size(); |
|
|
|
|
|
|
|
if (removed) |
|
|
|
|
|
|
|
event.getChannel().sendMessage("Song removed.").queue(); |
|
|
|
|
|
|
|
else if (size > 1) |
|
|
|
|
|
|
|
event.getChannel().sendMessage("That's not a number between 1 and " + size + "!").queue(); |
|
|
|
|
|
|
|
else if (size == 1) |
|
|
|
|
|
|
|
event.getChannel().sendMessage("There's only one song to remove and that's not one of them!").queue(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (NumberFormatException ex) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
event.getChannel().sendMessage(arg + " isn't a number!").queue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void onHelpCommand(GuildMessageReceivedEvent event) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
String help = "Commands available:\n" |
|
|
|
|
|
|
|
+ "!join <Channel> - Joins a voice channel\n" |
|
|
|
|
|
|
|
+ "!play <URL> - Downloads the track at that URL and adds it to the queue.\n" |
|
|
|
|
|
|
|
+ "!queue - Show the songs currently playing and the current queue.\n" |
|
|
|
|
|
|
|
+ "!remove <Index> - Remove the song at position <Index> from the queue.\n" |
|
|
|
|
|
|
|
+ "!skip - Skip the current song and play the next one.\n" |
|
|
|
|
|
|
|
+ "!restart - Try playing the current song again in case it goes wrong.\n"; |
|
|
|
|
|
|
|
event.getChannel().sendMessage(help).queue(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean downloadSong(Song song) throws IOException, RuntimeException, InterruptedException |
|
|
|
private boolean downloadSong(Song song) throws IOException, RuntimeException, InterruptedException |
|
|
|
{ |
|
|
|
{ |
|
|
|
String cmd = "/usr/bin/youtube-dl -x --audio-format=wav " + song.getUrl().toString(); |
|
|
|
String cmd = "/usr/bin/youtube-dl -x --audio-format=wav --no-playlist --write-info-json " + song.getUrl().toString(); |
|
|
|
System.out.println("Running command: " + cmd); |
|
|
|
System.out.println("Running command: " + cmd); |
|
|
|
// Process exec = Runtime.getRuntime().exec().split(" "));
|
|
|
|
// Process exec = Runtime.getRuntime().exec().split(" "));
|
|
|
|
Process exec = new ProcessBuilder(cmd.split(" ")).redirectOutput(ProcessBuilder.Redirect.PIPE).start(); |
|
|
|
Process exec = new ProcessBuilder(cmd.split(" ")).redirectOutput(ProcessBuilder.Redirect.PIPE).start(); |
|
|
|
exec.waitFor(10000, TimeUnit.MILLISECONDS); |
|
|
|
exec.waitFor(10, TimeUnit.SECONDS); |
|
|
|
InputStream in = exec.getInputStream(); |
|
|
|
InputStream in = exec.getInputStream(); |
|
|
|
String output = new String(in.readAllBytes(), Charset.defaultCharset()); |
|
|
|
String output = new String(in.readAllBytes(), Charset.defaultCharset()); |
|
|
|
System.out.println(output); |
|
|
|
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) |
|
|
|
if (exec.exitValue() != 0) |
|
|
|
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue()); |
|
|
|
throw new RuntimeException("youtube-dl failed with error " + exec.exitValue()); |
|
|
|
Matcher matcher = Pattern.compile("Destination: (.*\\.wav)").matcher(output); |
|
|
|
|
|
|
|
matcher.find(); |
|
|
|
|
|
|
|
song.setLocation(new File(matcher.group(0))); |
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
// String destination = matcher.group(1);
|
|
|
|
// String destination = matcher.group(1);
|
|
|
|
// return destination;
|
|
|
|
// return destination;
|
|
|
|