1919package me.duncte123.skybot.commands.music
2020
2121import com.github.natanbc.reliqua.limiter.RateLimiter
22- import me.duncte123.botcommons.StringUtils
22+ import dev.arbjerg.lavalink.client.Link
2323import me.duncte123.botcommons.messaging.EmbedUtils
2424import me.duncte123.botcommons.messaging.MessageUtils.sendEmbed
2525import me.duncte123.botcommons.messaging.MessageUtils.sendMsg
2626import me.duncte123.botcommons.web.WebParserUtils
2727import me.duncte123.botcommons.web.WebUtils
28+ import me.duncte123.lyrics.model.Lyrics
29+ import me.duncte123.lyrics.model.TextLyrics
30+ import me.duncte123.lyrics.model.TimedLyrics
2831import me.duncte123.skybot.Variables
2932import me.duncte123.skybot.objects.command.CommandContext
3033import me.duncte123.skybot.objects.command.MusicCommand
3134import me.duncte123.skybot.objects.config.DunctebotConfig
35+ import me.duncte123.skybot.utils.chunkForEmbed
3236import net.dv8tion.jda.api.EmbedBuilder
3337import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
3438import net.dv8tion.jda.api.interactions.commands.OptionType
3539import net.dv8tion.jda.api.interactions.commands.build.SubcommandData
40+ import reactor.core.scheduler.Schedulers
3641import java.net.URLEncoder
3742import java.nio.charset.StandardCharsets
3843
@@ -49,6 +54,7 @@ class LyricsCommand : MusicCommand() {
4954 val args = ctx.args
5055
5156 if (args.isNotEmpty()) {
57+ // TODO: search with lavalink for lyrics
5258 handleSearch(ctx.argsRaw, ctx.config) {
5359 if (it == null ) {
5460 sendMsg(ctx, " There where no lyrics found for `${ctx.argsRaw} `" )
@@ -68,13 +74,20 @@ class LyricsCommand : MusicCommand() {
6874 return
6975 }
7076
71- val search = playingTrack.info.title.trim()
72-
73- // just search for the title, the author might be a weird youtube channel
74- handleSearch(search, ctx.config) {
77+ loadLyricsFromLavalink(player.link) {
7578 if (it == null ) {
76- sendMsg(ctx, " There where no lyrics found for `$search `" )
77- return @handleSearch
79+ // TODO: fallback for genius
80+ val searchItem = " ${playingTrack.info.title} - ${playingTrack.info.author} "
81+
82+ handleSearch(searchItem, ctx.config) { embed ->
83+ if (embed == null ) {
84+ sendMsg(ctx, " There where no lyrics found for `${playingTrack.info.title} `" )
85+ return @handleSearch
86+ }
87+
88+ sendEmbed(ctx, embed)
89+ }
90+ return @loadLyricsFromLavalink
7891 }
7992
8093 sendEmbed(ctx, it)
@@ -105,25 +118,32 @@ class LyricsCommand : MusicCommand() {
105118
106119 event.deferReply().queue()
107120
108- val search = playingTrack.info.title.trim()
109-
110- // just search for the title, the author might be a weird youtube channel
111- handleSearch(search, variables.config) {
121+ loadLyricsFromLavalink(player.link) {
112122 if (it == null ) {
113- event.hook.sendMessage(" There where no lyrics found for `$search `" ).queue()
114- return @handleSearch
123+ val searchItem = " ${playingTrack.info.title} - ${playingTrack.info.author} "
124+
125+ handleSearch(searchItem, variables.config) { embed ->
126+ if (embed == null ) {
127+ event.hook.sendMessage(" There where no lyrics found for `${playingTrack.info.title} `" )
128+ .queue()
129+ return @handleSearch
130+ }
131+
132+ event.hook.sendMessageEmbeds(embed.build()).queue()
133+ }
134+ return @loadLyricsFromLavalink
115135 }
116136
117137 event.hook.sendMessageEmbeds(it.build()).queue()
118138 }
119-
120139 return
121140 }
122141
123142 event.deferReply().queue()
124143
125144 val search = opt.asString
126145
146+ // TODO: search with lavalink for lyrics
127147 handleSearch(search, variables.config) {
128148 if (it == null ) {
129149 event.hook.sendMessage(" There where no lyrics found for `$search `" ).queue()
@@ -134,21 +154,76 @@ class LyricsCommand : MusicCommand() {
134154 }
135155 }
136156
157+ private fun loadLyricsFromLavalink (link : Link , cb : (EmbedBuilder ? ) -> Unit ) {
158+ val sessionId = link.node.sessionId!!
159+ val guildId = link.guildId
160+
161+ link.node.customJsonRequest(Lyrics ::class .java) {
162+ it.path(" /v4/sessions/$sessionId /players/$guildId /lyrics" )
163+ }
164+ .publishOn(Schedulers .boundedElastic())
165+ .doOnError { cb(null ) }
166+ .doOnSuccess {
167+ val lyricInfo = when (it) {
168+ is TimedLyrics -> {
169+ // Block is safe here, player is already cached
170+ val position = link.getPlayer().block()!! .state.position
171+
172+ val text = buildString {
173+ it.lines.forEach { line ->
174+ if (line.range.start <= position && position <= line.range.end) {
175+ append(" __**${line.line} **__\n " )
176+ } else {
177+ append(" ${line.line} \n " )
178+ }
179+ }
180+ }
181+
182+ LyricInfo (
183+ it.track.albumArt.last().url,
184+ it.track.title,
185+ null ,
186+ text
187+ )
188+ }
189+
190+ is TextLyrics -> LyricInfo (
191+ it.track.albumArt.last().url,
192+ it.track.title,
193+ null ,
194+ it.text
195+ )
196+
197+ else -> null
198+ }
199+
200+ lyricInfo?.let { info ->
201+ cb(buildLyricsEmbed(info))
202+ }
203+ }
204+ .subscribe()
205+ }
206+
207+ private fun buildLyricsEmbed (data : LyricInfo ): EmbedBuilder {
208+ val builder = EmbedUtils .getDefaultEmbed()
209+ .setTitle(" Lyrics for ${data.title} " , data.url)
210+ .setThumbnail(data.artUrl)
211+
212+ data.lyrics.chunkForEmbed(450 ).forEachIndexed { index, chunk ->
213+ builder.addField(" **[${index + 1 } ]**" , chunk, true )
214+ }
215+
216+ return builder
217+ }
218+
137219 private fun handleSearch (search : String , config : DunctebotConfig , cb : (EmbedBuilder ? ) -> Unit ) {
138220 searchForSong(search, config) {
139221 if (it == null ) {
140222 cb(null )
141223 return @searchForSong
142224 }
143225
144- cb(
145- EmbedUtils .getDefaultEmbed()
146- .setTitle(" Lyrics for $search " , it.url)
147- .setThumbnail(it.art)
148- .setDescription(StringUtils .abbreviate(it.lyrics, 1900 ))
149- .appendDescription(" \n\n Full lyrics on [genuis.com](${it.url} )" )
150- .setFooter(" Powered by genuis.com" )
151- )
226+ cb(buildLyricsEmbed(it))
152227 }
153228 }
154229
@@ -189,6 +264,7 @@ class LyricsCommand : MusicCommand() {
189264 callback(
190265 LyricInfo (
191266 data[" song_art_image_url" ].asText(),
267+ " " ,
192268 data[" url" ].asText(),
193269 lyrics
194270 )
@@ -205,7 +281,7 @@ class LyricsCommand : MusicCommand() {
205281 val text = lyricsContainer.first()!!
206282 .wholeText()
207283 .replace(" <br>" , " \n " )
208- .replace(" \n\n\n " , " \n\n " )
284+ .replace(" \n\n\n " , " \n " )
209285 .trim()
210286
211287 callback(text)
@@ -214,5 +290,5 @@ class LyricsCommand : MusicCommand() {
214290 }
215291 }
216292
217- private data class LyricInfo (val art : String , val url : String , val lyrics : String )
293+ private data class LyricInfo (val artUrl : String , val title : String , val url : String? , val lyrics : String )
218294}
0 commit comments