From afb27df33dc7cb7df16266e83bded7616a5b0243 Mon Sep 17 00:00:00 2001 From: Matthew Cech Date: Thu, 17 Oct 2019 22:39:56 -0700 Subject: [PATCH] gitflow-hotfix-stash: slowmerge --- .classpath | 6 +- src/commands/music/CommandMusicMain.java | 7 +- src/commands/music/SubCommandAddTrack.java | 2 +- src/main/Main.java | 7 +- src/trash/AudioPlayerSendHandler.java | 41 +++++++ src/trash/GuildMusicManager.java | 34 ++++++ src/trash/MainFAKE.java | 126 +++++++++++++++++++++ src/trash/TrackScheduler.java | 55 +++++++++ 8 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 src/trash/AudioPlayerSendHandler.java create mode 100644 src/trash/GuildMusicManager.java create mode 100644 src/trash/MainFAKE.java create mode 100644 src/trash/TrackScheduler.java diff --git a/.classpath b/.classpath index 571ccd1..6cf8f5a 100644 --- a/.classpath +++ b/.classpath @@ -24,8 +24,8 @@ - - - + + + diff --git a/src/commands/music/CommandMusicMain.java b/src/commands/music/CommandMusicMain.java index 7176498..f9417b8 100644 --- a/src/commands/music/CommandMusicMain.java +++ b/src/commands/music/CommandMusicMain.java @@ -2,6 +2,7 @@ package commands.music; import core.Command; import core.LocStrings; +import core.SubCommandFormattable; import core.SubCommandFramework; import dataStructures.*; @@ -9,7 +10,7 @@ public class CommandMusicMain extends Command { SubCommandFramework framework = new SubCommandFramework(); public CommandMusicMain(KittyRole level, KittyRating rating) - { + { super(level, rating); framework.addCommand("join", new SubCommandJoin(KittyRole.General, KittyRating.Safe)); framework.addCommand("leave", new SubCommandLeave(KittyRole.General, KittyRating.Safe)); @@ -22,6 +23,8 @@ public class CommandMusicMain extends Command @Override public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) { - framework.run(guild, channel, user, input).Call(res); + SubCommandFormattable scf = framework.run(guild, channel, user, input); + System.out.println("SCF: " + scf.resString); + scf.Call(res); } } \ No newline at end of file diff --git a/src/commands/music/SubCommandAddTrack.java b/src/commands/music/SubCommandAddTrack.java index d8f7472..8ffef3f 100644 --- a/src/commands/music/SubCommandAddTrack.java +++ b/src/commands/music/SubCommandAddTrack.java @@ -25,8 +25,8 @@ public class SubCommandAddTrack extends SubCommand //Get track from Youtube String video = YT.getYT(input.args); ac.loadAndPlay(video, guild); + System.out.println("Internal: " + ac.response); - System.out.println(ac.response); return new SubCommandFormattable(video); } } \ No newline at end of file diff --git a/src/main/Main.java b/src/main/Main.java index 5f4beaa..463ad75 100644 --- a/src/main/Main.java +++ b/src/main/Main.java @@ -24,13 +24,14 @@ import dataStructures.Response; import dataStructures.UserInput; import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent; import net.dv8tion.jda.core.hooks.ListenerAdapter; +import trash.MainFAKE; import utils.GlobalLog; // http://www.slf4j.org/ - this JDA logging tool has been disabled by specifying NOP implementation. // This is the application entry point, and bot startup location! @SuppressWarnings("unused") -public class Main extends ListenerAdapter +public class Main// extends ListenerAdapter { // Variables and bot specific objects private static KittyCore kittyCore; @@ -45,6 +46,9 @@ public class Main extends ListenerAdapter // Initialization and setup public static void main(String[] args) throws InterruptedException, LoginException, Exception { + MainFAKE.mainFAKE(args); + } + /* // Factory startup. The ordering is intentional. GlobalLog.initialize(); databaseManager = ObjectBuilderFactory.constructDatabaseManager(); @@ -115,4 +119,5 @@ public class Main extends ListenerAdapter if(!Superintendent.perCommandUpkeepPost(kittyCore, databaseManager)) return; } + */ } \ No newline at end of file diff --git a/src/trash/AudioPlayerSendHandler.java b/src/trash/AudioPlayerSendHandler.java new file mode 100644 index 0000000..daf7414 --- /dev/null +++ b/src/trash/AudioPlayerSendHandler.java @@ -0,0 +1,41 @@ +package trash; + +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; + + public AudioPlayerSendHandler(AudioPlayer audioPlayer) + { + this.audioPlayer = audioPlayer; + } + + @Override + public boolean canProvide() + { + lastFrame = audioPlayer.provide(); + return lastFrame != null; + } + + @Override + public byte[] provide20MsAudio() + { + return lastFrame.getData(); + } + + @Override + public boolean isOpus() + { + return true; + } +} \ No newline at end of file diff --git a/src/trash/GuildMusicManager.java b/src/trash/GuildMusicManager.java new file mode 100644 index 0000000..478a3b0 --- /dev/null +++ b/src/trash/GuildMusicManager.java @@ -0,0 +1,34 @@ +package trash; +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; + +/** + * Holder for both the player and a track scheduler for one guild. + */ +public class GuildMusicManager { + /** + * Audio player for the guild. + */ + public final AudioPlayer player; + /** + * Track scheduler for the player. + */ + public final TrackScheduler scheduler; + + /** + * Creates a player and a track scheduler. + * @param manager Audio player manager to use for creating the player. + */ + public GuildMusicManager(AudioPlayerManager manager) { + player = manager.createPlayer(); + scheduler = new TrackScheduler(player); + player.addListener(scheduler); + } + + /** + * @return Wrapper around AudioPlayer to use it as an AudioSendHandler. + */ + public AudioPlayerSendHandler getSendHandler() { + return new AudioPlayerSendHandler(player); + } +} \ No newline at end of file diff --git a/src/trash/MainFAKE.java b/src/trash/MainFAKE.java new file mode 100644 index 0000000..9b4f006 --- /dev/null +++ b/src/trash/MainFAKE.java @@ -0,0 +1,126 @@ +package trash; +import java.util.HashMap; +import java.util.Map; + +import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; +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.AccountType; +import net.dv8tion.jda.core.JDA; +import net.dv8tion.jda.core.JDABuilder; +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.events.message.guild.GuildMessageReceivedEvent; +import net.dv8tion.jda.core.hooks.ListenerAdapter; +import net.dv8tion.jda.core.managers.AudioManager; +import offline.Ref; + +public class MainFAKE extends ListenerAdapter { + public static void mainFAKE(String[] args) throws Exception { + JDA bot = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking(); + bot.addEventListener(new MainFAKE()); + } + + private final AudioPlayerManager playerManager; + private final Map musicManagers; + + private MainFAKE() { + this.musicManagers = new HashMap<>(); + + this.playerManager = new DefaultAudioPlayerManager(); + AudioSourceManagers.registerRemoteSources(playerManager); + AudioSourceManagers.registerLocalSource(playerManager); + } + + private synchronized GuildMusicManager getGuildAudioPlayer(Guild guild) { + long guildId = Long.parseLong(guild.getId()); + GuildMusicManager musicManager = musicManagers.get(guildId); + + if (musicManager == null) { + musicManager = new GuildMusicManager(playerManager); + musicManagers.put(guildId, musicManager); + } + + guild.getAudioManager().setSendingHandler(musicManager.getSendHandler()); + + return musicManager; + } + + @Override + public void onGuildMessageReceived(GuildMessageReceivedEvent event) { + String[] command = event.getMessage().getContentRaw().split(" ", 2); + + if ("!play".equals(command[0]) && command.length == 2) { + loadAndPlay(event.getChannel(), command[1]); + } else if ("!skip".equals(command[0])) { + skipTrack(event.getChannel()); + } + + super.onGuildMessageReceived(event); + } + + private void loadAndPlay(final TextChannel channel, final String trackUrl) { + GuildMusicManager musicManager = getGuildAudioPlayer(channel.getGuild()); + + playerManager.loadItemOrdered(musicManager, trackUrl, new AudioLoadResultHandler() { + @Override + public void trackLoaded(AudioTrack track) { + channel.sendMessage("Adding to queue " + track.getInfo().title).queue(); + + play(channel.getGuild(), musicManager, track); + } + + @Override + public void playlistLoaded(AudioPlaylist playlist) { + AudioTrack firstTrack = playlist.getSelectedTrack(); + + if (firstTrack == null) { + firstTrack = playlist.getTracks().get(0); + } + + channel.sendMessage("Adding to queue " + firstTrack.getInfo().title + " (first track of playlist " + playlist.getName() + ")").queue(); + + play(channel.getGuild(), musicManager, firstTrack); + } + + @Override + public void noMatches() { + channel.sendMessage("Nothing found by " + trackUrl).queue(); + } + + @Override + public void loadFailed(FriendlyException exception) { + System.out.println(exception.getCause()); + channel.sendMessage("Could not play: " + exception.getMessage()).queue(); + } + }); + } + + private void play(Guild guild, GuildMusicManager musicManager, AudioTrack track) { + connectToFirstVoiceChannel(guild.getAudioManager()); + + musicManager.scheduler.queue(track); + } + + private void skipTrack(TextChannel channel) { + GuildMusicManager musicManager = getGuildAudioPlayer(channel.getGuild()); + musicManager.scheduler.nextTrack(); + + channel.sendMessage("Skipped to next track.").queue(); + } + + private static void connectToFirstVoiceChannel(AudioManager audioManager) { + if (!audioManager.isConnected() && !audioManager.isAttemptingToConnect()) { + for (VoiceChannel voiceChannel : audioManager.getGuild().getVoiceChannels()) { + audioManager.openAudioConnection(voiceChannel); + break; + } + } + } +} \ No newline at end of file diff --git a/src/trash/TrackScheduler.java b/src/trash/TrackScheduler.java new file mode 100644 index 0000000..b61b4e3 --- /dev/null +++ b/src/trash/TrackScheduler.java @@ -0,0 +1,55 @@ +package trash; +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * This class schedules tracks for the audio player. It contains the queue of tracks. + */ +public class TrackScheduler extends AudioEventAdapter { + private final AudioPlayer player; + private final BlockingQueue queue; + + /** + * @param player The audio player this scheduler uses + */ + public TrackScheduler(AudioPlayer player) { + this.player = player; + this.queue = new LinkedBlockingQueue<>(); + } + + /** + * Add the next track to queue or play right away if nothing is in the queue. + * + * @param track The track to play or add to queue. + */ + 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); + } + } + + /** + * Start the next track, stopping the current one if it is playing. + */ + 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 + public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { + // Only start the next track if the end reason is suitable for it (FINISHED or LOAD_FAILED) + if (endReason.mayStartNext) { + nextTrack(); + } + } +} \ No newline at end of file