From e0ac9f385fc360f4dd2451e26676633120356c10 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 9 Jul 2022 15:53:33 +0200 Subject: [PATCH] feat: `autorepeat-by-default` patch (#106) --- build.gradle.kts | 4 +- .../annotations/AutoRepeatCompatibility.kt | 13 ++++ .../fingerprints/AutoRepeatFingerprint.kt | 32 ++++++++ .../AutoRepeatParentFingerprint.kt | 35 +++++++++ .../autorepeat/patch/AutoRepeatPatch.kt | 76 +++++++++++++++++++ 5 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/annotations/AutoRepeatCompatibility.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatParentFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/patch/AutoRepeatPatch.kt diff --git a/build.gradle.kts b/build.gradle.kts index ee3a550f5..b151da605 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,3 @@ -import org.apache.tools.ant.taskdefs.ExecTask - plugins { kotlin("jvm") version "1.7.0" } @@ -24,7 +22,7 @@ repositories { dependencies { implementation(kotlin("stdlib")) - implementation("app.revanced:revanced-patcher:2.3.0") + implementation("app.revanced:revanced-patcher:2.4.0") implementation("app.revanced:multidexlib2:2.5.2.r2") } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/annotations/AutoRepeatCompatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/annotations/AutoRepeatCompatibility.kt new file mode 100644 index 000000000..8ffec97ba --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/annotations/AutoRepeatCompatibility.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.layout.autorepeat.annotations + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility( + [Package( + "com.google.android.youtube", arrayOf("17.24.35", "17.25.34") + )] +) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class AutoRepeatCompatibility \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatFingerprint.kt new file mode 100644 index 000000000..83722beb7 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatFingerprint.kt @@ -0,0 +1,32 @@ +package app.revanced.patches.youtube.layout.autorepeat.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patches.youtube.layout.autorepeat.annotations.AutoRepeatCompatibility +import org.jf.dexlib2.AccessFlags + +@Name("auto-repeat-fingerprint") +@MatchingMethod( + "Laamp;", "ae" +) +@FuzzyPatternScanMethod(2) +@AutoRepeatCompatibility +@Version("0.0.1") +//Finds method: +/* +public final void ae() { + aq(aabj.ENDED); + } + */ +object AutoRepeatFingerprint : MethodFingerprint( + "V", + AccessFlags.PUBLIC or AccessFlags.FINAL, + null, + null, + null, + customFingerprint = { methodDef -> methodDef.implementation!!.instructions.count() == 3 } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatParentFingerprint.kt new file mode 100644 index 000000000..6f60ac676 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/fingerprints/AutoRepeatParentFingerprint.kt @@ -0,0 +1,35 @@ +package app.revanced.patches.youtube.layout.autorepeat.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patches.youtube.layout.autorepeat.annotations.AutoRepeatCompatibility +import org.jf.dexlib2.AccessFlags + +@Name("auto-repeat-parent-fingerprint") +@MatchingMethod( + "Laamp;", "E" +) +@FuzzyPatternScanMethod(2) +@AutoRepeatCompatibility +@Version("0.0.1") +//This Fingerprints finds the play() method needed to be called when AutoRepeatPatch.shouldAutoRepeat() == true +/* +public final void E() { +Stuff happens +String str = "play() called when the player wasn't loaded."; +String str2 = "play() blocked because Background Playability failed"; +Stuff happens again +} + */ +object AutoRepeatParentFingerprint : MethodFingerprint( + "V", + AccessFlags.PUBLIC or AccessFlags.FINAL, + null, + null, + listOf("play() called when the player wasn't loaded.", "play() blocked because Background Playability failed"), + null +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/patch/AutoRepeatPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/patch/AutoRepeatPatch.kt new file mode 100644 index 000000000..c429a68b3 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/autorepeat/patch/AutoRepeatPatch.kt @@ -0,0 +1,76 @@ +package app.revanced.patches.youtube.layout.autorepeat.patch + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.impl.BytecodeData +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.removeInstruction +import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultError +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.annotations.Dependencies +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patcher.patch.impl.BytecodePatch +import app.revanced.patches.youtube.layout.autorepeat.annotations.AutoRepeatCompatibility +import app.revanced.patches.youtube.layout.autorepeat.fingerprints.AutoRepeatFingerprint +import app.revanced.patches.youtube.layout.autorepeat.fingerprints.AutoRepeatParentFingerprint +import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch + +@Patch(include = false) +@Dependencies(dependencies = [IntegrationsPatch::class]) +@Name("autorepeat-by-default") +@Description("Enables auto repeating of videos by default.") +@AutoRepeatCompatibility +@Version("0.0.1") +class AutoRepeatPatch : BytecodePatch( + listOf( + AutoRepeatParentFingerprint + ) +) { + override fun execute(data: BytecodeData): PatchResult { + //Get Result from the ParentFingerprint which is the playMethod we need to get. + val parentResult = AutoRepeatParentFingerprint.result + ?: return PatchResultError("ParentFingerprint did not resolve.") + + //this one needs to be called when app/revanced/integrations/patches/AutoRepeatPatch;->shouldAutoRepeat() returns true + val playMethod = parentResult.mutableMethod + AutoRepeatFingerprint.resolve(data, parentResult.classDef) + //String is: Laamp;->E()V + val methodToCall = playMethod.definingClass + "->" + playMethod.name + "()V"; + + //This is the method we search for + val result = AutoRepeatFingerprint.result + ?: return PatchResultError("FingerPrint did not resolve.") + val method = result.mutableMethod + + //Instructions to add to the smali code + val instructions = """ + invoke-static {}, Lapp/revanced/integrations/patches/AutoRepeatPatch;->shouldAutoRepeat()Z + move-result v0 + if-eqz v0, :noautorepeat + const/4 v0, 0x0 + invoke-virtual {}, $methodToCall + :noautorepeat + return-void + """ + + //Get the implementation so we can do a check for null and get instructions size. + val implementation = method.implementation + ?: return PatchResultError("No Method Implementation found!") + + //Since addInstructions needs an index which starts counting at 0 and size starts counting at 1, + //we have to remove 1 to get the latest instruction + val index = implementation.instructions.size-1 + + + //remove last instruction which is return-void + method.removeInstruction(index) + // Add our own instructions there + method.addInstructions(index, instructions) + + //Everything worked as expected, return Success + return PatchResultSuccess() + } +}