fix(YouTube - Hide Shorts components): Shorts shelves are sometimes not hidden

Co-authored-by: inotia00 <108592928+inotia00@users.noreply.github.com>
This commit is contained in:
oSumAtrIX 2026-03-21 19:44:53 +01:00
parent e52114076e
commit 09bce22aeb
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
7 changed files with 62 additions and 81 deletions

View file

@ -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");
}
}
}

View file

@ -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<StringFilterGroup> 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<StringFilterGroup> 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.

View file

@ -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);
}
}
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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.

View file

@ -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;
}