mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-12-25 04:05:50 +01:00
feat: add capability to filter from protobuf buffer
This commit is contained in:
parent
2a15f902d6
commit
5652c32345
@ -1,31 +0,0 @@
|
|||||||
package app.revanced.integrations.patches;
|
|
||||||
|
|
||||||
import app.revanced.integrations.settings.SettingsEnum;
|
|
||||||
|
|
||||||
final class ButtonsPatch extends Filter {
|
|
||||||
private final BlockRule actionBarRule;
|
|
||||||
|
|
||||||
public ButtonsPatch() {
|
|
||||||
actionBarRule = new BlockRule(null, "video_action_bar");
|
|
||||||
pathRegister.registerAll(
|
|
||||||
new BlockRule(SettingsEnum.HIDE_LIKE_DISLIKE_BUTTON, "|like_button", "dislike_button"),
|
|
||||||
new BlockRule(SettingsEnum.HIDE_DOWNLOAD_BUTTON, "download_button"),
|
|
||||||
new BlockRule(SettingsEnum.HIDE_PLAYLIST_BUTTON, "save_to_playlist_button"),
|
|
||||||
new BlockRule(SettingsEnum.HIDE_CLIP_BUTTON, "|clip_button.eml|"),
|
|
||||||
new BlockRule(SettingsEnum.HIDE_ACTION_BUTTONS, "ContainerType|video_action_button", "|CellType|CollectionType|CellType|ContainerType|button.eml|")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canHideActionBar() {
|
|
||||||
for (BlockRule rule : pathRegister) if (!rule.isEnabled()) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean filter(final String path, final String identifier) {
|
|
||||||
// If everything is hidden, then also hide the video bar itself.
|
|
||||||
if (canHideActionBar() && actionBarRule.check(identifier).isBlocked()) return true;
|
|
||||||
|
|
||||||
return pathRegister.contains(path);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package app.revanced.integrations.patches;
|
|
||||||
|
|
||||||
import app.revanced.integrations.settings.SettingsEnum;
|
|
||||||
import app.revanced.integrations.utils.LogHelper;
|
|
||||||
|
|
||||||
final class CommentsPatch extends Filter {
|
|
||||||
|
|
||||||
public CommentsPatch() {
|
|
||||||
var comments = new BlockRule(SettingsEnum.HIDE_COMMENTS_SECTION, "video_metadata_carousel", "_comments");
|
|
||||||
var previewComment = new BlockRule(
|
|
||||||
SettingsEnum.HIDE_PREVIEW_COMMENT,
|
|
||||||
"|carousel_item",
|
|
||||||
"comments_entry_point_teaser",
|
|
||||||
"comments_entry_point_simplebox"
|
|
||||||
);
|
|
||||||
|
|
||||||
this.pathRegister.registerAll(
|
|
||||||
comments,
|
|
||||||
previewComment
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean filter(String path, String _identifier) {
|
|
||||||
if (!pathRegister.contains(path)) return false;
|
|
||||||
|
|
||||||
LogHelper.printDebug(() -> "Blocked: " + path);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
package app.revanced.integrations.patches;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
import app.revanced.integrations.settings.SettingsEnum;
|
|
||||||
import app.revanced.integrations.utils.LogHelper;
|
|
||||||
import app.revanced.integrations.utils.ReVancedUtils;
|
|
||||||
|
|
||||||
public final class GeneralAdsPatch extends Filter {
|
|
||||||
private final String[] IGNORE = {
|
|
||||||
"home_video_with_context",
|
|
||||||
"related_video_with_context",
|
|
||||||
"comment_thread", // skip blocking anything in the comments
|
|
||||||
"|comment.", // skip blocking anything in the comments replies
|
|
||||||
"library_recent_shelf",
|
|
||||||
};
|
|
||||||
|
|
||||||
private final BlockRule custom = new CustomBlockRule(
|
|
||||||
SettingsEnum.CUSTOM_FILTER,
|
|
||||||
SettingsEnum.CUSTOM_FILTER_STRINGS
|
|
||||||
);
|
|
||||||
|
|
||||||
public GeneralAdsPatch() {
|
|
||||||
var communityPosts = new BlockRule(SettingsEnum.HIDE_COMMUNITY_POSTS, "post_base_wrapper");
|
|
||||||
var communityGuidelines = new BlockRule(SettingsEnum.HIDE_COMMUNITY_GUIDELINES, "community_guidelines");
|
|
||||||
var subscribersCommunityGuidelines = new BlockRule(SettingsEnum.HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES, "sponsorships_comments_upsell");
|
|
||||||
var channelMemberShelf = new BlockRule(SettingsEnum.HIDE_CHANNEL_MEMBER_SHELF, "member_recognition_shelf");
|
|
||||||
var compactBanner = new BlockRule(SettingsEnum.HIDE_COMPACT_BANNER, "compact_banner");
|
|
||||||
var inFeedSurvey = new BlockRule(SettingsEnum.HIDE_FEED_SURVEY, "in_feed_survey", "slimline_survey");
|
|
||||||
var medicalPanel = new BlockRule(SettingsEnum.HIDE_MEDICAL_PANELS, "medical_panel");
|
|
||||||
var merchandise = new BlockRule(SettingsEnum.HIDE_MERCHANDISE_BANNERS, "product_carousel");
|
|
||||||
var infoPanel = new BlockRule(SettingsEnum.HIDE_HIDE_INFO_PANELS, "publisher_transparency_panel", "single_item_information_panel");
|
|
||||||
var channelGuidelines = new BlockRule(SettingsEnum.HIDE_HIDE_CHANNEL_GUIDELINES, "channel_guidelines_entry_banner");
|
|
||||||
var audioTrackButton = new BlockRule(SettingsEnum.HIDE_AUDIO_TRACK_BUTTON, "multi_feed_icon_button");
|
|
||||||
var artistCard = new BlockRule(SettingsEnum.HIDE_ARTIST_CARDS, "official_card");
|
|
||||||
var chapterTeaser = new BlockRule(SettingsEnum.HIDE_CHAPTER_TEASER, "expandable_metadata", "macro_markers_carousel");
|
|
||||||
var viewProducts = new BlockRule(SettingsEnum.HIDE_PRODUCTS_BANNER, "product_item", "products_in_video");
|
|
||||||
var webLinkPanel = new BlockRule(SettingsEnum.HIDE_WEB_SEARCH_RESULTS, "web_link_panel");
|
|
||||||
var channelBar = new BlockRule(SettingsEnum.HIDE_CHANNEL_BAR, "channel_bar");
|
|
||||||
var relatedVideos = new BlockRule(SettingsEnum.HIDE_RELATED_VIDEOS, "fullscreen_related_videos");
|
|
||||||
var quickActions = new BlockRule(SettingsEnum.HIDE_QUICK_ACTIONS, "quick_actions");
|
|
||||||
var imageShelf = new BlockRule(SettingsEnum.HIDE_IMAGE_SHELF, "image_shelf");
|
|
||||||
var graySeparator = new BlockRule(SettingsEnum.HIDE_GRAY_SEPARATOR,
|
|
||||||
"cell_divider" // layout residue (gray line above the buttoned ad),
|
|
||||||
);
|
|
||||||
var paidContent = new BlockRule(SettingsEnum.HIDE_PAID_CONTENT, "paid_content_overlay");
|
|
||||||
var latestPosts = new BlockRule(SettingsEnum.HIDE_HIDE_LATEST_POSTS, "post_shelf");
|
|
||||||
var selfSponsor = new BlockRule(SettingsEnum.HIDE_SELF_SPONSOR, "cta_shelf_card");
|
|
||||||
var buttonedAd = new BlockRule(SettingsEnum.HIDE_BUTTONED_ADS,
|
|
||||||
"_buttoned_layout",
|
|
||||||
"full_width_square_image_layout",
|
|
||||||
"_ad_with",
|
|
||||||
"video_display_button_group_layout",
|
|
||||||
"landscape_image_wide_button_layout"
|
|
||||||
);
|
|
||||||
var generalAds = new BlockRule(
|
|
||||||
SettingsEnum.HIDE_GENERAL_ADS,
|
|
||||||
"ads_video_with_context",
|
|
||||||
"banner_text_icon",
|
|
||||||
"square_image_layout",
|
|
||||||
"watch_metadata_app_promo",
|
|
||||||
"video_display_full_layout",
|
|
||||||
"hero_promo_image",
|
|
||||||
"statement_banner",
|
|
||||||
"carousel_footered_layout",
|
|
||||||
"text_image_button_layout",
|
|
||||||
"primetime_promo",
|
|
||||||
"product_details",
|
|
||||||
"full_width_portrait_image_layout",
|
|
||||||
"brand_video_shelf"
|
|
||||||
);
|
|
||||||
var movieAds = new BlockRule(
|
|
||||||
SettingsEnum.HIDE_MOVIES_SECTION,
|
|
||||||
"browsy_bar",
|
|
||||||
"compact_movie",
|
|
||||||
"horizontal_movie_shelf",
|
|
||||||
"movie_and_show_upsell_card",
|
|
||||||
"compact_tvfilm_item",
|
|
||||||
"offer_module_root"
|
|
||||||
);
|
|
||||||
|
|
||||||
this.pathRegister.registerAll(
|
|
||||||
generalAds,
|
|
||||||
buttonedAd,
|
|
||||||
channelBar,
|
|
||||||
communityPosts,
|
|
||||||
paidContent,
|
|
||||||
latestPosts,
|
|
||||||
movieAds,
|
|
||||||
chapterTeaser,
|
|
||||||
communityGuidelines,
|
|
||||||
quickActions,
|
|
||||||
relatedVideos,
|
|
||||||
compactBanner,
|
|
||||||
inFeedSurvey,
|
|
||||||
viewProducts,
|
|
||||||
medicalPanel,
|
|
||||||
merchandise,
|
|
||||||
infoPanel,
|
|
||||||
channelGuidelines,
|
|
||||||
audioTrackButton,
|
|
||||||
artistCard,
|
|
||||||
selfSponsor,
|
|
||||||
webLinkPanel,
|
|
||||||
imageShelf,
|
|
||||||
subscribersCommunityGuidelines,
|
|
||||||
channelMemberShelf
|
|
||||||
);
|
|
||||||
|
|
||||||
var carouselAd = new BlockRule(SettingsEnum.HIDE_GENERAL_ADS,
|
|
||||||
"carousel_ad"
|
|
||||||
);
|
|
||||||
var shorts = new BlockRule(SettingsEnum.HIDE_SHORTS,
|
|
||||||
"shorts_shelf",
|
|
||||||
"inline_shorts",
|
|
||||||
"shorts_grid"
|
|
||||||
);
|
|
||||||
|
|
||||||
this.identifierRegister.registerAll(
|
|
||||||
shorts,
|
|
||||||
graySeparator,
|
|
||||||
carouselAd
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean filter(final String path, final String identifier) {
|
|
||||||
BlockResult result;
|
|
||||||
|
|
||||||
if (custom.isEnabled() && custom.check(path).isBlocked())
|
|
||||||
result = BlockResult.CUSTOM;
|
|
||||||
else if (ReVancedUtils.containsAny(path, IGNORE))
|
|
||||||
result = BlockResult.IGNORED;
|
|
||||||
else if (pathRegister.contains(path) || identifierRegister.contains(identifier))
|
|
||||||
result = BlockResult.DEFINED;
|
|
||||||
else
|
|
||||||
result = BlockResult.UNBLOCKED;
|
|
||||||
|
|
||||||
LogHelper.printDebug(() -> String.format("%s (ID: %s): %s", result.message, identifier, path));
|
|
||||||
|
|
||||||
return result.filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum BlockResult {
|
|
||||||
UNBLOCKED(false, "Unblocked"),
|
|
||||||
IGNORED(false, "Ignored"),
|
|
||||||
DEFINED(true, "Blocked"),
|
|
||||||
CUSTOM(true, "Custom");
|
|
||||||
|
|
||||||
final Boolean filter;
|
|
||||||
final String message;
|
|
||||||
|
|
||||||
BlockResult(boolean filter, String message) {
|
|
||||||
this.filter = filter;
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide a view.
|
|
||||||
*
|
|
||||||
* @param condition The setting to check for hiding the view.
|
|
||||||
* @param view The view to hide.
|
|
||||||
*/
|
|
||||||
private static void hideView(SettingsEnum condition, View view) {
|
|
||||||
if (!condition.getBoolean()) return;
|
|
||||||
|
|
||||||
LogHelper.printDebug(() -> "Hiding view with setting: " + condition);
|
|
||||||
|
|
||||||
ReVancedUtils.HideViewByLayoutParams(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the view, which shows ads in the homepage.
|
|
||||||
*
|
|
||||||
* @param view The view, which shows ads.
|
|
||||||
*/
|
|
||||||
public static void hideAdAttributionView(View view) {
|
|
||||||
hideView(SettingsEnum.HIDE_GENERAL_ADS, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the view, which shows reels in the homepage.
|
|
||||||
*
|
|
||||||
* @param view The view, which shows reels.
|
|
||||||
*/
|
|
||||||
public static void hideReelView(View view) {
|
|
||||||
hideView(SettingsEnum.HIDE_SHORTS, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
package app.revanced.integrations.patches;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Spliterator;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import app.revanced.integrations.settings.SettingsEnum;
|
|
||||||
import app.revanced.integrations.utils.LogHelper;
|
|
||||||
import app.revanced.integrations.utils.ReVancedUtils;
|
|
||||||
|
|
||||||
class BlockRule {
|
|
||||||
final static class BlockResult {
|
|
||||||
private final boolean blocked;
|
|
||||||
private final SettingsEnum setting;
|
|
||||||
|
|
||||||
public BlockResult(final SettingsEnum setting, final boolean blocked) {
|
|
||||||
this.setting = setting;
|
|
||||||
this.blocked = blocked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettingsEnum getSetting() {
|
|
||||||
return setting;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlocked() {
|
|
||||||
return blocked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final SettingsEnum setting;
|
|
||||||
private final String[] blocks;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a new rule for components.
|
|
||||||
*
|
|
||||||
* @param setting The setting which controls the blocking of this component.
|
|
||||||
* @param blocks The rules to block the component on.
|
|
||||||
*/
|
|
||||||
public BlockRule(final SettingsEnum setting, final String... blocks) {
|
|
||||||
this.setting = setting;
|
|
||||||
this.blocks = blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return setting.getBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockResult check(final String string) {
|
|
||||||
return new BlockResult(setting, string != null && ReVancedUtils.containsAny(string, blocks));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class CustomBlockRule extends BlockRule {
|
|
||||||
/**
|
|
||||||
* Initialize a new rule for components.
|
|
||||||
*
|
|
||||||
* @param setting The setting which controls the blocking of the components.
|
|
||||||
* @param filter The setting which contains the list of component names.
|
|
||||||
*/
|
|
||||||
public CustomBlockRule(final SettingsEnum setting, final SettingsEnum filter) {
|
|
||||||
super(setting, filter.getString().split(","));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
abstract class Filter {
|
|
||||||
final protected LithoBlockRegister pathRegister = new LithoBlockRegister();
|
|
||||||
final protected LithoBlockRegister identifierRegister = new LithoBlockRegister();
|
|
||||||
|
|
||||||
abstract boolean filter(final String path, final String identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
final class LithoBlockRegister implements Iterable<BlockRule> {
|
|
||||||
private final ArrayList<BlockRule> blocks = new ArrayList<>();
|
|
||||||
|
|
||||||
public void registerAll(BlockRule... blocks) {
|
|
||||||
this.blocks.addAll(Arrays.asList(blocks));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Iterator<BlockRule> iterator() {
|
|
||||||
return blocks.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@Override
|
|
||||||
public void forEach(@NonNull Consumer<? super BlockRule> action) {
|
|
||||||
blocks.forEach(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Spliterator<BlockRule> spliterator() {
|
|
||||||
return blocks.spliterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(String path) {
|
|
||||||
for (var rule : this) {
|
|
||||||
if (!rule.isEnabled()) continue;
|
|
||||||
|
|
||||||
var result = rule.check(path);
|
|
||||||
if (result.isBlocked()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class LithoFilterPatch {
|
|
||||||
private static final Filter[] filters = new Filter[]{
|
|
||||||
new GeneralAdsPatch(),
|
|
||||||
new ButtonsPatch(),
|
|
||||||
new CommentsPatch(),
|
|
||||||
};
|
|
||||||
|
|
||||||
public static boolean filter(final StringBuilder pathBuilder, final String identifier) {
|
|
||||||
var path = pathBuilder.toString();
|
|
||||||
if (path.isEmpty()) return false;
|
|
||||||
|
|
||||||
LogHelper.printDebug(() -> String.format("Searching (ID: %s): %s", identifier, path));
|
|
||||||
|
|
||||||
for (var filter : filters) {
|
|
||||||
if (filter.filter(path, identifier)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,297 @@
|
|||||||
|
package app.revanced.integrations.patches.litho;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import app.revanced.integrations.adremover.AdRemoverAPI;
|
||||||
|
import app.revanced.integrations.settings.SettingsEnum;
|
||||||
|
import app.revanced.integrations.utils.LogHelper;
|
||||||
|
import app.revanced.integrations.utils.ReVancedUtils;
|
||||||
|
|
||||||
|
public final class AdsFilter extends Filter {
|
||||||
|
private final String[] EXCEPTIONS;
|
||||||
|
|
||||||
|
private final CustomFilterGroup custom;
|
||||||
|
|
||||||
|
public AdsFilter() {
|
||||||
|
EXCEPTIONS = new String[]{
|
||||||
|
"home_video_with_context",
|
||||||
|
"related_video_with_context",
|
||||||
|
"comment_thread", // skip filtering anything in the comments
|
||||||
|
"|comment.", // skip filtering anything in the comments replies
|
||||||
|
"library_recent_shelf",
|
||||||
|
};
|
||||||
|
|
||||||
|
custom = new CustomFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_CUSTOM_ENABLED,
|
||||||
|
SettingsEnum.ADREMOVER_CUSTOM_REMOVAL
|
||||||
|
);
|
||||||
|
|
||||||
|
final var communityPosts = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_COMMUNITY_POSTS_REMOVAL,
|
||||||
|
"post_base_wrapper"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var communityGuidelines = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_COMMUNITY_GUIDELINES_REMOVAL,
|
||||||
|
"community_guidelines"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var subscribersCommunityGuidelines = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_SUBSCRIBERS_COMMUNITY_GUIDELINES_REMOVAL,
|
||||||
|
"sponsorships_comments_upsell"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
final var channelMemberShelf = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_CHANNEL_MEMBER_SHELF_REMOVAL,
|
||||||
|
"member_recognition_shelf"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var compactBanner = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_COMPACT_BANNER_REMOVAL,
|
||||||
|
"compact_banner"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var inFeedSurvey = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_FEED_SURVEY_REMOVAL,
|
||||||
|
"in_feed_survey",
|
||||||
|
"slimline_survey"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var medicalPanel = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_MEDICAL_PANEL_REMOVAL,
|
||||||
|
"medical_panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var paidContent = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_PAID_CONTENT_REMOVAL,
|
||||||
|
"paid_content_overlay"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var merchandise = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_MERCHANDISE_REMOVAL,
|
||||||
|
"product_carousel"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var infoPanel = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_INFO_PANEL_REMOVAL,
|
||||||
|
"publisher_transparency_panel",
|
||||||
|
"single_item_information_panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var latestPosts = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_HIDE_LATEST_POSTS,
|
||||||
|
"post_shelf"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var channelGuidelines = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_HIDE_CHANNEL_GUIDELINES,
|
||||||
|
"channel_guidelines_entry_banner"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var audioTrackButton = new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_AUDIO_TRACK_BUTTON,
|
||||||
|
"multi_feed_icon_button"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var artistCard = new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_ARTIST_CARDS,
|
||||||
|
"official_card"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var selfSponsor = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_SELF_SPONSOR_REMOVAL,
|
||||||
|
"cta_shelf_card"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var chapterTeaser = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_CHAPTER_TEASER_REMOVAL,
|
||||||
|
"expandable_metadata",
|
||||||
|
"macro_markers_carousel"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var viewProducts = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_VIEW_PRODUCTS,
|
||||||
|
"product_item",
|
||||||
|
"products_in_video"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var webLinkPanel = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_WEB_SEARCH_RESULTS,
|
||||||
|
"web_link_panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var channelBar = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_CHANNEL_BAR,
|
||||||
|
"channel_bar"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var relatedVideos = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_RELATED_VIDEOS,
|
||||||
|
"fullscreen_related_videos"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var quickActions = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_QUICK_ACTIONS,
|
||||||
|
"quick_actions"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var imageShelf = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_IMAGE_SHELF,
|
||||||
|
"image_shelf"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var graySeparator = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_GRAY_SEPARATOR,
|
||||||
|
"cell_divider" // layout residue (gray line above the buttoned ad),
|
||||||
|
);
|
||||||
|
|
||||||
|
final var buttonedAd = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_BUTTONED_REMOVAL,
|
||||||
|
"_buttoned_layout",
|
||||||
|
"full_width_square_image_layout",
|
||||||
|
"_ad_with",
|
||||||
|
"video_display_button_group_layout",
|
||||||
|
"landscape_image_wide_button_layout"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var generalAds = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_GENERAL_ADS_REMOVAL,
|
||||||
|
"ads_video_with_context",
|
||||||
|
"banner_text_icon",
|
||||||
|
"square_image_layout",
|
||||||
|
"watch_metadata_app_promo",
|
||||||
|
"video_display_full_layout",
|
||||||
|
"hero_promo_image",
|
||||||
|
"statement_banner",
|
||||||
|
"carousel_footered_layout",
|
||||||
|
"text_image_button_layout",
|
||||||
|
"primetime_promo",
|
||||||
|
"product_details",
|
||||||
|
"full_width_portrait_image_layout",
|
||||||
|
"brand_video_shelf"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var movieAds = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_MOVIE_REMOVAL,
|
||||||
|
"browsy_bar",
|
||||||
|
"compact_movie",
|
||||||
|
"horizontal_movie_shelf",
|
||||||
|
"movie_and_show_upsell_card",
|
||||||
|
"compact_tvfilm_item",
|
||||||
|
"offer_module_root"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.pathFilterGroups.addAll(
|
||||||
|
generalAds,
|
||||||
|
buttonedAd,
|
||||||
|
channelBar,
|
||||||
|
communityPosts,
|
||||||
|
paidContent,
|
||||||
|
latestPosts,
|
||||||
|
movieAds,
|
||||||
|
chapterTeaser,
|
||||||
|
communityGuidelines,
|
||||||
|
quickActions,
|
||||||
|
relatedVideos,
|
||||||
|
compactBanner,
|
||||||
|
inFeedSurvey,
|
||||||
|
viewProducts,
|
||||||
|
medicalPanel,
|
||||||
|
merchandise,
|
||||||
|
infoPanel,
|
||||||
|
channelGuidelines,
|
||||||
|
audioTrackButton,
|
||||||
|
artistCard,
|
||||||
|
selfSponsor,
|
||||||
|
webLinkPanel,
|
||||||
|
imageShelf,
|
||||||
|
subscribersCommunityGuidelines,
|
||||||
|
channelMemberShelf
|
||||||
|
);
|
||||||
|
|
||||||
|
final var carouselAd = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_GENERAL_ADS_REMOVAL,
|
||||||
|
"carousel_ad"
|
||||||
|
);
|
||||||
|
|
||||||
|
final var shorts = new StringFilterGroup(
|
||||||
|
SettingsEnum.ADREMOVER_SHORTS_REMOVAL,
|
||||||
|
"reels_player_overlay",
|
||||||
|
"shorts_shelf",
|
||||||
|
"inline_shorts",
|
||||||
|
"shorts_grid"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.identifierFilterGroups.addAll(
|
||||||
|
shorts,
|
||||||
|
graySeparator,
|
||||||
|
carouselAd
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
|
||||||
|
FilterResult result;
|
||||||
|
|
||||||
|
if (custom.isEnabled() && custom.contains(path).isFiltered())
|
||||||
|
result = FilterResult.CUSTOM;
|
||||||
|
else if (ReVancedUtils.containsAny(path, EXCEPTIONS))
|
||||||
|
result = FilterResult.EXCEPTION;
|
||||||
|
else if (pathFilterGroups.contains(path) || identifierFilterGroups.contains(identifier))
|
||||||
|
result = FilterResult.FILTERED;
|
||||||
|
else
|
||||||
|
result = FilterResult.UNFILTERED;
|
||||||
|
|
||||||
|
LogHelper.printDebug(() -> String.format("%s (ID: %s): %s", result.message, identifier, path));
|
||||||
|
|
||||||
|
return result.filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum FilterResult {
|
||||||
|
UNFILTERED(false, "Unfiltered"),
|
||||||
|
EXCEPTION(false, "Exception"),
|
||||||
|
FILTERED(true, "Filtered"),
|
||||||
|
CUSTOM(true, "Custom");
|
||||||
|
|
||||||
|
final Boolean filter;
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
FilterResult(boolean filter, String message) {
|
||||||
|
this.filter = filter;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide a view.
|
||||||
|
*
|
||||||
|
* @param condition The setting to check for hiding the view.
|
||||||
|
* @param view The view to hide.
|
||||||
|
*/
|
||||||
|
private static void hideView(SettingsEnum condition, View view) {
|
||||||
|
if (!condition.getBoolean()) return;
|
||||||
|
|
||||||
|
LogHelper.printDebug(() -> "Hiding view with setting: " + condition);
|
||||||
|
|
||||||
|
AdRemoverAPI.HideViewWithLayout1dp(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the view, which shows ads in the homepage.
|
||||||
|
*
|
||||||
|
* @param view The view, which shows ads.
|
||||||
|
*/
|
||||||
|
public static void hideAdAttributionView(View view) {
|
||||||
|
hideView(SettingsEnum.ADREMOVER_GENERAL_ADS_REMOVAL, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the view, which shows reels in the homepage.
|
||||||
|
*
|
||||||
|
* @param view The view, which shows reels.
|
||||||
|
*/
|
||||||
|
public static void hideReelView(View view) {
|
||||||
|
hideView(SettingsEnum.ADREMOVER_SHORTS_REMOVAL, view);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package app.revanced.integrations.patches.litho;
|
||||||
|
|
||||||
|
import app.revanced.integrations.settings.SettingsEnum;
|
||||||
|
|
||||||
|
final class ButtonsFilter extends Filter {
|
||||||
|
private final StringFilterGroup actionBarRule;
|
||||||
|
|
||||||
|
public ButtonsFilter() {
|
||||||
|
actionBarRule = new StringFilterGroup(
|
||||||
|
null,
|
||||||
|
"video_action_bar"
|
||||||
|
);
|
||||||
|
|
||||||
|
pathFilterGroups.addAll(
|
||||||
|
new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_LIKE_DISLIKE_BUTTON,
|
||||||
|
"|like_button",
|
||||||
|
"dislike_button"
|
||||||
|
),
|
||||||
|
new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_DOWNLOAD_BUTTON,
|
||||||
|
"download_button"
|
||||||
|
),
|
||||||
|
new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_PLAYLIST_BUTTON,
|
||||||
|
"save_to_playlist_button"
|
||||||
|
),
|
||||||
|
new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_CLIP_BUTTON,
|
||||||
|
"|clip_button.eml|"
|
||||||
|
),
|
||||||
|
new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_ACTION_BUTTONS,
|
||||||
|
"ContainerType|video_action_button",
|
||||||
|
"|CellType|CollectionType|CellType|ContainerType|button.eml|"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEveryFilterGroupEnabled() {
|
||||||
|
for (StringFilterGroup rule : pathFilterGroups)
|
||||||
|
if (!rule.isEnabled()) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
|
||||||
|
if (isEveryFilterGroupEnabled())
|
||||||
|
if (actionBarRule.contains(identifier).isFiltered()) return true;
|
||||||
|
|
||||||
|
return super.isFiltered(path, identifier, _protobufBufferArray);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package app.revanced.integrations.patches.litho;
|
||||||
|
|
||||||
|
import app.revanced.integrations.settings.SettingsEnum;
|
||||||
|
|
||||||
|
final class CommentsFilter extends Filter {
|
||||||
|
|
||||||
|
public CommentsFilter() {
|
||||||
|
var comments = new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_COMMENTS_SECTION,
|
||||||
|
"video_metadata_carousel",
|
||||||
|
"_comments"
|
||||||
|
);
|
||||||
|
|
||||||
|
var previewComment = new StringFilterGroup(
|
||||||
|
SettingsEnum.HIDE_PREVIEW_COMMENT,
|
||||||
|
"|carousel_item",
|
||||||
|
"comments_entry_point_teaser",
|
||||||
|
"comments_entry_point_simplebox"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.pathFilterGroups.addAll(
|
||||||
|
comments,
|
||||||
|
previewComment
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,249 @@
|
|||||||
|
package app.revanced.integrations.patches.litho;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import app.revanced.integrations.settings.SettingsEnum;
|
||||||
|
import app.revanced.integrations.utils.LogHelper;
|
||||||
|
import app.revanced.integrations.utils.ReVancedUtils;
|
||||||
|
|
||||||
|
abstract class FilterGroup<T> {
|
||||||
|
final static class FilterGroupResult {
|
||||||
|
private final boolean filtered;
|
||||||
|
private final SettingsEnum setting;
|
||||||
|
|
||||||
|
public FilterGroupResult(final SettingsEnum setting, final boolean filtered) {
|
||||||
|
this.setting = setting;
|
||||||
|
this.filtered = filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettingsEnum getSetting() {
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFiltered() {
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final SettingsEnum setting;
|
||||||
|
protected final T[] filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new filter group.
|
||||||
|
*
|
||||||
|
* @param setting The associated setting.
|
||||||
|
* @param filters The filters.
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public FilterGroup(final SettingsEnum setting, final T... filters) {
|
||||||
|
this.setting = setting;
|
||||||
|
this.filters = filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return setting.getBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract FilterGroupResult contains(final T stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringFilterGroup extends FilterGroup<String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link FilterGroup#FilterGroup(SettingsEnum, Object[])}
|
||||||
|
*/
|
||||||
|
public StringFilterGroup(final SettingsEnum setting, final String... filters) {
|
||||||
|
super(setting, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterGroupResult contains(final String string) {
|
||||||
|
return new FilterGroupResult(setting, string != null && ReVancedUtils.containsAny(string, filters));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CustomFilterGroup extends StringFilterGroup {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link FilterGroup#FilterGroup(SettingsEnum, Object[])}
|
||||||
|
*/
|
||||||
|
public CustomFilterGroup(final SettingsEnum setting, final SettingsEnum filter) {
|
||||||
|
super(setting, filter.getString().split(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class ByteArrayFilterGroup extends FilterGroup<byte[]> {
|
||||||
|
// Modified implementation from https://stackoverflow.com/a/1507813
|
||||||
|
private int indexOf(final byte[] data, final byte[] pattern) {
|
||||||
|
// Computes the failure function using a boot-strapping process,
|
||||||
|
// where the pattern is matched against itself.
|
||||||
|
|
||||||
|
final int[] failure = new int[pattern.length];
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 1; i < pattern.length; i++) {
|
||||||
|
while (j > 0 && pattern[j] != pattern[i]) {
|
||||||
|
j = failure[j - 1];
|
||||||
|
}
|
||||||
|
if (pattern[j] == pattern[i]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
failure[i] = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the first occurrence of the pattern in the byte array using
|
||||||
|
// KNP matching algorithm.
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
if (data.length == 0) return -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
while (j > 0 && pattern[j] != data[i]) {
|
||||||
|
j = failure[j - 1];
|
||||||
|
}
|
||||||
|
if (pattern[j] == data[i]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j == pattern.length) {
|
||||||
|
return i - pattern.length + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link FilterGroup#FilterGroup(SettingsEnum, Object[])}
|
||||||
|
*/
|
||||||
|
public ByteArrayFilterGroup(final SettingsEnum setting, final byte[]... filters) {
|
||||||
|
super(setting, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterGroupResult contains(final byte[] bytes) {
|
||||||
|
var matched = false;
|
||||||
|
for (byte[] filter : filters) {
|
||||||
|
if (indexOf(bytes, filter) == -1) continue;
|
||||||
|
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var filtered = matched;
|
||||||
|
return new FilterGroupResult(setting, filtered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<T> {
|
||||||
|
private final ArrayList<T> filterGroups = new ArrayList<>();
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
protected final void addAll(final T... filterGroups) {
|
||||||
|
this.filterGroups.addAll(Arrays.asList(filterGroups));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return filterGroups.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||||
|
@Override
|
||||||
|
public void forEach(@NonNull Consumer<? super T> action) {
|
||||||
|
filterGroups.forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Spliterator<T> spliterator() {
|
||||||
|
return filterGroups.spliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean contains(final V stack) {
|
||||||
|
for (T filterGroup : this) {
|
||||||
|
if (!filterGroup.isEnabled()) continue;
|
||||||
|
|
||||||
|
var result = filterGroup.contains(stack);
|
||||||
|
if (result.isFiltered()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class StringFilterGroupList extends FilterGroupList<String, StringFilterGroup> {
|
||||||
|
}
|
||||||
|
|
||||||
|
final class ByteArrayFilterGroupList extends FilterGroupList<byte[], ByteArrayFilterGroup> {
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Filter {
|
||||||
|
final protected StringFilterGroupList pathFilterGroups = new StringFilterGroupList();
|
||||||
|
final protected StringFilterGroupList identifierFilterGroups = new StringFilterGroupList();
|
||||||
|
final protected ByteArrayFilterGroupList protobufBufferFilterGroup = new ByteArrayFilterGroupList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given path, identifier or protobuf buffer is filtered by any {@link FilterGroup}.
|
||||||
|
* @return True if filtered, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean isFiltered(final String path, final String identifier, final byte[] protobufBufferArray) {
|
||||||
|
if (pathFilterGroups.contains(path)) {
|
||||||
|
LogHelper.printDebug(() -> String.format("Filtered path: %s", path));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifierFilterGroups.contains(identifier)) {
|
||||||
|
LogHelper.printDebug(() -> String.format("Filtered identifier: %s", identifier));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protobufBufferFilterGroup.contains(protobufBufferArray)) {
|
||||||
|
LogHelper.printDebug(() -> "Filtered from protobuf-buffer");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class LithoFilterPatch {
|
||||||
|
private static final Filter[] filters = new Filter[]{
|
||||||
|
new AdsFilter(),
|
||||||
|
new ButtonsFilter(),
|
||||||
|
new CommentsFilter(),
|
||||||
|
};
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static boolean filter(final StringBuilder pathBuilder, final String identifier, final ByteBuffer protobufBuffer) {
|
||||||
|
var path = pathBuilder.toString();
|
||||||
|
// It is assumed that protobufBuffer is empty as well in this case.
|
||||||
|
if (path.isEmpty()) return false;
|
||||||
|
|
||||||
|
LogHelper.printDebug(() -> String.format(
|
||||||
|
"Searching (ID: %s, Buffer-size: %s): %s",
|
||||||
|
identifier, protobufBuffer.remaining(), path
|
||||||
|
));
|
||||||
|
|
||||||
|
var protobufBufferArray = protobufBuffer.array();
|
||||||
|
|
||||||
|
// check if any filter-group
|
||||||
|
for (var filter : filters)
|
||||||
|
if (filter.isFiltered(path, identifier, protobufBufferArray)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user