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