mirror of
https://github.com/revanced/revanced-integrations.git
synced 2025-01-21 01:07:32 +01:00
refactor(YouTube - Litho filter): Simplify filtering callbacks (#539)
This commit is contained in:
parent
56cf001964
commit
aedb3eddd6
@ -1,6 +1,5 @@
|
||||
package app.revanced.integrations.patches.components;
|
||||
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@ -9,7 +8,7 @@ import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.ReVancedUtils;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class AdsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private final StringFilterGroup shoppingLinks;
|
||||
@ -23,6 +22,16 @@ public final class AdsFilter extends Filter {
|
||||
"library_recent_shelf"
|
||||
);
|
||||
|
||||
// Identifiers.
|
||||
|
||||
final var carouselAd = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_GENERAL_ADS,
|
||||
"carousel_ad"
|
||||
);
|
||||
addIdentifierCallbacks(carouselAd);
|
||||
|
||||
// Paths.
|
||||
|
||||
final var buttonedAd = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_BUTTONED_ADS,
|
||||
"_buttoned_layout",
|
||||
@ -61,11 +70,6 @@ public final class AdsFilter extends Filter {
|
||||
"offer_module_root"
|
||||
);
|
||||
|
||||
final var carouselAd = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_GENERAL_ADS,
|
||||
"carousel_ad"
|
||||
);
|
||||
|
||||
final var viewProducts = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_PRODUCTS_BANNER,
|
||||
"product_item",
|
||||
@ -92,7 +96,7 @@ public final class AdsFilter extends Filter {
|
||||
"cta_shelf_card"
|
||||
);
|
||||
|
||||
this.pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
generalAds,
|
||||
buttonedAd,
|
||||
merchandise,
|
||||
@ -102,20 +106,19 @@ public final class AdsFilter extends Filter {
|
||||
shoppingLinks,
|
||||
movieAds
|
||||
);
|
||||
this.identifierFilterGroupList.addAll(carouselAd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
if (exceptions.matches(path))
|
||||
return false;
|
||||
|
||||
// Check for the index because of likelihood of false positives.
|
||||
if (matchedGroup == shoppingLinks && matchedIndex != 0)
|
||||
if (matchedGroup == shoppingLinks && contentIndex != 0)
|
||||
return false;
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,7 @@ import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
final class ButtonsFilter extends Filter {
|
||||
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
|
||||
@ -20,14 +21,14 @@ final class ButtonsFilter extends Filter {
|
||||
null,
|
||||
VIDEO_ACTION_BAR_PATH
|
||||
);
|
||||
identifierFilterGroupList.addAll(actionBarGroup);
|
||||
addIdentifierCallbacks(actionBarGroup);
|
||||
|
||||
|
||||
bufferFilterPathGroup = new StringFilterGroup(
|
||||
null,
|
||||
"|CellType|CollectionType|CellType|ContainerType|button.eml|"
|
||||
);
|
||||
pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
new StringFilterGroup(
|
||||
SettingsEnum.HIDE_LIKE_DISLIKE_BUTTON,
|
||||
"|segmented_like_dislike_button"
|
||||
@ -48,33 +49,33 @@ final class ButtonsFilter extends Filter {
|
||||
);
|
||||
|
||||
bufferButtonsGroupList.addAll(
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_LIVE_CHAT_BUTTON,
|
||||
"yt_outline_message_bubble_overlap"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_REPORT_BUTTON,
|
||||
"yt_outline_flag"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_SHARE_BUTTON,
|
||||
"yt_outline_share"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_REMIX_BUTTON,
|
||||
"yt_outline_youtube_shorts_plus"
|
||||
),
|
||||
// Check for clip button both here and using a path filter,
|
||||
// as there's a chance the path is a generic action button and won't contain 'clip_button'
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_CLIP_BUTTON,
|
||||
"yt_outline_scissors"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_SHOP_BUTTON,
|
||||
"yt_outline_bag"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart"
|
||||
)
|
||||
@ -82,7 +83,7 @@ final class ButtonsFilter extends Filter {
|
||||
}
|
||||
|
||||
private boolean isEveryFilterGroupEnabled() {
|
||||
for (var group : pathFilterGroupList)
|
||||
for (var group : pathCallbacks)
|
||||
if (!group.isEnabled()) return false;
|
||||
|
||||
for (var group : bufferButtonsGroupList)
|
||||
@ -93,7 +94,7 @@ final class ButtonsFilter extends Filter {
|
||||
|
||||
@Override
|
||||
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
// If the current matched group is the action bar group,
|
||||
// in case every filter group is enabled, hide the action bar.
|
||||
if (matchedGroup == actionBarGroup) {
|
||||
@ -109,6 +110,6 @@ final class ButtonsFilter extends Filter {
|
||||
if (!bufferButtonsGroupList.check(protobufBufferArray).isFiltered()) return false;
|
||||
}
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package app.revanced.integrations.patches.components;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final class CommentsFilter extends Filter {
|
||||
|
||||
public CommentsFilter() {
|
||||
@ -18,7 +19,7 @@ final class CommentsFilter extends Filter {
|
||||
"comments_entry_point_simplebox"
|
||||
);
|
||||
|
||||
this.pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
comments,
|
||||
previewComment
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ import androidx.annotation.Nullable;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final class DescriptionComponentsFilter extends Filter {
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
@ -48,7 +49,7 @@ final class DescriptionComponentsFilter extends Filter {
|
||||
"transcript_section"
|
||||
);
|
||||
|
||||
pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
chapterSection,
|
||||
infoCardsSection,
|
||||
gameSection,
|
||||
@ -61,9 +62,9 @@ final class DescriptionComponentsFilter extends Filter {
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
if (exceptions.matches(path)) return false;
|
||||
|
||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package app.revanced.integrations.patches.components;
|
||||
|
||||
final class DummyFilter extends Filter { }
|
@ -2,10 +2,11 @@ package app.revanced.integrations.patches.components;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class HideInfoCardsFilterPatch extends Filter {
|
||||
|
||||
public HideInfoCardsFilterPatch() {
|
||||
identifierFilterGroupList.addAll(
|
||||
addIdentifierCallbacks(
|
||||
new StringFilterGroup(
|
||||
SettingsEnum.HIDE_INFO_CARDS,
|
||||
"info_card_teaser_overlay.eml"
|
||||
|
@ -1,24 +1,26 @@
|
||||
package app.revanced.integrations.patches.components;
|
||||
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.utils.LogHelper;
|
||||
import app.revanced.integrations.utils.StringTrieSearch;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch();
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylistsExceptions2 = new ByteArrayAsStringFilterGroup(
|
||||
private static final ByteArrayFilterGroup mixPlaylistsExceptions2 = new ByteArrayFilterGroup(
|
||||
null,
|
||||
"cell_description_body"
|
||||
);
|
||||
private final CustomFilterGroup custom;
|
||||
|
||||
private static final ByteArrayAsStringFilterGroup mixPlaylists = new ByteArrayAsStringFilterGroup(
|
||||
private static final ByteArrayFilterGroup mixPlaylists = new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_MIX_PLAYLISTS,
|
||||
"&list="
|
||||
);
|
||||
@ -44,6 +46,25 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"library_recent_shelf"
|
||||
);
|
||||
|
||||
// Identifiers.
|
||||
|
||||
final var graySeparator = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_GRAY_SEPARATOR,
|
||||
"cell_divider" // layout residue (gray line above the buttoned ad),
|
||||
);
|
||||
|
||||
final var chipsShelf = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHIPS_SHELF,
|
||||
"chips_shelf"
|
||||
);
|
||||
|
||||
addIdentifierCallbacks(
|
||||
graySeparator,
|
||||
chipsShelf
|
||||
);
|
||||
|
||||
// Paths.
|
||||
|
||||
custom = new CustomFilterGroup(
|
||||
SettingsEnum.CUSTOM_FILTER,
|
||||
SettingsEnum.CUSTOM_FILTER_STRINGS
|
||||
@ -155,10 +176,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"image_shelf"
|
||||
);
|
||||
|
||||
final var graySeparator = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_GRAY_SEPARATOR,
|
||||
"cell_divider" // layout residue (gray line above the buttoned ad),
|
||||
);
|
||||
|
||||
final var timedReactions = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_TIMED_REACTIONS,
|
||||
@ -181,11 +198,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"compact_sponsor_button"
|
||||
);
|
||||
|
||||
final var chipsShelf = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_CHIPS_SHELF,
|
||||
"chips_shelf"
|
||||
);
|
||||
|
||||
final var channelWatermark = new StringFilterGroup(
|
||||
SettingsEnum.HIDE_VIDEO_CHANNEL_WATERMARK,
|
||||
"featured_channel_watermark_overlay"
|
||||
@ -196,7 +208,11 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"mixed_content_shelf"
|
||||
);
|
||||
|
||||
this.pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
custom,
|
||||
expandableMetadata,
|
||||
inFeedSurvey,
|
||||
notifyMe,
|
||||
channelBar,
|
||||
communityPosts,
|
||||
paidContent,
|
||||
@ -204,13 +220,10 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
channelWatermark,
|
||||
communityGuidelines,
|
||||
quickActions,
|
||||
expandableMetadata,
|
||||
relatedVideos,
|
||||
compactBanner,
|
||||
inFeedSurvey,
|
||||
joinMembership,
|
||||
medicalPanel,
|
||||
notifyMe,
|
||||
videoQualityMenuFooter,
|
||||
infoPanel,
|
||||
subscribersCommunityGuidelines,
|
||||
@ -220,32 +233,26 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
timedReactions,
|
||||
imageShelf,
|
||||
channelMemberShelf,
|
||||
forYouShelf,
|
||||
custom
|
||||
);
|
||||
|
||||
this.identifierFilterGroupList.addAll(
|
||||
graySeparator,
|
||||
chipsShelf
|
||||
forYouShelf
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
|
||||
// The groups are excluded from the filter due to the exceptions list below.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
|
||||
if (matchedGroup != custom && exceptions.matches(path))
|
||||
return false; // Exceptions are not filtered.
|
||||
|
||||
// TODO: This also hides the feed Shorts shelf header
|
||||
if (matchedGroup == searchResultShelfHeader && matchedIndex != 0) return false;
|
||||
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,6 +195,14 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||
super(setting, filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Strings into byte arrays. Used to search for text in binary data.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public ByteArrayFilterGroup(SettingsEnum setting, String... filters) {
|
||||
super(setting, Arrays.stream(filters).map(String::getBytes).toArray(byte[][]::new));
|
||||
}
|
||||
|
||||
private synchronized void buildFailurePatterns() {
|
||||
if (failurePatterns != null) return; // Thread race and another thread already initialized the search.
|
||||
LogHelper.printDebug(() -> "Building failure array for: " + this);
|
||||
@ -211,12 +219,14 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||
int matchedLength = 0;
|
||||
int matchedIndex = -1;
|
||||
if (isEnabled()) {
|
||||
if (failurePatterns == null) {
|
||||
int[][] failures = failurePatterns;
|
||||
if (failures == null) {
|
||||
buildFailurePatterns(); // Lazy load.
|
||||
failures = failurePatterns;
|
||||
}
|
||||
for (int i = 0, length = filters.length; i < length; i++) {
|
||||
byte[] filter = filters[i];
|
||||
matchedIndex = indexOf(bytes, filter, failurePatterns[i]);
|
||||
matchedIndex = indexOf(bytes, filter, failures[i]);
|
||||
if (matchedIndex >= 0) {
|
||||
matchedLength = filter.length;
|
||||
break;
|
||||
@ -228,34 +238,16 @@ class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||
}
|
||||
|
||||
|
||||
final class ByteArrayAsStringFilterGroup extends ByteArrayFilterGroup {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public ByteArrayAsStringFilterGroup(SettingsEnum setting, String... filters) {
|
||||
super(setting, Arrays.stream(filters).map(String::getBytes).toArray(byte[][]::new));
|
||||
}
|
||||
}
|
||||
|
||||
abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
||||
|
||||
private final List<T> filterGroups = new ArrayList<>();
|
||||
/**
|
||||
* Search graph. Created only if needed.
|
||||
*/
|
||||
private volatile TrieSearch<V> search;
|
||||
private final TrieSearch<V> search = createSearchGraph();
|
||||
|
||||
@SafeVarargs
|
||||
protected final void addAll(final T... groups) {
|
||||
filterGroups.addAll(Arrays.asList(groups));
|
||||
search = null; // Rebuild, if already created.
|
||||
}
|
||||
|
||||
protected final synchronized void buildSearch() {
|
||||
// Since litho filtering is multi-threaded, this method can be concurrently called by multiple threads.
|
||||
if (search != null) return; // Thread race and another thread already initialized the search.
|
||||
LogHelper.printDebug(() -> "Creating prefix search tree for: " + this);
|
||||
TrieSearch<V> search = createSearchGraph();
|
||||
for (T group : filterGroups) {
|
||||
for (T group : groups) {
|
||||
if (!group.includeInSearch()) {
|
||||
continue;
|
||||
}
|
||||
@ -270,7 +262,6 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
||||
});
|
||||
}
|
||||
}
|
||||
this.search = search; // Must set after it's completely initialized.
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -293,9 +284,6 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
||||
}
|
||||
|
||||
protected FilterGroup.FilterGroupResult check(V stack) {
|
||||
if (search == null) {
|
||||
buildSearch(); // Lazy load.
|
||||
}
|
||||
FilterGroup.FilterGroupResult result = new FilterGroup.FilterGroupResult();
|
||||
search.matches(stack, result);
|
||||
return result;
|
||||
@ -322,42 +310,90 @@ final class ByteArrayFilterGroupList extends FilterGroupList<byte[], ByteArrayFi
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Filter {
|
||||
/**
|
||||
* All group filters must be set before the constructor call completes.
|
||||
* Otherwise {@link #isFiltered(String, String, byte[], FilterGroupList, FilterGroup, int)}
|
||||
* will never be called for any matches.
|
||||
/**
|
||||
* Filters litho based components.
|
||||
*
|
||||
* Callbacks to filter content are added using {@link #addIdentifierCallbacks(StringFilterGroup...)}
|
||||
* and {@link #addPathCallbacks(StringFilterGroup...)}.
|
||||
*
|
||||
* To filter {@link FilterContentType#PROTOBUFFER}, first add a callback to
|
||||
* either an identifier or a path.
|
||||
* Then inside {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
||||
* search for the buffer content using either a {@link ByteArrayFilterGroup} (if searching for 1 pattern)
|
||||
* or a {@link ByteArrayFilterGroupList} (if searching for more than 1 pattern).
|
||||
*
|
||||
* All callbacks must be registered before the constructor completes.
|
||||
*/
|
||||
abstract class Filter {
|
||||
|
||||
protected final StringFilterGroupList pathFilterGroupList = new StringFilterGroupList();
|
||||
protected final StringFilterGroupList identifierFilterGroupList = new StringFilterGroupList();
|
||||
public enum FilterContentType {
|
||||
IDENTIFIER,
|
||||
PATH,
|
||||
PROTOBUFFER
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier callbacks. Do not add to this instance,
|
||||
* and instead use {@link #addIdentifierCallbacks(StringFilterGroup...)}.
|
||||
*/
|
||||
protected final List<StringFilterGroup> identifierCallbacks = new ArrayList<>();
|
||||
/**
|
||||
* Path callbacks. Do not add to this instance,
|
||||
* and instead use {@link #addPathCallbacks(StringFilterGroup...)}.
|
||||
*/
|
||||
protected final List<StringFilterGroup> pathCallbacks = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Adds callbacks to {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
||||
* if any of the groups are found.
|
||||
*/
|
||||
protected final void addIdentifierCallbacks(StringFilterGroup... groups) {
|
||||
identifierCallbacks.addAll(Arrays.asList(groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds callbacks to {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
|
||||
* if any of the groups are found.
|
||||
*/
|
||||
protected final void addPathCallbacks(StringFilterGroup... groups) {
|
||||
pathCallbacks.addAll(Arrays.asList(groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after an enabled filter has been matched.
|
||||
* Default implementation is to always filter the matched item.
|
||||
* Default implementation is to always filter the matched component and log the action.
|
||||
* Subclasses can perform additional or different checks if needed.
|
||||
* <p>
|
||||
* If the content is to be filtered, subclasses should always
|
||||
* call this method (and never return a plain 'true').
|
||||
* That way the logs will always show when a component was filtered and which filter hide it.
|
||||
* <p>
|
||||
* Method is called off the main thread.
|
||||
*
|
||||
* @param matchedList The list the group filter belongs to.
|
||||
* @param matchedGroup The actual filter that matched.
|
||||
* @param matchedIndex Matched index of string/array.
|
||||
* @return True if the litho item should be filtered out.
|
||||
* @param contentType The type of content matched.
|
||||
* @param contentIndex Matched index of the identifier or path.
|
||||
* @return True if the litho component should be filtered out.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
if (SettingsEnum.DEBUG.getBoolean()) {
|
||||
if (matchedList == identifierFilterGroupList) {
|
||||
LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered identifier: " + identifier);
|
||||
String filterSimpleName = getClass().getSimpleName();
|
||||
if (contentType == FilterContentType.IDENTIFIER) {
|
||||
LogHelper.printDebug(() -> filterSimpleName + " Filtered identifier: " + identifier);
|
||||
} else {
|
||||
LogHelper.printDebug(() -> getClass().getSimpleName() + " Filtered path: " + path);
|
||||
LogHelper.printDebug(() -> filterSimpleName + " Filtered path: " + path);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder for actual filters.
|
||||
*/
|
||||
final class DummyFilter extends Filter { }
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings("unused")
|
||||
public final class LithoFilterPatch {
|
||||
@ -437,8 +473,10 @@ public final class LithoFilterPatch {
|
||||
|
||||
static {
|
||||
for (Filter filter : filters) {
|
||||
filterGroupLists(identifierSearchTree, filter, filter.identifierFilterGroupList);
|
||||
filterGroupLists(pathSearchTree, filter, filter.pathFilterGroupList);
|
||||
filterUsingCallbacks(identifierSearchTree, filter,
|
||||
filter.identifierCallbacks, Filter.FilterContentType.IDENTIFIER);
|
||||
filterUsingCallbacks(pathSearchTree, filter,
|
||||
filter.pathCallbacks, Filter.FilterContentType.PATH);
|
||||
}
|
||||
|
||||
LogHelper.printDebug(() -> "Using: "
|
||||
@ -448,18 +486,19 @@ public final class LithoFilterPatch {
|
||||
+ " (" + pathSearchTree.getEstimatedMemorySize() + " KB)");
|
||||
}
|
||||
|
||||
private static <T> void filterGroupLists(TrieSearch<T> pathSearchTree,
|
||||
Filter filter, FilterGroupList<T, ? extends FilterGroup<T>> list) {
|
||||
for (FilterGroup<T> group : list) {
|
||||
private static void filterUsingCallbacks(StringTrieSearch pathSearchTree,
|
||||
Filter filter, List<StringFilterGroup> groups,
|
||||
Filter.FilterContentType type) {
|
||||
for (StringFilterGroup group : groups) {
|
||||
if (!group.includeInSearch()) {
|
||||
continue;
|
||||
}
|
||||
for (T pattern : group.filters) {
|
||||
for (String pattern : group.filters) {
|
||||
pathSearchTree.addPattern(pattern, (textSearched, matchedStartIndex, matchedLength, callbackParameter) -> {
|
||||
if (!group.isEnabled()) return false;
|
||||
LithoFilterParameters parameters = (LithoFilterParameters) callbackParameter;
|
||||
return filter.isFiltered(parameters.identifier, parameters.path, parameters.protoBuffer,
|
||||
list, group, matchedStartIndex);
|
||||
group, type, matchedStartIndex);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter {
|
||||
public static volatile boolean isPlaybackSpeedMenuVisible;
|
||||
|
||||
public PlaybackSpeedMenuFilterPatch() {
|
||||
pathFilterGroupList.addAll(new StringFilterGroup(
|
||||
addPathCallbacks(new StringFilterGroup(
|
||||
null,
|
||||
"playback_speed_sheet_content.eml-js"
|
||||
));
|
||||
@ -20,7 +20,7 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter {
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
isPlaybackSpeedMenuVisible = true;
|
||||
|
||||
return false;
|
||||
|
@ -8,65 +8,64 @@ import androidx.annotation.RequiresApi;
|
||||
import app.revanced.integrations.settings.SettingsEnum;
|
||||
import app.revanced.integrations.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
|
||||
// Search the buffer only if the flyout menu path is found.
|
||||
// Handle the searching in this class instead of adding to the global filter group (which searches all the time)
|
||||
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
private final ByteArrayFilterGroup exception;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public PlayerFlyoutMenuItemsFilter() {
|
||||
exception = new ByteArrayAsStringFilterGroup(
|
||||
exception = new ByteArrayFilterGroup(
|
||||
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled
|
||||
SettingsEnum.HIDE_ADDITIONAL_SETTINGS_MENU,
|
||||
"quality_sheet"
|
||||
);
|
||||
|
||||
// Using pathFilterGroupList due to new flyout panel(A/B)
|
||||
pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
new StringFilterGroup(null, "overflow_menu_item.eml|")
|
||||
);
|
||||
|
||||
flyoutFilterGroupList.addAll(
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_CAPTIONS_MENU,
|
||||
"closed_caption"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_ADDITIONAL_SETTINGS_MENU,
|
||||
"yt_outline_gear"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_LOOP_VIDEO_MENU,
|
||||
"yt_outline_arrow_repeat_1_"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_AMBIENT_MODE_MENU,
|
||||
"yt_outline_screen_light"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_REPORT_MENU,
|
||||
"yt_outline_flag"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_HELP_MENU,
|
||||
"yt_outline_question_circle"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_MORE_INFO_MENU,
|
||||
"yt_outline_info_circle"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_SPEED_MENU,
|
||||
"yt_outline_play_arrow_half_circle"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_AUDIO_TRACK_MENU,
|
||||
"yt_outline_person_radar"
|
||||
),
|
||||
new ByteArrayAsStringFilterGroup(
|
||||
new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_WATCH_IN_VR_MENU,
|
||||
"yt_outline_vr"
|
||||
)
|
||||
@ -75,15 +74,15 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
// Shorts also use this player flyout panel
|
||||
if (PlayerType.getCurrent().isNoneOrHidden() || exception.check(protobufBufferArray).isFiltered())
|
||||
return false;
|
||||
|
||||
// Only 1 group is added to the parent class, so the matched group must be the overflow menu.
|
||||
if (matchedIndex == 0 && flyoutFilterGroupList.check(protobufBufferArray).isFiltered()) {
|
||||
// Only 1 path callback was added, so the matched group must be the overflow menu.
|
||||
if (contentIndex == 0 && flyoutFilterGroupList.check(protobufBufferArray).isFiltered()) {
|
||||
// Super class handles logging.
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -71,21 +71,21 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
|
||||
private final ByteArrayFilterGroupList videoIdFilterGroup = new ByteArrayFilterGroupList();
|
||||
|
||||
public ReturnYouTubeDislikeFilterPatch() {
|
||||
pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
new StringFilterGroup(SettingsEnum.RYD_SHORTS, "|shorts_dislike_button.eml|")
|
||||
);
|
||||
// After the dislikes icon name is some binary data and then the video id for that specific short.
|
||||
videoIdFilterGroup.addAll(
|
||||
// Video was previously disliked before video was opened.
|
||||
new ByteArrayAsStringFilterGroup(null, "ic_right_dislike_on_shadowed"),
|
||||
new ByteArrayFilterGroup(null, "ic_right_dislike_on_shadowed"),
|
||||
// Video was not already disliked.
|
||||
new ByteArrayAsStringFilterGroup(null, "ic_right_dislike_off_shadowed")
|
||||
new ByteArrayFilterGroup(null, "ic_right_dislike_off_shadowed")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
FilterGroup.FilterGroupResult result = videoIdFilterGroup.check(protobufBufferArray);
|
||||
if (result.isFiltered()) {
|
||||
String matchedVideoId = findVideoId(protobufBufferArray);
|
||||
@ -112,7 +112,7 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
|
||||
}
|
||||
|
||||
/**
|
||||
* This could use {@link TrieSearch}, but since the video ids are constantly changing
|
||||
* This could use {@link TrieSearch}, but since the patterns are constantly changing
|
||||
* the overhead of updating the Trie might negate the search performance gain.
|
||||
*/
|
||||
private static boolean byteArrayContainsString(@NonNull byte[] array, @NonNull String text) {
|
||||
|
@ -10,7 +10,7 @@ import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar;
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.hideViewBy1dpUnderCondition;
|
||||
import static app.revanced.integrations.utils.ReVancedUtils.hideViewUnderCondition;
|
||||
|
||||
/** @noinspection unused*/
|
||||
@SuppressWarnings("unused")
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public final class ShortsFilter extends Filter {
|
||||
public static PivotBar pivotBar; // Set by patch.
|
||||
@ -49,7 +49,7 @@ public final class ShortsFilter extends Filter {
|
||||
"suggested_action"
|
||||
);
|
||||
|
||||
identifierFilterGroupList.addAll(shorts, shelfHeader, thanksButton);
|
||||
addIdentifierCallbacks(shorts, shelfHeader, thanksButton);
|
||||
|
||||
// Shorts player components.
|
||||
var joinButton = new StringFilterGroup(
|
||||
@ -87,22 +87,22 @@ public final class ShortsFilter extends Filter {
|
||||
"ContainerType|shorts_video_action_button"
|
||||
);
|
||||
|
||||
pathFilterGroupList.addAll(
|
||||
addPathCallbacks(
|
||||
joinButton, subscribeButton, subscribeButtonPaused,
|
||||
channelBar, soundButton, infoPanel, videoActionButton
|
||||
);
|
||||
|
||||
var shortsCommentButton = new ByteArrayAsStringFilterGroup(
|
||||
var shortsCommentButton = new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_SHORTS_COMMENTS_BUTTON,
|
||||
"reel_comment_button"
|
||||
);
|
||||
|
||||
var shortsShareButton = new ByteArrayAsStringFilterGroup(
|
||||
var shortsShareButton = new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_SHORTS_SHARE_BUTTON,
|
||||
"reel_share_button"
|
||||
);
|
||||
|
||||
var shortsRemixButton = new ByteArrayAsStringFilterGroup(
|
||||
var shortsRemixButton = new ByteArrayFilterGroup(
|
||||
SettingsEnum.HIDE_SHORTS_REMIX_BUTTON,
|
||||
"reel_remix_button"
|
||||
);
|
||||
@ -112,19 +112,19 @@ public final class ShortsFilter extends Filter {
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
if (matchedList == pathFilterGroupList) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
if (contentType == FilterContentType.PATH) {
|
||||
// Always filter if matched.
|
||||
if (matchedGroup == soundButton ||
|
||||
matchedGroup == infoPanel ||
|
||||
matchedGroup == channelBar ||
|
||||
matchedGroup == subscribeButtonPaused
|
||||
) return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
) return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
|
||||
// Video action buttons (comment, share, remix) have the same path.
|
||||
if (matchedGroup == videoActionButton) {
|
||||
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) return super.isFiltered(
|
||||
identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex
|
||||
identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@ -133,18 +133,18 @@ public final class ShortsFilter extends Filter {
|
||||
// to avoid false positives.
|
||||
if (path.startsWith(REEL_CHANNEL_BAR_PATH))
|
||||
if (matchedGroup == subscribeButton) return super.isFiltered(
|
||||
identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex
|
||||
identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex
|
||||
);
|
||||
|
||||
return false;
|
||||
} else if (matchedGroup == shelfHeader) {
|
||||
// Because the header is used in watch history and possibly other places, check for the index,
|
||||
// which is 0 when the shelf header is used for Shorts.
|
||||
if (matchedIndex != 0) return false;
|
||||
if (contentIndex != 0) return false;
|
||||
}
|
||||
|
||||
// Super class handles logging.
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
public static void hideShortsShelf(final View shortsShelfView) {
|
||||
|
@ -9,11 +9,12 @@ import app.revanced.integrations.settings.SettingsEnum;
|
||||
* Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}.
|
||||
*/
|
||||
public final class VideoQualityMenuFilterPatch extends Filter {
|
||||
// Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
|
||||
// Must be volatile or synchronized, as litho filtering runs off main thread
|
||||
// and this field is then access from the main thread.
|
||||
public static volatile boolean isVideoQualityMenuVisible;
|
||||
|
||||
public VideoQualityMenuFilterPatch() {
|
||||
pathFilterGroupList.addAll(new StringFilterGroup(
|
||||
addPathCallbacks(new StringFilterGroup(
|
||||
SettingsEnum.RESTORE_OLD_VIDEO_QUALITY_MENU,
|
||||
"quick_quality_sheet_content.eml-js"
|
||||
));
|
||||
@ -21,7 +22,7 @@ public final class VideoQualityMenuFilterPatch extends Filter {
|
||||
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
|
||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||
isVideoQualityMenuVisible = true;
|
||||
|
||||
return false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user