From 09bce22aebc4e594063d2eec816ec9841134db31 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 21 Mar 2026 19:44:53 +0100 Subject: [PATCH] fix(YouTube - Hide Shorts components): Shorts shelves are sometimes not hidden Co-authored-by: inotia00 <108592928+inotia00@users.noreply.github.com> --- .../extension/shared/ConversionContext.java | 8 ++++ .../shared/patches/litho/Filter.java | 14 ------- .../patches/LayoutReloadObserverPatch.java | 8 ++-- .../litho/DescriptionComponentsFilter.java | 31 +++++--------- .../litho/HorizontalShelvesFilter.java | 29 +++++--------- .../patches/litho/LayoutComponentsFilter.java | 13 +----- .../youtube/patches/litho/ShortsFilter.java | 40 +++++++++++++------ 7 files changed, 62 insertions(+), 81 deletions(-) diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ConversionContext.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ConversionContext.java index ea1f94fc54..8233d8eab8 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/ConversionContext.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/ConversionContext.java @@ -9,5 +9,13 @@ public final class ConversionContext { StringBuilder patch_getPathBuilder(); String patch_getIdentifier(); + + default boolean isHomeFeedOrRelatedVideo() { + return toString().contains("horizontalCollectionSwipeProtector=null"); + } + + default boolean isSubscriptionOrLibrary() { + return toString().contains("heightConstraint=null"); + } } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java index 1cdcb7387d..fe1fb66725 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/Filter.java @@ -26,18 +26,12 @@ import java.util.List; public abstract class Filter { public enum FilterContentType { - CONTEXT, IDENTIFIER, PATH, ACCESSIBILITY, PROTOBUFFER } - /** - * Context callbacks. Do not add to this instance, - * and instead use {@link #addContextCallbacks(StringFilterGroup...)}. - */ - protected final List contextCallbacks = new ArrayList<>(); /** * Identifier callbacks. Do not add to this instance, * and instead use {@link #addIdentifierCallbacks(StringFilterGroup...)}. @@ -49,14 +43,6 @@ public abstract class Filter { */ public final List pathCallbacks = new ArrayList<>(); - /** - * Adds callbacks to {@link #isFiltered(ContextInterface, String, String, String, byte[], StringFilterGroup, FilterContentType, int)} - * if any of the groups are found. - */ - protected final void addContextCallbacks(StringFilterGroup... groups) { - contextCallbacks.addAll(Arrays.asList(groups)); - } - /** * Adds callbacks to {@link #isFiltered(ContextInterface, String, String, String, byte[], StringFilterGroup, FilterContentType, int)} * if any of the groups are found. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/LayoutReloadObserverPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/LayoutReloadObserverPatch.java index 95108882c6..6b43787d24 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/LayoutReloadObserverPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/LayoutReloadObserverPatch.java @@ -41,9 +41,11 @@ public class LayoutReloadObserverPatch { return; } - if (PlayerType.getCurrent() == PlayerType.WATCH_WHILE_MINIMIZED && - isActionBarVisible.compareAndSet(false, true)) { - Utils.runOnMainThreadDelayed(() -> isActionBarVisible.compareAndSet(true, false), 500); + PlayerType playerType = PlayerType.getCurrent(); + if (playerType == PlayerType.WATCH_WHILE_MINIMIZED || playerType == PlayerType.WATCH_WHILE_PICTURE_IN_PICTURE) { + if (isActionBarVisible.compareAndSet(false, true)) { + Utils.runOnMainThreadDelayed(() -> isActionBarVisible.compareAndSet(true, false), 250); + } } } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java index 9aedaa8f28..416ef28f39 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/DescriptionComponentsFilter.java @@ -134,26 +134,17 @@ public final class DescriptionComponentsFilter extends Filter { } @Override - public boolean isFiltered(ContextInterface contextInterface, - String identifier, - String accessibility, - String path, - byte[] buffer, - StringFilterGroup matchedGroup, - FilterContentType contentType, - int contentIndex) { - // The description panel can be opened in both the regular player and Shorts. - // If the description panel is opened in a Shorts, PlayerType is 'HIDDEN', - // so 'PlayerType.getCurrent().isMaximizedOrFullscreen()' does not guarantee that the description panel is open. - // Instead, use the engagement id to check if the description panel is opened. - if (!EngagementPanel.isDescription()) { - return false; - } - - // PlayerType when the description panel is opened: NONE, HIDDEN, - // WATCH_WHILE_MAXIMIZED, WATCH_WHILE_FULLSCREEN, WATCH_WHILE_SLIDING_MAXIMIZED_FULLSCREEN. - PlayerType playerType = PlayerType.getCurrent(); - if (!playerType.isNoneOrHidden() && !playerType.isMaximizedOrFullscreen()) { + public boolean isFiltered(ContextInterface contextInterface, + String identifier, + String accessibility, + String path, + byte[] buffer, + StringFilterGroup matchedGroup, + FilterContentType contentType, + int contentIndex) { + // Immediately after the layout is refreshed, litho components are updated before the UI is drawn. + // In this case, EngagementPanel.isDescription() cannot be used, and isActionBarVisible.get() should be used. + if (!EngagementPanel.isDescription() && !(PlayerType.getCurrent().isMaximizedOrFullscreen() || isActionBarVisible.get() || ShortsPlayerState.isOpen())) { return false; } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HorizontalShelvesFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HorizontalShelvesFilter.java index 49722901e8..6ed28b9b0d 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HorizontalShelvesFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/HorizontalShelvesFilter.java @@ -65,19 +65,16 @@ public final class HorizontalShelvesFilter extends Filter { ); } - private boolean hideShelves() { + private boolean hideShelves(ContextInterface contextInterface) { if (!Settings.HIDE_HORIZONTAL_SHELVES.get()) { return false; } - // Must check player type first, as search bar can be active behind the player. - if (PlayerType.getCurrent().isMaximizedOrFullscreen() || isActionBarVisible.get()) { - return false; - } - // Must check second, as search can be from any tab. - if (NavigationBar.isSearchBarActive()) { - return true; - } - return NavigationButton.getSelectedNavigationButton() != NavigationButton.LIBRARY; + return contextInterface.isHomeFeedOrRelatedVideo() + || PlayerType.getCurrent().isMaximizedOrFullscreen() + || isActionBarVisible.get() + || NavigationBar.isSearchBarActive() + || NavigationBar.isBackButtonVisible() + || NavigationButton.getSelectedNavigationButton() != NavigationButton.LIBRARY; } @Override @@ -95,15 +92,9 @@ public final class HorizontalShelvesFilter extends Filter { if (generalBuffers.check(buffer).isFiltered()) { return true; } - if (EngagementPanel.isDescription()) { - PlayerType playerType = PlayerType.getCurrent(); - // PlayerType when the description panel is opened: NONE, HIDDEN, - // WATCH_WHILE_MAXIMIZED, WATCH_WHILE_FULLSCREEN, WATCH_WHILE_SLIDING_MAXIMIZED_FULLSCREEN. - if (!playerType.isMaximizedOrFullscreen() && !playerType.isNoneOrHidden()) { - return false; - } - return descriptionBuffers.check(buffer).isFiltered(); + if (descriptionBuffers.check(buffer).isFiltered()) { + return EngagementPanel.isDescription() || PlayerType.getCurrent().isMaximizedOrFullscreen() || isActionBarVisible.get() || ShortsPlayerState.isOpen(); } - return hideShelves(); + return hideShelves(contextInterface); } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LayoutComponentsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LayoutComponentsFilter.java index ae243ad8af..55c1e0fb22 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LayoutComponentsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LayoutComponentsFilter.java @@ -76,7 +76,6 @@ public final class LayoutComponentsFilter extends Filter { private final StringFilterGroup chipBar; private final StringFilterGroup channelProfile; private final StringFilterGroupList channelProfileGroupList; - private final StringFilterGroupList communityPostStringFilterGroup; public LayoutComponentsFilter() { exceptions.addPatterns( @@ -142,16 +141,6 @@ public final class LayoutComponentsFilter extends Filter { "poll_post_responsive_root.e", "shared_post_root.e" ); - communityPostStringFilterGroup = new StringFilterGroupList(); - communityPostStringFilterGroup.addAll( - new StringFilterGroup( - null, - // home - "horizontalCollectionSwipeProtector=null", - // subscriptions - "heightConstraint=null" - ) - ); final var subscribersCommunityGuidelines = new StringFilterGroup( Settings.HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES, @@ -419,7 +408,7 @@ public final class LayoutComponentsFilter extends Filter { } if (matchedGroup == communityPosts) { - return communityPostStringFilterGroup.check(contextInterface.toString()).isFiltered(); + return contextInterface.isHomeFeedOrRelatedVideo() || contextInterface.isSubscriptionOrLibrary(); } if (exceptions.matches(path)) return false; // Exceptions are not filtered. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ShortsFilter.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ShortsFilter.java index 0da4d4edfe..94b69c6cae 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ShortsFilter.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/ShortsFilter.java @@ -56,6 +56,7 @@ public final class ShortsFilter extends Filter { private final StringFilterGroup shortsCompactFeedVideo; private final ByteArrayFilterGroup shortsCompactFeedVideoBuffer; private final StringFilterGroup channelProfile; + private final ByteArrayFilterGroup channelProfileShelfHeader; private final StringFilterGroup autoDubbedLabel; private final StringFilterGroup subscribeButton; @@ -95,6 +96,11 @@ public final class ShortsFilter extends Filter { "shorts_pivot_item" ); + channelProfileShelfHeader = new ByteArrayFilterGroup( + Settings.HIDE_SHORTS_CHANNEL, + "Shorts" + ); + // Feed Shorts shelf header. // Use a different filter group for this pattern, as it requires an additional check after matching. shelfHeaderIdentifier = new StringFilterGroup( @@ -412,8 +418,12 @@ public final class ShortsFilter extends Filter { if (contentIndex != 0) { return false; } - } - if (matchedGroup == channelProfile) { + // Check ConversationContext to not hide shelf header in channel profile + // This value does not exist in the shelf header in the channel profile + if (!contextInterface.isHomeFeedOrRelatedVideo()) { + return false; + } + } else if (matchedGroup == channelProfile) { return true; } @@ -433,7 +443,15 @@ public final class ShortsFilter extends Filter { } if (matchedGroup == shortsCompactFeedVideo) { - return shouldHideShortsFeedItems() && shortsCompactFeedVideoBuffer.check(buffer).isFiltered(); + return shouldHideShortsFeedItems() + && shortsCompactFeedVideoBuffer.check(buffer).isFiltered() + // The litho path of the feed video is 'video_lockup_with_attachment.e'. + // It appears shortsCompactFeedVideoBuffer is used after 20 seconds during autoplay in the feed in YouTube 20.44.38. + // If the Shorts shelf is hidden on the Home feed, the video in the feed will be hidden after 20 seconds have passed since autoplay began in the feed. + // + // When a video is autoplaying in the feed, no new components are drawn on the screen. + // Therefore, filtering is skipped when the current PlayerType is [INLINE_MINIMAL]. + && PlayerType.getCurrent() != PlayerType.INLINE_MINIMAL; } if (matchedGroup == shelfHeaderPath) { @@ -442,6 +460,11 @@ public final class ShortsFilter extends Filter { if (contentIndex != 0) { return false; } + // Check ConversationContext to not hide shelf header in channel profile + // This value does not exist in the shelf header in the channel profile + if (!contextInterface.isHomeFeedOrRelatedVideo()) { + return channelProfileShelfHeader.check(buffer).isFiltered(); + } return shouldHideShortsFeedItems(); } @@ -456,7 +479,7 @@ public final class ShortsFilter extends Filter { } if (matchedGroup == useButtons) { - return path.contains("button.e") && useButtonsBuffer.check(buffer).isFiltered(); + return path.contains("|button.e") && useButtonsBuffer.check(buffer).isFiltered(); } if (matchedGroup == suggestedAction) { @@ -493,15 +516,6 @@ public final class ShortsFilter extends Filter { if (!hideHome && !hideSubscriptions && !hideSearch && !hideVideoDescription && !hideHistory) { return false; } - // The Litho path of the feed video is 'video_lockup_with_attachment.e'. - // It appears shortsCompactFeedVideoBuffer is used after 20 seconds during autoplay in the feed in YouTube 20.44.38. - // If the Shorts shelf is hidden on the Home feed, the video in the feed will be hidden after 20 seconds have passed since autoplay began in the feed. - // - // When a video is autoplaying in the feed, no new components are drawn on the screen. - // Therefore, filtering is skipped when the current PlayerType is INLINE_MINIMAL. - if (PlayerType.getCurrent() == PlayerType.INLINE_MINIMAL) { - return false; - } if (hideHome && hideSubscriptions && hideSearch && hideVideoDescription && hideHistory) { return true; }