Implemented basic music playing
This commit is contained in:
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>");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in new issue
Block a user