This repository was archived by the owner on Mar 10, 2026. It is now read-only.
forked from ReVanced/revanced-patches-template
-
Notifications
You must be signed in to change notification settings - Fork 122
feat(YouTube): add Hook download actions patch
#70
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
185f755
feat(YouTube/Download Playlist Button): add playlist download button
Francesco146 28576cb
feat(YouTube/Download Playlist Button): add playlist external downloa…
Francesco146 3545259
refactor(YouTube/Download Playlist Button): remove duplicate resources
Francesco146 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
174 changes: 174 additions & 0 deletions
174
...main/kotlin/app/revanced/patches/youtube/misc/downloadactions/HookDownloadActionsPatch.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| package app.revanced.patches.youtube.misc.downloadactions | ||
|
|
||
| import app.revanced.patcher.data.BytecodeContext | ||
| import app.revanced.patcher.extensions.InstructionExtensions.addInstructions | ||
| import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels | ||
| import app.revanced.patcher.extensions.InstructionExtensions.getInstruction | ||
| import app.revanced.patcher.util.smali.ExternalLabel | ||
| import app.revanced.patches.youtube.misc.downloadactions.fingerprints.* | ||
| import app.revanced.patches.youtube.utils.compatibility.Constants | ||
| import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH | ||
| import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH | ||
| import app.revanced.patches.youtube.utils.mainactivity.MainActivityResolvePatch | ||
| import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch | ||
| import app.revanced.patches.youtube.utils.settings.SettingsPatch | ||
| import app.revanced.util.getReference | ||
| import app.revanced.util.indexOfFirstInstructionOrThrow | ||
| import app.revanced.util.patch.BaseBytecodePatch | ||
| import app.revanced.util.resultOrThrow | ||
| import com.android.tools.smali.dexlib2.Opcode | ||
| import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction | ||
| import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction | ||
| import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c | ||
| import com.android.tools.smali.dexlib2.iface.reference.MethodReference | ||
|
|
||
| @Suppress("unused") | ||
| object HookDownloadActionsPatch : BaseBytecodePatch( | ||
| name = "Hook download actions", | ||
| description = "Adds options to show the download playlist button and hook the download actions.", | ||
| dependencies = setOf( | ||
| MainActivityResolvePatch::class, | ||
| SharedResourceIdPatch::class, | ||
| SettingsPatch::class | ||
| ), | ||
| compatiblePackages = Constants.COMPATIBLE_PACKAGE, | ||
| fingerprints = setOf( | ||
| OfflineVideoEndpointFingerprint, | ||
| PiPPlaybackFingerprint, | ||
| AccessibilityOfflineButtonSyncFingerprint, | ||
| DownloadPlaylistButtonOnClickFingerprint | ||
| ) | ||
| ) { | ||
| private const val INTEGRATIONS_DOWNLOAD_PLAYLIST_BUTTON_CLASS_DESCRIPTOR = | ||
| "$MISC_PATH/HookDownloadAction;" | ||
|
|
||
| private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR = | ||
| "$INTEGRATIONS_PATH/utils/VideoUtils;" | ||
|
|
||
| override fun execute(context: BytecodeContext) { | ||
|
|
||
| // region Patch for hook download actions | ||
|
|
||
| OfflineVideoEndpointFingerprint.resultOrThrow().mutableMethod.apply { | ||
| addInstructionsWithLabels( | ||
| 0, """ | ||
| invoke-static/range {p3 .. p3}, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick(Ljava/lang/String;)Z | ||
|
Francesco146 marked this conversation as resolved.
|
||
| move-result v0 | ||
| if-eqz v0, :show_native_downloader | ||
| return-void | ||
| """, ExternalLabel("show_native_downloader", getInstruction(0)) | ||
| ) | ||
| } | ||
|
|
||
| PiPPlaybackFingerprint.resultOrThrow().let { | ||
|
Francesco146 marked this conversation as resolved.
|
||
| it.mutableMethod.apply { | ||
| val insertIndex = it.scanResult.patternScanResult!!.endIndex | ||
| val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA | ||
|
|
||
| addInstructions( | ||
| insertIndex, """ | ||
| invoke-static {v$insertRegister}, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->getExternalDownloaderLaunchedState(Z)Z | ||
| move-result v$insertRegister | ||
| """ | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| // endregion | ||
|
|
||
| // region Force show the playlist download button | ||
|
|
||
| AccessibilityOfflineButtonSyncFingerprint.resultOrThrow().let { parentResult -> | ||
| SetPlaylistDownloadButtonVisibilityFingerprint.also { | ||
| it.resolve( | ||
| context, | ||
| parentResult.classDef | ||
| ) | ||
| }.resultOrThrow().let { setVisibilityMethod -> | ||
| setVisibilityMethod.mutableMethod.apply { | ||
| // Find the index of if-nez | ||
| val insertIndex = setVisibilityMethod.scanResult.patternScanResult!!.startIndex + 2 | ||
| // Get register values used in if-nez | ||
| val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA | ||
|
|
||
| // Add instructions just above the index of if-nez | ||
| addInstructions( | ||
| insertIndex, | ||
| """ | ||
| invoke-static {}, $INTEGRATIONS_DOWNLOAD_PLAYLIST_BUTTON_CLASS_DESCRIPTOR->isPlaylistDownloadButtonHooked()Z | ||
| move-result v$insertRegister | ||
| """ | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // endregion | ||
|
|
||
| // region Hook Download Playlist Button OnClick method | ||
|
|
||
| DownloadPlaylistButtonOnClickFingerprint.resultOrThrow().let { | ||
| it.mutableMethod.apply { | ||
|
|
||
| // region Get the index of the instruction that initializes the onClickListener | ||
|
|
||
| val onClickListenerInitializeIndex = indexOfFirstInstructionOrThrow { | ||
| val reference = ((this as? ReferenceInstruction)?.reference as? MethodReference) | ||
|
|
||
| opcode == Opcode.INVOKE_VIRTUAL_RANGE | ||
| && reference?.parameterTypes?.first() == "Ljava/lang/String;" | ||
| } | ||
|
|
||
| // endregion | ||
|
|
||
| // region Get the class that contains the onClick method | ||
|
|
||
| val onClickListenerInitializeReference = | ||
| getInstruction<ReferenceInstruction>(onClickListenerInitializeIndex).reference | ||
|
|
||
|
|
||
| val onClickClass = context.findClass( | ||
| (onClickListenerInitializeReference as MethodReference).returnType | ||
| )!!.mutableClass | ||
|
|
||
| // endregion | ||
|
|
||
| onClickClass.methods.find { method -> method.name == "onClick" }?.apply { | ||
|
|
||
| // region Get the index of playlist id | ||
|
|
||
| val insertIndex = implementation!!.instructions.indexOfFirst { instruction -> | ||
| instruction.opcode == Opcode.INVOKE_STATIC | ||
| && instruction.getReference<MethodReference>()?.name == "isEmpty" | ||
| } | ||
|
|
||
| val insertRegister = getInstruction<Instruction35c>(insertIndex).registerC | ||
|
|
||
| // endregion | ||
|
|
||
| addInstructions( | ||
| insertIndex, | ||
| """ | ||
| invoke-static {v$insertRegister}, $INTEGRATIONS_DOWNLOAD_PLAYLIST_BUTTON_CLASS_DESCRIPTOR->startPlaylistDownloadActivity(Ljava/lang/String;)Ljava/lang/String; | ||
|
Francesco146 marked this conversation as resolved.
|
||
| move-result-object v$insertRegister | ||
| """.trimIndent() | ||
|
Francesco146 marked this conversation as resolved.
|
||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // endregion | ||
|
|
||
| /** | ||
| * Add settings | ||
| */ | ||
| SettingsPatch.addPreference( | ||
| arrayOf( | ||
| "PREFERENCE_SCREEN: GENERAL", | ||
| "SETTINGS: HOOK_DOWNLOAD_ACTIONS" | ||
| ) | ||
| ) | ||
|
|
||
| SettingsPatch.updatePatchStatus(this) | ||
|
Francesco146 marked this conversation as resolved.
|
||
| } | ||
| } | ||
12 changes: 12 additions & 0 deletions
12
...es/youtube/misc/downloadactions/fingerprints/AccessibilityOfflineButtonSyncFingerprint.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package app.revanced.patches.youtube.misc.downloadactions.fingerprints | ||
|
|
||
| import app.revanced.patcher.extensions.or | ||
| import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.AccessibilityOfflineButtonSync | ||
| import app.revanced.util.fingerprint.LiteralValueFingerprint | ||
| import com.android.tools.smali.dexlib2.AccessFlags | ||
|
|
||
| object AccessibilityOfflineButtonSyncFingerprint : LiteralValueFingerprint( | ||
| accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, | ||
| returnType = "V", | ||
| literalSupplier = { AccessibilityOfflineButtonSync } | ||
| ) |
29 changes: 29 additions & 0 deletions
29
...hes/youtube/misc/downloadactions/fingerprints/DownloadPlaylistButtonOnClickFingerprint.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package app.revanced.patches.youtube.misc.downloadactions.fingerprints | ||
|
|
||
| import app.revanced.patcher.extensions.or | ||
| import app.revanced.patcher.fingerprint.MethodFingerprint | ||
| import app.revanced.patches.youtube.misc.downloadactions.fingerprints.DownloadPlaylistButtonOnClickFingerprint.PLAYLIST_ON_CLICK_INITIALIZE_PAREMETER | ||
| import app.revanced.util.getReference | ||
| import app.revanced.util.indexOfFirstInstruction | ||
| import com.android.tools.smali.dexlib2.AccessFlags | ||
| import com.android.tools.smali.dexlib2.Opcode | ||
| import com.android.tools.smali.dexlib2.iface.reference.MethodReference | ||
|
|
||
|
|
||
| object DownloadPlaylistButtonOnClickFingerprint : MethodFingerprint( | ||
| accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, | ||
| returnType = "V", | ||
| customFingerprint = { methodDef, _ -> | ||
| methodDef.indexOfFirstInstruction { | ||
| opcode == Opcode.INVOKE_VIRTUAL && | ||
| getReference<MethodReference>()?.parameterTypes == PLAYLIST_ON_CLICK_INITIALIZE_PAREMETER | ||
| } >= 0 | ||
| } | ||
| ) { | ||
| val PLAYLIST_ON_CLICK_INITIALIZE_PAREMETER = listOf( | ||
| "Ljava/lang/String;", | ||
| "Lcom/google/android/apps/youtube/app/offline/ui/OfflineArrowView;", | ||
| "I", | ||
| "Landroid/view/View${'$'}OnClickListener;" | ||
| ) | ||
| } |
2 changes: 1 addition & 1 deletion
2
...prints/OfflineVideoEndpointFingerprint.kt → ...prints/OfflineVideoEndpointFingerprint.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ns/fingerprints/PiPPlaybackFingerprint.kt → ...ns/fingerprints/PiPPlaybackFingerprint.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
...utube/misc/downloadactions/fingerprints/SetPlaylistDownloadButtonVisibilityFingerprint.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package app.revanced.patches.youtube.misc.downloadactions.fingerprints | ||
|
|
||
| import app.revanced.patcher.fingerprint.MethodFingerprint | ||
| import com.android.tools.smali.dexlib2.Opcode | ||
|
|
||
| object SetPlaylistDownloadButtonVisibilityFingerprint : MethodFingerprint( | ||
| returnType = "V", | ||
| opcodes = listOf( | ||
| Opcode.INVOKE_VIRTUAL, | ||
| Opcode.MOVE_RESULT, | ||
| Opcode.IF_NEZ, | ||
| Opcode.IGET, | ||
| Opcode.CONST_4 | ||
| ) | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.