Implemented basic music playing

This commit is contained in:
Anthony committed 2018-03-04 21:50:23 -05:00
1 parent 989ba7ec8b
commit 54873d85a6
8 files changed
+337 -4

No files matched your search

@@ -0,0 +1,68 @@
package netneutrality.command.music;
/*
* Copyright (c) anthorob
*
* This file is closed-source. It should not be distributed or copied in any way or form
*/
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import netneutrality.command.AbstractCommand;
import netneutrality.command.Visibility;
import netneutrality.music.MusicManager;
public class PlayCommand extends AbstractCommand {
@Override
public boolean needAdmin() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getCommand() {
return "play";
}
@Override
public String[] getAlias() {
return new String[0];
}
@Override
public String getUsage() {
return null;
}
@Override
public Visibility getVisibility() {
return Visibility.CHANNEL;
}
@Override
public String execute(MessageReceivedEvent event, String args) {
Member member = event.getMember();
MusicManager player = MusicManager.get(event.getGuild());
if (member.getVoiceState().inVoiceChannel()) {
VoiceChannel vChannel = member.getVoiceState().getChannel();
if (player.isConnected()) {
if (player.isConnectedTo(vChannel)) {
player.play(args.split(" ")[0], event.getTextChannel());
} else
event.getTextChannel().sendMessage("Not in your voice channel").queue();
} else
event.getTextChannel().sendMessage("Not in a voice channel").queue();
} else
event.getTextChannel().sendMessage("You are not in a voice channel").queue();
return null;
}
}
@@ -0,0 +1,10 @@
package netneutrality.command.music;
/*
* Copyright (c) anthorob
*
* This file is closed-source. It should not be distributed or copied in any way or form
*/
public class StopCommand {
}
@@ -0,0 +1,70 @@
package netneutrality.command.music;
/*
* Copyright (c) anthorob
*
* This file is closed-source. It should not be distributed or copied in any way or form
*/
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import netneutrality.command.AbstractCommand;
import netneutrality.command.Visibility;
import netneutrality.music.MusicManager;
public class VolumeCommand extends AbstractCommand {
@Override
public boolean needAdmin() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getCommand() {
return "volume";
}
@Override
public String[] getAlias() {
return new String[0];
}
@Override
public String getUsage() {
return null;
}
@Override
public Visibility getVisibility() {
return Visibility.CHANNEL;
}
@Override
public String execute(MessageReceivedEvent event, String args) {
Member member = event.getMember();
MusicManager player = MusicManager.get(event.getGuild());
if (member.getVoiceState().inVoiceChannel()) {
VoiceChannel vChannel = member.getVoiceState().getChannel();
if (player.isConnected()) {
if (player.isConnectedTo(vChannel)) {
int volume;
volume = Integer.parseInt(args.split(" ")[0]);
player.setVolume(volume);
} else
event.getTextChannel().sendMessage("Not in your voice channel").queue();
} else
event.getTextChannel().sendMessage("Not in a voice channel").queue();
} else
event.getTextChannel().sendMessage("You are not in a voice channel").queue();
return null;
}
}
@@ -27,12 +27,16 @@ public class WebModule {
* @return an instance of the WebModule class with the Response of the Web Request * @return an instance of the WebModule class with the Response of the Web Request
* @throws IOException * @throws IOException
*/ */
public WebModule request() throws IOException { public WebModule request() {
Request request = new Request.Builder().url(this.url) Request request = new Request.Builder().url(this.url)
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36") .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36")
.build(); .build();
try {
response = client.newCall(request).execute(); response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
return this; return this;
} }
@@ -145,5 +149,29 @@ public class WebModule {
return response.body().string(); return response.body().string();
} }
public String getTextInHtmlTag(String tag) {
String body = "";
try {
body = getRawData();
} catch (IOException | RuntimeException e) {
e.printStackTrace();
}
int startOfTag = body.indexOf(tag);
int endOfTag = body.indexOf(getEndHtmlTag(tag));
if (startOfTag < 0 || endOfTag < 0) {
return "";
}
return body.substring(startOfTag + tag.length(), endOfTag - (tag.length() - 1));
}
private String getEndHtmlTag(String startingTag) {
String endingTag = "";
if (startingTag.charAt(0) == '<' && startingTag.charAt(1) != '/' ) {
endingTag = "</" + startingTag.substring(1, startingTag.length() - 1) + ">";
}
System.out.println("endingTag = " + endingTag);
return endingTag;
}
} }
@@ -0,0 +1,54 @@
package netneutrality.music;
/*
* Copyright (c) anthorob
*
* This file is closed-source. It should not be distributed or copied in any way or form
*/
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
import net.dv8tion.jda.core.audio.AudioSendHandler;
/**
* This is a wrapper around AudioPlayer which makes it behave as an AudioSendHandler for JDA. As JDA calls canProvide
* before every call to provide20MsAudio(), we pull the frame in canProvide() and use the frame we already pulled in
* provide20MsAudio().
*/
public class AudioPlayerSendHandler implements AudioSendHandler {
private final AudioPlayer audioPlayer;
private AudioFrame lastFrame;
/**
* @param audioPlayer Audio player to wrap.
*/
public AudioPlayerSendHandler(AudioPlayer audioPlayer) {
this.audioPlayer = audioPlayer;
}
@Override
public boolean canProvide() {
if (lastFrame == null) {
lastFrame = audioPlayer.provide();
}
return lastFrame != null;
}
@Override
public byte[] provide20MsAudio() {
if (lastFrame == null) {
lastFrame = audioPlayer.provide();
}
byte[] data = lastFrame != null ? lastFrame.data : null;
lastFrame = null;
return data;
}
@Override
public boolean isOpus() {
return true;
}
}
@@ -6,13 +6,20 @@ package netneutrality.music;
* This file is closed-source. It should not be distributed or copied in any way or form * This file is closed-source. It should not be distributed or copied in any way or form
*/ */
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers; import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import net.dv8tion.jda.core.entities.Guild; import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel; import net.dv8tion.jda.core.entities.VoiceChannel;
import netneutrality.MusicBot; import netneutrality.MusicBot;
import netneutrality.handler.LogHandler;
import netneutrality.utils.YoutubeClass;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -30,10 +37,13 @@ public class MusicManager {
this.guildId = guild.getIdLong(); this.guildId = guild.getIdLong();
player = playerManager.createPlayer(); player = playerManager.createPlayer();
guild.getAudioManager().setSendingHandler(new AudioPlayerSendHandler(player));
scheduler = new TrackScheduler(player); scheduler = new TrackScheduler(player);
player.addListener(scheduler); player.addListener(scheduler);
player.setVolume(30);
AudioSourceManagers.registerRemoteSources(playerManager); AudioSourceManagers.registerRemoteSources(playerManager);
AudioSourceManagers.registerLocalSource(playerManager);
managers.put(guild.getId(), this); managers.put(guild.getId(), this);
} }
@@ -79,4 +89,42 @@ public class MusicManager {
public synchronized void stopMusic() { public synchronized void stopMusic() {
player.destroy(); player.destroy();
} }
public synchronized void play(String url, TextChannel channel) {
String youtubeCode = YoutubeClass.getCodeFromUrl(url);
System.out.println(youtubeCode);
if (!youtubeCode.isEmpty()) {
playerManager.loadItem(youtubeCode, new AudioLoadResultHandler() {
@Override
public void trackLoaded(AudioTrack audioTrack) {
channel.sendMessage("Starting track [" + audioTrack.getInfo().title + "]").queue();
scheduler.queue(audioTrack);
}
@Override
public void playlistLoaded(AudioPlaylist playlist) {
for (AudioTrack track : playlist.getTracks()) {
scheduler.queue(track);
}
}
@Override
public void noMatches() {
System.out.println("Oh shit! I got nothing");
}
@Override
public void loadFailed(FriendlyException e) {
LogHandler.log(e);
}
});
}
}
public void setVolume(int volume) {
player.setVolume(volume);
}
} }
@@ -12,12 +12,32 @@ import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class TrackScheduler extends AudioEventAdapter { public class TrackScheduler extends AudioEventAdapter {
private AudioPlayer player; private final AudioPlayer player;
private final BlockingQueue<AudioTrack> queue;
public TrackScheduler(AudioPlayer player) { public TrackScheduler(AudioPlayer player) {
this.player = player; this.player = player;
this.queue = new LinkedBlockingQueue<>();
}
public void queue(AudioTrack track) {
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If
// something is playing, it returns false and does nothing. In that case the player was already playing so this
// track goes to the queue instead.
if (!player.startTrack(track, true)) {
queue.offer(track);
}
}
public void nextTrack() {
// Start the next track, regardless of if something is already playing or not. In case queue was empty, we are
// giving null to startTrack, which is a valid argument and will simply stop the player.
player.startTrack(queue.poll(), false);
} }
@Override @Override
@@ -38,7 +58,7 @@ public class TrackScheduler extends AudioEventAdapter {
@Override @Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
if (endReason.mayStartNext) { if (endReason.mayStartNext) {
// Start next track nextTrack();
} }
// endReason == FINISHED: A track finished or died by an exception (mayStartNext = true). // endReason == FINISHED: A track finished or died by an exception (mayStartNext = true).
@@ -0,0 +1,35 @@
package netneutrality.utils;
/*
* Copyright (c) anthorob
*
* This file is closed-source. It should not be distributed or copied in any way or form
*/
import netneutrality.http.WebModule;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class YoutubeClass {
public static Pattern urlPattern = Pattern.compile("^(?:https?:\\/\\/)?(?:(?:www\\.)?)?(?:youtube\\.com|youtu\\.be)\\/.*?(?:embed|e|v|watch.*?v=)?\\/?([-_a-z0-9]{10,})?(?:&?index=\\d+)?(?>(?:playlist\\?|&)?list=([^#\\\\&\\?]{12,}))?", Pattern.CASE_INSENSITIVE);
private final static Pattern youtubeCode = Pattern.compile("^[A-Za-z0-9_-]{11}$");
public static String getCodeFromUrl(String url) {
Matcher matcher = urlPattern.matcher(url);
if (matcher.find()) {
if (matcher.group(1) != null) {
return matcher.group(1);
}
}
return "";
}
public static String getVideoTitle(String url) {
WebModule youtube = new WebModule(url).request();
return youtube.getTextInHtmlTag("<title>");
}
}