diff --git a/app/src/main/java/com/dd3boh/outertune/utils/YTPlayerUtils.kt b/app/src/main/java/com/dd3boh/outertune/utils/YTPlayerUtils.kt index 7ecc361ce..468e5507c 100644 --- a/app/src/main/java/com/dd3boh/outertune/utils/YTPlayerUtils.kt +++ b/app/src/main/java/com/dd3boh/outertune/utils/YTPlayerUtils.kt @@ -66,8 +66,14 @@ object YTPlayerUtils { */ val signatureTimestamp = getSignatureTimestampOrNull(videoId) + // --- TODO: GET WEB PO TOKENS HERE --- + val webPlayerPot = "" // TODO + val webStreamingPot = "" // TODO + // --- + val mainPlayerResponse = - YouTube.player(videoId, playlistId, MAIN_CLIENT, signatureTimestamp).getOrThrow() + YouTube.player(videoId, playlistId, MAIN_CLIENT, signatureTimestamp, webPlayerPot) + .getOrThrow() val audioConfig = mainPlayerResponse.playerConfig?.audioConfig val videoDetails = mainPlayerResponse.videoDetails @@ -84,19 +90,27 @@ object YTPlayerUtils { streamExpiresInSeconds = null // decide which client to use - if (clientIndex == -1) { - // try with streams from main client first + val client = + if (clientIndex == -1) { + // try with streams from main client first + MAIN_CLIENT + } else { + // after main client use fallback clients + STREAM_FALLBACK_CLIENTS[clientIndex] + } + + // get player response for streams + if (client == MAIN_CLIENT) { streamPlayerResponse = mainPlayerResponse } else { - // after main client use fallback clients - val client = STREAM_FALLBACK_CLIENTS[clientIndex] if (client.loginRequired && YouTube.cookie == null) { // skip client if it requires login but user is not logged in continue } streamPlayerResponse = - YouTube.player(videoId, playlistId, client, signatureTimestamp).getOrNull() + YouTube.player(videoId, playlistId, client, signatureTimestamp, webPlayerPot) + .getOrNull() } // process current client response @@ -111,6 +125,10 @@ object YTPlayerUtils { streamUrl = findUrlOrNull(format, videoId) ?: continue streamExpiresInSeconds = streamPlayerResponse.streamingData?.expiresInSeconds ?: continue + if (client.useWebPoTokens) { + streamUrl += "&pot=$webStreamingPot"; + } + if (clientIndex == STREAM_FALLBACK_CLIENTS.size - 1) { /** skip [validateStatus] for last client */ break diff --git a/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt b/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt index fa3db72e9..d5c4c8daa 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/InnerTube.kt @@ -155,6 +155,7 @@ class InnerTube { videoId: String, playlistId: String?, signatureTimestamp: Int?, + webPlayerPot: String?, ) = httpClient.post("player") { // Skip network call for local content if (isLocalContent(videoId)) { @@ -184,12 +185,16 @@ class InnerTube { }, videoId = videoId, playlistId = playlistId, - playbackContext = - if (client.useSignatureTimestamp && signatureTimestamp != null) { - PlayerBody.PlaybackContext(PlayerBody.PlaybackContext.ContentPlaybackContext( + playbackContext = if (client.useSignatureTimestamp && signatureTimestamp != null) { + PlayerBody.PlaybackContext( + PlayerBody.PlaybackContext.ContentPlaybackContext( signatureTimestamp - )) - } else null + ) + ) + } else null, + serviceIntegrityDimensions = if (client.useWebPoTokens && webPlayerPot != null) { + PlayerBody.ServiceIntegrityDimensions(webPlayerPot) + } else null ) ) } diff --git a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt index 0d66f71b2..7dbee8c16 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/YouTube.kt @@ -658,8 +658,8 @@ object YouTube { innerTube.deletePlaylist(WEB_REMIX, playlistId) } - suspend fun player(videoId: String, playlistId: String? = null, client: YouTubeClient, signatureTimestamp: Int? = null): Result = runCatching { - innerTube.player(client, videoId, playlistId, signatureTimestamp).body() + suspend fun player(videoId: String, playlistId: String? = null, client: YouTubeClient, signatureTimestamp: Int? = null, webPlayerPot: String? = null): Result = runCatching { + innerTube.player(client, videoId, playlistId, signatureTimestamp, webPlayerPot).body() } suspend fun next(endpoint: WatchEndpoint, continuation: String? = null): Result = runCatching { diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/YouTubeClient.kt b/innertube/src/main/java/com/zionhuang/innertube/models/YouTubeClient.kt index 8ef170838..02da1a2bc 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/YouTubeClient.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/YouTubeClient.kt @@ -12,6 +12,7 @@ data class YouTubeClient( val loginSupported: Boolean = false, val loginRequired: Boolean = false, val useSignatureTimestamp: Boolean = false, + val useWebPoTokens: Boolean = false, val isEmbedded: Boolean = false, // val origin: String? = null, // val referer: String? = null, @@ -51,6 +52,7 @@ data class YouTubeClient( userAgent = USER_AGENT_WEB, loginSupported = true, useSignatureTimestamp = true, + useWebPoTokens = true, ) val WEB_CREATOR = YouTubeClient( diff --git a/innertube/src/main/java/com/zionhuang/innertube/models/body/PlayerBody.kt b/innertube/src/main/java/com/zionhuang/innertube/models/body/PlayerBody.kt index e721516e5..04231d674 100644 --- a/innertube/src/main/java/com/zionhuang/innertube/models/body/PlayerBody.kt +++ b/innertube/src/main/java/com/zionhuang/innertube/models/body/PlayerBody.kt @@ -10,6 +10,7 @@ data class PlayerBody( val playlistId: String?, val cpn: String? = "wzf9Y0nqz6AUe2Vr", // need some random cpn to get same algorithm for sig val playbackContext: PlaybackContext? = null, + val serviceIntegrityDimensions: ServiceIntegrityDimensions? = null, val contentCheckOk: Boolean = true, val racyCheckOk: Boolean = true, ) { @@ -22,4 +23,9 @@ data class PlayerBody( val signatureTimestamp: Int ) } + + @Serializable + data class ServiceIntegrityDimensions( + val poToken: String + ) }