From 389f414acc59b4c3f68281d612764a380f3855f3 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Fri, 27 Sep 2024 19:52:29 +0900 Subject: [PATCH] feat(YouTube/Hide feed components): add `Hide related videos` setting --- .../feed/components/FeedComponentsPatch.kt | 68 ++++++++++++++++++- .../EngagementPanelUpdateFingerprint.kt | 21 ++++++ ...inearLayoutManagerItemCountsFingerprint.kt | 19 ++++++ .../youtube/settings/host/values/strings.xml | 7 ++ .../youtube/settings/xml/revanced_prefs.xml | 3 + 5 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/EngagementPanelUpdateFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/LinearLayoutManagerItemCountsFingerprint.kt diff --git a/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt index a896aef65..4e8973778 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt @@ -7,6 +7,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.litho.LithoFilterPatch import app.revanced.patches.youtube.feed.components.fingerprints.BreakingNewsFingerprint @@ -19,32 +20,41 @@ import app.revanced.patches.youtube.feed.components.fingerprints.ChannelTabBuild import app.revanced.patches.youtube.feed.components.fingerprints.ChannelTabRendererFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.ElementParserFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.ElementParserParentFingerprint +import app.revanced.patches.youtube.feed.components.fingerprints.EngagementPanelUpdateFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.FilterBarHeightFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.LatestVideosButtonFingerprint +import app.revanced.patches.youtube.feed.components.fingerprints.LinearLayoutManagerItemCountsFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.RelatedChipCloudFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.SearchResultsChipBarFingerprint import app.revanced.patches.youtube.feed.components.fingerprints.ShowMoreButtonFingerprint import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE +import app.revanced.patches.youtube.utils.fingerprints.EngagementPanelBuilderFingerprint import app.revanced.patches.youtube.utils.fingerprints.ScrollTopParentFingerprint import app.revanced.patches.youtube.utils.integrations.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.integrations.Constants.FEED_CLASS_DESCRIPTOR +import app.revanced.patches.youtube.utils.integrations.Constants.FEED_PATH import app.revanced.patches.youtube.utils.navigation.NavigationBarHookPatch import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.CaptionToggleContainer import app.revanced.patches.youtube.utils.settings.SettingsPatch +import app.revanced.util.alsoResolve import app.revanced.util.getReference +import app.revanced.util.getWalkerMethod +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow 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.Method 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.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil @Suppress("unused") object FeedComponentsPatch : BaseBytecodePatch( @@ -67,12 +77,14 @@ object FeedComponentsPatch : BaseBytecodePatch( ChannelListSubMenuTabletSyntheticFingerprint, ChannelTabRendererFingerprint, ElementParserParentFingerprint, + EngagementPanelBuilderFingerprint, FilterBarHeightFingerprint, LatestVideosButtonFingerprint, + LinearLayoutManagerItemCountsFingerprint, RelatedChipCloudFingerprint, ScrollTopParentFingerprint, SearchResultsChipBarFingerprint, - ShowMoreButtonFingerprint + ShowMoreButtonFingerprint, ) ) { private const val CAROUSEL_SHELF_FILTER_CLASS_DESCRIPTOR = @@ -85,6 +97,8 @@ object FeedComponentsPatch : BaseBytecodePatch( "$COMPONENTS_PATH/FeedVideoViewsFilter;" private const val KEYWORD_FILTER_CLASS_DESCRIPTOR = "$COMPONENTS_PATH/KeywordContentFilter;" + private const val RELATED_VIDEO_CLASS_DESCRIPTOR = + "$FEED_PATH/RelatedVideoPatch;" override fun execute(context: BytecodeContext) { @@ -139,6 +153,58 @@ object FeedComponentsPatch : BaseBytecodePatch( // endregion + // region patch for hide relative video + + fun Method.indexOfEngagementPanelBuilderInstruction(targetMethod: MutableMethod) = + indexOfFirstInstruction { + opcode == Opcode.INVOKE_DIRECT && + MethodUtil.methodSignaturesMatch(targetMethod, getReference()!!) + } + + EngagementPanelBuilderFingerprint.resultOrThrow().let { + it.mutableClass.methods.filter { method -> + method.indexOfEngagementPanelBuilderInstruction(it.mutableMethod) >= 0 + }.forEach { method -> + method.apply { + val index = indexOfEngagementPanelBuilderInstruction(it.mutableMethod) + val register = getInstruction(index + 1).registerA + + addInstruction( + index + 2, + "invoke-static {v$register}, " + + "$RELATED_VIDEO_CLASS_DESCRIPTOR->showEngagementPanel(Ljava/lang/Object;)V" + ) + } + } + } + + EngagementPanelUpdateFingerprint.alsoResolve( + context, EngagementPanelBuilderFingerprint + ).mutableMethod.addInstruction( + 0, + "invoke-static {}, $RELATED_VIDEO_CLASS_DESCRIPTOR->hideEngagementPanel()V" + ) + + // BytecodeUtils.getWalkerMethod must be used here + // Otherwise, MethodWalker finds the wrong class in YouTube 18.29.38: + // https://github.com/ReVanced/revanced-patcher/issues/309 + LinearLayoutManagerItemCountsFingerprint.resultOrThrow().let { + val methodWalker = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.endIndex) + methodWalker.apply { + val index = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT) + val register = getInstruction(index).registerA + + addInstructions( + index + 1, """ + invoke-static {v$register}, $RELATED_VIDEO_CLASS_DESCRIPTOR->overrideItemCounts(I)I + move-result v$register + """ + ) + } + } + + // endregion + // region patch for hide subscriptions channel section for tablet arrayOf( diff --git a/src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/EngagementPanelUpdateFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/EngagementPanelUpdateFingerprint.kt new file mode 100644 index 000000000..4d378d659 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/EngagementPanelUpdateFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.feed.components.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +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 + +internal object EngagementPanelUpdateFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + parameters = listOf("L", "Z"), + customFingerprint = { methodDef, _ -> + methodDef.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Ljava/util/ArrayDeque;->pop()Ljava/lang/Object;" + } >= 0 + } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/LinearLayoutManagerItemCountsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/LinearLayoutManagerItemCountsFingerprint.kt new file mode 100644 index 000000000..0a4dc4688 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/feed/components/fingerprints/LinearLayoutManagerItemCountsFingerprint.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.youtube.feed.components.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal object LinearLayoutManagerItemCountsFingerprint : MethodFingerprint( + returnType = "I", + accessFlags = AccessFlags.FINAL.value, + parameters = listOf("L", "L", "L", "Z"), + opcodes = listOf( + Opcode.IF_NEZ, + Opcode.IF_LEZ, + Opcode.INVOKE_VIRTUAL, + ), + customFingerprint = { methodDef, _ -> + methodDef.definingClass == "Landroid/support/v7/widget/LinearLayoutManager;" + } +) diff --git a/src/main/resources/youtube/settings/host/values/strings.xml b/src/main/resources/youtube/settings/host/values/strings.xml index db3113250..3b702897a 100644 --- a/src/main/resources/youtube/settings/host/values/strings.xml +++ b/src/main/resources/youtube/settings/host/values/strings.xml @@ -304,6 +304,13 @@ Note: Enabling this also hide Notify me button." Limitations: • Shorts cannot be hidden. • Videos with 0 views are not filtered." + Hide related videos + Related videos are hidden. + Related videos are shown. + "This setting limits the maximum number of layouts that can be loaded on the player screen. + +If the layout of the player screen changes due to server-side changes, unintended layouts may be hidden on the player screen." + General diff --git a/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/src/main/resources/youtube/settings/xml/revanced_prefs.xml index 225b35a18..f41080a10 100644 --- a/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -95,6 +95,9 @@ + + +