diff --git a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.kt b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.kt index 6fac8a9bb..17f0f53d5 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.kt +++ b/extensions/shared/src/main/java/app/revanced/extension/shared/patches/client/AppClient.kt @@ -28,6 +28,8 @@ object AppClient { else "19.29.1" + private const val DEVICE_MAKE_IOS = "Apple" + private const val OS_NAME_IOS = "iOS" /** * The device machine id for the iPhone 15 Pro Max (iPhone16,2), * used to get HDR with AV1 hardware decoding. @@ -95,6 +97,7 @@ object AppClient { * See [this GitLab](https://dumps.tadiphone.dev/dumps/oculus/eureka) for more information. */ private const val DEVICE_MODEL_ANDROID_VR = "Quest 3" + private const val DEVICE_MAKE_ANDROID_VR = "Oculus" private const val OS_VERSION_ANDROID_VR = "12" /** @@ -119,6 +122,7 @@ object AppClient { * See [this GitLab](https://dumps.tadiphone.dev/dumps/google/kirkwood) for more information. */ private const val DEVICE_MODEL_ANDROID_UNPLUGGED = "Google TV Streamer" + private const val DEVICE_MAKE_ANDROID_UNPLUGGED = "Google" private const val OS_VERSION_ANDROID_UNPLUGGED = "14" private const val ANDROID_SDK_VERSION_ANDROID_UNPLUGGED = "34" private val USER_AGENT_ANDROID_UNPLUGGED = androidUserAgent( @@ -128,6 +132,20 @@ object AppClient { ) + // ANDROID CREATOR + /** + * Video not playable: Livestream / HDR + * Note: Audio track is not available + */ + private const val PACKAGE_NAME_ANDROID_CREATOR = "com.google.android.apps.youtube.creator" + private const val CLIENT_VERSION_ANDROID_CREATOR = "23.47.101" + + private val USER_AGENT_ANDROID_CREATOR = androidUserAgent( + PACKAGE_NAME_ANDROID_CREATOR, + CLIENT_VERSION_ANDROID_CREATOR + ) + + // ANDROID MUSIC /** * Video not playable: All videos that can't be played on YouTube Music @@ -139,7 +157,6 @@ object AppClient { * It is not the default client yet, as it requires sufficient testing. */ private const val CLIENT_VERSION_ANDROID_MUSIC = "4.27.53" - private val ANDROID_SDK_VERSION_ANDROID_MUSIC = Build.VERSION.SDK_INT.toString() private val USER_AGENT_ANDROID_MUSIC = androidUserAgent( PACKAGE_NAME_ANDROID_MUSIC, CLIENT_VERSION_ANDROID_MUSIC @@ -195,15 +212,24 @@ object AppClient { } } + @Suppress("DEPRECATION") enum class ClientType( /** * [YouTube client type](https://github.com/zerodytrash/YouTube-Internal-Clients?tab=readme-ov-file#clients) */ val id: Int, + /** + * Device model, equivalent to [Build.MANUFACTURER] (System property: ro.product.vendor.manufacturer) + */ + val deviceMake: String = Build.MANUFACTURER, /** * Device model, equivalent to [Build.MODEL] (System property: ro.product.model) */ val deviceModel: String = Build.MODEL, + /** + * Device OS name. + */ + val osName: String = "Android", /** * Device OS version, equivalent to [Build.VERSION.RELEASE] (System property: ro.system.build.version.release) */ @@ -216,7 +242,7 @@ object AppClient { * Android SDK version, equivalent to [Build.VERSION.SDK] (System property: ro.build.version.sdk) * Field is null if not applicable. */ - val androidSdkVersion: String? = null, + val androidSdkVersion: String = Build.VERSION.SDK, /** * App version. */ @@ -242,6 +268,7 @@ object AppClient { ) { ANDROID_VR( id = 28, + deviceMake = DEVICE_MAKE_ANDROID_VR, deviceModel = DEVICE_MODEL_ANDROID_VR, osVersion = OS_VERSION_ANDROID_VR, userAgent = USER_AGENT_ANDROID_VR, @@ -251,6 +278,7 @@ object AppClient { ), ANDROID_UNPLUGGED( id = 29, + deviceMake = DEVICE_MAKE_ANDROID_UNPLUGGED, deviceModel = DEVICE_MODEL_ANDROID_UNPLUGGED, osVersion = OS_VERSION_ANDROID_UNPLUGGED, userAgent = USER_AGENT_ANDROID_UNPLUGGED, @@ -259,9 +287,18 @@ object AppClient { requireAuth = true, friendlyName = "Android TV" ), + ANDROID_CREATOR( + id = 14, + userAgent = USER_AGENT_ANDROID_CREATOR, + clientVersion = CLIENT_VERSION_ANDROID_CREATOR, + requireAuth = true, + friendlyName = "Android Studio" + ), IOS_UNPLUGGED( id = 33, + deviceMake = DEVICE_MAKE_IOS, deviceModel = DEVICE_MODEL_IOS, + osName = OS_NAME_IOS, osVersion = OS_VERSION_IOS, userAgent = USER_AGENT_IOS_UNPLUGGED, clientVersion = CLIENT_VERSION_IOS_UNPLUGGED, @@ -273,7 +310,9 @@ object AppClient { ), 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, @@ -287,7 +326,6 @@ object AppClient { ANDROID_MUSIC( id = 21, userAgent = USER_AGENT_ANDROID_MUSIC, - androidSdkVersion = ANDROID_SDK_VERSION_ANDROID_MUSIC, clientVersion = CLIENT_VERSION_ANDROID_MUSIC, requireAuth = true, friendlyName = "Android Music" @@ -300,6 +338,7 @@ object AppClient { ANDROID_VR, ANDROID_UNPLUGGED, IOS_UNPLUGGED, + ANDROID_CREATOR, IOS, ) 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 fc888b116..a94f79810 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 @@ -207,7 +207,7 @@ public class SpoofStreamingDataPatch { String id = uri.getQueryParameter("id"); if (id == null) { - Logger.printException(() -> "Ignoring request with no id. Url: " + url); + Logger.printException(() -> "Ignoring request with no id: " + url); return; } 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/patches/spoof/requests/PlayerRoutes.kt index 666e04858..9f43b02bd 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/patches/spoof/requests/PlayerRoutes.kt @@ -62,14 +62,12 @@ object PlayerRoutes { val client = JSONObject() client.put("clientName", clientType.clientName) client.put("clientVersion", clientType.clientVersion) + client.put("deviceMake", clientType.deviceMake) client.put("deviceModel", clientType.deviceModel) + client.put("osName", clientType.osName) client.put("osVersion", clientType.osVersion) - if (clientType.androidSdkVersion != null) { + if (clientType.osName == "Android") { client.put("androidSdkVersion", clientType.androidSdkVersion) - client.put("osName", "Android") - } else { - client.put("deviceMake", "Apple") - client.put("osName", "iOS") } if (!clientType.supportsCookies) { client.put("hl", LOCALE_LANGUAGE) @@ -130,12 +128,22 @@ object PlayerRoutes { } @JvmStatic + fun getPlayerResponseConnectionFromRoute(route: CompiledRoute, clientType: AppClient.ClientType): HttpURLConnection { + return getPlayerResponseConnectionFromRoute(route, clientType.userAgent, clientType.id.toString()) + } + + @JvmStatic + fun getPlayerResponseConnectionFromRoute(route: CompiledRoute, clientType: WebClient.ClientType): HttpURLConnection { + return getPlayerResponseConnectionFromRoute(route, clientType.userAgent, clientType.id.toString()) + } + @Throws(IOException::class) - fun getPlayerResponseConnectionFromRoute(route: CompiledRoute, userAgent: String): HttpURLConnection { + fun getPlayerResponseConnectionFromRoute(route: CompiledRoute, userAgent: String, clientVersion: String): HttpURLConnection { val connection = Requester.getConnectionFromCompiledRoute(YT_API_URL, route) connection.setRequestProperty("Content-Type", "application/json") connection.setRequestProperty("User-Agent", userAgent) + connection.setRequestProperty("X-YouTube-Client-Version", clientVersion) connection.useCaches = false connection.doOutput = true @@ -144,4 +152,5 @@ object PlayerRoutes { connection.readTimeout = CONNECTION_TIMEOUT_MILLISECONDS return connection } + } \ No newline at end of file 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 0e0542f67..5423e0653 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 @@ -171,7 +171,7 @@ class StreamingDataRequest private constructor( Logger.printDebug { "Fetching video streams for: $videoId using client: $clientType" } try { - val connection = getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType.userAgent) + val connection = getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType) connection.connectTimeout = HTTP_TIMEOUT_MILLISECONDS connection.readTimeout = HTTP_TIMEOUT_MILLISECONDS 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 72d765c16..91711bd7c 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 @@ -121,7 +121,7 @@ class MusicRequest private constructor(private val videoId: String, private val Logger.printDebug { "Fetching playlist request for: $videoId using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(PlayerRoutes.GET_PLAYLIST_PAGE, clientType.userAgent) + val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(PlayerRoutes.GET_PLAYLIST_PAGE, clientType) val requestBody = PlayerRoutes.createApplicationRequestBody(clientType, videoId, "RD$videoId") @@ -158,7 +158,7 @@ class MusicRequest private constructor(private val videoId: String, private val Logger.printDebug { "Fetching playability request for: $videoId using client: $clientTypeName" } try { - val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(PlayerRoutes.GET_CATEGORY, clientType.userAgent) + val connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(PlayerRoutes.GET_CATEGORY, clientType) val requestBody = PlayerRoutes.createWebInnertubeBody(clientType, videoId)