From 77864f41f4c1ddd868ac67f5c33c27fee72b4d20 Mon Sep 17 00:00:00 2001
From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Date: Sun, 14 Sep 2025 03:38:13 +0400
Subject: [PATCH] unofficial 20.37 support
---
.../youtube/patches/VersionCheckPatch.java | 2 +
.../extension/youtube/settings/Settings.java | 9 +++
patches/api/patches.api | 1 +
.../youtube/layout/miniplayer/Fingerprints.kt | 3 +
.../layout/miniplayer/MiniplayerPatch.kt | 55 ++++++++++++-------
.../layout/spoofappversion/Fingerprints.kt | 11 +++-
.../misc/links/BypassURLRedirectsPatch.kt | 7 ++-
.../youtube/misc/links/Fingerprints.kt | 24 +++++++-
.../misc/playservice/VersionCheckPatch.kt | 3 +
.../resources/addresources/values/arrays.xml | 21 ++++++-
10 files changed, 109 insertions(+), 27 deletions(-)
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java
index e9b292986..97749cf75 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/VersionCheckPatch.java
@@ -23,4 +23,6 @@ public class VersionCheckPatch {
public static final boolean IS_20_21_OR_GREATER = isVersionOrGreater("20.21.00");
public static final boolean IS_20_22_OR_GREATER = isVersionOrGreater("20.22.00");
+
+ public static final boolean IS_20_37_OR_GREATER = isVersionOrGreater("20.37.00");
}
diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
index ab1414b8e..c0e6b2246 100644
--- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
+++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
@@ -21,6 +21,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_2;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
+import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.TABLET;
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability;
@@ -47,6 +48,7 @@ import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrow
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
+import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
@@ -478,6 +480,13 @@ public class Settings extends BaseSettings {
migrateOldSettingToNew(DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU, ADVANCED_VIDEO_QUALITY_MENU);
migrateOldSettingToNew(DEPRECATED_AUTO_CAPTIONS, DISABLE_AUTO_CAPTIONS);
+ // 20.37+ YT removed parts of the code for the legacy tablet miniplayer.
+ // This check must remain until the Tablet type is eventually removed.
+ if (VersionCheckPatch.IS_20_37_OR_GREATER && MINIPLAYER_TYPE.get() == TABLET) {
+ Logger.printInfo(() -> "Resetting miniplayer tablet type");
+ MINIPLAYER_TYPE.resetToDefault();
+ }
+
// Migrate renamed enum.
//noinspection deprecation
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
diff --git a/patches/api/patches.api b/patches/api/patches.api
index e5df609ca..a236254ea 100644
--- a/patches/api/patches.api
+++ b/patches/api/patches.api
@@ -1549,6 +1549,7 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
public static final fun is_20_30_or_greater ()Z
public static final fun is_20_31_or_greater ()Z
public static final fun is_20_34_or_greater ()Z
+ public static final fun is_20_37_or_greater ()Z
}
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt
index 7455cc373..7aaf7d221 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/Fingerprints.kt
@@ -174,6 +174,9 @@ internal val miniplayerOverrideNoContextFingerprint by fingerprint {
)
}
+/**
+ * 20.36 and lower. Codes appears to be removed in 20.37+
+ */
internal val miniplayerResponseModelSizeCheckFingerprint by fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt
index 1e6ad3626..2112acd68 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt
@@ -99,8 +99,14 @@ val miniplayerPatch = bytecodePatch(
preferences +=
- if (is_20_03_or_greater) {
+ if (is_20_37_or_greater) {
ListPreference("revanced_miniplayer_type")
+ } else if (is_20_03_or_greater) {
+ ListPreference(
+ key = "revanced_miniplayer_type",
+ entriesKey = "revanced_miniplayer_type_legacy_20_03_entries",
+ entryValuesKey = "revanced_miniplayer_type_legacy_20_03_entry_values"
+ )
} else if (is_19_43_or_greater) {
ListPreference(
key = "revanced_miniplayer_type",
@@ -210,7 +216,7 @@ val miniplayerPatch = bytecodePatch(
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(F)F
move-result v$register
- """,
+ """
)
}
}
@@ -226,30 +232,41 @@ val miniplayerPatch = bytecodePatch(
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
move-result v$register
- """,
+ """
)
}
// region Enable tablet miniplayer.
+ // Parts of the YT code is removed in 20.37+ and the legacy player no longer works.
- miniplayerOverrideNoContextFingerprint.match(
- miniplayerDimensionsCalculatorParentFingerprint.originalClassDef,
- ).method.apply {
- findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
- }
-
- // endregion
-
- // region Legacy tablet miniplayer hooks.
- miniplayerOverrideFingerprint.let {
- val appNameStringIndex = it.instructionMatches.last().index
- navigate(it.originalMethod).to(appNameStringIndex).stop().apply {
- findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
+ if (!is_20_37_or_greater) {
+ miniplayerOverrideNoContextFingerprint.match(
+ miniplayerDimensionsCalculatorParentFingerprint.originalClassDef,
+ ).method.apply {
+ findReturnIndicesReversed().forEach { index ->
+ insertLegacyTabletMiniplayerOverride(
+ index
+ )
+ }
}
- }
- miniplayerResponseModelSizeCheckFingerprint.let {
- it.method.insertLegacyTabletMiniplayerOverride(it.instructionMatches.last().index)
+ // endregion
+
+ // region Legacy tablet miniplayer hooks.
+ miniplayerOverrideFingerprint.let {
+ val appNameStringIndex = it.instructionMatches.last().index
+ navigate(it.originalMethod).to(appNameStringIndex).stop().apply {
+ findReturnIndicesReversed().forEach { index ->
+ insertLegacyTabletMiniplayerOverride(
+ index
+ )
+ }
+ }
+ }
+
+ miniplayerResponseModelSizeCheckFingerprint.let {
+ it.method.insertLegacyTabletMiniplayerOverride(it.instructionMatches.last().index)
+ }
}
// endregion
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt
index 33c20c22f..2341c01d7 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/Fingerprints.kt
@@ -10,17 +10,22 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val toolBarButtonFingerprint by fingerprint {
- returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
- parameters("Landroid/view/MenuItem;")
+ returns("V")
instructions(
resourceLiteral(ResourceType.ID, "menu_item_view"),
methodCall(returnType = "I", opcode = Opcode.INVOKE_INTERFACE),
- opcode(Opcode.MOVE_RESULT, maxAfter = 0), // Value is zero if resource does not exist.
+ opcode(Opcode.MOVE_RESULT, maxAfter = 0),
fieldAccess(type = "Landroid/widget/ImageView;", opcode = Opcode.IGET_OBJECT, maxAfter = 6),
methodCall("Landroid/content/res/Resources;", "getDrawable", maxAfter = 8),
methodCall("Landroid/widget/ImageView;", "setImageDrawable", maxAfter = 4)
)
+ custom { method, _ ->
+ // 20.37+ has second parameter of "Landroid/content/Context;"
+ val parameterCount = method.parameterTypes.count()
+ (parameterCount == 1 || parameterCount == 2)
+ && method.parameterTypes.firstOrNull() == "Landroid/view/MenuItem;"
+ }
}
internal val spoofAppVersionFingerprint by fingerprint {
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt
index 23025d176..ea9e27380 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/BypassURLRedirectsPatch.kt
@@ -7,6 +7,7 @@ import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
+import app.revanced.patches.youtube.misc.playservice.is_20_37_or_greater
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@@ -42,7 +43,11 @@ val bypassURLRedirectsPatch = bytecodePatch(
)
arrayOf(
- abUriParserFingerprint to 2,
+ if (is_20_37_or_greater) {
+ (abUriParserFingerprint to 2)
+ } else {
+ (abUriParserLegacyFingerprint to 2)
+ },
httpUriParserFingerprint to 0
).forEach { (fingerprint, index) ->
fingerprint.method.apply {
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt
index cbd4a3c64..c63e7e24f 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/links/Fingerprints.kt
@@ -1,13 +1,14 @@
package app.revanced.patches.youtube.misc.links
-import app.revanced.patcher.checkCast
-import app.revanced.patcher.fieldAccess
import app.revanced.patcher.fingerprint
import app.revanced.patcher.methodCall
import app.revanced.patcher.string
import com.android.tools.smali.dexlib2.AccessFlags
-internal val abUriParserFingerprint by fingerprint {
+/**
+ * 20.36 and lower.
+ */
+internal val abUriParserLegacyFingerprint by fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
@@ -18,6 +19,23 @@ internal val abUriParserFingerprint by fingerprint {
)
}
+/**
+ * 20.37+
+ */
+internal val abUriParserFingerprint by fingerprint {
+ accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+ returns("Ljava/lang/Object;")
+ parameters("Ljava/lang/Object;")
+ instructions(
+ // Method is a switch statement of unrelated code,
+ // and there's no strings or anything unique to fingerprint.
+ methodCall(smali = "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;"),
+ methodCall(smali = "Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;"),
+ methodCall(smali = "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;"),
+ methodCall(smali = "Ljava/util/List;->get(I)Ljava/lang/Object;"),
+ )
+}
+
internal val httpUriParserFingerprint by fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Landroid/net/Uri;")
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt
index ee08aac0a..074fcb553 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt
@@ -79,6 +79,8 @@ var is_20_31_or_greater = false
private set
var is_20_34_or_greater = false
private set
+var is_20_37_or_greater = false
+ private set
val versionCheckPatch = resourcePatch(
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
@@ -122,5 +124,6 @@ val versionCheckPatch = resourcePatch(
is_20_30_or_greater = 253105000 <= playStoreServicesVersion
is_20_31_or_greater = 253205000 <= playStoreServicesVersion
is_20_34_or_greater = 253505000 <= playStoreServicesVersion
+ is_20_37_or_greater = 253805000 <= playStoreServicesVersion
}
}
diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml
index 9a9a29dd9..58e66f11f 100644
--- a/patches/src/main/resources/addresources/values/arrays.xml
+++ b/patches/src/main/resources/addresources/values/arrays.xml
@@ -218,13 +218,32 @@
- @string/revanced_miniplayer_type_entry_0
- @string/revanced_miniplayer_type_entry_1
- @string/revanced_miniplayer_type_entry_2
- - @string/revanced_miniplayer_type_entry_3
+
- @string/revanced_miniplayer_type_entry_4
- @string/revanced_miniplayer_type_entry_5
- @string/revanced_miniplayer_type_entry_6
- @string/revanced_miniplayer_type_entry_7
+ - DISABLED
+ - DEFAULT
+ - MINIMAL
+ - MODERN_1
+ - MODERN_2
+ - MODERN_3
+ - MODERN_4
+
+
+ - @string/revanced_miniplayer_type_entry_0
+ - @string/revanced_miniplayer_type_entry_1
+ - @string/revanced_miniplayer_type_entry_2
+ - @string/revanced_miniplayer_type_entry_3
+ - @string/revanced_miniplayer_type_entry_4
+ - @string/revanced_miniplayer_type_entry_5
+ - @string/revanced_miniplayer_type_entry_6
+ - @string/revanced_miniplayer_type_entry_7
+
+
- DISABLED
- DEFAULT
- MINIMAL