fix(YouTube - Spoof streaming data): Enhanced bitrate not available on Android VR, Android TV

This commit is contained in:
inotia00 2025-01-06 21:18:04 +09:00
parent aeb6750063
commit 8663436d3d
5 changed files with 61 additions and 13 deletions

View File

@ -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,
)

View File

@ -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;
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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)