diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/requests/PlaylistRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/requests/PlaylistRequest.kt index 90fb6f963..62b98a5e3 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/requests/PlaylistRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/misc/requests/PlaylistRequest.kt @@ -2,8 +2,10 @@ package app.revanced.extension.music.patches.misc.requests import android.annotation.SuppressLint import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createApplicationRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_PLAYLIST_PAGE import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.settings.AppLanguage import app.revanced.extension.shared.utils.Logger @@ -136,10 +138,11 @@ class PlaylistRequest private constructor( Logger.printDebug { "Fetching playlist request for: $videoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_PLAYLIST_PAGE, + val connection = getInnerTubeResponseConnectionFromRoute( + GET_PLAYLIST_PAGE, clientType ) + /** * For some reason, the tracks in Top Songs have the playlistId of the album: * [ReVanced_Extended#2835](https://github.com/inotia00/ReVanced_Extended/issues/2835) @@ -152,7 +155,7 @@ class PlaylistRequest private constructor( * So we can work around this by setting the language to English when sending the request. */ val requestBody = - PlayerRoutes.createApplicationRequestBody( + createApplicationRequestBody( clientType = clientType, videoId = videoId, playlistId = playlistId, diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/YouTubeAppClient.kt b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeAppClient.kt similarity index 90% rename from extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/YouTubeAppClient.kt rename to extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeAppClient.kt index f32c1b425..4c0ddbbda 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/YouTubeAppClient.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeAppClient.kt @@ -1,4 +1,4 @@ -package app.revanced.extension.shared.patches.client +package app.revanced.extension.shared.innertube.client import android.os.Build import app.revanced.extension.shared.settings.BaseSettings @@ -11,24 +11,6 @@ import java.util.Locale */ object YouTubeAppClient { // IOS - /** - * Video not playable: Paid / Movie / Private / Age-restricted - * Note: Audio track available - */ - private const val PACKAGE_NAME_IOS = "com.google.ios.youtube" - - /** - * The hardcoded client version of the iOS app used for InnerTube requests with this client. - * - * It can be extracted by getting the latest release version of the app on - * [the App Store page of the YouTube app](https://apps.apple.com/us/app/youtube-watch-listen-stream/id544007664/), - * in the `What’s New` section. - */ - private val CLIENT_VERSION_IOS = if (forceAVC()) - "17.40.5" - else - "20.10.4" - private const val DEVICE_MAKE_IOS = "Apple" private const val OS_NAME_IOS = "iOS" @@ -49,7 +31,6 @@ object YouTubeAppClient { "13_7" else "18_3_2" - private val USER_AGENT_IOS = iOSUserAgent(PACKAGE_NAME_IOS, CLIENT_VERSION_IOS) // IOS UNPLUGGED @@ -202,6 +183,7 @@ object YouTubeAppClient { * Example: 'com.google.ios.youtube/16.38.2 (iPhone9,4; U; CPU iOS 14_7_1 like Mac OS X; en_AU)' * Source: https://github.com/mitmproxy/mitmproxy/issues/4836. */ + @Suppress("SameParameterValue") private fun iOSUserAgent( packageName: String, clientVersion: String @@ -278,10 +260,6 @@ object YouTubeAppClient { * If true, 'Authorization' must be included. */ val requireAuth: Boolean = false, - /** - * Whether a poToken is required to get playback for more than 1 minute. - */ - val requirePoToken: Boolean = false, /** * Client name for innertube body. */ @@ -362,22 +340,6 @@ object YouTubeAppClient { "iOS TV Force AVC" else "iOS TV" - ), - IOS( - id = 5, - deviceMake = DEVICE_MAKE_IOS, - deviceModel = DEVICE_MODEL_IOS, - osName = OS_NAME_IOS, - osVersion = OS_VERSION_IOS, - userAgent = USER_AGENT_IOS, - clientVersion = CLIENT_VERSION_IOS, - supportsCookies = false, - requirePoToken = true, - clientName = "IOS", - friendlyName = if (forceAVC()) - "iOS Force AVC" - else - "iOS" ); companion object { diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/MusicAppClient.java b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeMusicAppClient.java similarity index 98% rename from extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/MusicAppClient.java rename to extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeMusicAppClient.java index 21b580c8d..24081156a 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/MusicAppClient.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeMusicAppClient.java @@ -1,10 +1,10 @@ -package app.revanced.extension.shared.patches.client; +package app.revanced.extension.shared.innertube.client; import android.os.Build; import java.util.Locale; -public class MusicAppClient { +public class YouTubeMusicAppClient { // Response to the '/next' request is 'Please update to continue using the app': // https://github.com/inotia00/ReVanced_Extended/issues/2743 @@ -46,7 +46,7 @@ public class MusicAppClient { private static final String DEVICE_MAKE_IOS_MUSIC = "Apple"; private static final String OS_NAME_IOS_MUSIC = "iOS"; - private MusicAppClient() { + private YouTubeMusicAppClient() { } private static String androidUserAgent(String clientVersion) { diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/YouTubeWebClient.kt b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeWebClient.kt similarity index 72% rename from extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/YouTubeWebClient.kt rename to extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeWebClient.kt index 331d1d9a4..3f9c5e8c0 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/YouTubeWebClient.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/client/YouTubeWebClient.kt @@ -1,14 +1,10 @@ -package app.revanced.extension.shared.patches.client +package app.revanced.extension.shared.innertube.client /** * Used to fetch video information. */ @Suppress("unused") object YouTubeWebClient { - /** - * This user agent does not require a PoToken in [ClientType.MWEB] - * https://github.com/yt-dlp/yt-dlp/blob/0b6b7742c2e7f2a1fcb0b54ef3dd484bab404b3f/yt_dlp/extractor/youtube.py#L259 - */ private const val USER_AGENT_SAFARI = "Mozilla/5.0 (iPad; CPU OS 16_7_10 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1,gzip(gfe)" @@ -26,11 +22,11 @@ object YouTubeWebClient { * Client version. */ @JvmField - val clientVersion: String + val clientVersion: String, ) { MWEB( id = 2, - clientVersion = "2.20241202.07.00" + clientVersion = "2.20241202.07.00", ), WEB_REMIX( id = 29, diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.kt b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/requests/InnerTubeRequestBody.kt similarity index 68% rename from extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.kt rename to extensions/shared/src/main/java/app/revanced/extension/shared/innertube/requests/InnerTubeRequestBody.kt index 5ef4a4212..9e6154bbd 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/PlayerRoutes.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/requests/InnerTubeRequestBody.kt @@ -1,9 +1,8 @@ -package app.revanced.extension.shared.patches.spoof.requests +package app.revanced.extension.shared.innertube.requests -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.client.YouTubeWebClient +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.client.YouTubeWebClient import app.revanced.extension.shared.requests.Requester -import app.revanced.extension.shared.requests.Route import app.revanced.extension.shared.requests.Route.CompiledRoute import app.revanced.extension.shared.settings.BaseSettings import app.revanced.extension.shared.utils.Logger @@ -21,96 +20,17 @@ import java.util.Locale import java.util.TimeZone @Suppress("deprecation") -object PlayerRoutes { - @JvmField - val CREATE_PLAYLIST: CompiledRoute = Route( - Route.Method.POST, - "playlist/create" + - "?prettyPrint=false" + - "&fields=playlistId" - ).compile() - - @JvmField - val DELETE_PLAYLIST: CompiledRoute = Route( - Route.Method.POST, - "playlist/delete" + - "?prettyPrint=false" - ).compile() - - @JvmField - val EDIT_PLAYLIST: CompiledRoute = Route( - Route.Method.POST, - "browse/edit_playlist" + - "?prettyPrint=false" + - "&fields=status," + - "playlistEditResults" - ).compile() - - @JvmField - val GET_PLAYLISTS: CompiledRoute = Route( - Route.Method.POST, - "playlist/get_add_to_playlist" + - "?prettyPrint=false" + - "&fields=contents.addToPlaylistRenderer.playlists.playlistAddToOptionRenderer" - ).compile() - - @JvmField - val GET_CATEGORY: CompiledRoute = Route( - Route.Method.POST, - "player" + - "?prettyPrint=false" + - "&fields=microformat.playerMicroformatRenderer.category" - ).compile() - - @JvmField - val GET_SET_VIDEO_ID: CompiledRoute = Route( - Route.Method.POST, - "next" + - "?prettyPrint=false" + - "&fields=contents.singleColumnWatchNextResults." + - "playlist.playlist.contents.playlistPanelVideoRenderer." + - "playlistSetVideoId" - ).compile() - - @JvmField - val GET_PLAYLIST_PAGE: CompiledRoute = Route( - Route.Method.POST, - "next" + - "?prettyPrint=false" + - "&fields=contents.singleColumnWatchNextResults.playlist.playlist" - ).compile() - - @JvmField - val GET_STREAMING_DATA: CompiledRoute = Route( - Route.Method.POST, - "player" + - "?fields=streamingData" + - "&alt=proto" - ).compile() - - @JvmField - val GET_VIDEO_ACTION_BUTTON: CompiledRoute = Route( - Route.Method.POST, - "next" + - "?prettyPrint=false" + - "&fields=contents.singleColumnWatchNextResults." + - "results.results.contents.slimVideoMetadataSectionRenderer." + - "contents.elementRenderer.newElement.type.componentType." + - "model.videoActionBarModel.buttons.buttonViewModel" - ).compile() - - @JvmField - val GET_VIDEO_DETAILS: CompiledRoute = Route( - Route.Method.POST, - "player" + - "?prettyPrint=false" + - "&fields=videoDetails.channelId," + - "videoDetails.isLiveContent," + - "videoDetails.isUpcoming" - ).compile() +object InnerTubeRequestBody { private const val YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/" + private const val AUTHORIZATION_HEADER = "Authorization" + private val REQUEST_HEADER_KEYS = setOf( + AUTHORIZATION_HEADER, // Available only to logged-in users. + "X-GOOG-API-FORMAT-VERSION", + "X-Goog-Visitor-Id" + ) + /** * TCP connection and HTTP read timeout */ @@ -230,16 +150,10 @@ object PlayerRoutes { client.put("osName", clientType.osName) client.put("osVersion", clientType.osVersion) client.put("androidSdkVersion", clientType.androidSdkVersion) - if (clientType.gmscoreVersionCode != null) { - client.put("gmscoreVersionCode", clientType.gmscoreVersionCode) - } - client.put( - "hl", - LOCALE_LANGUAGE - ) + client.put("hl", LOCALE_LANGUAGE) client.put("gl", LOCALE_COUNTRY) client.put("timeZone", TIME_ZONE_ID) - client.put("utcOffsetMinutes", "$UTC_OFFSET_MINUTES") + client.put("utcOffsetMinutes", UTC_OFFSET_MINUTES.toString()) val context = JSONObject() context.put("client", client) @@ -269,7 +183,7 @@ object PlayerRoutes { videoIds.put(0, videoId) innerTubeBody.put("videoIds", videoIds) } catch (e: JSONException) { - Logger.printException({ "Failed to create playlist innerTubeBody" }, e) + Logger.printException({ "Failed to create create/playlist innerTubeBody" }, e) } return innerTubeBody.toString().toByteArray(StandardCharsets.UTF_8) @@ -284,7 +198,7 @@ object PlayerRoutes { try { innerTubeBody.put("playlistId", playlistId) } catch (e: JSONException) { - Logger.printException({ "Failed to create playlist innerTubeBody" }, e) + Logger.printException({ "Failed to create delete/playlist innerTubeBody" }, e) } return innerTubeBody.toString().toByteArray(StandardCharsets.UTF_8) @@ -314,7 +228,7 @@ object PlayerRoutes { actionsArray.put(0, actionsObject) innerTubeBody.put("actions", actionsArray) } catch (e: JSONException) { - Logger.printException({ "Failed to create playlist innerTubeBody" }, e) + Logger.printException({ "Failed to create edit/playlist innerTubeBody" }, e) } return innerTubeBody.toString().toByteArray(StandardCharsets.UTF_8) @@ -330,7 +244,7 @@ object PlayerRoutes { innerTubeBody.put("playlistId", playlistId) innerTubeBody.put("excludeWatchLater", false) } catch (e: JSONException) { - Logger.printException({ "Failed to create playlist innerTubeBody" }, e) + Logger.printException({ "Failed to create get/playlists innerTubeBody" }, e) } return innerTubeBody.toString().toByteArray(StandardCharsets.UTF_8) @@ -354,44 +268,57 @@ object PlayerRoutes { actionsArray.put(0, actionsObject) innerTubeBody.put("actions", actionsArray) } catch (e: JSONException) { - Logger.printException({ "Failed to create playlist innerTubeBody" }, e) + Logger.printException({ "Failed to create save/playlist innerTubeBody" }, e) } return innerTubeBody.toString().toByteArray(StandardCharsets.UTF_8) } @JvmStatic - fun getPlayerResponseConnectionFromRoute( + fun getInnerTubeResponseConnectionFromRoute( route: CompiledRoute, - clientType: YouTubeAppClient.ClientType - ): HttpURLConnection { - return getPlayerResponseConnectionFromRoute( - route, - clientType.userAgent, - clientType.id.toString(), - clientType.clientVersion - ) - } + clientType: YouTubeAppClient.ClientType, + requestHeader: Map? = null, + connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, + readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, + ) = getInnerTubeResponseConnectionFromRoute( + route = route, + userAgent = clientType.userAgent, + clientId = clientType.id.toString(), + clientVersion = clientType.clientVersion, + supportsCookies = clientType.supportsCookies, + requestHeader = requestHeader, + connectTimeout = connectTimeout, + readTimeout = readTimeout, + ) @JvmStatic - fun getPlayerResponseConnectionFromRoute( + fun getInnerTubeResponseConnectionFromRoute( route: CompiledRoute, - clientType: YouTubeWebClient.ClientType - ): HttpURLConnection { - return getPlayerResponseConnectionFromRoute( - route, - clientType.userAgent, - clientType.id.toString(), - clientType.clientVersion, - ) - } + clientType: YouTubeWebClient.ClientType, + requestHeader: Map? = null, + connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, + readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, + ) = getInnerTubeResponseConnectionFromRoute( + route = route, + userAgent = clientType.userAgent, + clientId = clientType.id.toString(), + clientVersion = clientType.clientVersion, + requestHeader = requestHeader, + connectTimeout = connectTimeout, + readTimeout = readTimeout, + ) @Throws(IOException::class) - fun getPlayerResponseConnectionFromRoute( + fun getInnerTubeResponseConnectionFromRoute( route: CompiledRoute, userAgent: String, clientId: String, - clientVersion: String + clientVersion: String, + supportsCookies: Boolean = true, + requestHeader: Map? = null, + connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, + readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, ): HttpURLConnection { val connection = Requester.getConnectionFromCompiledRoute(YT_API_URL, route) @@ -403,8 +330,24 @@ object PlayerRoutes { connection.useCaches = false connection.doOutput = true - connection.connectTimeout = CONNECTION_TIMEOUT_MILLISECONDS - connection.readTimeout = CONNECTION_TIMEOUT_MILLISECONDS + connection.connectTimeout = connectTimeout + connection.readTimeout = readTimeout + + if (requestHeader != null) { + for (key in REQUEST_HEADER_KEYS) { + var value = requestHeader[key] + if (value != null) { + if (key == AUTHORIZATION_HEADER) { + if (!supportsCookies) { + continue + } + } + + connection.setRequestProperty(key, value) + } + } + } + return connection } diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/requests/InnerTubeRoutes.kt b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/requests/InnerTubeRoutes.kt new file mode 100644 index 000000000..48ee66f8f --- /dev/null +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/innertube/requests/InnerTubeRoutes.kt @@ -0,0 +1,108 @@ +package app.revanced.extension.shared.innertube.requests + +import app.revanced.extension.shared.requests.Route +import app.revanced.extension.shared.requests.Route.CompiledRoute + +object InnerTubeRoutes { + + @JvmField + val CREATE_PLAYLIST = compileRoute( + endpoint = "playlist/create", + fields = "playlistId", + ) + + @JvmField + val DELETE_PLAYLIST = compileRoute( + endpoint = "playlist/delete", + ) + + @JvmField + val EDIT_PLAYLIST = compileRoute( + endpoint = "browse/edit_playlist", + fields = "status," + "playlistEditResults", + ) + + @JvmField + val GET_CATEGORY = compileRoute( + endpoint = "player", + fields = "microformat.playerMicroformatRenderer.category", + ) + + @JvmField + val GET_PLAYLISTS = compileRoute( + endpoint = "playlist/get_add_to_playlist", + fields = "contents.addToPlaylistRenderer.playlists.playlistAddToOptionRenderer", + ) + + @JvmField + val GET_SET_VIDEO_ID = compileRoute( + endpoint = "next", + fields = "contents.singleColumnWatchNextResults." + + "playlist.playlist.contents.playlistPanelVideoRenderer." + + "playlistSetVideoId", + ) + + @JvmField + val GET_PLAYLIST_PAGE = compileRoute( + endpoint = "next", + fields = "contents.singleColumnWatchNextResults.playlist.playlist", + ) + + @JvmField + val GET_STREAMING_DATA = compileRoute( + endpoint = "player", + fields = "streamingData", + alt = "proto", + prettier = true, + ) + + @JvmField + val GET_VIDEO_ACTION_BUTTON = compileRoute( + endpoint = "next", + fields = "contents.singleColumnWatchNextResults." + + "results.results.contents.slimVideoMetadataSectionRenderer." + + "contents.elementRenderer.newElement.type.componentType." + + "model.videoActionBarModel.buttons.buttonViewModel" + ) + + @JvmField + val GET_VIDEO_DETAILS = compileRoute( + endpoint = "player", + fields = "videoDetails.channelId," + + "videoDetails.isLiveContent," + + "videoDetails.isUpcoming" + ) + + private fun compileRoute( + endpoint: String, + fields: String? = null, + alt: String? = null, + prettier: Boolean = false, + ): CompiledRoute { + var query = Array(4) { "&" } + var i = 0 + query[i] = "?" + + val sb = StringBuilder(endpoint) + if (prettier == false) { + sb.append(query[i++]) + sb.append("prettyPrint=false") + } + if (fields != null) { + sb.append(query[i++]) + sb.append("fields=") + sb.append(fields) + } + if (alt != null) { + sb.append(query[i++]) + sb.append("alt=") + sb.append(alt) + } + + return Route( + Route.Method.POST, + sb.toString() + ).compile() + } + +} \ No newline at end of file diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofClientPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofClientPatch.java index f26063a4f..a0faa7e06 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofClientPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofClientPatch.java @@ -1,11 +1,11 @@ package app.revanced.extension.shared.patches.spoof; -import app.revanced.extension.shared.patches.client.MusicAppClient.ClientType; -import app.revanced.extension.music.settings.Settings; +import app.revanced.extension.shared.innertube.client.YouTubeMusicAppClient.ClientType; +import app.revanced.extension.shared.settings.BaseSettings; @SuppressWarnings("unused") public class SpoofClientPatch extends BlockRequestPatch { - private static final ClientType CLIENT_TYPE = Settings.SPOOF_CLIENT_TYPE.get(); + private static final ClientType CLIENT_TYPE = BaseSettings.SPOOF_CLIENT_TYPE.get(); /** * Injection point. diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java index ef9d62ae7..9b087b276 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/SpoofStreamingDataPatch.java @@ -10,7 +10,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import app.revanced.extension.shared.patches.client.YouTubeAppClient.ClientType; +import app.revanced.extension.shared.innertube.client.YouTubeAppClient.ClientType; import app.revanced.extension.shared.patches.spoof.requests.StreamingDataRequest; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.Setting; @@ -19,10 +19,6 @@ import app.revanced.extension.shared.utils.Utils; @SuppressWarnings("unused") public class SpoofStreamingDataPatch extends BlockRequestPatch { - private static final String PO_TOKEN = - BaseSettings.SPOOF_STREAMING_DATA_PO_TOKEN.get(); - private static final String VISITOR_DATA = - BaseSettings.SPOOF_STREAMING_DATA_VISITOR_DATA.get(); private static final boolean SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION = SPOOF_STREAMING_DATA && BaseSettings.SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION.get(); @@ -79,7 +75,7 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch { /** * Injection point. */ - public static void fetchStreams(String url, Map requestHeaders) { + public static void fetchStreams(String url, Map requestHeader) { if (SPOOF_STREAMING_DATA) { String id = Utils.getVideoIdFromRequest(url); if (id == null) { @@ -89,7 +85,7 @@ public class SpoofStreamingDataPatch extends BlockRequestPatch { return; } - StreamingDataRequest.fetchRequest(id, requestHeaders, VISITOR_DATA, PO_TOKEN); + StreamingDataRequest.fetchRequest(id, requestHeader); } } diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.kt index 425ccfcb3..018bb48d5 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/spoof/requests/StreamingDataRequest.kt @@ -1,14 +1,13 @@ package app.revanced.extension.shared.patches.spoof.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes.GET_STREAMING_DATA -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes.createApplicationRequestBody -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes.getPlayerResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createApplicationRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_STREAMING_DATA import app.revanced.extension.shared.settings.BaseSettings import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import org.apache.commons.lang3.StringUtils import java.io.BufferedInputStream import java.io.ByteArrayOutputStream import java.io.IOException @@ -32,21 +31,19 @@ import java.util.concurrent.TimeoutException * did use its own client streams. */ class StreamingDataRequest private constructor( - videoId: String, playerHeaders: Map, - visitorId: String, botGuardPoToken: String + videoId: String, + requestHeader: Map, ) { private val videoId: String private val future: Future init { - Objects.requireNonNull(playerHeaders) + Objects.requireNonNull(requestHeader) this.videoId = videoId this.future = Utils.submitOnBackgroundThread { fetch( videoId, - playerHeaders, - visitorId, - botGuardPoToken + requestHeader, ) } } @@ -86,33 +83,16 @@ class StreamingDataRequest private constructor( companion object { private const val AUTHORIZATION_HEADER = "Authorization" - private const val VISITOR_ID_HEADER = "X-Goog-Visitor-Id" - private val REQUEST_HEADER_KEYS = arrayOf( - AUTHORIZATION_HEADER, // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - VISITOR_ID_HEADER - ) + private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 + private val SPOOF_STREAMING_DATA_TYPE: YouTubeAppClient.ClientType = BaseSettings.SPOOF_STREAMING_DATA_TYPE.get() - private val CLIENT_ORDER_TO_USE: Array = YouTubeAppClient.availableClientTypes(SPOOF_STREAMING_DATA_TYPE) - private val DEFAULT_CLIENT_IS_ANDROID_VR_NO_AUTH: Boolean = SPOOF_STREAMING_DATA_TYPE == YouTubeAppClient.ClientType.ANDROID_VR_NO_AUTH - private var lastSpoofedClientType: YouTubeAppClient.ClientType? = null - - - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ - private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 + private var lastSpoofedClientFriendlyName: String? = null @GuardedBy("itself") val cache: MutableMap = Collections.synchronizedMap( @@ -126,22 +106,24 @@ class StreamingDataRequest private constructor( @JvmStatic val lastSpoofedClientName: String - get() = lastSpoofedClientType - ?.friendlyName - ?: "Unknown" + get() { + return if (lastSpoofedClientFriendlyName != null) { + lastSpoofedClientFriendlyName!! + } else { + "Unknown" + } + } @JvmStatic fun fetchRequest( - videoId: String, fetchHeaders: Map, - visitorId: String, botGuardPoToken: String + videoId: String, + fetchHeaders: Map, ) { // Always fetch, even if there is an existing request for the same video. cache[videoId] = StreamingDataRequest( videoId, - fetchHeaders, - visitorId, - botGuardPoToken + fetchHeaders ) } @@ -157,64 +139,28 @@ class StreamingDataRequest private constructor( private fun send( clientType: YouTubeAppClient.ClientType, videoId: String, - playerHeaders: Map, - visitorId: String, - botGuardPoToken: String + requestHeader: Map, ): HttpURLConnection? { Objects.requireNonNull(clientType) Objects.requireNonNull(videoId) - Objects.requireNonNull(playerHeaders) + Objects.requireNonNull(requestHeader) val startTime = System.currentTimeMillis() Logger.printDebug { "Fetching video streams for: $videoId using client: $clientType" } try { val connection = - getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - - val usePoToken = - clientType.requirePoToken && !StringUtils.isAnyEmpty(botGuardPoToken, visitorId) - - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - if (key == AUTHORIZATION_HEADER) { - if (!clientType.supportsCookies) { - Logger.printDebug { "Not including request header: $key" } - continue - } - } - if (key == VISITOR_ID_HEADER && usePoToken) { - val originalVisitorId: String = value - Logger.printDebug { "Original visitor id:\n$originalVisitorId" } - Logger.printDebug { "Replaced visitor id:\n$visitorId" } - value = visitorId - } - - connection.setRequestProperty(key, value) - } - } - - val requestBody: ByteArray - if (usePoToken) { - requestBody = createApplicationRequestBody( - clientType = clientType, - videoId = videoId, - botGuardPoToken = botGuardPoToken, - visitorId = visitorId, - setLocale = DEFAULT_CLIENT_IS_ANDROID_VR_NO_AUTH, + getInnerTubeResponseConnectionFromRoute( + GET_STREAMING_DATA, + clientType, + requestHeader ) - Logger.printDebug { "Set poToken (botGuardPoToken):\n$botGuardPoToken" } - } else { - requestBody = - createApplicationRequestBody( - clientType = clientType, - videoId = videoId, - setLocale = DEFAULT_CLIENT_IS_ANDROID_VR_NO_AUTH, - ) - } + + val requestBody = createApplicationRequestBody( + clientType = clientType, + videoId = videoId, + setLocale = DEFAULT_CLIENT_IS_ANDROID_VR_NO_AUTH, + ) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -243,15 +189,15 @@ class StreamingDataRequest private constructor( } private fun fetch( - videoId: String, playerHeaders: Map, - visitorId: String, botGuardPoToken: String + videoId: String, + requestHeader: Map, ): ByteBuffer? { - lastSpoofedClientType = null + lastSpoofedClientFriendlyName = null // Retry with different client if empty response body is received. for (clientType in CLIENT_ORDER_TO_USE) { if (clientType.requireAuth && - playerHeaders[AUTHORIZATION_HEADER] == null + requestHeader[AUTHORIZATION_HEADER] == null ) { Logger.printDebug { "Skipped login-required client (incognito mode or not logged in)\nClient: $clientType\nVideo: $videoId" } continue @@ -259,9 +205,7 @@ class StreamingDataRequest private constructor( send( clientType, videoId, - playerHeaders, - visitorId, - botGuardPoToken + requestHeader, )?.let { connection -> try { // gzip encoding doesn't response with content length (-1), @@ -271,14 +215,14 @@ class StreamingDataRequest private constructor( } else { BufferedInputStream(connection.inputStream).use { inputStream -> ByteArrayOutputStream().use { stream -> - val buffer = ByteArray(2048) + val buffer = ByteArray(4096) var bytesRead: Int while ((inputStream.read(buffer) .also { bytesRead = it }) >= 0 ) { stream.write(buffer, 0, bytesRead) } - lastSpoofedClientType = clientType + lastSpoofedClientFriendlyName = clientType.friendlyName return ByteBuffer.wrap(stream.toByteArray()) } } diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index 004766e99..71fe7fbba 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -3,10 +3,10 @@ package app.revanced.extension.shared.settings; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import app.revanced.extension.shared.innertube.client.YouTubeAppClient; +import app.revanced.extension.shared.innertube.client.YouTubeMusicAppClient; import app.revanced.extension.shared.patches.ReturnYouTubeUsernamePatch.DisplayFormat; import app.revanced.extension.shared.patches.WatchHistoryPatch.WatchHistoryType; -import app.revanced.extension.shared.patches.client.MusicAppClient; -import app.revanced.extension.shared.patches.client.YouTubeAppClient; import app.revanced.extension.shared.patches.spoof.SpoofStreamingDataPatch.AudioStreamLanguageOverrideAvailability; /** @@ -31,7 +31,7 @@ public class BaseSettings { * Some patches are in a shared path, so they are declared here. */ public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true); - public static final EnumSetting SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", MusicAppClient.ClientType.IOS_MUSIC_6_21, true); + public static final EnumSetting SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", YouTubeMusicAppClient.ClientType.IOS_MUSIC_6_21, true); /** * These settings are used by YouTube. @@ -44,10 +44,7 @@ public class BaseSettings { public static final BooleanSetting SPOOF_STREAMING_DATA_SKIP_RESPONSE_ENCRYPTION = new BooleanSetting("revanced_spoof_streaming_data_skip_response_encryption", TRUE, true); public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE); // Client type must be last spoof setting due to cyclic references. - public static final EnumSetting SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", YouTubeAppClient.ClientType.ANDROID_UNPLUGGED, true); - - public static final StringSetting SPOOF_STREAMING_DATA_PO_TOKEN = new StringSetting("revanced_spoof_streaming_data_po_token", "", true); - public static final StringSetting SPOOF_STREAMING_DATA_VISITOR_DATA = new StringSetting("revanced_spoof_streaming_data_visitor_data", "", true); + public static final EnumSetting SPOOF_STREAMING_DATA_TYPE = new EnumSetting<>("revanced_spoof_streaming_data_type", YouTubeAppClient.ClientType.ANDROID_VR, true); /** * These settings are used by YouTube and YouTube Music. diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/requests/VideoDetailsRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/requests/VideoDetailsRequest.kt index 1db71775a..64c3897aa 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/requests/VideoDetailsRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/requests/VideoDetailsRequest.kt @@ -2,8 +2,10 @@ package app.revanced.extension.youtube.patches.general.requests import android.annotation.SuppressLint import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeWebClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeWebClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createWebInnertubeBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_VIDEO_DETAILS import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils @@ -86,12 +88,11 @@ class VideoDetailsRequest private constructor( Logger.printDebug { "Fetching video details request for: $videoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_VIDEO_DETAILS, + val connection = getInnerTubeResponseConnectionFromRoute( + GET_VIDEO_DETAILS, clientType ) - val requestBody = - PlayerRoutes.createWebInnertubeBody(clientType, videoId) + val requestBody = createWebInnertubeBody(clientType, videoId) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/requests/ActionButtonRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/requests/ActionButtonRequest.kt index af38ab459..b485dec6f 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/requests/ActionButtonRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/player/requests/ActionButtonRequest.kt @@ -1,8 +1,10 @@ package app.revanced.extension.youtube.patches.player.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createApplicationRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_VIDEO_ACTION_BUTTON import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils @@ -20,10 +22,10 @@ import java.util.concurrent.TimeoutException class ActionButtonRequest private constructor( private val videoId: String, - private val playerHeaders: Map, + private val requestHeader: Map, ) { private val future: Future> = Utils.submitOnBackgroundThread { - fetch(videoId, playerHeaders) + fetch(videoId, requestHeader) } val array: Array @@ -52,14 +54,6 @@ class ActionButtonRequest private constructor( } companion object { - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 @GuardedBy("itself") @@ -73,11 +67,11 @@ class ActionButtonRequest private constructor( }) @JvmStatic - fun fetchRequestIfNeeded(videoId: String, playerHeaders: Map) { + fun fetchRequestIfNeeded(videoId: String, requestHeader: Map) { Objects.requireNonNull(videoId) synchronized(cache) { if (!cache.containsKey(videoId)) { - cache[videoId] = ActionButtonRequest(videoId, playerHeaders) + cache[videoId] = ActionButtonRequest(videoId, requestHeader) } } } @@ -93,43 +87,28 @@ class ActionButtonRequest private constructor( Logger.printInfo({ toastMessage }, ex) } - private val REQUEST_HEADER_KEYS = arrayOf( - "Authorization", // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - ) - - private fun sendRequest(videoId: String, playerHeaders: Map): JSONObject? { + private fun sendRequest(videoId: String, requestHeader: Map): JSONObject? { Objects.requireNonNull(videoId) val startTime = System.currentTimeMillis() - // '/next' request does not require PoToken. + // '/next' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching playlist request for: $videoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_VIDEO_ACTION_BUTTON, - clientType - ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - // Since [THANKS] button and [CLIP] button are shown only with the logged in, // Set the [Authorization] field to property to get the correct action buttons. - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } + val connection = getInnerTubeResponseConnectionFromRoute( + GET_VIDEO_ACTION_BUTTON, + clientType, + requestHeader, + ) - val requestBody = - PlayerRoutes.createApplicationRequestBody( - clientType = clientType, - videoId = videoId - ) + val requestBody = createApplicationRequestBody( + clientType = clientType, + videoId = videoId + ) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -214,8 +193,11 @@ class ActionButtonRequest private constructor( return emptyArray() } - private fun fetch(videoId: String, playerHeaders: Map): Array { - val json = sendRequest(videoId, playerHeaders) + private fun fetch( + videoId: String, + requestHeader: Map + ): Array { + val json = sendRequest(videoId, requestHeader) if (json != null) { return parseResponse(json) } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/CreatePlaylistRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/CreatePlaylistRequest.kt index bf72d8966..69ad16e98 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/CreatePlaylistRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/CreatePlaylistRequest.kt @@ -1,12 +1,15 @@ package app.revanced.extension.youtube.patches.utils.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createApplicationRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createPlaylistRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.CREATE_PLAYLIST +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_SET_VIDEO_ID import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import app.revanced.extension.youtube.patches.utils.requests.CreatePlaylistRequest.Companion.HTTP_TIMEOUT_MILLISECONDS import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -20,10 +23,10 @@ import java.util.concurrent.TimeoutException class CreatePlaylistRequest private constructor( private val videoId: String, - private val playerHeaders: Map, + private val requestHeader: Map, ) { private val future: Future> = Utils.submitOnBackgroundThread { - fetch(videoId, playerHeaders) + fetch(videoId, requestHeader) } val playlistId: Pair? @@ -52,14 +55,6 @@ class CreatePlaylistRequest private constructor( } companion object { - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 @GuardedBy("itself") @@ -80,11 +75,11 @@ class CreatePlaylistRequest private constructor( } @JvmStatic - fun fetchRequestIfNeeded(videoId: String, playerHeaders: Map) { + fun fetchRequestIfNeeded(videoId: String, requestHeader: Map) { Objects.requireNonNull(videoId) synchronized(cache) { if (!cache.containsKey(videoId)) { - cache[videoId] = CreatePlaylistRequest(videoId, playerHeaders) + cache[videoId] = CreatePlaylistRequest(videoId, requestHeader) } } } @@ -100,40 +95,26 @@ class CreatePlaylistRequest private constructor( Logger.printInfo({ toastMessage }, ex) } - private val REQUEST_HEADER_KEYS = arrayOf( - "Authorization", // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - ) - - private fun sendCreatePlaylistRequest(videoId: String, playerHeaders: Map): JSONObject? { + private fun sendCreatePlaylistRequest( + videoId: String, + requestHeader: Map + ): JSONObject? { Objects.requireNonNull(videoId) val startTime = System.currentTimeMillis() - // 'playlist/create' request does not require PoToken. + // 'playlist/create' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching create playlist request for: $videoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.CREATE_PLAYLIST, - clientType + val connection = getInnerTubeResponseConnectionFromRoute( + CREATE_PLAYLIST, + clientType, + requestHeader, ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } - - val requestBody = - PlayerRoutes.createPlaylistRequestBody( - videoId = videoId - ) + val requestBody = createPlaylistRequestBody(videoId = videoId) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -159,36 +140,31 @@ class CreatePlaylistRequest private constructor( return null } - private fun sendSetVideoIdRequest(videoId: String, playlistId: String, playerHeaders: Map): JSONObject? { + private fun sendSetVideoIdRequest( + videoId: String, + playlistId: String, + requestHeader: Map + ): JSONObject? { Objects.requireNonNull(playlistId) val startTime = System.currentTimeMillis() - // 'playlist/create' request does not require PoToken. + // 'playlist/create' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching set video id request for: $playlistId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_SET_VIDEO_ID, - clientType + val connection = getInnerTubeResponseConnectionFromRoute( + GET_SET_VIDEO_ID, + clientType, + requestHeader ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } - - val requestBody = - PlayerRoutes.createApplicationRequestBody( - clientType = clientType, - videoId = videoId, - playlistId = playlistId - ) + val requestBody = createApplicationRequestBody( + clientType = clientType, + videoId = videoId, + playlistId = playlistId + ) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -240,8 +216,8 @@ class CreatePlaylistRequest private constructor( if (secondaryContentsJsonObject is JSONObject) { return secondaryContentsJsonObject - .getJSONObject("playlistPanelVideoRenderer") - .getString("playlistSetVideoId") + .getJSONObject("playlistPanelVideoRenderer") + .getString("playlistSetVideoId") } } catch (e: JSONException) { val jsonForMessage = json.toString() @@ -254,12 +230,15 @@ class CreatePlaylistRequest private constructor( return null } - private fun fetch(videoId: String, playerHeaders: Map): Pair? { - val createPlaylistJson = sendCreatePlaylistRequest(videoId, playerHeaders) + private fun fetch( + videoId: String, + requestHeader: Map + ): Pair? { + val createPlaylistJson = sendCreatePlaylistRequest(videoId, requestHeader) if (createPlaylistJson != null) { val playlistId = parseCreatePlaylistResponse(createPlaylistJson) if (playlistId != null) { - val setVideoIdJson = sendSetVideoIdRequest(videoId, playlistId, playerHeaders) + val setVideoIdJson = sendSetVideoIdRequest(videoId, playlistId, requestHeader) if (setVideoIdJson != null) { val setVideoId = parseSetVideoIdResponse(setVideoIdJson) if (setVideoId != null) { diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/DeletePlaylistRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/DeletePlaylistRequest.kt index 7a0c8fc95..60675fe30 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/DeletePlaylistRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/DeletePlaylistRequest.kt @@ -1,12 +1,13 @@ package app.revanced.extension.youtube.patches.utils.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.deletePlaylistRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.DELETE_PLAYLIST import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import app.revanced.extension.youtube.patches.utils.requests.DeletePlaylistRequest.Companion.HTTP_TIMEOUT_MILLISECONDS import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -20,12 +21,12 @@ import java.util.concurrent.TimeoutException class DeletePlaylistRequest private constructor( private val playlistId: String, - private val playerHeaders: Map, + private val requestHeader: Map, ) { private val future: Future = Utils.submitOnBackgroundThread { fetch( playlistId, - playerHeaders, + requestHeader, ) } @@ -55,14 +56,6 @@ class DeletePlaylistRequest private constructor( } companion object { - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 @GuardedBy("itself") @@ -85,14 +78,14 @@ class DeletePlaylistRequest private constructor( @JvmStatic fun fetchRequestIfNeeded( playlistId: String, - playerHeaders: Map + requestHeader: Map ) { Objects.requireNonNull(playlistId) synchronized(cache) { if (!cache.containsKey(playlistId)) { cache[playlistId] = DeletePlaylistRequest( playlistId, - playerHeaders + requestHeader ) } } @@ -109,40 +102,26 @@ class DeletePlaylistRequest private constructor( Logger.printInfo({ toastMessage }, ex) } - private val REQUEST_HEADER_KEYS = arrayOf( - "Authorization", // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - ) - private fun sendRequest( playlistId: String, - playerHeaders: Map + requestHeader: Map ): JSONObject? { Objects.requireNonNull(playlistId) val startTime = System.currentTimeMillis() - // 'playlist/delete' request does not require PoToken. + // 'playlist/delete' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching delete playlist request, playlistId: $playlistId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.DELETE_PLAYLIST, + val connection = getInnerTubeResponseConnectionFromRoute( + DELETE_PLAYLIST, clientType, + requestHeader ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } - - val requestBody = PlayerRoutes.deletePlaylistRequestBody(playlistId) + val requestBody = deletePlaylistRequestBody(playlistId) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -184,9 +163,9 @@ class DeletePlaylistRequest private constructor( private fun fetch( playlistId: String, - playerHeaders: Map + requestHeader: Map ): Boolean? { - val json = sendRequest(playlistId, playerHeaders) + val json = sendRequest(playlistId, requestHeader) if (json != null) { return parseResponse(json) } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt index 7372cb89a..826b36b8a 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/EditPlaylistRequest.kt @@ -1,12 +1,13 @@ package app.revanced.extension.youtube.patches.utils.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.editPlaylistRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.EDIT_PLAYLIST import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import app.revanced.extension.youtube.patches.utils.requests.EditPlaylistRequest.Companion.HTTP_TIMEOUT_MILLISECONDS import org.apache.commons.lang3.StringUtils import org.json.JSONException import org.json.JSONObject @@ -23,14 +24,14 @@ class EditPlaylistRequest private constructor( private val videoId: String, private val playlistId: String, private val setVideoId: String?, - private val playerHeaders: Map, + private val requestHeader: Map, ) { private val future: Future = Utils.submitOnBackgroundThread { fetch( videoId, playlistId, setVideoId, - playerHeaders, + requestHeader, ) } @@ -60,14 +61,6 @@ class EditPlaylistRequest private constructor( } companion object { - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 @GuardedBy("itself") @@ -99,7 +92,7 @@ class EditPlaylistRequest private constructor( videoId: String, playlistId: String, setVideoId: String?, - playerHeaders: Map + requestHeader: Map ) { Objects.requireNonNull(videoId) synchronized(cache) { @@ -108,7 +101,7 @@ class EditPlaylistRequest private constructor( videoId, playlistId, setVideoId, - playerHeaders + requestHeader ) } } @@ -125,47 +118,32 @@ class EditPlaylistRequest private constructor( Logger.printInfo({ toastMessage }, ex) } - private val REQUEST_HEADER_KEYS = arrayOf( - "Authorization", // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - ) - private fun sendRequest( videoId: String, playlistId: String, setVideoId: String?, - playerHeaders: Map + requestHeader: Map ): JSONObject? { Objects.requireNonNull(videoId) val startTime = System.currentTimeMillis() - // 'browse/edit_playlist' request does not require PoToken. + // 'browse/edit_playlist' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching edit playlist request, videoId: $videoId, playlistId: $playlistId, setVideoId: $setVideoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.EDIT_PLAYLIST, - clientType + val connection = getInnerTubeResponseConnectionFromRoute( + EDIT_PLAYLIST, + clientType, + requestHeader ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } - - val requestBody = - PlayerRoutes.editPlaylistRequestBody( - videoId = videoId, - playlistId = playlistId, - setVideoId = setVideoId, - ) + val requestBody = editPlaylistRequestBody( + videoId = videoId, + playlistId = playlistId, + setVideoId = setVideoId + ) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -197,7 +175,8 @@ class EditPlaylistRequest private constructor( if (remove) { return "" } - val playlistEditResultsJSONObject = json.getJSONArray("playlistEditResults").get(0) + val playlistEditResultsJSONObject = + json.getJSONArray("playlistEditResults").get(0) if (playlistEditResultsJSONObject is JSONObject) { return playlistEditResultsJSONObject @@ -220,9 +199,9 @@ class EditPlaylistRequest private constructor( videoId: String, playlistId: String, setVideoId: String?, - playerHeaders: Map + requestHeader: Map ): String? { - val json = sendRequest(videoId, playlistId, setVideoId, playerHeaders) + val json = sendRequest(videoId, playlistId, setVideoId, requestHeader) if (json != null) { return parseResponse(json, StringUtils.isNotEmpty(setVideoId)) } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/GetPlaylistsRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/GetPlaylistsRequest.kt index 54f54f33c..5a57f00e2 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/GetPlaylistsRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/GetPlaylistsRequest.kt @@ -1,12 +1,13 @@ package app.revanced.extension.youtube.patches.utils.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getPlaylistsRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_PLAYLISTS import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import app.revanced.extension.youtube.patches.utils.requests.GetPlaylistsRequest.Companion.HTTP_TIMEOUT_MILLISECONDS import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -20,12 +21,12 @@ import java.util.concurrent.TimeoutException class GetPlaylistsRequest private constructor( private val playlistId: String, - private val playerHeaders: Map, + private val requestHeader: Map, ) { private val future: Future>> = Utils.submitOnBackgroundThread { fetch( playlistId, - playerHeaders, + requestHeader, ) } @@ -55,14 +56,6 @@ class GetPlaylistsRequest private constructor( } companion object { - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 @GuardedBy("itself") @@ -85,14 +78,14 @@ class GetPlaylistsRequest private constructor( @JvmStatic fun fetchRequestIfNeeded( playlistId: String, - playerHeaders: Map + requestHeader: Map ) { Objects.requireNonNull(playlistId) synchronized(cache) { if (!cache.containsKey(playlistId)) { cache[playlistId] = GetPlaylistsRequest( playlistId, - playerHeaders + requestHeader ) } } @@ -109,40 +102,26 @@ class GetPlaylistsRequest private constructor( Logger.printInfo({ toastMessage }, ex) } - private val REQUEST_HEADER_KEYS = arrayOf( - "Authorization", // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - ) - private fun sendRequest( playlistId: String, - playerHeaders: Map + requestHeader: Map ): JSONObject? { Objects.requireNonNull(playlistId) val startTime = System.currentTimeMillis() - // 'playlist/get_add_to_playlist' request does not require PoToken. + // 'playlist/get_add_to_playlist' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching get playlists request, playlistId: $playlistId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_PLAYLISTS, - clientType + val connection = getInnerTubeResponseConnectionFromRoute( + GET_PLAYLISTS, + clientType, + requestHeader ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } - - val requestBody = PlayerRoutes.getPlaylistsRequestBody(playlistId) + val requestBody = getPlaylistsRequestBody(playlistId) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -223,9 +202,9 @@ class GetPlaylistsRequest private constructor( private fun fetch( playlistId: String, - playerHeaders: Map + requestHeader: Map ): Array>? { - val json = sendRequest(playlistId, playerHeaders) + val json = sendRequest(playlistId, requestHeader) if (json != null) { return parseResponse(json) } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/SavePlaylistRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/SavePlaylistRequest.kt index c5d3a611a..58b737869 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/SavePlaylistRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/utils/requests/SavePlaylistRequest.kt @@ -1,12 +1,13 @@ package app.revanced.extension.youtube.patches.utils.requests import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.savePlaylistRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.EDIT_PLAYLIST import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils -import app.revanced.extension.youtube.patches.utils.requests.SavePlaylistRequest.Companion.HTTP_TIMEOUT_MILLISECONDS import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -21,13 +22,13 @@ import java.util.concurrent.TimeoutException class SavePlaylistRequest private constructor( private val playlistId: String, private val libraryId: String, - private val playerHeaders: Map, + private val requestHeader: Map, ) { private val future: Future = Utils.submitOnBackgroundThread { fetch( playlistId, libraryId, - playerHeaders, + requestHeader, ) } @@ -57,14 +58,6 @@ class SavePlaylistRequest private constructor( } companion object { - /** - * TCP connection and HTTP read timeout. - */ - private const val HTTP_TIMEOUT_MILLISECONDS = 10 * 1000 - - /** - * Any arbitrarily large value, but must be at least twice [HTTP_TIMEOUT_MILLISECONDS] - */ private const val MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000 @GuardedBy("itself") @@ -88,14 +81,14 @@ class SavePlaylistRequest private constructor( fun fetchRequestIfNeeded( playlistId: String, libraryId: String, - playerHeaders: Map + requestHeader: Map ) { Objects.requireNonNull(playlistId) synchronized(cache) { cache[libraryId] = SavePlaylistRequest( playlistId, libraryId, - playerHeaders + requestHeader ) } } @@ -111,43 +104,28 @@ class SavePlaylistRequest private constructor( Logger.printInfo({ toastMessage }, ex) } - private val REQUEST_HEADER_KEYS = arrayOf( - "Authorization", // Available only to logged-in users. - "X-GOOG-API-FORMAT-VERSION", - "X-Goog-Visitor-Id" - ) - private fun sendRequest( playlistId: String, libraryId: String, - playerHeaders: Map + requestHeader: Map ): JSONObject? { Objects.requireNonNull(playlistId) Objects.requireNonNull(libraryId) val startTime = System.currentTimeMillis() - // 'browse/edit_playlist' request does not require PoToken. + // 'browse/edit_playlist' endpoint does not require PoToken. val clientType = YouTubeAppClient.ClientType.ANDROID val clientTypeName = clientType.name Logger.printDebug { "Fetching edit playlist request, playlistId: $playlistId, libraryId: $libraryId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.EDIT_PLAYLIST, - clientType + val connection = getInnerTubeResponseConnectionFromRoute( + EDIT_PLAYLIST, + clientType, + requestHeader ) - connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS - connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS - for (key in REQUEST_HEADER_KEYS) { - var value = playerHeaders[key] - if (value != null) { - connection.setRequestProperty(key, value) - } - } - - val requestBody = - PlayerRoutes.savePlaylistRequestBody(libraryId, playlistId) + val requestBody = savePlaylistRequestBody(libraryId, playlistId) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody) @@ -190,9 +168,9 @@ class SavePlaylistRequest private constructor( private fun fetch( playlistId: String, libraryId: String, - playerHeaders: Map + requestHeader: Map ): Boolean? { - val json = sendRequest(playlistId, libraryId,playerHeaders) + val json = sendRequest(playlistId, libraryId, requestHeader) if (json != null) { return parseResponse(json) } diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/requests/MusicRequest.kt b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/requests/MusicRequest.kt index d48ca8b51..a5283ad3d 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/requests/MusicRequest.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/video/requests/MusicRequest.kt @@ -2,9 +2,13 @@ package app.revanced.extension.youtube.patches.video.requests import android.annotation.SuppressLint import androidx.annotation.GuardedBy -import app.revanced.extension.shared.patches.client.YouTubeAppClient -import app.revanced.extension.shared.patches.client.YouTubeWebClient -import app.revanced.extension.shared.patches.spoof.requests.PlayerRoutes +import app.revanced.extension.shared.innertube.client.YouTubeAppClient +import app.revanced.extension.shared.innertube.client.YouTubeWebClient +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createApplicationRequestBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.createWebInnertubeBody +import app.revanced.extension.shared.innertube.requests.InnerTubeRequestBody.getInnerTubeResponseConnectionFromRoute +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_CATEGORY +import app.revanced.extension.shared.innertube.requests.InnerTubeRoutes.GET_PLAYLIST_PAGE import app.revanced.extension.shared.requests.Requester import app.revanced.extension.shared.utils.Logger import app.revanced.extension.shared.utils.Utils @@ -124,12 +128,12 @@ class MusicRequest private constructor( Logger.printDebug { "Fetching playlist request for: $videoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_PLAYLIST_PAGE, + val connection = getInnerTubeResponseConnectionFromRoute( + GET_PLAYLIST_PAGE, clientType ) val requestBody = - PlayerRoutes.createApplicationRequestBody( + createApplicationRequestBody( clientType = clientType, videoId = videoId, playlistId = "RD$videoId" @@ -168,12 +172,11 @@ class MusicRequest private constructor( Logger.printDebug { "Fetching microformat request for: $videoId, using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute( - PlayerRoutes.GET_CATEGORY, + val connection = getInnerTubeResponseConnectionFromRoute( + GET_CATEGORY, clientType ) - val requestBody = - PlayerRoutes.createWebInnertubeBody(clientType, videoId) + val requestBody = createWebInnertubeBody(clientType, videoId) connection.setFixedLengthStreamingMode(requestBody.size) connection.outputStream.write(requestBody)