230 lines
8.6 KiB
Kotlin
230 lines
8.6 KiB
Kotlin
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<AudioTrack>()
|
|
/*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()
|
|
}
|
|
|
|
}
|
|
|