package nl.voidcorp.dbot.music import com.sedmelluq.discord.lavaplayer.player.AudioPlayer import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioTrack import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioTrack import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioTrack import com.sedmelluq.discord.lavaplayer.track.AudioTrack import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason import net.dv8tion.jda.core.EmbedBuilder import net.dv8tion.jda.core.entities.* import nl.voidcorp.dbot.log import java.awt.Color import java.time.LocalDateTime import java.util.* import java.util.concurrent.TimeUnit import kotlin.concurrent.timerTask import kotlin.math.roundToInt class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel: VoiceChannel) : AudioEventAdapter() { val musicChannel: TextChannel var loop = false init { player.addListener(this) val audio = guild.audioManager audio.sendingHandler = AudioPlayerSendHandler(player) audio.openAudioConnection(voiceChannel) musicChannel = guild.getTextChannelsByName("music-bot", true).firstOrNull() ?: (guild.getTextChannelsByName("music", true).firstOrNull() ?: (guild.getTextChannelsByName("bot", true).firstOrNull() ?: (guild.getTextChannelsByName("general", true).firstOrNull() ?: guild.defaultChannel!!))) } private val q = ArrayDeque() /*override fun onEvent(event: AudioEvent) { if (event is TrackEndEvent) { } }*/ override fun onTrackEnd(player: AudioPlayer, track: AudioTrack, endReason: AudioTrackEndReason) { pressedSkip = 0 if (q.isNotEmpty()) { val t = q.poll()!! if (loop) q.addLast(t.makeClone().apply { userData = t.userData }) player.playTrack(t) } else { if (loop) { player.playTrack(track.makeClone().apply { userData = track.userData }) return } stopPlay(endReason) player.destroy() } } override fun onTrackStart(player: AudioPlayer, track: AudioTrack) { musicChannel.sendMessage(getCurrentTrackInfo()).append("Now playing").queue() /*if (track is YoutubeAudioTrack) { if (track.userData is Member) } else if (track is SoundCloudAudioTrack) { val scsm = track.sourceManager as SoundCloudAudioSourceManager scsm.updateClientId() val art = khttp.get("http://api.soundcloud.com/tracks/${track.info.identifier}?client_id=${scsm.clientId}", headers = mapOf("Content-Type" to "application/json")).jsonObject["artwork_url"].toString().replace("large", "t300x300") if (track.userData is Member) musicChannel.sendMessage(EmbedBuilder() .setFooter("Requested by ${(track.userData as Member).effectiveName}", (track.userData as Member) .user.effectiveAvatarUrl).setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) .setThumbnail(art) .setTimestamp(LocalDateTime.now()).setColor(Color.decode("#ff8800")).build()).append("Now playing").queue() } */ } fun queue(track: AudioTrack, member: Member) { track.userData = member if (q.isEmpty() && player.playingTrack == null) { log.info("Queue is empty, playing a track") player.playTrack(track) } else { log.info("Track Queue'd") q.offer(track) } } fun insertFront(track: AudioTrack, member: Member) { track.userData = member if (q.isEmpty() && player.playingTrack == null) { log.info("Queue is empty, playing a track") player.playTrack(track) } else { log.info("Track inserted") q.push(track) } } fun skip() { /*if (q.isNotEmpty()) { log.info("skipped Track!") player.playTrack(q.remove()) } else { stopPlay(AudioTrackEndReason.REPLACED) player.destroy() }*/ player.stopTrack() } fun isQueueEmpty() = q.isEmpty() fun isSongPlaying() = player.playingTrack != null fun stopPlay(endReason: AudioTrackEndReason) { if (endReason != AudioTrackEndReason.REPLACED) { guildMusicMap.remove(guild.idLong) musicChannel.sendMessage( EmbedBuilder().setColor(musicChannel.guild.selfMember.color).setTitle("I'm done here").setDescription( "There are no more songs left in the queue." ).setTimestamp(LocalDateTime.now()).build() ).queue() Timer().schedule(timerTask { guild.audioManager.closeAudioConnection() }, 2000) } } fun getCurrentTrackInfo(): MessageEmbed = getTrackInfo(player.playingTrack) fun getTrackInfo(track: AudioTrack): MessageEmbed { val eb = EmbedBuilder().setTimestamp(LocalDateTime.now()) if (track.userData is Member) { eb.setFooter( "Requested by ${(track.userData as Member).effectiveName}", (track.userData as Member) .user.effectiveAvatarUrl ) } when (track) { is YoutubeAudioTrack -> eb.setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) .setThumbnail("https://img.youtube.com/vi/${track.info.identifier}/hqdefault.jpg") .setColor(Color.decode("#ff0000")) is SoundCloudAudioTrack -> { val scsm = track.sourceManager as SoundCloudAudioSourceManager scsm.updateClientId() val art = khttp.get( "http://api.soundcloud.com/tracks/${track.info.identifier}?client_id=${scsm.clientId}", headers = mapOf("Content-Type" to "application/json") ).jsonObject["artwork_url"].toString().replace("large", "t300x300") eb .setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) .setThumbnail(if (art == "null") null else art) .setColor(Color.decode("#ff5500")) } is HttpAudioTrack -> eb.setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) .setColor(Color.decode("#005A9C")) else -> eb.setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) .setColor(musicChannel.guild.selfMember.color) } eb.setDescription("${millisToTime(track.position)}/${millisToTime(track.duration)}") return eb.build() } fun millisToTime(millis: Long): String { var ms = millis val days = TimeUnit.MILLISECONDS.toDays(ms) ms -= TimeUnit.DAYS.toMillis(days) val hours = TimeUnit.MILLISECONDS.toHours(ms) ms -= TimeUnit.HOURS.toMillis(hours) val minutes = TimeUnit.MILLISECONDS.toMinutes(ms) ms -= TimeUnit.MINUTES.toMillis(minutes) val seconds = TimeUnit.MILLISECONDS.toSeconds(ms) val current = String.format( "%02d:%02d:%02d:%02d", days, hours, minutes, seconds ).removePrefix("00:").removePrefix("00:") return current } fun getTrackList(member: Member): MessageEmbed { return EmbedBuilder().setTitle("Hey ${member.effectiveName}, here is the playlist${if (loop) ", it loops!" else ""}") .setTimestamp(LocalDateTime.now()).setColor(musicChannel.guild.selfMember.color) .setFooter("Requested by ${member.effectiveName}", member.user.effectiveAvatarUrl).setDescription( "**${player.playingTrack.info.title}**, requested by ${if (player.playingTrack.userData is Member) (player.playingTrack.userData as Member).effectiveName else "someone unknown..."} (*now playing*)\n" + q.joinToString(separator = "\n") { "**${it.info.title}**, requested by ${if (it.userData is Member) (it.userData as Member).effectiveName else "someone unknown..."}" } ).build() } var pressedSkip = 0 fun shouldSkip(): Boolean { val half = (voiceChannel.members.size.run { this - 1 }.toDouble() / 2).roundToInt() val press = ++pressedSkip return press >= half } fun clearQueue() { q.clear() loop = false player.stopTrack() } }