From 0b7b2f7e4042ef95e6f08cfe90a35c13b9538bee Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Sun, 8 Jan 2023 19:13:22 +0100 Subject: [PATCH] Fix issues with innertube API --- gradle/libs.versions.toml | 1 + music/build.gradle.kts | 4 +++- .../dev/schlaubi/mikmusic/core/Config.kt | 2 +- .../dev/schlaubi/mikmusic/core/MusicPlugin.kt | 7 ++++-- .../mikmusic/innerttube/InnerTubeClient.kt | 13 +++++++++- .../schlaubi/mikmusic/innerttube/autoplay.kt | 13 +++++----- .../mikmusic/musicchannel/MusicChannelUI.kt | 15 ++++++------ .../dev/schlaubi/mikmusic/player/AutoPlay.kt | 7 +++--- .../schlaubi/mikmusic/util/VideoFormatter.kt | 7 ++++-- test.http | 24 +++++++++++++++++++ 10 files changed, 67 insertions(+), 26 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 98145c671..5891bd7ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,6 +33,7 @@ krontab = { group = "dev.inmo", name = "krontab", version = "0.8.2" } ksp-api = { group = "com.google.devtools.ksp", name = "symbol-processing-api", version = "1.7.22-1.0.8" } rhino = { group = "org.mozilla", name = "rhino", version = "1.7.14" } ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" } +ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" } ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" } ktor-server-content-negotiation = { group = "io.ktor", name = "ktor-server-content-negotiation", version.ref = "ktor" } diff --git a/music/build.gradle.kts b/music/build.gradle.kts index 6a0529578..3aa5f8fea 100644 --- a/music/build.gradle.kts +++ b/music/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } group = "dev.schlaubi.mikbot" -version = "2.13.0" +version = "2.14.0" dependencies { api(libs.lavakord.kord) @@ -32,6 +32,8 @@ dependencies { // Image Color Client api(projects.utils.imageColorClient) api(projects.utils.imageColorClientKord) + + implementation(libs.ktor.client.logging) } tasks { diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/core/Config.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/core/Config.kt index d7b33c212..20471d17f 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/core/Config.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/core/Config.kt @@ -8,5 +8,5 @@ object Config : EnvironmentConfig("") { val YOUTUBE_API_KEY by environment val SPOTIFY_CLIENT_ID by getEnv("") val SPOTIFY_CLIENT_SECRET by getEnv("") - val IMAGE_COLOR_SERVICE_URL by environment + val IMAGE_COLOR_SERVICE_URL by environment.optional() } diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/core/MusicPlugin.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/core/MusicPlugin.kt index bbaf44984..f59776209 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/core/MusicPlugin.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/core/MusicPlugin.kt @@ -40,8 +40,11 @@ class MusicPlugin(wrapper: PluginWrapper) : Plugin(wrapper) { override suspend fun ExtensibleBotBuilder.apply() { hooks { beforeKoinSetup { - loadModule { - single { ImageColorClient(Config.IMAGE_COLOR_SERVICE_URL) } + val imageColorServiceUrl = Config.IMAGE_COLOR_SERVICE_URL + if (imageColorServiceUrl != null) { + loadModule { + single { ImageColorClient(imageColorServiceUrl) } + } } } } diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/InnerTubeClient.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/InnerTubeClient.kt index f7a2aa78b..a2de07688 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/InnerTubeClient.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/InnerTubeClient.kt @@ -5,10 +5,12 @@ import dev.schlaubi.mikbot.plugin.api.util.convertToISO import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.logging.* import io.ktor.client.request.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json +import mu.KotlinLogging private val youtubeMusic = Url("https://music.youtube.com") private val youtube = Url("https://www.youtube.com") @@ -22,13 +24,14 @@ private fun musicContext(locale: Locale): InnerTubeContext { return InnerTubeContext( InnerTubeContext.Client( "WEB_REMIX", - "1.20220502.01.00", + "1.20230102.01.00", isoLocale.language, isoLocale.country!! ) ) } +private val LOG = KotlinLogging.logger { } object InnerTubeClient { private val client = HttpClient { @@ -40,6 +43,14 @@ object InnerTubeClient { json(json) } + + install(Logging) { + logger = object : Logger { + override fun log(message: String) = LOG.debug(message) + } + + level = if (LOG.isDebugEnabled) LogLevel.ALL else LogLevel.NONE + } } suspend fun requestNextSongs( diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/autoplay.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/autoplay.kt index 64a572067..7bf12df8c 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/autoplay.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/innerttube/autoplay.kt @@ -53,16 +53,16 @@ data class MusicQueueRenderer( } @Serializable -data class PlaylistPanelRenderer( - val title: String, - val contents: List, -) +data class PlaylistPanelRenderer(val contents: List) @Serializable data class PlaylistPanelVideoRendererContent( - val playlistPanelVideoRenderer: PlaylistPanelVideoRenderer, + val playlistPanelVideoWrapperRenderer: PlaylistPanelVideoWrapperRenderer? = null ) +@Serializable +data class PlaylistPanelVideoWrapperRenderer(val primaryRenderer: PlaylistPanelVideoRenderer) + @Serializable data class PlaylistPanelVideoRenderer( val title: Runs, @@ -70,8 +70,7 @@ data class PlaylistPanelVideoRenderer( val navigationEndpoints: NavigationEndpoints? = null, val videoId: String, @SerialName("longBylineText") - val longByLineText: Runs? = null, - val playlistSetVideoId: String, + val longByLineText: Runs? = null ) { @Serializable data class NavigationEndpoints( diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/musicchannel/MusicChannelUI.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/musicchannel/MusicChannelUI.kt index 5026291ae..796b43f66 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/musicchannel/MusicChannelUI.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/musicchannel/MusicChannelUI.kt @@ -1,7 +1,6 @@ package dev.schlaubi.mikmusic.musicchannel import com.kotlindiscord.kord.extensions.i18n.TranslationsProvider -import com.kotlindiscord.kord.extensions.koin.KordExContext import dev.kord.common.entity.ButtonStyle import dev.kord.common.entity.DiscordPartialEmoji import dev.kord.common.entity.Snowflake @@ -16,7 +15,6 @@ import dev.kord.rest.builder.message.modify.actionRow import dev.kord.rest.builder.message.modify.embed import dev.kord.x.emoji.DiscordEmoji import dev.kord.x.emoji.Emojis -import dev.nycode.imagecolor.ImageColorClient import dev.schlaubi.mikbot.plugin.api.util.convertToISO import dev.schlaubi.mikmusic.core.settings.MusicSettingsDatabase import dev.schlaubi.mikmusic.player.ChapterQueuedTrack @@ -123,12 +121,13 @@ suspend fun updateMessage( additionalCondition = !playingQueueTrack.isOnLast ) } - musicButton( - musicPlayer, - autoPlay, - Emojis.blueCar, - enabled = musicPlayer.autoPlay != null - ) + // Disabled due to YouTube changing the underlying API +// musicButton( +// musicPlayer, +// autoPlay, +// Emojis.blueCar, +// enabled = musicPlayer.autoPlay != null +// ) } actionRow { musicButton( diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/player/AutoPlay.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/player/AutoPlay.kt index 9f7aff180..96bcf5332 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/player/AutoPlay.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/player/AutoPlay.kt @@ -6,7 +6,6 @@ import dev.schlaubi.lavakord.rest.TrackResponse import dev.schlaubi.lavakord.rest.loadItem import dev.schlaubi.mikbot.plugin.api.util.Translator import dev.schlaubi.mikmusic.innerttube.InnerTubeClient -import dev.schlaubi.mikmusic.innerttube.PlaylistPanelVideoRendererContent import dev.schlaubi.mikmusic.innerttube.Text import dev.schlaubi.mikmusic.util.LinkedListSerializer import dev.schlaubi.mikmusic.util.youtubeId @@ -54,7 +53,7 @@ private suspend fun MusicPlayer.fetchAutoPlay(songId: String, playlistId: String .content .playlistPanelRenderer .contents - .map(PlaylistPanelVideoRendererContent::playlistPanelVideoRenderer) + .mapNotNull { it.playlistPanelVideoWrapperRenderer?.primaryRenderer } val newPlayListId = songRenderers.firstOrNull()?.navigationEndpoints?.watchEndpoint?.playlistId val songs = songRenderers .drop(1) // First song is requested song @@ -68,7 +67,7 @@ private suspend fun MusicPlayer.fetchAutoPlay(songId: String, playlistId: String } context(EmbedBuilder) - suspend fun MusicPlayer.addAutoPlaySongs(translate: Translator) { +suspend fun MusicPlayer.addAutoPlaySongs(translate: Translator) { val songs = autoPlay?.songs?.take(5) if (!songs.isNullOrEmpty()) { field { @@ -97,7 +96,7 @@ suspend fun MusicPlayer.findNextAutoPlayedSong(lastSong: Track?): Track? { } context(MusicPlayer) - private suspend fun AutoPlayContext.Track.fetchTrack(): Track? { +private suspend fun AutoPlayContext.Track.fetchTrack(): Track? { val response = link.loadItem("https://www.youtube.com/watch?v=$id") return if (response.loadType == TrackResponse.LoadType.TRACK_LOADED) { response.track.toTrack() diff --git a/music/src/main/kotlin/dev/schlaubi/mikmusic/util/VideoFormatter.kt b/music/src/main/kotlin/dev/schlaubi/mikmusic/util/VideoFormatter.kt index 202178b7c..cf9391d50 100644 --- a/music/src/main/kotlin/dev/schlaubi/mikmusic/util/VideoFormatter.kt +++ b/music/src/main/kotlin/dev/schlaubi/mikmusic/util/VideoFormatter.kt @@ -7,6 +7,7 @@ import dev.kord.rest.builder.message.EmbedBuilder import dev.nycode.imagecolor.ImageColorClient import dev.schlaubi.lavakord.audio.player.Track import dev.schlaubi.mikbot.plugin.api.util.Translator +import dev.schlaubi.mikmusic.core.Config private val imageColorClient by KordExContext.get().inject() @@ -42,8 +43,10 @@ suspend fun EmbedBuilder.addSong(translate: Translator, track: Track) { url = info.thumbnails.high.url } - imageColorClient.fetchImageColorOrNull(info.thumbnails.high.url)?.let { imageColor -> - color = Color(imageColor) + if (Config.IMAGE_COLOR_SERVICE_URL != null) { + imageColorClient.fetchImageColorOrNull(info.thumbnails.high.url)?.let { imageColor -> + color = Color(imageColor) + } } author { diff --git a/test.http b/test.http index b00c2f902..687ae1b06 100644 --- a/test.http +++ b/test.http @@ -31,3 +31,27 @@ Referer: https://music.youtube.com/ } } } + +### + +POST https://music.youtube.com/youtubei/v1/next?key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30&prettyPrint=false +origin: https://music.youtube.com +Content-Type: application/json + +{ + "videoId": "Yx6GdcG5XdM", + "playlistId": "RDAMVMYx6GdcG5XdM", + "params": "8gEAmgMDCNgE", + "context": { + "client": { + "hl": "de", + "gl": "DE", + "clientName": "WEB_REMIX", + "clientVersion": "1.20230102.01.00" + } + } +} + +### + +