diff --git a/CHANGELOG.md b/CHANGELOG.md index 868f9da50..9a5fa023d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,177 @@ +# [4.8.0-dev.24](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.23...v4.8.0-dev.24) (2024-05-21) + + +### Bug Fixes + +* **YouTube - Client spoof:** Spoof client to fix playback ([#3199](https://github.com/ReVanced/revanced-patches/issues/3199)) ([bec1eef](https://github.com/ReVanced/revanced-patches/commit/bec1eef10f2eb4e15696acb271357f1621543de1)) + +# [4.8.0-dev.23](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.22...v4.8.0-dev.23) (2024-05-18) + + +### Features + +* **Piccoma:** Add `Spoof Android device ID` patch ([#3145](https://github.com/ReVanced/revanced-patches/issues/3145)) ([d953c6b](https://github.com/ReVanced/revanced-patches/commit/d953c6bdd4315d2ba44845fd569a3d12ac4d1af0)) + +# [4.8.0-dev.22](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.21...v4.8.0-dev.22) (2024-05-18) + + +### Bug Fixes + +* Use correct preference key ([3732b2c](https://github.com/ReVanced/revanced-patches/commit/3732b2ce6b617b4c1c6647397b614f8a040eece3)) + +# [4.8.0-dev.21](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.20...v4.8.0-dev.21) (2024-05-18) + + +### Features + +* **Piccoma:** Add `Disable tracking` patch ([#3143](https://github.com/ReVanced/revanced-patches/issues/3143)) ([8ab9e8f](https://github.com/ReVanced/revanced-patches/commit/8ab9e8f89d2bd014138e31dab7004f8ba77cae10)) +* **YouTube - Navigation buttons:** Add option to hide navigation button labels ([#3189](https://github.com/ReVanced/revanced-patches/issues/3189)) ([f9dc705](https://github.com/ReVanced/revanced-patches/commit/f9dc7050513b9fdb7766838a63a172f1478296f7)) + +# [4.8.0-dev.20](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.19...v4.8.0-dev.20) (2024-05-16) + + +### Features + +* **YT Music:** Add support for `7.01.52` ([#3177](https://github.com/ReVanced/revanced-patches/issues/3177)) ([e9bfb25](https://github.com/ReVanced/revanced-patches/commit/e9bfb25dfe85754fd7fa5c9db934bb4fc52e4694)) + +# [4.8.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.18...v4.8.0-dev.19) (2024-05-16) + + +### Features + +* **YouTube - Hide Shorts components:** Hide 'Buy super thanks' button ([#3176](https://github.com/ReVanced/revanced-patches/issues/3176)) ([89c1548](https://github.com/ReVanced/revanced-patches/commit/89c154861c8b3afa665542e97ff201c3e84410b2)) + +# [4.8.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.17...v4.8.0-dev.18) (2024-05-14) + + +### Bug Fixes + +* **YouTube Music:** Make `Hide 'Get Music Premium' label` and `Remove upgrade button` compatible with latest version ([#3164](https://github.com/ReVanced/revanced-patches/issues/3164)) ([3ff20de](https://github.com/ReVanced/revanced-patches/commit/3ff20dee4aea49ca77dcd3fbe148287b55a2b5e3)) + +# [4.8.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.16...v4.8.0-dev.17) (2024-05-12) + + +### Features + +* **Photomath:** Support version `8.37.0` ([#3109](https://github.com/ReVanced/revanced-patches/issues/3109)) ([fb02b48](https://github.com/ReVanced/revanced-patches/commit/fb02b481e2be8c2bc4441dc5b3dc6a9df3a2a379)) + +# [4.8.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.15...v4.8.0-dev.16) (2024-05-12) + + +### Features + +* **WarnWetter - Promo code unlock:** Constrain to last working version ([#3110](https://github.com/ReVanced/revanced-patches/issues/3110)) ([92fc8aa](https://github.com/ReVanced/revanced-patches/commit/92fc8aaad80f8fad35b75e6de032692986211536)) + +# [4.8.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.14...v4.8.0-dev.15) (2024-05-11) + + +### Bug Fixes + +* **YouTube - Restore old video quality menu:** Show advanced quality menu in Shorts quality flyout ([#3155](https://github.com/ReVanced/revanced-patches/issues/3155)) ([c2b5bb7](https://github.com/ReVanced/revanced-patches/commit/c2b5bb723416e43a920817f97b9e0ee4ceab4f6b)) + +# [4.8.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.13...v4.8.0-dev.14) (2024-05-08) + + +### Bug Fixes + +* **YouTube - SponsorBlock:** Show correct segment times if video is over 24 hours in length ([#3138](https://github.com/ReVanced/revanced-patches/issues/3138)) ([6cdf697](https://github.com/ReVanced/revanced-patches/commit/6cdf697e8e47f6d53964497703dbe79fab3b1821)) + +# [4.8.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.12...v4.8.0-dev.13) (2024-05-06) + + +### Bug Fixes + +* **YouTube - Navigation buttons:** Adjust summary text of switch notification button ([#3130](https://github.com/ReVanced/revanced-patches/issues/3130)) ([cc8b4c9](https://github.com/ReVanced/revanced-patches/commit/cc8b4c913ed25d07fd4000cfd6318bb06a9d27c0)) + +# [4.8.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.11...v4.8.0-dev.12) (2024-05-06) + + +### Bug Fixes + +* **YouTube - Player flyout menu:** Remove obsolete `Hide report menu` ([d627d44](https://github.com/ReVanced/revanced-patches/commit/d627d44ad07fa32bb2f247ce24a3591ec5e1be0e)) + +# [4.8.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.10...v4.8.0-dev.11) (2024-05-06) + + +### Bug Fixes + +* **Reddit is Fun - Spoof client:** Fix login by updating the authorization subdomain from "old" to "ssl" ([b156cb1](https://github.com/ReVanced/revanced-patches/commit/b156cb1d8996c4314d59e3441c6b85d8f704cdff)) + +# [4.8.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.9...v4.8.0-dev.10) (2024-05-02) + + +### Features + +* **Tumblr:** Add `Disable Ad-Free Banner` patch ([#3091](https://github.com/ReVanced/revanced-patches/issues/3091)) ([54baf08](https://github.com/ReVanced/revanced-patches/commit/54baf08f777b7c975fa0b6508f0a4de19ac491f4)) + +# [4.8.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.8...v4.8.0-dev.9) (2024-04-29) + + +### Bug Fixes + +* Correctly handle patches jar path if it contains exclamation marks ([056e2d7](https://github.com/ReVanced/revanced-patches/commit/056e2d7dd5bbacb7dc6b109b3e2d44d55e7eb7d3)) + +# [4.8.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.7...v4.8.0-dev.8) (2024-04-23) + + +### Bug Fixes + +* **YouTube - Hide ads:** Fix string typo ([ecc56d6](https://github.com/ReVanced/revanced-patches/commit/ecc56d643a0c4e5f25b933431f097a03d4bf2e69)) + +# [4.8.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.6...v4.8.0-dev.7) (2024-04-23) + + +### Bug Fixes + +* URL decode path to JAR containing spaces to get JAR manifest ([#3079](https://github.com/ReVanced/revanced-patches/issues/3079)) ([e1bbcb3](https://github.com/ReVanced/revanced-patches/commit/e1bbcb338dd7fce895b606440bd6f040d5486a64)) + +# [4.8.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.5...v4.8.0-dev.6) (2024-04-23) + + +### Features + +* **YouTube - Hide ads:** Add option to hide the 'Visit store' button on channel pages ([#3077](https://github.com/ReVanced/revanced-patches/issues/3077)) ([03d2cfa](https://github.com/ReVanced/revanced-patches/commit/03d2cfafbf977340456598a848858ac9452c853f)) + +# [4.8.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.4...v4.8.0-dev.5) (2024-04-23) + + +### Bug Fixes + +* **YouTube - Hide Shorts components:** Rename option title to make it consistent ([4d6e34b](https://github.com/ReVanced/revanced-patches/commit/4d6e34b0540a3334bd77b2b48a1a5e10329171c8)) + +# [4.8.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.3...v4.8.0-dev.4) (2024-04-23) + + +### Features + +* **YouTube - Comments:** Add option to hide timestamp and emoji buttons ([#3076](https://github.com/ReVanced/revanced-patches/issues/3076)) ([7efe5ae](https://github.com/ReVanced/revanced-patches/commit/7efe5aefb252a2ed908907ff218b879e2ad1a331)) + +# [4.8.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.2...v4.8.0-dev.3) (2024-04-22) + + +### Bug Fixes + +* Publicize abstract property ([b7c108e](https://github.com/ReVanced/revanced-patches/commit/b7c108ee201c84df31b079f3fecb6cc2f5eaf9f1)) + +# [4.8.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.8.0-dev.1...v4.8.0-dev.2) (2024-04-21) + + +### Bug Fixes + +* Case patch option title correctly ([259c8b4](https://github.com/ReVanced/revanced-patches/commit/259c8b4e58df51d92d7e19417e13afa3848afc73)) + +# [4.8.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.7.0...v4.8.0-dev.1) (2024-04-21) + + +### Bug Fixes + +* **YouTube - Hide video action buttons:** Remove obsolete `hide Shop button` ([#3057](https://github.com/ReVanced/revanced-patches/issues/3057)) ([b5e34f3](https://github.com/ReVanced/revanced-patches/commit/b5e34f3aabc1d9df8c41f92251618243caecdc9f)) + + +### Features + +* **YouTube - Hide Shorts components:** Hide like / dislike button in video ads ([#3062](https://github.com/ReVanced/revanced-patches/issues/3062)) ([1296985](https://github.com/ReVanced/revanced-patches/commit/12969853adfe530eb6006df38e1a5aa30b28fdf9)) + # [4.7.0](https://github.com/ReVanced/revanced-patches/compare/v4.6.0...v4.7.0) (2024-04-21) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 70d6ebad5..ef79cf77f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,7 +64,7 @@ This document describes how to contribute to ReVanced Patches. ## 📖 Resources to help you get started -* The [documentation](https://github.com/ReVanced/revanced-patcher/tree/docs/docs) contains the fundamentals +* The [documentation](https://github.com/ReVanced/revanced-patcher/tree/main/docs) contains the fundamentals of ReVanced Patcher and how to use ReVanced Patcher to create patches * [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on * [Issues](https://github.com/ReVanced/revanced-patches/issues) are where we keep track of bugs and feature requests diff --git a/api/revanced-patches.api b/api/revanced-patches.api index 6ee21cc71..672e18088 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -30,6 +30,7 @@ public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggin public final class app/revanced/patches/all/misc/hex/HexPatch : app/revanced/patches/shared/misc/hex/BaseHexPatch { public fun ()V + public fun getReplacements ()Ljava/util/List; } public final class app/revanced/patches/all/misc/network/OverrideCertificatePinningPatch : app/revanced/patcher/patch/ResourcePatch { @@ -486,6 +487,18 @@ public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPat public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + +public final class app/revanced/patches/piccomafr/tracking/DisableTrackingPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/piccomafr/tracking/DisableTrackingPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + public final class app/revanced/patches/pixiv/ads/HideAdsPatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/pixiv/ads/HideAdsPatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V @@ -575,6 +588,7 @@ public final class app/revanced/patches/reddit/customclients/joeyforreddit/detec public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch; public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V + public fun patchMiscellaneous (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V } @@ -677,6 +691,7 @@ public abstract class app/revanced/patches/shared/misc/hex/BaseHexPatch : app/re public fun ()V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V + public abstract fun getReplacements ()Ljava/util/List; } public final class app/revanced/patches/shared/misc/hex/BaseHexPatch$Replacement { @@ -1060,6 +1075,12 @@ public final class app/revanced/patches/tumblr/ads/DisableDashboardAds : app/rev public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + public final class app/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/tumblr/annoyances/inappupdate/DisableInAppUpdatePatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V @@ -1610,12 +1631,10 @@ public final class app/revanced/patches/youtube/misc/dimensions/spoof/SpoofDevic public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } -public final class app/revanced/patches/youtube/misc/fix/playback/ClientSpoofPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { - public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/ClientSpoofPatch; - public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; - public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple; - public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V - public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V +public final class app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch : app/revanced/patcher/patch/BytecodePatch { @@ -1630,6 +1649,14 @@ public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignature public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V } +public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch; + public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; + public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple; + public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V + public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V +} + public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch { public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch; } diff --git a/gradle.properties b/gradle.properties index 9fbff2fe7..eb93da797 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.7.0 +version = 4.8.0-dev.24 diff --git a/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt b/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt index 164eaa13a..7aaee12b4 100644 --- a/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt +++ b/src/main/kotlin/app/revanced/patches/all/misc/hex/HexPatch.kt @@ -19,7 +19,7 @@ class HexPatch : BaseHexPatch() { // Replace the custom option type with a stringArrayOption once the issue is resolved. private val replacementsOption by registerNewPatchOption, List>( key = "replacements", - title = "replacements", + title = "Replacements", description = """ Hexadecimal patterns to search for and replace with another in a target file. diff --git a/src/main/kotlin/app/revanced/patches/music/layout/premium/fingerprints/HideGetPremiumFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/layout/premium/fingerprints/HideGetPremiumFingerprint.kt index c5c346409..e97251de5 100644 --- a/src/main/kotlin/app/revanced/patches/music/layout/premium/fingerprints/HideGetPremiumFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/layout/premium/fingerprints/HideGetPremiumFingerprint.kt @@ -12,8 +12,6 @@ internal object HideGetPremiumFingerprint : MethodFingerprint( listOf( Opcode.IF_NEZ, Opcode.CONST_16, - Opcode.GOTO, - Opcode.NOP, Opcode.INVOKE_VIRTUAL, ), listOf("FEmusic_history", "FEmusic_offline"), diff --git a/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/fingerprints/PivotBarConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/fingerprints/PivotBarConstructorFingerprint.kt index 09980c6a9..ee7ffbcb8 100644 --- a/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/fingerprints/PivotBarConstructorFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/fingerprints/PivotBarConstructorFingerprint.kt @@ -13,7 +13,6 @@ internal object PivotBarConstructorFingerprint : MethodFingerprint( Opcode.CHECK_CAST, Opcode.INVOKE_INTERFACE, Opcode.GOTO, - Opcode.NOP, Opcode.IPUT_OBJECT, Opcode.RETURN_VOID, ), diff --git a/src/main/kotlin/app/revanced/patches/music/misc/integrations/fingerprints/ApplicationInitFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/misc/integrations/fingerprints/ApplicationInitFingerprint.kt index d825b4e8d..45ebf8377 100644 --- a/src/main/kotlin/app/revanced/patches/music/misc/integrations/fingerprints/ApplicationInitFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/misc/integrations/fingerprints/ApplicationInitFingerprint.kt @@ -1,19 +1,10 @@ package app.revanced.patches.music.misc.integrations.fingerprints import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint -import com.android.tools.smali.dexlib2.Opcode internal object ApplicationInitFingerprint : IntegrationsFingerprint( returnType = "V", parameters = emptyList(), - opcodes = listOf( - Opcode.NEW_INSTANCE, - Opcode.INVOKE_DIRECT, - Opcode.INVOKE_STATIC, - Opcode.NEW_INSTANCE, - Opcode.INVOKE_DIRECT, - Opcode.INVOKE_VIRTUAL - ), strings = listOf("activity"), customFingerprint = { methodDef, _ -> methodDef.name == "onCreate" }, ) diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt b/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt index d1c97cdec..0ea645e52 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch.kt @@ -14,7 +14,7 @@ import kotlin.random.Random name = "Spoof device ID", description = "Spoofs device ID to mitigate manual bans by developers.", dependencies = [SignatureDetectionPatch::class], - compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])] + compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.37.0"])] ) @Suppress("unused") object SpoofDeviceIdPatch : BytecodePatch( diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt b/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt index ef3e29f68..fe01d7b59 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/photomath/detection/deviceid/fingerprints/GetDeviceIdFingerprint.kt @@ -1,9 +1,21 @@ package app.revanced.patches.photomath.detection.deviceid.fingerprints import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode internal object GetDeviceIdFingerprint : MethodFingerprint( returnType = "Ljava/lang/String;", - strings = listOf("androidId", "android_id"), + opcodes = listOf( + Opcode.SGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_NEZ, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + ), parameters = listOf() ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt b/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt index 97c42de2b..cd784ab20 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/photomath/detection/signature/fingerprints/CheckSignatureFingerprint.kt @@ -5,11 +5,9 @@ import com.android.tools.smali.dexlib2.Opcode internal object CheckSignatureFingerprint : MethodFingerprint( strings = listOf( - "packageInfo.signatures", - "currentSignature" + "signatures", ), opcodes = listOf( - Opcode.CONST_STRING, Opcode.CONST_STRING, Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC, diff --git a/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt b/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt index 147555907..0c6fb6689 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt +++ b/src/main/kotlin/app/revanced/patches/photomath/misc/annoyances/HideUpdatePopupPatch.kt @@ -13,7 +13,7 @@ import app.revanced.util.exception name = "Hide update popup", description = "Prevents the update popup from showing up.", dependencies = [SignatureDetectionPatch::class], - compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])] + compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.37.0"])] ) @Suppress("unused") object HideUpdatePopupPatch : BytecodePatch( diff --git a/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt b/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt index 8895696cd..3885c5c23 100644 --- a/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt +++ b/src/main/kotlin/app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPatch.kt @@ -13,7 +13,7 @@ import app.revanced.patches.photomath.misc.unlock.plus.fingerprints.IsPlusUnlock @Patch( name = "Unlock plus", dependencies = [SignatureDetectionPatch::class, EnableBookpointPatch::class], - compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])] + compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.37.0"])] ) @Suppress("unused") object UnlockPlusPatch : BytecodePatch( diff --git a/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt b/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt new file mode 100644 index 000000000..a2a920526 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatch.kt @@ -0,0 +1,59 @@ +package app.revanced.patches.piccomafr.misc + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption +import app.revanced.patches.piccomafr.misc.fingerprints.GetAndroidIDFingerprint +import app.revanced.util.exception + +@Patch( + name = "Spoof Android device ID", + description = "Spoofs the Android device ID used by the app for account authentication." + + "This can be used to copy the account to another device.", + compatiblePackages = [ + CompatiblePackage( + "com.piccomaeurope.fr", + [ + "6.4.0", + "6.4.1", + "6.4.2", + "6.4.3", + "6.4.4", + "6.4.5", + "6.5.0", + "6.5.1", + "6.5.2", + "6.5.3", + "6.5.4", + "6.6.0", + "6.6.1", + "6.6.2", + ], + ), + ], + use = false, +) +@Suppress("unused") +object SpoofAndroidDeviceIdPatch : BytecodePatch( + setOf(GetAndroidIDFingerprint), +) { + private var androidDeviceId = + stringPatchOption( + key = "android-device-id", + default = "0011223344556677", + title = "Android device ID", + description = "The Android device ID to spoof to.", + required = true, + ) { it!!.matches("[A-Fa-f0-9]{16}".toRegex()) } + + override fun execute(context: BytecodeContext) = GetAndroidIDFingerprint.result?.mutableMethod?.addInstructions( + 0, + """ + const-string v0, "$androidDeviceId" + return-object v0 + """, + ) ?: throw GetAndroidIDFingerprint.exception +} diff --git a/src/main/kotlin/app/revanced/patches/piccomafr/misc/fingerprints/GetAndroidIDFingerprint.kt b/src/main/kotlin/app/revanced/patches/piccomafr/misc/fingerprints/GetAndroidIDFingerprint.kt new file mode 100644 index 000000000..31e1ca6ab --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/piccomafr/misc/fingerprints/GetAndroidIDFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.piccomafr.misc.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + + +internal object GetAndroidIDFingerprint : MethodFingerprint( + parameters = listOf("Landroid/content/Context"), + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + strings = listOf( + "context", + "android_id" + ), + returnType = "Ljava/lang/String" +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt new file mode 100644 index 000000000..0830e5a74 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/DisableTrackingPatch.kt @@ -0,0 +1,80 @@ +package app.revanced.patches.piccomafr.tracking + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.piccomafr.tracking.fingerprints.AppMesurementFingerprint +import app.revanced.patches.piccomafr.tracking.fingerprints.FacebookSDKFingerprint +import app.revanced.patches.piccomafr.tracking.fingerprints.FirebaseInstallFingerprint +import app.revanced.util.exception +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +@Patch( + name = "Disable tracking", + description = "Disables tracking by replacing tracking URLs with example.com.", + compatiblePackages = [ + CompatiblePackage( + "com.piccomaeurope.fr", + [ + "6.4.0", + "6.4.1", + "6.4.2", + "6.4.3", + "6.4.4", + "6.4.5", + "6.5.0", + "6.5.1", + "6.5.2", + "6.5.3", + "6.5.4", + "6.6.0", + "6.6.1", + "6.6.2", + ], + ), + ], +) +@Suppress("unused") +object DisableTrackingPatch : BytecodePatch( + setOf(FacebookSDKFingerprint, FirebaseInstallFingerprint, AppMesurementFingerprint), +) { + override fun execute(context: BytecodeContext) { + FacebookSDKFingerprint.result?.mutableMethod?.apply { + getInstructions().filter { instruction -> + instruction.opcode == Opcode.CONST_STRING + }.forEach { instruction -> + instruction as OneRegisterInstruction + + replaceInstruction( + instruction.location.index, + "const-string v${instruction.registerA}, \"example.com\"", + ) + } + } ?: throw FacebookSDKFingerprint.exception + + FirebaseInstallFingerprint.result?.mutableMethod?.apply { + getInstructions().filter { + it.opcode == Opcode.CONST_STRING + }.filter { + it.getReference()?.string == "firebaseinstallations.googleapis.com" + }.forEach { instruction -> + instruction as OneRegisterInstruction + + replaceInstruction( + instruction.location.index, + "const-string v${instruction.registerA}, \"example.com\"", + ) + } + } ?: throw FirebaseInstallFingerprint.exception + + AppMesurementFingerprint.result?.mutableMethod?.addInstruction(0, "return-void") + ?: throw AppMesurementFingerprint.exception + } +} diff --git a/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/AppMesurementFingerprint.kt b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/AppMesurementFingerprint.kt new file mode 100644 index 000000000..499491a35 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/AppMesurementFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.piccomafr.tracking.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + + +internal object AppMesurementFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + strings = listOf( + "config/app/", + "Fetching remote configuration" + ), + returnType = "V" +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FacebookSDKFingerprint.kt b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FacebookSDKFingerprint.kt new file mode 100644 index 000000000..ff481e814 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FacebookSDKFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.piccomafr.tracking.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + + +internal object FacebookSDKFingerprint : MethodFingerprint( + accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR, + strings = listOf( + "instagram.com", + "facebook.com" + ), + returnType = "V" +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FirebaseInstallFingerprint.kt b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FirebaseInstallFingerprint.kt new file mode 100644 index 000000000..ebaa6c125 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/piccomafr/tracking/fingerprints/FirebaseInstallFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.piccomafr.tracking.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + + +internal object FirebaseInstallFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PRIVATE.value, + strings = listOf( + "https://%s/%s/%s", + "firebaseinstallations.googleapis.com" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch.kt index ab110821e..5663a0223 100644 --- a/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch.kt +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatch.kt @@ -10,8 +10,10 @@ import app.revanced.patches.reddit.customclients.BaseSpoofClientPatch import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.BasicAuthorizationFingerprint import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.BuildAuthorizationStringFingerprint import app.revanced.patches.reddit.customclients.redditisfun.api.fingerprints.GetUserAgentFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - +import com.android.tools.smali.dexlib2.iface.reference.StringReference @Suppress("unused") object SpoofClientPatch : BaseSpoofClientPatch( @@ -20,8 +22,8 @@ object SpoofClientPatch : BaseSpoofClientPatch( userAgentFingerprints = setOf(GetUserAgentFingerprint), compatiblePackages = setOf( CompatiblePackage("com.andrewshu.android.reddit"), - CompatiblePackage("com.andrewshu.android.redditdonation") - ) + CompatiblePackage("com.andrewshu.android.redditdonation"), + ), ) { override fun Set.patchClientId(context: BytecodeContext) { /** @@ -59,7 +61,23 @@ object SpoofClientPatch : BaseSpoofClientPatch( """ const-string v0, "$userAgent" return-object v0 - """ + """, ) } -} \ No newline at end of file + + override fun Set.patchMiscellaneous(context: BytecodeContext) { + // Reddit messed up and does not append a redirect uri to the authorization url to old.reddit.com/login. + // Replace old.reddit.com with ssl.reddit.com to fix this. + BuildAuthorizationStringFingerprint.result!!.mutableMethod.apply { + val index = indexOfFirstInstruction { + getReference()?.contains("old.reddit.com") == true + } + + val targetRegister = getInstruction(index).registerA + replaceInstruction( + index, + "const-string v$targetRegister, \"https://ssl.reddit.com/api/v1/authorize.compact\"", + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/hex/BaseHexPatch.kt b/src/main/kotlin/app/revanced/patches/shared/misc/hex/BaseHexPatch.kt index 781444d68..9758483c9 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/hex/BaseHexPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/misc/hex/BaseHexPatch.kt @@ -6,7 +6,7 @@ import app.revanced.patcher.patch.RawResourcePatch import kotlin.math.max abstract class BaseHexPatch : RawResourcePatch() { - internal abstract val replacements: List + abstract val replacements: List override fun execute(context: ResourceContext) { replacements.groupBy { it.targetFilePath }.forEach { (targetFilePath, replacements) -> diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch.kt b/src/main/kotlin/app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch.kt index 7c461f6ee..0178ed5af 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch.kt @@ -8,10 +8,13 @@ import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchException import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint.IRegisterResolver import app.revanced.patches.shared.misc.integrations.fingerprints.ReVancedUtilsPatchesVersionFingerprint +import app.revanced.util.exception import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.Method +import java.net.URLDecoder +import java.nio.charset.StandardCharsets import java.util.jar.JarFile abstract class BaseIntegrationsPatch( @@ -73,8 +76,8 @@ abstract class BaseIntegrationsPatch( val urlString = classUrl.toString() if (urlString.startsWith("jar:file:")) { - val end = urlString.indexOf('!') - return urlString.substring("jar:file:".length, end) + val end = urlString.lastIndexOf('!') + return URLDecoder.decode(urlString.substring("jar:file:".length, end), StandardCharsets.UTF_8) } } throw IllegalStateException("Not running from inside a JAR file.") @@ -137,7 +140,7 @@ abstract class BaseIntegrationsPatch( "invoke-static/range { v$contextRegister .. v$contextRegister }, " + "$integrationsDescriptor->setContext(Landroid/content/Context;)V", ) - } ?: throw PatchException("Could not find hook target fingerprint.") + } ?: throw this.exception } interface IHookInsertIndexResolver : (Method) -> Int { diff --git a/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt b/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt new file mode 100644 index 000000000..50791fa5d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/tumblr/annoyances/adfree/DisableAdFreeBannerPatch.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.tumblr.annoyances.adfree + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.tumblr.featureflags.OverrideFeatureFlagsPatch + +@Patch( + name = "Disable Ad-Free Banner", + description = "Disables the banner with a frog, prompting you to buy Tumblr Ad-Free.", + dependencies = [OverrideFeatureFlagsPatch::class], + compatiblePackages = [CompatiblePackage("com.tumblr")], +) +@Suppress("unused") +object DisableAdFreeBannerPatch : BytecodePatch(emptySet()) { + override fun execute(context: BytecodeContext) { + // Disable the "AD_FREE_CTA_BANNER" ("Whether or not to show ad free prompt") feature flag. + OverrideFeatureFlagsPatch.addOverride("adFreeCtaBanner", "false") + } +} diff --git a/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt b/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt index 719c65593..b034444b7 100644 --- a/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/twitch/misc/settings/SettingsPatch.kt @@ -62,7 +62,17 @@ object SettingsPatch : BytecodePatch( AddResourcesPatch(this::class) PreferenceScreen.MISC.OTHER.addPreferences( - SwitchPreference("revanced_debug") + // The debug setting is shared across multiple apps and the key must be the same. + // But the title and summary must be different, otherwise when the strings file is flattened + // for Crowdin push, Crowdin gets confused by the duplicate keys. + // FIXME: Ideally the shared debug strings are extracted into a common app group + // and then both apps import that. But for now unique unique title and summary keys also works. + SwitchPreference( + key = "revanced_debug", + titleKey = "revanced_twitch_debug_title", + summaryOnKey = "revanced_twitch_debug_summary_on", + summaryOffKey = "revanced_twitch_debug_summary_off" + ) ) // Hook onCreate to handle fragment creation diff --git a/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt b/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt index 2f40f8aef..0fb762b33 100644 --- a/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt +++ b/src/main/kotlin/app/revanced/patches/warnwetter/misc/promocode/PromoCodeUnlockPatch.kt @@ -12,7 +12,7 @@ import app.revanced.patches.warnwetter.misc.promocode.fingerprints.PromoCodeUnlo name = "Promo code unlock", description = "Disables the validation of promo code. Any code will work to unlock all features.", dependencies = [FirebaseGetCertPatch::class], - compatiblePackages = [CompatiblePackage("de.dwd.warnapp")] + compatiblePackages = [CompatiblePackage("de.dwd.warnapp", ["4.2.2"])] ) @Suppress("unused") object PromoCodeUnlockPatch : BytecodePatch( @@ -28,4 +28,4 @@ object PromoCodeUnlockPatch : BytecodePatch( """ ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt index 8cc3ed113..1ef5e7723 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsResourcePatch.kt @@ -34,6 +34,7 @@ object HideAdsResourcePatch : ResourcePatch() { SwitchPreference("revanced_hide_self_sponsor_ads"), SwitchPreference("revanced_hide_products_banner"), SwitchPreference("revanced_hide_shopping_links"), + SwitchPreference("revanced_hide_visit_store_button"), SwitchPreference("revanced_hide_web_search_results"), SwitchPreference("revanced_hide_merchandise_banners"), ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt index deaebe49c..20757d6e2 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt @@ -65,8 +65,7 @@ object HideButtonsPatch : ResourcePatch() { SwitchPreference("revanced_hide_download_button"), SwitchPreference("revanced_hide_thanks_button"), SwitchPreference("revanced_hide_clip_button"), - SwitchPreference("revanced_hide_playlist_button"), - SwitchPreference("revanced_hide_shop_button") + SwitchPreference("revanced_hide_playlist_button") ), ) ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt index 1cdbe20d1..73df8d577 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt @@ -1,6 +1,7 @@ package app.revanced.patches.youtube.layout.buttons.navigation import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch @@ -12,11 +13,16 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sor import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.ANDROID_AUTOMOTIVE_STRING import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.AddCreateButtonViewFingerprint +import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.CreatePivotBarFingerprint import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.util.exception +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Patch( name = "Navigation buttons", @@ -49,14 +55,17 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction "19.08.36", "19.09.38", "19.10.39", - "19.11.43" + "19.11.43", ], ), ], ) @Suppress("unused") object NavigationButtonsPatch : BytecodePatch( - setOf(AddCreateButtonViewFingerprint), + setOf( + AddCreateButtonViewFingerprint, + CreatePivotBarFingerprint, + ), ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/NavigationButtonsPatch;" @@ -74,6 +83,7 @@ object NavigationButtonsPatch : BytecodePatch( SwitchPreference("revanced_hide_create_button"), SwitchPreference("revanced_hide_subscriptions_button"), SwitchPreference("revanced_switch_create_with_notifications_button"), + SwitchPreference("revanced_hide_navigation_button_labels"), ), ), ) @@ -99,6 +109,21 @@ object NavigationButtonsPatch : BytecodePatch( } } ?: throw AddCreateButtonViewFingerprint.exception + // Hide navigation button labels. + CreatePivotBarFingerprint.result?.mutableMethod?.apply { + val setTextIndex = indexOfFirstInstruction { + getReference()?.name == "setText" + } + + val targetRegister = getInstruction(setTextIndex).registerC + + addInstruction( + setTextIndex, + "invoke-static { v$targetRegister }, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->hideNavigationButtonLabels(Landroid/widget/TextView;)V", + ) + } ?: throw CreatePivotBarFingerprint.exception + // Hook navigation button created, in order to hide them. NavigationBarHookPatch.hookNavigationButtonCreated(INTEGRATIONS_CLASS_DESCRIPTOR) } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/CreatePivotBarFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/CreatePivotBarFingerprint.kt new file mode 100644 index 000000000..07b32ba2c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/CreatePivotBarFingerprint.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal object CreatePivotBarFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + parameters = listOf( + "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;", + "Landroid/widget/TextView;", + "Ljava/lang/CharSequence;", + ), + opcodes = listOf( + Opcode.INVOKE_VIRTUAL, + Opcode.RETURN_VOID, + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt index 9a6ee70e6..789701ba8 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt @@ -58,7 +58,8 @@ object CommentsPatch : ResourcePatch() { "revanced_comments_screen", preferences = setOf( SwitchPreference("revanced_hide_preview_comment"), - SwitchPreference("revanced_hide_comments_section") + SwitchPreference("revanced_hide_comments_section"), + SwitchPreference("revanced_hide_comment_timestamp_and_emoji_buttons") ), sorting = PreferenceScreen.Sorting.UNSORTED ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 8944dbbf9..d98276000 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -103,7 +103,6 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_info_panels"), SwitchPreference("revanced_hide_join_membership_button"), SwitchPreference("revanced_hide_medical_panels"), - SwitchPreference("revanced_hide_playables"), SwitchPreference("revanced_hide_quick_actions"), SwitchPreference("revanced_hide_related_videos"), SwitchPreference("revanced_hide_subscribers_community_guidelines"), @@ -122,6 +121,7 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_mix_playlists"), SwitchPreference("revanced_hide_movies_section"), SwitchPreference("revanced_hide_notify_me_button"), + SwitchPreference("revanced_hide_playables"), SwitchPreference("revanced_hide_search_result_recommendations"), SwitchPreference("revanced_hide_search_result_shelf_header"), SwitchPreference("revanced_hide_show_more_button"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt index 9d1d48a90..689df3c7e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt @@ -63,7 +63,6 @@ object HidePlayerFlyoutMenuPatch : ResourcePatch() { SwitchPreference("revanced_hide_player_flyout_additional_settings"), SwitchPreference("revanced_hide_player_flyout_loop_video"), SwitchPreference("revanced_hide_player_flyout_ambient_mode"), - SwitchPreference("revanced_hide_player_flyout_report"), SwitchPreference("revanced_hide_player_flyout_help"), SwitchPreference("revanced_hide_player_flyout_speed"), SwitchPreference("revanced_hide_player_flyout_lock_screen"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt index 3471c5dff..1469c21d0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt @@ -13,11 +13,14 @@ import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch import app.revanced.util.exception +import app.revanced.util.getReference import app.revanced.util.indexOfIdResourceOrThrow import app.revanced.util.injectHideViewCall +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference @Patch( name = "Hide Shorts components", @@ -155,17 +158,24 @@ object HideShortsComponentsPatch : BytecodePatch( } private enum class ShortsButtons(private val resourceName: String, private val methodName: String) { + LIKE("reel_dyn_like", "hideLikeButton"), + DISLIKE("reel_dyn_dislike", "hideDislikeButton"), COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"), REMIX("reel_dyn_remix", "hideShortsRemixButton"), - SHARE("reel_dyn_share", "hideShortsShareButton"), - ; + SHARE("reel_dyn_share", "hideShortsShareButton"); fun injectHideCall(method: MutableMethod) { val referencedIndex = method.indexOfIdResourceOrThrow(resourceName) - val setIdIndex = referencedIndex + 1 + val instruction = method.implementation!!.instructions + .subList(referencedIndex, referencedIndex + 20) + .first { + it.opcode == Opcode.INVOKE_VIRTUAL && it.getReference()?.name == "setId" + } + + val setIdIndex = instruction.location.index val viewRegister = method.getInstruction(setIdIndex).registerC - method.injectHideViewCall(setIdIndex, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName) + method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName) } } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt index 47381c2d5..598ec8580 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsResourcePatch.kt @@ -41,6 +41,7 @@ object HideShortsComponentsResourcePatch : ResourcePatch() { SwitchPreference("revanced_hide_shorts_shop_button"), SwitchPreference("revanced_hide_shorts_tagged_products"), SwitchPreference("revanced_hide_shorts_search_suggestions"), + SwitchPreference("revanced_hide_shorts_super_thanks_button"), SwitchPreference("revanced_hide_shorts_location_label"), SwitchPreference("revanced_hide_shorts_channel_bar"), SwitchPreference("revanced_hide_shorts_info_panel"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt new file mode 100644 index 000000000..f843c85f5 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt @@ -0,0 +1,335 @@ +package app.revanced.patches.youtube.misc.fix.playback + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.or +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* +import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch +import app.revanced.util.getReference +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter + +@Patch( + name = "Spoof client", + description = "Spoofs the client to allow video playback.", + dependencies = [ + SpoofClientResourcePatch::class, + PlayerResponseMethodHookPatch::class, + SettingsPatch::class, + AddResourcesPatch::class, + UserAgentClientSpoofPatch::class, + PlayerResponseMethodHookPatch::class, + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", + [ + "18.37.36", + "18.38.44", + "18.43.45", + "18.44.41", + "18.45.43", + "18.48.39", + "18.49.37", + "19.01.34", + "19.02.39", + "19.03.36", + "19.04.38", + "19.05.36", + "19.06.39", + "19.07.40", + "19.08.36", + "19.09.38", + "19.10.39", + "19.11.43", + ], + ), + ], +) +object SpoofClientPatch : BytecodePatch( + setOf( + // Client type spoof. + BuildInitPlaybackRequestFingerprint, + BuildPlayerRequestURIFingerprint, + SetPlayerRequestClientTypeFingerprint, + CreatePlayerRequestBodyFingerprint, + + // Storyboard spoof. + StoryboardRendererSpecFingerprint, + PlayerResponseModelImplRecommendedLevelFingerprint, + StoryboardRendererDecoderRecommendedLevelFingerprint, + PlayerResponseModelImplGeneralFingerprint, + StoryboardRendererDecoderSpecFingerprint, + ), +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/youtube/patches/spoof/SpoofClientPatch;" + private const val CLIENT_INFO_CLASS_DESCRIPTOR = + "Lcom/google/protos/youtube/api/innertube/InnertubeContext\$ClientInfo;" + + override fun execute(context: BytecodeContext) { + AddResourcesPatch(this::class) + + SettingsPatch.PreferenceScreen.MISC.addPreferences( + PreferenceScreen( + key = "revanced_spoof_client_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_spoof_client"), + SwitchPreference("revanced_spoof_client_use_ios"), + ), + ), + + ) + + // region Block /initplayback requests to fall back to /get_watch requests. + + BuildInitPlaybackRequestFingerprint.resultOrThrow().let { + val moveUriStringIndex = it.scanResult.patternScanResult!!.startIndex + + it.mutableMethod.apply { + val targetRegister = getInstruction(moveUriStringIndex).registerA + + addInstructions( + moveUriStringIndex + 1, + """ + invoke-static { v$targetRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$targetRegister + """, + ) + } + } + + // endregion + + // region Block /get_watch requests to fall back to /player requests. + + BuildPlayerRequestURIFingerprint.resultOrThrow().let { + val invokeToStringIndex = it.scanResult.patternScanResult!!.startIndex + + it.mutableMethod.apply { + val uriRegister = getInstruction(invokeToStringIndex).registerC + + addInstructions( + invokeToStringIndex, + """ + invoke-static { v$uriRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; + move-result-object v$uriRegister + """, + ) + } + } + + // endregion + + // region Get field references to be used below. + + val (clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) = + SetPlayerRequestClientTypeFingerprint.resultOrThrow().let { result -> + // Field in the player request object that holds the client info object. + val clientInfoField = result.mutableMethod + .getInstructions().first { instruction -> + // requestMessage.clientInfo = clientInfoBuilder.build(); + instruction.opcode == Opcode.IPUT_OBJECT && + instruction.getReference()?.type == CLIENT_INFO_CLASS_DESCRIPTOR + }.getReference() ?: throw PatchException("Could not find clientInfoField") + + // Client info object's client type field. + val clientInfoClientTypeField = result.mutableMethod + .getInstruction(result.scanResult.patternScanResult!!.endIndex) + .getReference() ?: throw PatchException("Could not find clientInfoClientTypeField") + + // Client info object's client version field. + val clientInfoClientVersionField = result.mutableMethod + .getInstruction(result.scanResult.stringsScanResult!!.matches.first().index + 1) + .getReference() ?: throw PatchException("Could not find clientInfoClientVersionField") + + Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) + } + + // endregion + + // region Spoof client type for /player requests. + + CreatePlayerRequestBodyFingerprint.resultOrThrow().let { result -> + val setClientInfoMethodName = "patch_setClientInfo" + val checkCastIndex = result.scanResult.patternScanResult!!.startIndex + var clientInfoContainerClassName: String + + result.mutableMethod.apply { + val checkCastInstruction = getInstruction(checkCastIndex) + val requestMessageInstanceRegister = checkCastInstruction.registerA + clientInfoContainerClassName = checkCastInstruction.getReference()!!.type + + addInstruction( + checkCastIndex + 1, + "invoke-static { v$requestMessageInstanceRegister }," + + " ${result.classDef.type}->$setClientInfoMethodName($clientInfoContainerClassName)V", + ) + } + + // Change requestMessage.clientInfo.clientType and requestMessage.clientInfo.clientVersion to the spoofed values. + // Do this in a helper method, to remove the need of picking out multiple free registers from the hooked code. + result.mutableClass.methods.add( + ImmutableMethod( + result.mutableClass.type, + setClientInfoMethodName, + listOf(ImmutableMethodParameter(clientInfoContainerClassName, null, "clientInfoContainer")), + "V", + AccessFlags.PRIVATE or AccessFlags.STATIC, + null, + null, + MutableMethodImplementation(3), + ).toMutable().apply { + addInstructions( + """ + invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isClientSpoofingEnabled()Z + move-result v0 + if-eqz v0, :disabled + + iget-object v0, p0, $clientInfoField + + # Set client type to the spoofed value. + iget v1, v0, $clientInfoClientTypeField + invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientTypeId(I)I + move-result v1 + iput v1, v0, $clientInfoClientTypeField + + # Set client version to the spoofed value. + iget-object v1, v0, $clientInfoClientVersionField + invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientVersion(Ljava/lang/String;)Ljava/lang/String; + move-result-object v1 + iput-object v1, v0, $clientInfoClientVersionField + + :disabled + return-void + """, + ) + }, + ) + } + + // endregion + + // region Fix storyboard if Android Testsuite is used. + + PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter( + "$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(" + + "Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", + ) + + // Hook recommended seekbar thumbnails quality level for regular videos. + StoryboardRendererDecoderRecommendedLevelFingerprint.resultOrThrow().let { + val endIndex = it.scanResult.patternScanResult!!.endIndex + + it.mutableMethod.apply { + val originalValueRegister = + getInstruction(endIndex).registerA + + addInstructions( + endIndex + 1, + """ + invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I + move-result v$originalValueRegister + """, + ) + } + } + + // Hook the recommended precise seeking thumbnails quality. + PlayerResponseModelImplRecommendedLevelFingerprint.resultOrThrow().let { + val endIndex = it.scanResult.patternScanResult!!.endIndex + + it.mutableMethod.apply { + val originalValueRegister = + getInstruction(endIndex).registerA + + addInstructions( + endIndex, + """ + invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I + move-result v$originalValueRegister + """, + ) + } + } + + // TODO: Hook the seekbar recommended level for Shorts to fix Shorts low quality seekbar thumbnails. + + /** + * Hook StoryBoard renderer url. + */ + PlayerResponseModelImplGeneralFingerprint.resultOrThrow().let { + val getStoryBoardIndex = it.scanResult.patternScanResult!!.endIndex + + it.mutableMethod.apply { + val getStoryBoardRegister = getInstruction(getStoryBoardIndex).registerA + + addInstructions( + getStoryBoardIndex, + """ + invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$getStoryBoardRegister + """, + ) + } + } + + // Hook the seekbar thumbnail decoder, required for Shorts. + StoryboardRendererDecoderSpecFingerprint.resultOrThrow().let { + val storyBoardUrlIndex = it.scanResult.patternScanResult!!.startIndex + 1 + + it.mutableMethod.apply { + val getStoryBoardRegister = getInstruction(storyBoardUrlIndex).registerA + + addInstructions( + storyBoardUrlIndex + 1, + """ + invoke-static { v$getStoryBoardRegister }, ${INTEGRATIONS_CLASS_DESCRIPTOR}->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$getStoryBoardRegister + """, + ) + } + } + + StoryboardRendererSpecFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val storyBoardUrlParams = "p0" + + addInstructions( + 0, + """ + if-nez $storyBoardUrlParams, :ignore + invoke-static { $storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; + move-result-object $storyBoardUrlParams + :ignore + nop + """, + ) + } + } + + // endregion + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientResourcePatch.kt new file mode 100644 index 000000000..0de192e3f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientResourcePatch.kt @@ -0,0 +1,18 @@ +package app.revanced.patches.youtube.misc.fix.playback + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch + +@Patch(dependencies = [ResourceMappingPatch::class]) +internal object SpoofClientResourcePatch : ResourcePatch() { + internal var scrubbedPreviewThumbnailResourceId: Long = -1 + + override fun execute(context: ResourceContext) { + scrubbedPreviewThumbnailResourceId = ResourceMappingPatch[ + "id", + "thumbnail", + ] + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt index 4e9a7833c..5593ee72c 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt @@ -68,7 +68,7 @@ object SpoofSignaturePatch : BytecodePatch( // Hook the player parameters. PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter( - "$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;", + "$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", ) // Force the seekbar time and chapters to always show up. @@ -104,7 +104,7 @@ object SpoofSignaturePatch : BytecodePatch( } // If storyboard spoofing is turned off, then hide the empty seekbar thumbnail view. - ScrubbedPreviewLayoutFingerprint.result?.apply { + SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.result?.apply { val endIndex = scanResult.patternScanResult!!.endIndex mutableMethod.apply { val imageViewFieldName = getInstruction(endIndex).reference @@ -116,7 +116,7 @@ object SpoofSignaturePatch : BytecodePatch( """, ) } - } ?: throw ScrubbedPreviewLayoutFingerprint.exception + } ?: throw SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.exception /** * Hook StoryBoard renderer url diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt index d65e23ee3..c29c94381 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt @@ -4,9 +4,9 @@ import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.ResourcePatch import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.youtube.misc.settings.SettingsPatch -@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class]) +@Patch(dependencies = [ResourceMappingPatch::class]) +@Deprecated("This patch will be removed in the future.") object SpoofSignatureResourcePatch : ResourcePatch() { internal var scrubbedPreviewThumbnailResourceId: Long = -1 diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/ClientSpoofPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt similarity index 89% rename from src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/ClientSpoofPatch.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt index aeb1788d7..2c0eaceb3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/ClientSpoofPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt @@ -2,8 +2,6 @@ package app.revanced.patches.youtube.misc.fix.playback import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch import app.revanced.patches.all.misc.transformation.IMethodCall @@ -16,14 +14,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -@Patch( - name = "Client spoof", - description = "Spoofs the client to allow video playback.", - compatiblePackages = [ - CompatiblePackage("com.google.android.youtube"), - ], -) -object ClientSpoofPatch : BaseTransformInstructionsPatch() { +object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch() { private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube" private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE = "Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;" diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildInitPlaybackRequestFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildInitPlaybackRequestFingerprint.kt new file mode 100644 index 000000000..4b63fd130 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildInitPlaybackRequestFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object BuildInitPlaybackRequestFingerprint : MethodFingerprint( + returnType = "Lorg/chromium/net/UrlRequest\$Builder;", + opcodes = listOf( + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with. + ), + strings = listOf( + "Content-Type", + "Range", + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildPlayerRequestURIFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildPlayerRequestURIFingerprint.kt new file mode 100644 index 000000000..1cdab0f35 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/BuildPlayerRequestURIFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object BuildPlayerRequestURIFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + opcodes = listOf( + Opcode.INVOKE_VIRTUAL, // Register holds player request URI. + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.MONITOR_EXIT, + Opcode.RETURN_OBJECT, + ), + strings = listOf( + "youtubei/v1", + "key", + "asig", + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyFingerprint.kt new file mode 100644 index 000000000..5abe29e67 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object CreatePlayerRequestBodyFingerprint : MethodFingerprint( + returnType = "V", + parameters = listOf("L"), + opcodes = listOf( + Opcode.CHECK_CAST, + Opcode.IGET, + Opcode.AND_INT_LIT16, + ), + strings = listOf("ms"), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt index fbf360fb8..ab4be8915 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt @@ -1,8 +1,8 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints -import app.revanced.util.containsWideLiteralInstructionValue import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.util.containsWideLiteralInstructionValue import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -13,11 +13,11 @@ internal object PlayerResponseModelImplGeneralFingerprint : MethodFingerprint( opcodes = listOf( Opcode.RETURN_OBJECT, Opcode.CONST_4, - Opcode.RETURN_OBJECT + Opcode.RETURN_OBJECT, ), customFingerprint = handler@{ methodDef, _ -> if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) return@handler false methodDef.containsWideLiteralInstructionValue(55735497) - } -) \ No newline at end of file + }, +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt index 0ee384140..fbd9b0b82 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt @@ -1,8 +1,8 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints -import app.revanced.util.containsWideLiteralInstructionValue import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.util.containsWideLiteralInstructionValue import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -13,11 +13,11 @@ internal object PlayerResponseModelImplLiveStreamFingerprint : MethodFingerprint opcodes = listOf( Opcode.RETURN_OBJECT, Opcode.CONST_4, - Opcode.RETURN_OBJECT + Opcode.RETURN_OBJECT, ), customFingerprint = handler@{ methodDef, _ -> if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) return@handler false methodDef.containsWideLiteralInstructionValue(70276274) - } + }, ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt index 9dd826bfb..ee54d7762 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt @@ -1,8 +1,8 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints -import app.revanced.util.containsWideLiteralInstructionValue import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.util.containsWideLiteralInstructionValue import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -13,11 +13,11 @@ internal object PlayerResponseModelImplRecommendedLevelFingerprint : MethodFinge opcodes = listOf( Opcode.SGET_OBJECT, Opcode.IGET, - Opcode.RETURN + Opcode.RETURN, ), customFingerprint = handler@{ methodDef, _ -> if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) return@handler false methodDef.containsWideLiteralInstructionValue(55735497) - } -) \ No newline at end of file + }, +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt index 2781a6716..08e486523 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt @@ -1,7 +1,7 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.misc.fix.playback.SpoofSignatureResourcePatch +import app.revanced.patches.youtube.misc.fix.playback.SpoofClientResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -23,5 +23,5 @@ internal object ScrubbedPreviewLayoutFingerprint : LiteralValueFingerprint( Opcode.IPUT_OBJECT, // preview imageview ), // This resource is used in ~ 40 different locations, but this method has a distinct list of parameters to match to. - literalSupplier = { SpoofSignatureResourcePatch.scrubbedPreviewThumbnailResourceId } -) \ No newline at end of file + literalSupplier = { SpoofClientResourcePatch.scrubbedPreviewThumbnailResourceId }, +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt new file mode 100644 index 000000000..e9d91e761 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object SetPlayerRequestClientTypeFingerprint : MethodFingerprint( + strings = listOf("10.29"), + opcodes = listOf( + Opcode.IGET, + Opcode.IPUT, // Sets ClientInfo.clientId. + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.kt new file mode 100644 index 000000000..90220a1c3 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SpoofSignaturePatchScrubbedPreviewLayoutFingerprint.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.misc.fix.playback.SpoofSignatureResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal object SpoofSignaturePatchScrubbedPreviewLayoutFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("Landroid/content/Context;", "Landroid/util/AttributeSet;", "I", "I"), + opcodes = listOf( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.IPUT_OBJECT, // preview imageview + ), + // This resource is used in ~ 40 different locations, but this method has a distinct list of parameters to match to. + literalSupplier = { SpoofSignatureResourcePatch.scrubbedPreviewThumbnailResourceId }, +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt index 32dd4b19a..e56867eae 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt @@ -17,7 +17,7 @@ internal object StoryboardRendererDecoderRecommendedLevelFingerprint : MethodFin Opcode.MOVE_RESULT_OBJECT, Opcode.IPUT_OBJECT, Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT + Opcode.MOVE_RESULT, ), - strings = listOf("#-1#") + strings = listOf("#-1#"), ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt index 5d47a9bcf..bd934d2a3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt @@ -6,8 +6,8 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode /** -* Resolves to the same method as [StoryboardRendererDecoderRecommendedLevelFingerprint]. -*/ + * Resolves to the same method as [StoryboardRendererDecoderRecommendedLevelFingerprint]. + */ internal object StoryboardRendererDecoderSpecFingerprint : MethodFingerprint( returnType = "V", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, @@ -19,5 +19,5 @@ internal object StoryboardRendererDecoderSpecFingerprint : MethodFingerprint( Opcode.CONST_4, Opcode.IF_NEZ, ), - strings = listOf("#-1#") + strings = listOf("#-1#"), ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt index 8a151246d..172001c53 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt @@ -14,4 +14,4 @@ internal object StoryboardThumbnailParentFingerprint : MethodFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, returnType = "Landroid/graphics/Bitmap;", strings = listOf("Storyboard regionDecoder.decodeRegion exception - "), -) \ No newline at end of file +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt index 4c9e526ec..f8fe027d5 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch.kt @@ -3,7 +3,7 @@ package app.revanced.patches.youtube.misc.gms import app.revanced.patches.shared.fingerprints.CastContextFetchFingerprint import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch import app.revanced.patches.youtube.layout.buttons.cast.HideCastButtonPatch -import app.revanced.patches.youtube.misc.fix.playback.ClientSpoofPatch +import app.revanced.patches.youtube.misc.fix.playback.SpoofClientPatch import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption @@ -27,13 +27,18 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch( integrationsPatchDependency = IntegrationsPatch::class, dependencies = setOf( HideCastButtonPatch::class, - ClientSpoofPatch::class, + SpoofClientPatch::class, ), gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch, compatiblePackages = setOf( CompatiblePackage( "com.google.android.youtube", setOf( + "18.37.36", + "18.38.44", + "18.43.45", + "18.44.41", + "18.45.43", "18.48.39", "18.49.37", "19.01.34", @@ -46,7 +51,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch( "19.08.36", "19.09.38", "19.10.39", - "19.11.43" + "19.11.43", ), ), ), diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt index 36d0066c6..dc7f343b9 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt @@ -119,7 +119,7 @@ object VideoInformationPatch : BytecodePatch( // Call before any other video id hooks, // so they can use VideoInformation and check if the video id is for a Short. PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameterBeforeVideoId( - "$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseSignature(Ljava/lang/String;Z)Ljava/lang/String;") + "$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseSignature(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;") /* * Set the video time method diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt index 135779eec..03711953a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt @@ -24,24 +24,39 @@ object PlayerResponseMethodHookPatch : private const val PARAMETER_PROTO_BUFFER = 3 private const val PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING = 11 - // Temporary 4-bit registers used to pass the parameters to integrations. - private const val REGISTER_VIDEO_ID = 0 - private const val REGISTER_PROTO_BUFFER = 1 - private const val REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING = 2 + // Registers used to pass the parameters to integrations. + private var playerResponseMethodCopyRegisters = false + private lateinit var REGISTER_VIDEO_ID : String + private lateinit var REGISTER_PROTO_BUFFER : String + private lateinit var REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING : String private lateinit var playerResponseMethod: MutableMethod - private var numberOfInstructionsAdded = 0 override fun execute(context: BytecodeContext) { playerResponseMethod = PlayerParameterBuilderFingerprint.result?.mutableMethod ?: throw PlayerParameterBuilderFingerprint.exception + + // On some app targets the method has too many registers pushing the parameters past v15. + // If needed, move the parameters to 4-bit registers so they can be passed to integrations. + playerResponseMethodCopyRegisters = playerResponseMethod.implementation!!.registerCount - + playerResponseMethod.parameterTypes.size + PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING > 15 + + if (playerResponseMethodCopyRegisters) { + REGISTER_VIDEO_ID = "v0" + REGISTER_PROTO_BUFFER = "v1" + REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING = "v2" + } else { + REGISTER_VIDEO_ID = "p$PARAMETER_VIDEO_ID" + REGISTER_PROTO_BUFFER = "p$PARAMETER_PROTO_BUFFER" + REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING = "p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING" + } } override fun close() { fun hookVideoId(hook: Hook) { playerResponseMethod.addInstruction( - 0, "invoke-static {v$REGISTER_VIDEO_ID, v$REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook" + 0, "invoke-static {$REGISTER_VIDEO_ID, $REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook" ) numberOfInstructionsAdded++ } @@ -50,8 +65,8 @@ object PlayerResponseMethodHookPatch : playerResponseMethod.addInstructions( 0, """ - invoke-static {v$REGISTER_PROTO_BUFFER, v$REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook - move-result-object v$REGISTER_PROTO_BUFFER + invoke-static {$REGISTER_PROTO_BUFFER, $REGISTER_VIDEO_ID, $REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook + move-result-object $REGISTER_PROTO_BUFFER """ ) numberOfInstructionsAdded += 2 @@ -67,22 +82,22 @@ object PlayerResponseMethodHookPatch : videoIdHooks.forEach(::hookVideoId) beforeVideoIdHooks.forEach(::hookProtoBufferParameter) - // On some app targets the method has too many registers pushing the parameters past v15. - // Move the parameters to 4-bit registers so they can be passed to integrations. - playerResponseMethod.addInstructions( - 0, """ - move-object/from16 v$REGISTER_VIDEO_ID, p$PARAMETER_VIDEO_ID - move-object/from16 v$REGISTER_PROTO_BUFFER, p$PARAMETER_PROTO_BUFFER - move/from16 v$REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING, p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING + if (playerResponseMethodCopyRegisters) { + playerResponseMethod.addInstructions( + 0, """ + move-object/from16 $REGISTER_VIDEO_ID, p$PARAMETER_VIDEO_ID + move-object/from16 $REGISTER_PROTO_BUFFER, p$PARAMETER_PROTO_BUFFER + move/from16 $REGISTER_IS_SHORT_AND_OPENING_OR_PLAYING, p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING """, - ) - numberOfInstructionsAdded += 3 + ) + numberOfInstructionsAdded += 3 - // Move the modified register back. - playerResponseMethod.addInstruction( - numberOfInstructionsAdded, - "move-object/from16 p$PARAMETER_PROTO_BUFFER, v$REGISTER_PROTO_BUFFER" - ) + // Move the modified register back. + playerResponseMethod.addInstruction( + numberOfInstructionsAdded, + "move-object/from16 p$PARAMETER_PROTO_BUFFER, $REGISTER_PROTO_BUFFER" + ) + } } internal abstract class Hook(private val methodDescriptor: String) { diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt index a2ccd3e94..e7297fde6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuPatch.kt @@ -2,14 +2,18 @@ package app.revanced.patches.youtube.video.videoqualitymenu import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch import app.revanced.patches.youtube.misc.recyclerviewtree.hook.RecyclerViewTreeHookPatch +import app.revanced.patches.youtube.video.videoqualitymenu.fingerprints.VideoQualityMenuOptionsFingerprint import app.revanced.patches.youtube.video.videoqualitymenu.fingerprints.VideoQualityMenuViewInflateFingerprint +import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( @@ -50,7 +54,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction ) @Suppress("unused") object RestoreOldVideoQualityMenuPatch : BytecodePatch( - setOf(VideoQualityMenuViewInflateFingerprint) + setOf(VideoQualityMenuViewInflateFingerprint, VideoQualityMenuOptionsFingerprint) ) { private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/components/VideoQualityMenuFilterPatch;" @@ -60,7 +64,8 @@ object RestoreOldVideoQualityMenuPatch : BytecodePatch( override fun execute(context: BytecodeContext) { // region Patch for the old type of the video quality menu. - // Only used when spoofing to old app version. + // Used for regular videos when spoofing to old app version, + // and for the Shorts quality flyout on newer app versions. VideoQualityMenuViewInflateFingerprint.result?.let { it.mutableMethod.apply { @@ -76,6 +81,29 @@ object RestoreOldVideoQualityMenuPatch : BytecodePatch( } } + // Force YT to add the 'advanced' quality menu for Shorts. + VideoQualityMenuOptionsFingerprint.resultOrThrow().let { + val result = it.scanResult.patternScanResult!! + val startIndex = result.startIndex + val endIndex = result.endIndex + + it.mutableMethod.apply { + val freeRegister = getInstruction(startIndex).registerA + + // A condition controls whether to show the three or four items quality menu. + // Force the four items quality menu to make the "Advanced" item visible, necessary for the patch. + addInstructionsWithLabels( + startIndex + 1, + """ + invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation()Z + move-result v$freeRegister + if-nez v$freeRegister, :includeAdvancedMenu + """, + ExternalLabel("includeAdvancedMenu", getInstruction(endIndex)) + ) + } + } + // endregion // region Patch for the new type of the video quality menu. diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch.kt index 8de2a9b0e..41e1b8ad2 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/RestoreOldVideoQualityMenuResourcePatch.kt @@ -13,6 +13,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch ) object RestoreOldVideoQualityMenuResourcePatch : ResourcePatch() { internal var videoQualityBottomSheetListFragmentTitle = -1L + internal var videoQualityQuickMenuAdvancedMenuDescription = -1L override fun execute(context: ResourceContext) { AddResourcesPatch(this::class) @@ -26,5 +27,10 @@ object RestoreOldVideoQualityMenuResourcePatch : ResourcePatch() { "layout", "video_quality_bottom_sheet_list_fragment_title", ] + + videoQualityQuickMenuAdvancedMenuDescription = ResourceMappingPatch[ + "string", + "video_quality_quick_menu_advanced_menu_description" + ] } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/fingerprints/VideoQualityMenuOptionsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/fingerprints/VideoQualityMenuOptionsFingerprint.kt new file mode 100644 index 000000000..0d1a2cc6d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/videoqualitymenu/fingerprints/VideoQualityMenuOptionsFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.video.videoqualitymenu.fingerprints + +import app.revanced.patches.youtube.video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal object VideoQualityMenuOptionsFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.STATIC.value, + parameters = listOf("Landroid/content/Context", "L", "L"), + returnType = "[L", + opcodes = listOf( + Opcode.IF_EQZ, // Check if advanced menu should be shown. + Opcode.NEW_ARRAY, + Opcode.APUT_OBJECT, + Opcode.APUT_OBJECT, + Opcode.APUT_OBJECT, + Opcode.RETURN_OBJECT, + Opcode.CONST_4 // Advanced menu code path. + ), + literalSupplier = { RestoreOldVideoQualityMenuResourcePatch.videoQualityQuickMenuAdvancedMenuDescription } +) \ No newline at end of file diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index f59fcb612..621b7c43b 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -171,7 +171,7 @@ Hide channel bar Channel bar is hidden Channel bar is shown - Hide playables + Hide Playables Playables are hidden Playables are shown Hide quick actions in fullscreen @@ -249,7 +249,7 @@ General ads are hidden General ads are shown Hide fullscreen ads - Fullscreen ads are hidden + Fullscreen ads are hidden\n\nThis feature is only available for older devices Fullscreen ads are shown Hide buttoned ads Buttoned ads are hidden @@ -266,13 +266,17 @@ Hide shopping links in video description Shopping links are hidden Shopping links are shown + + Hide the \'Visit store\' button on channel pages + Button is hidden + Button is shown Hide web search results Web search results are hidden Web search results are shown Hide merchandise banners Merchandise banners are hidden Merchandise banners are shown - Could not hide fullscreen ad. Hide setting disabled to prevent issues. + Hide fullscreen ads only works with older devices Hide YouTube Premium promotions @@ -393,11 +397,6 @@ Hide Save to playlist Save to playlist button is hidden Save to playlist button is shown - - Hide Shop - Shop button is hidden - Shop button is shown Hide autoplay button @@ -436,8 +435,11 @@ Subscriptions button is shown Switch Create with Notifications - Create button is switched with Notifications button + Create button is switched with Notifications button\n\nNote: Enabling this also forcibly hides video ads Create button is not switched with Notifications button + Hide navigation button labels + Labels are hidden + Labels are shown Flyout menu @@ -458,11 +460,6 @@ Hide Ambient mode Ambient mode menu is hidden Ambient mode menu is shown - - Hide Report - Report menu is hidden - Report menu is shown Hide Help & feedback Help & feedback menu is hidden @@ -508,6 +505,9 @@ Hide comments section Comment section is hidden Comment section is shown + Hide timestamp and emoji buttons + Comment timestamp and emoji buttons are hidden + Comment timestamp and emoji buttons are shown Hide crowdfunding box @@ -586,6 +586,9 @@ Hide shop button Shop button is hidden Shop button is shown + Hide super thanks button + Super thanks button is hidden + Super thanks button is shown Hide tagged products Tagged products are hidden Tagged products are shown @@ -621,7 +624,7 @@ Hide channel bar Channel bar is hidden Channel bar is shown - Hide Shorts video title + Hide video title Title is hidden Title is shown Hide sound metadata label @@ -861,7 +864,7 @@ Are the times correct? - The segment lasts from %1$02d:%2$02d to %3$02d:%4$02d (%5$d minutes %6$02d seconds)\nIs it ready to submit? + The segment is from\n\n%1$s\nto\n%2$s\n\n(%3$s)\n\nReady to submit? Start must be before the end Mark two locations on the time bar first Preview the segment, and ensure it skips smoothly @@ -904,7 +907,7 @@ Version spoofed Version not spoofed App version will be spoofed to an older version of YouTube.\n\nThis will change the appearance and features of the app, but unknown side effects may occur.\n\nIf later turned off, it is recommended to clear the app data to prevent UI bugs. - Spoof app version target @@ -1082,6 +1085,21 @@ Slide to seek is enabled Slide to seek is not enabled + + Spoof client + Spoof the client to prevent playback issues + Spoof client + Client is spoofed + Client is not spoofed\n\nVideo playback may not work + Turning off this setting may cause video playback issues. + + Spoof client to iOS + Spoof the client to iOS instead of an Android Testsuite + Client is spoofed to an iOS client\n\nSide effects include:\n• 60 fps video may not be available\n• No HDR video\n• Some videos may not load\n• Higher video qualities may be missing + Client is spoofed to an Android Testsuite client (iOS client is used for live streams)\n\nSide effects include:\n• Subtitles are missing\n• Player gestures may not work\n• Low quality Shorts seekbar thumbnails + Spoof client thumbnails not available (API timed out) + Spoof client thumbnails temporarily not available: %s + Spoof app signature @@ -1155,9 +1173,9 @@ Other settings Client-side ads Server-side surestream ads - Debug logging - Debug logs are enabled - Debug logs are disabled + Debug logging + Debug logs are enabled + Debug logs are disabled \ No newline at end of file