fix(YouTube - Spoof video streams): Add 'Android Creator' (#4262)

This commit is contained in:
LisoUseInAIKyrios 2025-01-06 11:58:27 +01:00 committed by GitHub
parent bacf32648f
commit 0479dd265e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 72 additions and 12 deletions

View File

@ -11,7 +11,9 @@ public enum ClientType {
ANDROID_VR_NO_AUTH( ANDROID_VR_NO_AUTH(
28, 28,
"ANDROID_VR", "ANDROID_VR",
"Oculus",
"Quest 3", "Quest 3",
"Android",
"12", "12",
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip", "com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
"32", // Android 12.1 "32", // Android 12.1
@ -19,10 +21,14 @@ public enum ClientType {
false, false,
"Android VR No auth" "Android VR No auth"
), ),
// Chromecast with Google TV 4K.
// https://dumps.tadiphone.dev/dumps/google/kirkwood
ANDROID_UNPLUGGED( ANDROID_UNPLUGGED(
29, 29,
"ANDROID_UNPLUGGED", "ANDROID_UNPLUGGED",
"Google",
"Google TV Streamer", "Google TV Streamer",
"Android",
"14", "14",
"com.google.android.apps.youtube.unplugged/8.49.0 (Linux; U; Android 14; GB) gzip", "com.google.android.apps.youtube.unplugged/8.49.0 (Linux; U; Android 14; GB) gzip",
"34", "34",
@ -33,7 +39,9 @@ public enum ClientType {
ANDROID_VR( ANDROID_VR(
ANDROID_VR_NO_AUTH.id, ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName, ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.deviceMake,
ANDROID_VR_NO_AUTH.deviceModel, ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osName,
ANDROID_VR_NO_AUTH.osVersion, ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.userAgent, ANDROID_VR_NO_AUTH.userAgent,
ANDROID_VR_NO_AUTH.androidSdkVersion, ANDROID_VR_NO_AUTH.androidSdkVersion,
@ -41,18 +49,21 @@ public enum ClientType {
true, true,
"Android VR" "Android VR"
), ),
IOS_UNPLUGGED(33, IOS_UNPLUGGED(
33,
"IOS_UNPLUGGED", "IOS_UNPLUGGED",
"Apple",
forceAVC() forceAVC()
? "iPhone12,5" // 11 Pro Max (last device with iOS 13) ? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
: "iPhone16,2", // 15 Pro Max : "iPhone16,2", // 15 Pro Max
"iOS",
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1. // iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
forceAVC() forceAVC()
? "13.7.17H35" // Last release of iOS 13. ? "13.7.17H35" // Last release of iOS 13.
: "18.1.1.22B91", : "18.2.22C152",
forceAVC() forceAVC()
? "com.google.ios.youtubeunplugged/6.45 (iPhone; U; CPU iOS 13_7 like Mac OS X)" ? "com.google.ios.youtubeunplugged/6.45 (iPhone12,5; U; CPU iOS 13_7 like Mac OS X)"
: "com.google.ios.youtubeunplugged/8.33 (iPhone; U; CPU iOS 18_1_1 like Mac OS X)", : "com.google.ios.youtubeunplugged/8.49 (iPhone16,2; U; CPU iOS 18_2_22 like Mac OS X)",
null, null,
// Version number should be a valid iOS release. // Version number should be a valid iOS release.
// https://www.ipa4fun.com/history/152043/ // https://www.ipa4fun.com/history/152043/
@ -60,11 +71,24 @@ public enum ClientType {
// but 6.45 is the last version that supports iOS 13. // but 6.45 is the last version that supports iOS 13.
forceAVC() forceAVC()
? "6.45" ? "6.45"
: "8.33", : "8.49",
true, true,
forceAVC() forceAVC()
? "iOS TV Force AVC" ? "iOS TV Force AVC"
: "iOS TV" : "iOS TV"
),
ANDROID_CREATOR(
14,
"ANDROID_CREATOR",
Build.MANUFACTURER,
Build.MODEL,
"Android",
"11",
"com.google.android.apps.youtube.creator/24.45.100 (Linux; U; Android 11) gzip",
"30",
"24.45.100",
true,
"Android Creator"
); );
private static boolean forceAVC() { private static boolean forceAVC() {
@ -80,10 +104,20 @@ public enum ClientType {
public final String clientName; public final String clientName;
/** /**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model) * Device model, equivalent to {@link Build#MANUFACTURER} (System property: ro.product.vendor.manufacturer)
*/
public final String deviceMake;
/**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.vendor.model)
*/ */
public final String deviceModel; public final String deviceModel;
/**
* Device OS name.
*/
public final String osName;
/** /**
* Device OS version. * Device OS version.
*/ */
@ -118,7 +152,9 @@ public enum ClientType {
ClientType(int id, ClientType(int id,
String clientName, String clientName,
String deviceMake,
String deviceModel, String deviceModel,
String osName,
String osVersion, String osVersion,
String userAgent, String userAgent,
@Nullable String androidSdkVersion, @Nullable String androidSdkVersion,
@ -127,7 +163,9 @@ public enum ClientType {
String friendlyName) { String friendlyName) {
this.id = id; this.id = id;
this.clientName = clientName; this.clientName = clientName;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel; this.deviceModel = deviceModel;
this.osName = osName;
this.osVersion = osVersion; this.osVersion = osVersion;
this.userAgent = userAgent; this.userAgent = userAgent;
this.androidSdkVersion = androidSdkVersion; this.androidSdkVersion = androidSdkVersion;

View File

@ -119,17 +119,19 @@ public class SpoofVideoStreamsPatch {
return; return;
} }
// 'get_drm_license' has no video id and appears to happen when waiting for a paid video to start.
// 'heartbeat' has no video id and appears to be only after playback has started. // 'heartbeat' has no video id and appears to be only after playback has started.
// 'refresh' has no video id and appears to happen when waiting for a livestream to start. // 'refresh' has no video id and appears to happen when waiting for a livestream to start.
// 'ad_break' has no video id. // 'ad_break' has no video id.
if (path.contains("heartbeat") || path.contains("refresh") || path.contains("ad_break")) { if (path.contains("get_drm_license") || path.contains("heartbeat")
|| path.contains("refresh") || path.contains("ad_break")) {
Logger.printDebug(() -> "Ignoring path: " + path); Logger.printDebug(() -> "Ignoring path: " + path);
return; return;
} }
String id = uri.getQueryParameter("id"); String id = uri.getQueryParameter("id");
if (id == null) { if (id == null) {
Logger.printException(() -> "Ignoring request with no id. Url: " + url); Logger.printException(() -> "Ignoring request with no id: " + url);
return; return;
} }

View File

@ -50,7 +50,9 @@ final class PlayerRoutes {
client.put("hl", language.getLanguage()); client.put("hl", language.getLanguage());
client.put("clientName", clientType.clientName); client.put("clientName", clientType.clientName);
client.put("clientVersion", clientType.clientVersion); client.put("clientVersion", clientType.clientVersion);
client.put("deviceMake", clientType.deviceMake);
client.put("deviceModel", clientType.deviceModel); client.put("deviceModel", clientType.deviceModel);
client.put("osName", clientType.osName);
client.put("osVersion", clientType.osVersion); client.put("osVersion", clientType.osVersion);
if (clientType.androidSdkVersion != null) { if (clientType.androidSdkVersion != null) {
client.put("androidSdkVersion", clientType.androidSdkVersion); client.put("androidSdkVersion", clientType.androidSdkVersion);
@ -76,6 +78,7 @@ final class PlayerRoutes {
connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", clientType.userAgent); connection.setRequestProperty("User-Agent", clientType.userAgent);
connection.setRequestProperty("X-YouTube-Client-Version", String.valueOf(clientType.id));
connection.setUseCaches(false); connection.setUseCaches(false);
connection.setDoOutput(true); connection.setDoOutput(true);

View File

@ -191,8 +191,8 @@ public class StreamingDataRequest {
// gzip encoding doesn't response with content length (-1), // gzip encoding doesn't response with content length (-1),
// but empty response body does. // but empty response body does.
if (connection.getContentLength() == 0) { if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get()) { if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Logger.printException(() -> "Ignoring empty client: " + clientType); Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
} }
} else { } else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream()); try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());

View File

@ -81,7 +81,20 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
(clientType == ClientType.IOS_UNPLUGGED (clientType == ClientType.IOS_UNPLUGGED
? "ios_tv" ? "ios_tv"
: "android"); : "android");
setTitle(str(key + "_title")); String title = str(key + "_title");
setSummary(str(key + "_summary")); String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
// Android Creator does not support HDR.
if (clientType == ClientType.ANDROID_CREATOR) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_hdr");
}
}
setTitle(title);
setSummary(summary);
} }
} }

View File

@ -116,6 +116,7 @@
<string-array name="revanced_spoof_video_streams_client_type_entries"> <string-array name="revanced_spoof_video_streams_client_type_entries">
<item>Android VR</item> <item>Android VR</item>
<item>@string/revanced_spoof_video_streams_client_type_android_vr_no_auth</item> <item>@string/revanced_spoof_video_streams_client_type_android_vr_no_auth</item>
<item>Android Creator</item>
<item>Android TV</item> <item>Android TV</item>
<item>iOS TV</item> <item>iOS TV</item>
</string-array> </string-array>
@ -123,6 +124,7 @@
<!-- Extension enum names. --> <!-- Extension enum names. -->
<item>ANDROID_VR</item> <item>ANDROID_VR</item>
<item>ANDROID_VR_NO_AUTH</item> <item>ANDROID_VR_NO_AUTH</item>
<item>ANDROID_CREATOR</item>
<item>ANDROID_UNPLUGGED</item> <item>ANDROID_UNPLUGGED</item>
<item>IOS_UNPLUGGED</item> <item>IOS_UNPLUGGED</item>
</string-array> </string-array>

View File

@ -1395,6 +1395,8 @@ AVC has a maximum resolution of 1080p, Opus audio codec is not available, and vi
<string name="revanced_spoof_video_streams_about_android_summary">"• Audio track menu is missing <string name="revanced_spoof_video_streams_about_android_summary">"• Audio track menu is missing
• Stable volume is not available • Stable volume is not available
• Force original audio is not available"</string> • Force original audio is not available"</string>
<string name="revanced_spoof_video_streams_about_no_av1">• No AV1 video codec</string>
<string name="revanced_spoof_video_streams_about_no_hdr">• No HDR video</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Show in Stats for nerds</string> <string name="revanced_spoof_streaming_data_stats_for_nerds_title">Show in Stats for nerds</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Client type is shown in Stats for nerds</string> <string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Client type is shown in Stats for nerds</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Client is hidden in Stats for nerds</string> <string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Client is hidden in Stats for nerds</string>