Implemented basic music playing
This commit is contained in:
1 parent
989ba7ec8b
commit
54873d85a6
8 files changed
+336
-3
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
|
||||
* @throws IOException
|
||||
*/
|
||||
public WebModule request() throws IOException {
|
||||
public WebModule request() {
|
||||
|
||||
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")
|
||||
.build();
|
||||
try {
|
||||
response = client.newCall(request).execute();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -145,5 +149,29 @@ public class WebModule {
|
||||
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
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
|
||||
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.TextChannel;
|
||||
import net.dv8tion.jda.core.entities.VoiceChannel;
|
||||
import netneutrality.MusicBot;
|
||||
import netneutrality.handler.LogHandler;
|
||||
import netneutrality.utils.YoutubeClass;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -30,10 +37,13 @@ public class MusicManager {
|
||||
|
||||
this.guildId = guild.getIdLong();
|
||||
player = playerManager.createPlayer();
|
||||
guild.getAudioManager().setSendingHandler(new AudioPlayerSendHandler(player));
|
||||
scheduler = new TrackScheduler(player);
|
||||
player.addListener(scheduler);
|
||||
player.setVolume(30);
|
||||
|
||||
AudioSourceManagers.registerRemoteSources(playerManager);
|
||||
AudioSourceManagers.registerLocalSource(playerManager);
|
||||
|
||||
managers.put(guild.getId(), this);
|
||||
}
|
||||
@@ -79,4 +89,42 @@ public class MusicManager {
|
||||
public synchronized void stopMusic() {
|
||||
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.AudioTrackEndReason;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class TrackScheduler extends AudioEventAdapter {
|
||||
|
||||
private AudioPlayer player;
|
||||
private final AudioPlayer player;
|
||||
private final BlockingQueue<AudioTrack> queue;
|
||||
|
||||
public TrackScheduler(AudioPlayer 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
|
||||
@@ -38,7 +58,7 @@ public class TrackScheduler extends AudioEventAdapter {
|
||||
@Override
|
||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||
if (endReason.mayStartNext) {
|
||||
// Start next track
|
||||
nextTrack();
|
||||
}
|
||||
|
||||
// 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>");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user