Skip to content

Commit

Permalink
fix(YouTube - Spoof video streams): Add 'Android Creator' (#4262)
Browse files Browse the repository at this point in the history
  • Loading branch information
LisoUseInAIKyrios authored Jan 6, 2025
1 parent bacf326 commit 0479dd2
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,24 @@ public enum ClientType {
ANDROID_VR_NO_AUTH(
28,
"ANDROID_VR",
"Oculus",
"Quest 3",
"Android",
"12",
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
"32", // Android 12.1
"1.56.21",
false,
"Android VR No auth"
),
// Chromecast with Google TV 4K.
// https://dumps.tadiphone.dev/dumps/google/kirkwood
ANDROID_UNPLUGGED(
29,
"ANDROID_UNPLUGGED",
"Google",
"Google TV Streamer",
"Android",
"14",
"com.google.android.apps.youtube.unplugged/8.49.0 (Linux; U; Android 14; GB) gzip",
"34",
Expand All @@ -33,38 +39,56 @@ public enum ClientType {
ANDROID_VR(
ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.deviceMake,
ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osName,
ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.userAgent,
ANDROID_VR_NO_AUTH.androidSdkVersion,
ANDROID_VR_NO_AUTH.clientVersion,
true,
"Android VR"
),
IOS_UNPLUGGED(33,
IOS_UNPLUGGED(
33,
"IOS_UNPLUGGED",
"Apple",
forceAVC()
? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
: "iPhone16,2", // 15 Pro Max
"iOS",
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
forceAVC()
? "13.7.17H35" // Last release of iOS 13.
: "18.1.1.22B91",
: "18.2.22C152",
forceAVC()
? "com.google.ios.youtubeunplugged/6.45 (iPhone; 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/6.45 (iPhone12,5; U; CPU iOS 13_7 like Mac OS X)"
: "com.google.ios.youtubeunplugged/8.49 (iPhone16,2; U; CPU iOS 18_2_22 like Mac OS X)",
null,
// Version number should be a valid iOS release.
// https://www.ipa4fun.com/history/152043/
// Some newer versions can also force AVC,
// but 6.45 is the last version that supports iOS 13.
forceAVC()
? "6.45"
: "8.33",
: "8.49",
true,
forceAVC()
? "iOS TV Force AVC"
: "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() {
Expand All @@ -80,10 +104,20 @@ private static boolean forceAVC() {
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;

/**
* Device OS name.
*/
public final String osName;

/**
* Device OS version.
*/
Expand Down Expand Up @@ -118,7 +152,9 @@ private static boolean forceAVC() {

ClientType(int id,
String clientName,
String deviceMake,
String deviceModel,
String osName,
String osVersion,
String userAgent,
@Nullable String androidSdkVersion,
Expand All @@ -127,7 +163,9 @@ private static boolean forceAVC() {
String friendlyName) {
this.id = id;
this.clientName = clientName;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel;
this.osName = osName;
this.osVersion = osVersion;
this.userAgent = userAgent;
this.androidSdkVersion = androidSdkVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,19 @@ public static void fetchStreams(String url, Map<String, String> requestHeaders)
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.
// 'refresh' has no video id and appears to happen when waiting for a livestream to start.
// '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);
return;
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ static String createInnertubeBody(ClientType clientType) {
client.put("hl", language.getLanguage());
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) {
client.put("androidSdkVersion", clientType.androidSdkVersion);
Expand All @@ -76,6 +78,7 @@ static HttpURLConnection getPlayerResponseConnectionFromRoute(Route.CompiledRout

connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", clientType.userAgent);
connection.setRequestProperty("X-YouTube-Client-Version", String.valueOf(clientType.id));

connection.setUseCaches(false);
connection.setDoOutput(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ private static ByteBuffer fetch(String videoId, Map<String, String> playerHeader
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get()) {
Logger.printException(() -> "Ignoring empty client: " + clientType);
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
}
} else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,20 @@ private void updateUI() {
(clientType == ClientType.IOS_UNPLUGGED
? "ios_tv"
: "android");
setTitle(str(key + "_title"));
setSummary(str(key + "_summary"));
String title = str(key + "_title");
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);
}
}
2 changes: 2 additions & 0 deletions patches/src/main/resources/addresources/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,15 @@
<string-array name="revanced_spoof_video_streams_client_type_entries">
<item>Android VR</item>
<item>@string/revanced_spoof_video_streams_client_type_android_vr_no_auth</item>
<item>Android Creator</item>
<item>Android TV</item>
<item>iOS TV</item>
</string-array>
<string-array name="revanced_spoof_video_streams_client_type_entry_values">
<!-- Extension enum names. -->
<item>ANDROID_VR</item>
<item>ANDROID_VR_NO_AUTH</item>
<item>ANDROID_CREATOR</item>
<item>ANDROID_UNPLUGGED</item>
<item>IOS_UNPLUGGED</item>
</string-array>
Expand Down
2 changes: 2 additions & 0 deletions patches/src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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
• Stable volume is not available
• 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_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>
Expand Down

0 comments on commit 0479dd2

Please # to comment.