diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt index b40ab90..3d3e0aa 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt @@ -125,6 +125,7 @@ object YouTube { visitorData = "CgtsZG1ySnZiQWtSbyiMjuGSBg%3D%3D" ) ) + val DefaultAndroid = Context( client = Client( clientName = "ANDROID", @@ -132,6 +133,14 @@ object YouTube { visitorData = null, ) ) + + val DefaultAgeRestrictionBypass = Context( + client = Client( + clientName = "TVHTML5_SIMPLY_EMBEDDED_PLAYER", + clientVersion = "2.0", + visitorData = null, + ) + ) } } @@ -457,7 +466,7 @@ object YouTube { suspend fun player(videoId: String, playlistId: String? = null): Result? { return runCatching { - client.post("/youtubei/v1/player") { + val playerResponse = client.post("/youtubei/v1/player") { contentType(ContentType.Application.Json) setBody( PlayerBody( @@ -469,6 +478,52 @@ object YouTube { parameter("key", Key) parameter("prettyPrint", false) }.body() + + if (playerResponse.playabilityStatus.status == "OK") { + playerResponse + } else { + @Serializable + data class AudioStream( + val url: String, + val bitrate: Long + ) + + @Serializable + data class PipedResponse( + val audioStreams: List + ) + + val safePlayerResponse = client.post("/youtubei/v1/player") { + contentType(ContentType.Application.Json) + setBody( + PlayerBody( + context = Context.DefaultAgeRestrictionBypass, + videoId = videoId, + playlistId = playlistId, + ) + ) + parameter("key", Key) + parameter("prettyPrint", false) + }.body() + + if (safePlayerResponse.playabilityStatus.status != "OK") { + return@runCatching playerResponse + } + + val audioStreams = client.get("https://pipedapi.kavin.rocks/streams/$videoId") { + contentType(ContentType.Application.Json) + }.body().audioStreams + + safePlayerResponse.copy( + streamingData = safePlayerResponse.streamingData?.copy( + adaptiveFormats = safePlayerResponse.streamingData.adaptiveFormats.map { adaptiveFormat -> + adaptiveFormat.copy( + url = audioStreams.find { it.bitrate == adaptiveFormat.bitrate }?.url + ) + } + ) + ) + } }.recoverIfCancelled() } diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/PlayerResponse.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/PlayerResponse.kt index 29a7895..f877575 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/PlayerResponse.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/PlayerResponse.kt @@ -43,7 +43,7 @@ data class PlayerResponse( val lastModified: Long?, val loudnessDb: Double?, val audioSampleRate: Int?, - val url: String, + val url: String?, ) }