OttoBot/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt

367 lines
11 KiB
Kotlin

package nl.voidcorp.dbot.music
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer
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 net.dv8tion.jda.core.audio.AudioSendHandler
import nl.voidcorp.dbot.addAll
import nl.voidcorp.dbot.commands
import nl.voidcorp.dbot.commands.*
import nl.voidcorp.dbot.log
import nl.voidcorp.dbot.playerManager
val guildMusicMap = mutableMapOf<Long, TrackScheduler>()
fun initMusic() {
AudioSourceManagers.registerRemoteSources(playerManager)
val queueCommand = UnityMusicCommand(
"play",
aliases = mutableListOf("p", "queue", "q"),
help = "Use this command to queue a song, if you don't add a link it will search on youtube with the specified arguments\n\nExecute with no arguments to view the current queue!",
howTo = "q [link]"
) { event, scheduler ->
if (event.args.isEmpty()) {
if (!guildMusicMap.containsKey(event.guild.idLong) || (guildMusicMap[event.guild.idLong]!!.player.playingTrack == null))
event.reply("The track list is empty") else
event.reply(guildMusicMap[event.guild.idLong]!!.getTrackList(event.member))
return@UnityMusicCommand
}
playerManager.loadItem(event.args, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.queue(track, event.member)
}
override fun noMatches() {
getLinkFromSearch(event, scheduler, false)
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.queue(t, event.member)
}
}
})
}
val ytCommand = UnityMusicCommand(
"youtube",
aliases = mutableListOf("yt"),
help = "Search YouTube for a song!",
howTo = "youtube <search term or link>"
) { event, scheduler ->
if (event.args.isEmpty()) {
event.reply("Please supply a song name or URL!")
return@UnityMusicCommand
}
playerManager.loadItem(event.args, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.queue(track, event.member)
}
override fun noMatches() {
getLinkFromSearch(event, scheduler, false)
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.queue(t, event.member)
}
}
})
}
val soundcloudCommand = UnityMusicCommand(
"soundcloud",
help = "Play a song via SoundCloud!",
aliases = mutableListOf("sc"),
howTo = "sc <link or search term>"
) { event, scheduler ->
if (event.args.isEmpty()) {
event.reply("Please supply a song name or URL!")
return@UnityMusicCommand
}
playerManager.loadItem(event.args, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.queue(track, event.member)
}
override fun noMatches() {
getLinkFromSearch(event, scheduler, false, "scsearch")
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.queue(t, event.member)
}
}
})
}
val playCommand = UnityMusicCommand(
"fplay",
"Force this song to be the next!",
aliases = mutableListOf("fp"),
howTo = "play <url>",
category = MusicCategoryPrivate
) { event, scheduler ->
if (event.args.isEmpty()) {
event.reply("Please supply a song name or URL!")
return@UnityMusicCommand
}
playerManager.loadItem(event.args, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.insertFront(track, event.member)
if (!scheduler.isQueueEmpty())
scheduler.skip()
}
override fun noMatches() {
getLinkFromSearch(event, scheduler, true)
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.insertFront(t, event.member)
}
}
})
}
val skipCommand = UnityCommand(
"fskip",
help = "Force skips the current song, admin only",
aliases = mutableListOf("fs", "ffs"),
category = MusicCategoryPrivate
) { event ->
val scheduler = guildMusicMap[event.guild.idLong]
if (scheduler == null) {
event.reply("There is no music playing?")
} else {
scheduler.skip()
}
}
val voteSkipCommand = UnityMusicCommand(
"vskip",
help = "Starts a vote to skip the current song",
aliases = mutableListOf("voteskip", "skip", "vs", "s")
) { event, scheduler ->
val ss = scheduler.shouldSkip()
if (ss) {
event.reply("Vote passed, skipping song!")
scheduler.skip()
} else {
event.reply("Vote registered!")
}
}
val npCommand = UnityMusicCommand(
"nowplaying",
aliases = mutableListOf("np"),
help = "Show the currently playing song"
) { event, scheduler ->
if (!scheduler.isSongPlaying()) {
event.reply("There is no song playing?")
} else {
event.reply(scheduler.getCurrentTrackInfo())
}
}
val attachmentPlay = UnityMusicCommand(
"attachment",
help = "Play any attached song, if it is in a normal format that is...\n\n`just drag and drop the song on your Discord window and in the optional comment add !attach(ment)`",
aliases = mutableListOf("attach")
) { event, scheduler ->
val attach = event.message.attachments.firstOrNull()
if (attach == null) {
event.reply("I can't play an attachment without an attachment...")
} else {
playerManager.loadItem(attach.url, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.queue(track, event.member)
if (!scheduler.isQueueEmpty() or scheduler.isSongPlaying()) {
scheduler.getTrackInfo(track)
}
}
override fun noMatches() {
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.queue(t, event.member)
}
}
})
}
}
val loopCommand =
UnityMusicCommand("loop", "Loops the queue!\nOr disables it!", MusicCategory) { event, scheduler ->
if (scheduler.loop) {
event.reply("The queue will no longer loop!")
} else {
event.reply("The queue wil now loop")
}
scheduler.loop = !scheduler.loop
}
val stopCommand = UnityMusicCommand(
"stop",
"Stops this shit!",
MusicCategoryPrivate,
aliases = mutableListOf()
) { event, scheduler ->
scheduler.clearQueue()
event.reply("Stopped all playback!")
}
commands.addAll(
playCommand,
skipCommand,
queueCommand,
ytCommand,
soundcloudCommand,
npCommand,
attachmentPlay,
voteSkipCommand,
loopCommand,
stopCommand
)
}
fun getLinkFromSearch(
event: UnityCommandEvent,
scheduler: TrackScheduler,
shouldInsertFront: Boolean,
searchPrefix: String = "ytsearch"
) {
log.info("Searching youtube for '${event.args}'")
playerManager.loadItem("$searchPrefix:${event.args}", object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
if (shouldInsertFront) {
scheduler.insertFront(track, event.member)
if (!scheduler.isQueueEmpty())
scheduler.skip()
} else
scheduler.queue(track, event.member)
/*event.reply(
EmbedBuilder().setImage(r.snippet.thumbnails.high.url).setTitle(r.snippet.title, "https://youtu.be/$id")
.setAuthor(r.snippet.channelTitle).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl)
.setDescription(r.snippet.description).build()
)*/
}
override fun noMatches() {
}
override fun playlistLoaded(playlist: AudioPlaylist) {
if (!playlist.isSearchResult)
for (t in playlist.tracks) {
if (shouldInsertFront) {
scheduler.insertFront(t, event.member)
if (!scheduler.isQueueEmpty())
scheduler.skip()
} else
scheduler.queue(t, event.member)
}
else {
val track = playlist.tracks.first()
if (shouldInsertFront)
scheduler.insertFront(track, event.member)
else
scheduler.queue(track, event.member)
if (!scheduler.isQueueEmpty() or scheduler.isSongPlaying()) {
if (scheduler.player.playingTrack != track)
event.reply(scheduler.getTrackInfo(track))
}
}
}
})
}
class AudioPlayerSendHandler(private val audioPlayer: AudioPlayer) : AudioSendHandler {
private var lastFrame: AudioFrame? = null
override fun canProvide(): Boolean {
lastFrame = audioPlayer.provide()
return lastFrame != null
}
override fun provide20MsAudio(): ByteArray {
return lastFrame!!.data
}
override fun isOpus(): Boolean {
return true
}
}