From 6d88cb49ec739700866290babcba5fb3032dbced Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 8 Mar 2024 09:09:15 +0400 Subject: [PATCH] fix(YouTube - Downloads): Use new task context (#2841) --- api/revanced-patches.api | 1 + .../integrations/BaseIntegrationsPatch.kt | 4 +- .../interaction/downloads/DownloadsPatch.kt | 71 +++++++++++++------ .../downloads/DownloadsResourcePatch.kt | 2 +- ...ownloadActionCommandResolverFingerprint.kt | 14 ++++ ...dActionCommandResolverParentFingerprint.kt | 17 +++++ .../DownloadButtonActionFingerprint.kt | 7 -- ...egacyDownloadCommandResolverFingerprint.kt | 24 +++++++ .../ReturnYouTubeDislikePatch.kt | 3 +- .../thumbnails/AlternativeThumbnailsPatch.kt | 9 +-- .../playeroverlay/PlayerOverlaysHookPatch.kt | 29 ++++---- ...layerOverlaysOnFinishInflateFingerprint.kt | 10 ++- .../kotlin/app/revanced/util/BytecodeUtils.kt | 3 + .../resources/addresources/values/strings.xml | 6 +- 14 files changed, 142 insertions(+), 58 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverParentFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadButtonActionFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/LegacyDownloadCommandResolverFingerprint.kt diff --git a/api/revanced-patches.api b/api/revanced-patches.api index 9de9da57..4e1ad810 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -1748,6 +1748,7 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V + public static final fun resultOrThrow (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult; public static final fun returnEarly (Ljava/util/List;Z)V public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)V public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V 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 eb7f7e87..058a05a6 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 @@ -64,8 +64,8 @@ abstract class BaseIntegrationsPatch( method.addInstruction( 0, - "sput-object v$contextRegister, " + - "$integrationsDescriptor->context:Landroid/content/Context;", + "invoke-static/range { v$contextRegister .. v$contextRegister }, " + + "$integrationsDescriptor->setContext(Landroid/content/Context;)V", ) } ?: throw PatchException("Could not find hook target fingerprint.") } diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt index 82e46f5d..71f81777 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt @@ -1,16 +1,18 @@ package app.revanced.patches.youtube.interaction.downloads 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.interaction.downloads.fingerprints.DownloadButtonActionFingerprint +import app.revanced.patches.youtube.interaction.downloads.fingerprints.DownloadActionCommandResolverFingerprint +import app.revanced.patches.youtube.interaction.downloads.fingerprints.DownloadActionCommandResolverParentFingerprint +import app.revanced.patches.youtube.interaction.downloads.fingerprints.LegacyDownloadCommandResolverFingerprint import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsBytecodePatch +import app.revanced.patches.youtube.shared.fingerprints.MainActivityFingerprint import app.revanced.patches.youtube.video.information.VideoInformationPatch -import app.revanced.util.exception +import app.revanced.util.resultOrThrow @Patch( name = "Downloads", @@ -39,8 +41,10 @@ import app.revanced.util.exception @Suppress("unused") object DownloadsPatch : BytecodePatch( setOf( - DownloadButtonActionFingerprint, - ), + DownloadActionCommandResolverParentFingerprint, + LegacyDownloadCommandResolverFingerprint, + MainActivityFingerprint + ) ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/DownloadsPatch;" private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;" @@ -49,19 +53,46 @@ object DownloadsPatch : BytecodePatch( PlayerControlsBytecodePatch.initializeControl("$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V") PlayerControlsBytecodePatch.injectVisibilityCheckCall("$BUTTON_DESCRIPTOR->changeVisibility(Z)V") - DownloadButtonActionFingerprint.result?.let { - it.mutableMethod.apply { - addInstructionsWithLabels( - 2, - """ - invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick()Z - move-result v0 - if-eqz v0, :show_dialog - return-void - """, - ExternalLabel("show_dialog", getInstruction(2)), - ) - } - } ?: throw DownloadButtonActionFingerprint.exception + // Main activity is used to launch downloader intent. + MainActivityFingerprint.resultOrThrow().mutableMethod.apply { + addInstruction( + implementation!!.instructions.lastIndex, + "invoke-static { p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->activityCreated(Landroid/app/Activity;)V" + ) + } + + val commonInstructions = """ + move-result v0 + if-eqz v0, :show_native_downloader + return-void + :show_native_downloader + nop + """ + + DownloadActionCommandResolverFingerprint.resolve(context, + DownloadActionCommandResolverParentFingerprint.resultOrThrow().classDef) + DownloadActionCommandResolverFingerprint.resultOrThrow().mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick()Z + $commonInstructions + """ + ) + } + + // Legacy fingerprint is used for old spoofed versions, + // or if download playlist is pressed on any version. + // Downloading playlists is not yet supported, + // as the code this hooks does not easily expost the playlist id. + LegacyDownloadCommandResolverFingerprint.resultOrThrow().mutableMethod.apply { + addInstructionsWithLabels( + 0, + """ + invoke-static/range {p1 .. p1}, $INTEGRATIONS_CLASS_DESCRIPTOR->inAppDownloadPlaylistLegacyOnClick(Ljava/lang/String;)Z + $commonInstructions + """ + ) + } } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsResourcePatch.kt index bef29f94..24285e53 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsResourcePatch.kt @@ -31,8 +31,8 @@ internal object DownloadsResourcePatch : ResourcePatch() { sorting = Sorting.UNSORTED, preferences = setOf( SwitchPreference("revanced_external_downloader"), + SwitchPreference("revanced_external_downloader_action_button"), TextPreference("revanced_external_downloader_name", inputType = InputType.TEXT), - SwitchPreference("revanced_use_in_app_download_button"), ), ), ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverFingerprint.kt new file mode 100644 index 00000000..2c74eec1 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverFingerprint.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.interaction.downloads.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves to the class found in [DownloadActionCommandResolverParentFingerprint]. + */ +internal object DownloadActionCommandResolverFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("L", "Ljava/util/Map;") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverParentFingerprint.kt new file mode 100644 index 00000000..8af33d1e --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadActionCommandResolverParentFingerprint.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.interaction.downloads.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object DownloadActionCommandResolverParentFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("L", "L"), + strings = listOf( + // Strings are not unique and found in other methods. + "com.google.android.libraries.youtube.logging.interaction_logger", + "Unknown command" + ), + literalSupplier = { 16 } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadButtonActionFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadButtonActionFingerprint.kt deleted file mode 100644 index 4246f4b8..00000000 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/DownloadButtonActionFingerprint.kt +++ /dev/null @@ -1,7 +0,0 @@ -package app.revanced.patches.youtube.interaction.downloads.fingerprints - -import app.revanced.patcher.fingerprint.MethodFingerprint - -internal object DownloadButtonActionFingerprint : MethodFingerprint( - strings = listOf("offline/get_download_action"), -) diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/LegacyDownloadCommandResolverFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/LegacyDownloadCommandResolverFingerprint.kt new file mode 100644 index 00000000..36796010 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/fingerprints/LegacyDownloadCommandResolverFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.interaction.downloads.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 + +/** + * For spoofing to older versions. Also called if download playlist is pressed for any version. + */ +internal object LegacyDownloadCommandResolverFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("Ljava/lang/String;", "Ljava/lang/String;", "L", "L"), + strings = listOf(""), + opcodes = listOf( + Opcode.MOVE_OBJECT_FROM16, + Opcode.MOVE_OBJECT_FROM16, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + ) +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt index 93b9cc0c..c911da3a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt @@ -32,6 +32,7 @@ import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.util.exception import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.resultOrThrow 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 @@ -88,8 +89,6 @@ object ReturnYouTubeDislikePatch : BytecodePatch( private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/components/ReturnYouTubeDislikeFilterPatch;" - private fun MethodFingerprint.resultOrThrow() = result ?: throw exception - override fun execute(context: BytecodeContext) { // region Inject newVideoLoaded event handler to update dislikes when a new video is loaded. diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt index 4c642c7b..bf9162e5 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch.kt @@ -23,7 +23,7 @@ import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.reques import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnSucceededFingerprint import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.util.exception +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 @@ -144,11 +144,8 @@ object AlternativeThumbnailsPatch : BytecodePatch( NonInteractivePreference("revanced_alt_thumbnail_stills_about"), ) - fun MethodFingerprint.getResultOrThrow() = - result ?: throw exception - fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) = - also { resolve(context, fingerprint.getResultOrThrow().classDef) }.getResultOrThrow() + also { resolve(context, fingerprint.resultOrThrow().classDef) }.resultOrThrow() fun MethodFingerprint.resolveAndLetMutableMethod( fingerprint: MethodFingerprint, @@ -172,7 +169,7 @@ object AlternativeThumbnailsPatch : BytecodePatch( // The URL is required for the failure callback hook, but the URL field is obfuscated. // Add a helper get method that returns the URL field. - RequestFingerprint.getResultOrThrow().apply { + RequestFingerprint.resultOrThrow().apply { // The url is the only string field that is set inside the constructor. val urlFieldInstruction = mutableMethod.getInstructions().first { if (it.opcode != Opcode.IPUT_OBJECT) return@first false diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch.kt index a48cc48c..c94ac05d 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch.kt @@ -3,30 +3,31 @@ package app.revanced.patches.youtube.misc.playeroverlay import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.playeroverlay.fingerprint.PlayerOverlaysOnFinishInflateFingerprint +import app.revanced.util.exception @Patch( - description = "Hook for adding custom overlays to the video player.", + description = "Hook for the video player overlay", dependencies = [IntegrationsPatch::class], - compatiblePackages = [ - CompatiblePackage("com.google.android.youtube", [ - "18.32.39" - ]) - ] ) + +/** + * Edit: This patch is not in use and may not work. + */ @Suppress("unused") -object PlayerOverlaysHookPatch : BytecodePatch( // TODO: delete this unused outdated patch and its integration code. +object PlayerOverlaysHookPatch : BytecodePatch( setOf(PlayerOverlaysOnFinishInflateFingerprint) ) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/PlayerOverlaysHookPatch;" + override fun execute(context: BytecodeContext) { - // hook YouTubePlayerOverlaysLayout.onFinishInflate() - val method = PlayerOverlaysOnFinishInflateFingerprint.result!!.mutableMethod - method.addInstruction( - method.implementation!!.instructions.size - 2, - "invoke-static { p0 }, Lapp/revanced/integrations/youtube/patches/PlayerOverlaysHookPatch;->YouTubePlayerOverlaysLayout_onFinishInflateHook(Ljava/lang/Object;)V" - ) + PlayerOverlaysOnFinishInflateFingerprint.result?.mutableMethod?.apply { + addInstruction( + implementation!!.instructions.lastIndex, + "invoke-static { p0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->playerOverlayInflated(Landroid/view/ViewGroup;)V" + ) + } ?: throw PlayerOverlaysOnFinishInflateFingerprint.exception } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/fingerprint/PlayerOverlaysOnFinishInflateFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/fingerprint/PlayerOverlaysOnFinishInflateFingerprint.kt index 1d40fc36..11658c95 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/fingerprint/PlayerOverlaysOnFinishInflateFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/playeroverlay/fingerprint/PlayerOverlaysOnFinishInflateFingerprint.kt @@ -1,10 +1,14 @@ package app.revanced.patches.youtube.misc.playeroverlay.fingerprint - +import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags internal object PlayerOverlaysOnFinishInflateFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", customFingerprint = { methodDef, _ -> - methodDef.definingClass.endsWith("YouTubePlayerOverlaysLayout;") && methodDef.name == "onFinishInflate" - } + methodDef.definingClass.endsWith("/YouTubePlayerOverlaysLayout;") + && methodDef.name == "onFinishInflate" + }, ) diff --git a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index f47ae90d..8daf7770 100644 --- a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -15,6 +15,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction import com.android.tools.smali.dexlib2.iface.reference.Reference import com.android.tools.smali.dexlib2.util.MethodUtil + +fun MethodFingerprint.resultOrThrow() = result ?: throw exception + /** * The [PatchException] of failing to resolve a [MethodFingerprint]. * diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index 4801f8e9..c4731436 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -232,12 +232,12 @@ Show external download button Download button shown in player Download button not shown in player + Override download action button + Download button opens your external downloader + Download button opens the native in-app downloader Downloader package name Package name of your installed external downloader app, such as NewPipe or Seal %s is not installed. Please install it. - Use in-app download button - Button will launch the external downloader - Button will launch the native in-app downloader Disable precise seeking gesture