=Music bug fixes

-Playlist not loading correctly
This commit is contained in:
Alex
2019-10-19 03:35:43 -04:00
19 changed files with 446 additions and 71 deletions
+29
View File
@@ -0,0 +1,29 @@
package commands.general;
import core.Command;
import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.Response;
import dataStructures.UserInput;
import network.NetworkSauceNAO;
public class CommandGetSauce extends Command
{
NetworkSauceNAO sauce = new NetworkSauceNAO();
public CommandGetSauce(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
public String getHelpText() { return LocStrings.stub("GetSauceInfo"); };
@Override
public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
res.send(sauce.getSauce(input.args));
}
}
+10 -3
View File
@@ -2,6 +2,7 @@ package commands.music;
import core.Command;
import core.LocStrings;
import core.SubCommandFormattable;
import core.SubCommandFramework;
import dataStructures.*;
@@ -9,18 +10,24 @@ 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));
framework.addCommand("add", new SubCommandAddTrack(KittyRole.General, KittyRating.Safe));
framework.addCommand("skip", new SubCommandSkip(KittyRole.General, KittyRating.Safe));
framework.addCommand("stop", new SubCommandStop(KittyRole.General, KittyRating.Safe));
framework.addCommand("playlist", new SubCommandPlaylist(KittyRole.General, KittyRating.Safe));
}
@Override
public String getHelpText() { return LocStrings.stub("MusicInfo"); }
public String getHelpText() {return LocStrings.stub("MusicInfo"); }
@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);
}
}
+11 -4
View File
@@ -1,7 +1,5 @@
package commands.music;
import com.google.api.services.youtube.YouTube;
import core.SubCommand;
import core.SubCommandFormattable;
import dataStructures.KittyChannel;
@@ -10,16 +8,25 @@ import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.UserInput;
import network.NetworkYoutube;
public class SubCommandAddTrack extends SubCommand
{
public SubCommandAddTrack(KittyRole level, KittyRating rating) { super(level, rating); }
NetworkYoutube YT = new NetworkYoutube();
@Override
public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
{
return null;
//Get track from Youtube
String video = input.args;
if(!(video.startsWith("https://www.youtube") || video.startsWith("https://www.youtu.be")))
{
video = YT.getYT(video);
}
guild.audio.playVideo(video);
return new SubCommandFormattable(video);
}
}
@@ -0,0 +1,38 @@
package commands.music;
import java.util.ArrayList;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import core.SubCommand;
import core.SubCommandFormattable;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.UserInput;
public class SubCommandPlaylist extends SubCommand
{
public SubCommandPlaylist(KittyRole roleLevel, KittyRating contentRating) {super(roleLevel, contentRating); }
@Override
public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
{
ArrayList <AudioTrack> playlist = guild.audio.getPlaylist();
String stringPlaylist = "";
for(AudioTrack track : playlist)
{
stringPlaylist += (playlist.indexOf(track) + 1) + ": " + track.getInfo().title + "\n";
}
if(stringPlaylist.equals(""))
{
return new SubCommandFormattable("Nothing Queued!");
}
return new SubCommandFormattable(stringPlaylist);
}
}
+23
View File
@@ -0,0 +1,23 @@
package commands.music;
import core.SubCommand;
import core.SubCommandFormattable;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.UserInput;
public class SubCommandSkip extends SubCommand
{
public SubCommandSkip(KittyRole level, KittyRating rating) { super(level, rating); }
{
}
@Override
public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
{
return new SubCommandFormattable(guild.audio.skipVideo(guild.audio.player));
}
}
+24
View File
@@ -0,0 +1,24 @@
package commands.music;
import core.SubCommand;
import core.SubCommandFormattable;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.UserInput;
public class SubCommandStop extends SubCommand
{
public SubCommandStop(KittyRole roleLevel, KittyRating contentRating) {super(roleLevel, contentRating);}
@Override
public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
{
return new SubCommandFormattable(guild.audio.stopPlayer(guild.audio.player));
}
}
-1
View File
@@ -106,7 +106,6 @@ public class CommandManager
this.register(LocCommands.stub("raffle"), new CommandRaffleMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("poll"), new CommandPollMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("music"), new CommandMusicMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("defaultdance"), new CommandDefaultDance(KittyRole.General, KittyRating.Safe));
}
// Allows the command manager to keep track of a command. Takes a pair (the un-localized and localzied commands)
+23 -5
View File
@@ -7,22 +7,36 @@ import java.util.concurrent.Semaphore;
import javax.security.auth.login.LoginException;
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.player.AudioPlayerManager;
//import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import core.lua.PluginManager;
import dataStructures.*;
import dataStructures.KittyChannel;
import dataStructures.KittyCore;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import main.Main;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.entities.Emote;
import net.dv8tion.jda.core.entities.Game;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
import offline.Ref;
import utils.AdminControl;
//import utils.AudioUtils;
import utils.GlobalLog;
import utils.LogFilter;
import utils.audio.AudioUtils;
// NOTE(wisp): Isolated factory to assist with storage and caching if needed.
// This also minimizes the number of places JDA interacts with our codebase.
@@ -55,7 +69,7 @@ public class ObjectBuilderFactory
private static CommandManager commandManager;
//Audio track manager
// private static AudioPlayerManager audioPlayer = new DefaultAudioPlayerManager();
private static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
// Localization classes - these are singletons, but should be initialized before almost all other
// things so their inclusion in the factory is to ensure they're started at the correct time.
@@ -90,6 +104,9 @@ public class ObjectBuilderFactory
database = null;
stats = null;
AudioSourceManagers.registerRemoteSources(playerManager);
AudioSourceManagers.registerLocalSource(playerManager);
// Start by reading from things that are external. Because
// we require these things to be resolved before the rest of the application,
// we place them here.
@@ -147,7 +164,7 @@ public class ObjectBuilderFactory
else
{
// Construct a new guild with defaults
guild = new KittyGuild(uid, new AdminControl(event.getGuild()), emotesString);
guild = new KittyGuild(uid, new AdminControl(event.getGuild()), emotesString, new AudioUtils(event.getGuild(), playerManager));
DatabaseManager.instance.globalRegister(guild);
guildCache.put(uid, guild);
}
@@ -365,7 +382,8 @@ public class ObjectBuilderFactory
{
lazyInit();
kitty = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking();
kitty = new JDABuilder(AccountType.BOT)
.setToken(Ref.TestToken).buildBlocking();
kitty.getPresence().setGame(Game.playing("with digital yarn"));
kitty.addEventListener(new Main());
+7 -4
View File
@@ -5,7 +5,7 @@ import java.util.ArrayList;
import core.DatabaseManager;
import core.DatabaseTrackedObject;
import utils.AdminControl;
import utils.AudioUtils;
import utils.audio.AudioUtils;
// Context for a given guild for kittybot. Primarily designed to hold guild-specific settings.
public class KittyGuild extends DatabaseTrackedObject
@@ -22,7 +22,7 @@ public class KittyGuild extends DatabaseTrackedObject
public ArrayList<String> emoji = new ArrayList<String>();
public ArrayList <KittyPoll> choices = new ArrayList<KittyPoll>();
public AdminControl control;
public AudioUtils audio;
public AudioUtils audio;
public ArrayList <KittyUser> raffleUsersUnchosen = new ArrayList<KittyUser>();
public ArrayList <KittyUser> raffleUsersChosen = new ArrayList<KittyUser>();
@@ -42,8 +42,9 @@ public class KittyGuild extends DatabaseTrackedObject
}
// Default content for a guild
public KittyGuild(String uniqueID, AdminControl adminControl, ArrayList <String> emoji)
public KittyGuild(String uniqueID, AdminControl adminControl, ArrayList <String> emoji, AudioUtils audio)
{
super(uniqueID);
this.uniqueID = uniqueID;
roleList = new KittyTrackedVector(roleListName, uniqueID);
@@ -54,11 +55,12 @@ public class KittyGuild extends DatabaseTrackedObject
this.contentRating = KittyRating.Safe;
this.polling = false;
this.emoji = emoji;
this.audio = audio;
setCommandIndicator("!");
}
// Explicit constructor
public KittyGuild(String commandIndicator, KittyRating contentRating, KittyUser guildOwner, String uniqueID)
public KittyGuild(String commandIndicator, KittyRating contentRating, KittyUser guildOwner, String uniqueID, AudioUtils audio)
{
super(uniqueID);
this.uniqueID = uniqueID;
@@ -70,6 +72,7 @@ public class KittyGuild extends DatabaseTrackedObject
this.contentRating = contentRating;
this.polling = false;
this.guildOwner = guildOwner;
this.audio = audio;
}
public String startPoll(String poll)
+18 -1
View File
@@ -1,9 +1,21 @@
package main;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.security.auth.login.LoginException;
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 com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
import core.CharacterManager;
import core.CommandEnabler;
import core.CommandManager;
@@ -21,9 +33,13 @@ import dataStructures.UserInput;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
import net.dv8tion.jda.core.entities.Game;
import net.dv8tion.jda.core.audio.AudioSendHandler;
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;
import utils.GlobalLog;
@@ -42,6 +58,7 @@ public class Main extends ListenerAdapter
private static PluginManager pluginManager;
private static CharacterManager charManager;
// Initialization and setup
public static void main(String[] args) throws InterruptedException, LoginException, Exception
{
+48
View File
@@ -0,0 +1,48 @@
package network;
import java.util.List;
import com.google.gson.Gson;
import offline.Ref;
import utils.HTTPUtils;
public class NetworkSauceNAO
{
private static final Gson jsonParser_ = new Gson();
private static final String API_ROOT = "https://saucenao.com/search.php?db=999&output_type=2&numres=10&api_key=" + Ref.SauceNAOKey +"&url=";
NetworkE621 e621Sauce = new NetworkE621();
private class SauceNAOResponseObject
{
List<SauceNAOResult> results;
}
private class SauceNAOResult
{
SauceNAOData data;
}
private class SauceNAOData
{
String [] ext_urls;
}
public String getSauce(String input)
{
input = input.trim();
input = input.replace("+", "%2B");
input = input.replace(" ", "%20");
String res = HTTPUtils.sendGETRequest(API_ROOT + input);
SauceNAOResponseObject sauce = jsonParser_.fromJson(res, SauceNAOResponseObject.class);
String sauceUrl = sauce.results.get(0).data.ext_urls[0];
if(sauceUrl.startsWith("https://e621.net/post/show/"))
{
System.out.println("id:" + sauceUrl.substring(26));
return e621Sauce.getE621("id:" + sauceUrl.substring(27)).toString();
}
return sauce.results.get(0).data.ext_urls[0];
}
}
+45 -4
View File
@@ -1,14 +1,55 @@
package network;
import com.google.api.services.youtube.YouTube;
import java.util.List;
import com.google.gson.Gson;
import offline.Ref;
import utils.HTTPUtils;
public class NetworkYoutube
{
private static YouTube youtube;
///////////////////////////////////////
// Internal JSON class and variables //
//////////////////////////////////.////
public NetworkYoutube()
//https://www.youtube.com/watch?v=
private static final Gson jsonParser_ = new Gson();
private static final String API_ROOT = "https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=10&q=";
// Use this as the base
public class YTOuter
{
List<YTInner> items;
}
}
public class YTInner
{
TYID id;
YTSnippet snippet;
}
public class TYID
{
String videoId;
}
public class YTSnippet
{
String title;
String description;
}
public String getYT(String input)
{
input = input.trim();
input = input.replace("+", "%2B");
input = input.replace(" ", "%20");
String res = HTTPUtils.sendGETRequest(API_ROOT + input + "&order=viewCount&type=video&topicId=%2Fm%2F04rlf&videoDefinition=high&key=" + Ref.youtube);
YTOuter videoObj = jsonParser_.fromJson(res, YTOuter.class);
return "https://www.youtube.com/watch?v=" + videoObj.items.get(0).id.videoId;
}
}
-48
View File
@@ -1,48 +0,0 @@
package utils;
/***
* https://developers.google.com/youtube/v3/code_samples/code_snippets?apix_params=%7B%22part%22%3A%22snippet%22%2C%22maxResults%22%3A25%2C%22q%22%3A%22surfing%22%7D
* https://github.com/sedmelluq/LavaPlayer#jda-integration
* https://ci.dv8tion.net/job/JDA/javadoc/net/dv8tion/jda/core/entities/VoiceChannel.html
* https://developers.google.com/youtube/registering_an_application#Create_API_Keys
*/
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.managers.AudioManager;
public class AudioUtils
{
private Guild guild;
AudioManager audio;
AudioPlayer player;
public AudioUtils(Guild guild)
{
this.guild = guild;
audio = guild.getAudioManager();
}
public boolean joinChannel(String channelName)
{
VoiceChannel channel = null;
channel = guild.getVoiceChannelsByName(channelName, true).get(0);
if(channel != null)
{
audio.openAudioConnection(channel);
return true;
}
return false;
}
public boolean leaveChannel()
{
if(audio.isConnected())
{
audio.closeAudioConnection();
return true;
}
return false;
}
}
+164
View File
@@ -0,0 +1,164 @@
package utils.audio;
import java.util.ArrayList;
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.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
import net.dv8tion.jda.core.audio.AudioSendHandler;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.managers.AudioManager;
public class AudioUtils
{
private Guild guild;
public AudioManager am;
public final AudioPlayer player;
public final AudioPlayerManager playerManager;
private boolean isPlaying = false;
private ArrayList <AudioTrack> playlist = new ArrayList<AudioTrack>();
public AudioUtils(Guild guild, AudioPlayerManager playerManager)
{
player = playerManager.createPlayer();
this.playerManager = playerManager;
this.guild = guild;
am = guild.getAudioManager();
}
public boolean joinChannel(String channelName)
{
VoiceChannel channel = null;
channel = guild.getVoiceChannelsByName(channelName, true).get(0);
if(channel != null)
{
am.openAudioConnection(channel);
return true;
}
return false;
}
public boolean leaveChannel()
{
if(am.isConnected())
{
am.closeAudioConnection();
return true;
}
return false;
}
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;
}
}
public class SmallAudio implements AudioLoadResultHandler
{
private AudioPlayer player;
private SmallAudio(AudioPlayer player)
{
this.player = player;
}
@Override public void loadFailed(FriendlyException arg0) { }
@Override public void noMatches() { }
@Override public void playlistLoaded(AudioPlaylist arg0) { }
@Override
public void trackLoaded(AudioTrack track)
{
synchronized(playlist)
{
playlist.add(track);
}
if(!isPlaying)
{
isPlaying = true;
startPlay(player);
}
}
}
public String playVideo(String videoURL)
{
guild.getAudioManager().setSendingHandler(new AudioPlayerSendHandler(player));
playerManager.loadItemOrdered(player, videoURL, new SmallAudio(player));
return null;
}
private void startPlay(AudioPlayer player)
{
do
{
if(player.getPlayingTrack() == null)
{
player.startTrack(playlist.get(0), false);
if(!playlist.isEmpty())
playlist.remove(0);
}
}
while(!playlist.isEmpty());
isPlaying = false;
}
public String skipVideo(AudioPlayer player)
{
player.stopTrack();
try
{
playlist.remove(0);
}
catch(Exception e)
{
return "Stopped";
}
return "Skipped";
}
public String stopPlayer(AudioPlayer player)
{
playlist.clear();
player.stopTrack();
return "Stopped";
}
public ArrayList<AudioTrack> getPlaylist()
{
return playlist;
}
}