fix(youtube): separate hide-ads to hide-layout-components patch

This commit is contained in:
oSumAtrIX 2023-06-14 03:30:14 +02:00
parent e2fa445c3f
commit bdce0298c4
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
3 changed files with 263 additions and 231 deletions

View File

@ -1,160 +1,23 @@
package app.revanced.integrations.patches.components;
import android.os.Build;
import android.view.View;
import androidx.annotation.RequiresApi;
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;
// region Mix playlists
private final ByteArrayAsStringFilterGroup mixPlaylists;
private final ByteArrayAsStringFilterGroup imageHosting;
// endregion
@RequiresApi(api = Build.VERSION_CODES.N)
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
"home_video_with_context", // Don't filter anything in the home page video component.
"related_video_with_context", // Don't filter anything in the related video component.
"comment_thread", // Don't filter anything in the comments.
"|comment.", // Don't filter anything in the comments replies.
"library_recent_shelf",
};
custom = new CustomFilterGroup(
SettingsEnum.CUSTOM_FILTER,
SettingsEnum.CUSTOM_FILTER_STRINGS
);
final var communityPosts = new StringFilterGroup(
SettingsEnum.HIDE_COMMUNITY_POSTS,
"post_base_wrapper"
);
final var communityGuidelines = new StringFilterGroup(
SettingsEnum.HIDE_COMMUNITY_GUIDELINES,
"community_guidelines"
);
final var subscribersCommunityGuidelines = new StringFilterGroup(
SettingsEnum.HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES,
"sponsorships_comments_upsell"
);
final var channelMemberShelf = new StringFilterGroup(
SettingsEnum.HIDE_CHANNEL_MEMBER_SHELF,
"member_recognition_shelf"
);
final var compactBanner = new StringFilterGroup(
SettingsEnum.HIDE_COMPACT_BANNER,
"compact_banner"
);
final var inFeedSurvey = new StringFilterGroup(
SettingsEnum.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
);
final var medicalPanel = new StringFilterGroup(
SettingsEnum.HIDE_MEDICAL_PANELS,
"medical_panel"
);
final var paidContent = new StringFilterGroup(
SettingsEnum.HIDE_PAID_CONTENT,
"paid_content_overlay"
);
final var merchandise = new StringFilterGroup(
SettingsEnum.HIDE_MERCHANDISE_BANNERS,
"product_carousel"
);
final var infoPanel = new StringFilterGroup(
SettingsEnum.HIDE_HIDE_INFO_PANELS,
"publisher_transparency_panel",
"single_item_information_panel"
);
final var latestPosts = new StringFilterGroup(
SettingsEnum.HIDE_HIDE_LATEST_POSTS,
"post_shelf"
);
final var channelGuidelines = new StringFilterGroup(
SettingsEnum.HIDE_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.HIDE_SELF_SPONSOR,
"cta_shelf_card"
);
final var chapterTeaser = new StringFilterGroup(
SettingsEnum.HIDE_CHAPTER_TEASER,
"expandable_metadata",
"macro_markers_carousel"
);
final var viewProducts = new StringFilterGroup(
SettingsEnum.HIDE_PRODUCTS_BANNER,
"product_item",
"products_in_video"
);
final var webLinkPanel = new StringFilterGroup(
SettingsEnum.HIDE_WEB_SEARCH_RESULTS,
"web_link_panel"
);
final var channelBar = new StringFilterGroup(
SettingsEnum.HIDE_CHANNEL_BAR,
"channel_bar"
);
final var relatedVideos = new StringFilterGroup(
SettingsEnum.HIDE_RELATED_VIDEOS,
"fullscreen_related_videos"
);
final var quickActions = new StringFilterGroup(
SettingsEnum.HIDE_QUICK_ACTIONS,
"quick_actions"
);
final var imageShelf = new StringFilterGroup(
SettingsEnum.HIDE_IMAGE_SHELF,
"image_shelf"
);
final var graySeparator = new StringFilterGroup(
SettingsEnum.HIDE_GRAY_SEPARATOR,
"cell_divider" // layout residue (gray line above the buttoned ad),
);
final var buttonedAd = new StringFilterGroup(
SettingsEnum.HIDE_BUTTONED_ADS,
"_buttoned_layout",
@ -192,107 +55,50 @@ public final class AdsFilter extends Filter {
"offer_module_root"
);
// region Mix playlists
mixPlaylists = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_MIX_PLAYLISTS,
"&list=",
"YouTube Music"
);
imageHosting = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_MIX_PLAYLISTS, // Unused
"ggpht.com"
);
// endregion
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.HIDE_GENERAL_ADS,
"carousel_ad"
);
this.identifierFilterGroups.addAll(
graySeparator,
carouselAd
final var viewProducts = new StringFilterGroup(
SettingsEnum.HIDE_PRODUCTS_BANNER,
"product_item",
"products_in_video"
);
}
private boolean isMixPlaylistFiltered(final byte[] _protobufBufferArray) {
if (!mixPlaylists.isEnabled()) return false;
final var webLinkPanel = new StringFilterGroup(
SettingsEnum.HIDE_WEB_SEARCH_RESULTS,
"web_link_panel"
);
// Two checks are required to prevent false positives.
final var merchandise = new StringFilterGroup(
SettingsEnum.HIDE_MERCHANDISE_BANNERS,
"product_carousel"
);
// First check if the current buffer potentially contains a mix playlist.
if (!mixPlaylists.check(_protobufBufferArray).isFiltered()) return false;
final var selfSponsor = new StringFilterGroup(
SettingsEnum.HIDE_SELF_SPONSOR,
"cta_shelf_card"
);
// Ensure that the buffer actually contains a mix playlist.
return imageHosting.check(_protobufBufferArray).isFiltered();
this.pathFilterGroups.addAll(
generalAds,
buttonedAd,
merchandise,
viewProducts,
selfSponsor,
webLinkPanel,
movieAds
);
this.identifierFilterGroups.addAll(carouselAd);
}
@Override
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
FilterResult result;
if (ReVancedUtils.containsAny(path, exceptions))
return false;
if (custom.isEnabled() && custom.check(path).isFiltered())
result = FilterResult.CUSTOM;
else if (ReVancedUtils.containsAny(path, exceptions))
result = FilterResult.EXCEPTION;
else {
var filtered =
pathFilterGroups.contains(path) || // Check if the path is filtered.
identifierFilterGroups.contains(identifier) || // Check if the identifier is filtered.
isMixPlaylistFiltered(_protobufBufferArray); // Check if the buffer contains a mix playlist.
result = filtered ? FilterResult.FILTERED : 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;
}
return super.isFiltered(path, identifier, _protobufBufferArray);
}
/**

View File

@ -0,0 +1,217 @@
package app.revanced.integrations.patches.components;
import android.os.Build;
import android.view.View;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.ReVancedUtils;
public final class LayoutComponentsFilter extends Filter {
private final String[] exceptions;
private final CustomFilterGroup custom;
// region Mix playlists
private final ByteArrayAsStringFilterGroup mixPlaylists;
private final ByteArrayAsStringFilterGroup imageHosting;
// endregion
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() {
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.CUSTOM_FILTER,
SettingsEnum.CUSTOM_FILTER_STRINGS
);
final var communityPosts = new StringFilterGroup(
SettingsEnum.HIDE_COMMUNITY_POSTS,
"post_base_wrapper"
);
final var communityGuidelines = new StringFilterGroup(
SettingsEnum.HIDE_COMMUNITY_GUIDELINES,
"community_guidelines"
);
final var subscribersCommunityGuidelines = new StringFilterGroup(
SettingsEnum.HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES,
"sponsorships_comments_upsell"
);
final var channelMemberShelf = new StringFilterGroup(
SettingsEnum.HIDE_CHANNEL_MEMBER_SHELF,
"member_recognition_shelf"
);
final var compactBanner = new StringFilterGroup(
SettingsEnum.HIDE_COMPACT_BANNER,
"compact_banner"
);
final var inFeedSurvey = new StringFilterGroup(
SettingsEnum.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
);
final var medicalPanel = new StringFilterGroup(
SettingsEnum.HIDE_MEDICAL_PANELS,
"medical_panel"
);
final var paidContent = new StringFilterGroup(
SettingsEnum.HIDE_PAID_CONTENT,
"paid_content_overlay"
);
final var infoPanel = new StringFilterGroup(
SettingsEnum.HIDE_HIDE_INFO_PANELS,
"publisher_transparency_panel",
"single_item_information_panel"
);
final var latestPosts = new StringFilterGroup(
SettingsEnum.HIDE_HIDE_LATEST_POSTS,
"post_shelf"
);
final var channelGuidelines = new StringFilterGroup(
SettingsEnum.HIDE_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 chapterTeaser = new StringFilterGroup(
SettingsEnum.HIDE_CHAPTER_TEASER,
"expandable_metadata",
"macro_markers_carousel"
);
final var channelBar = new StringFilterGroup(
SettingsEnum.HIDE_CHANNEL_BAR,
"channel_bar"
);
final var relatedVideos = new StringFilterGroup(
SettingsEnum.HIDE_RELATED_VIDEOS,
"fullscreen_related_videos"
);
final var quickActions = new StringFilterGroup(
SettingsEnum.HIDE_QUICK_ACTIONS,
"quick_actions"
);
final var imageShelf = new StringFilterGroup(
SettingsEnum.HIDE_IMAGE_SHELF,
"image_shelf"
);
final var graySeparator = new StringFilterGroup(
SettingsEnum.HIDE_GRAY_SEPARATOR,
"cell_divider" // layout residue (gray line above the buttoned ad),
);
// region Mix playlists
mixPlaylists = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_MIX_PLAYLISTS,
"&list=",
"YouTube Music"
);
imageHosting = new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_MIX_PLAYLISTS, // Unused
"ggpht.com"
);
// endregion
this.pathFilterGroups.addAll(
channelBar,
communityPosts,
paidContent,
latestPosts,
chapterTeaser,
communityGuidelines,
quickActions,
relatedVideos,
compactBanner,
inFeedSurvey,
medicalPanel,
infoPanel,
channelGuidelines,
audioTrackButton,
artistCard,
imageShelf,
subscribersCommunityGuidelines,
channelMemberShelf
);
final var carouselAd = new StringFilterGroup(
SettingsEnum.HIDE_GENERAL_ADS,
"carousel_ad"
);
this.identifierFilterGroups.addAll(
graySeparator,
carouselAd
);
}
private boolean isMixPlaylistFiltered(final byte[] _protobufBufferArray) {
if (!mixPlaylists.isEnabled()) return false;
// Two checks are required to prevent false positives.
// First check if the current buffer potentially contains a mix playlist.
if (!mixPlaylists.check(_protobufBufferArray).isFiltered()) return false;
// Ensure that the buffer actually contains a mix playlist.
return imageHosting.check(_protobufBufferArray).isFiltered();
}
@Override
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
if (custom.isEnabled() && custom.check(path).isFiltered())
return true;
if (ReVancedUtils.containsAny(path, exceptions))
return false; // Exceptions are not filtered.
if (super.isFiltered(path, identifier, _protobufBufferArray))
return true;
return isMixPlaylistFiltered(_protobufBufferArray);
}
/**
* Hide the view, which shows ads in the homepage.
*
* @param view The view, which shows ads.
*/
public static void hideAdAttributionView(View view) {
ReVancedUtils.hideViewBy1dpUnderCondition(SettingsEnum.HIDE_GENERAL_ADS, view);
}
}

View File

@ -238,7 +238,10 @@ public final class LithoFilterPatch {
@SuppressWarnings("unused")
public static boolean filter(final StringBuilder pathBuilder, final String identifier, final ByteBuffer protobufBuffer) {
// TODO: Maybe this can be moved to the Filter class, to prevent unnecessary string creation
// because some filters might not need the path.
var path = pathBuilder.toString();
// It is assumed that protobufBuffer is empty as well in this case.
if (path.isEmpty()) return false;
@ -249,10 +252,16 @@ public final class LithoFilterPatch {
var protobufBufferArray = protobufBuffer.array();
// check if any filter-group
for (var filter : filters)
if (filter.isFiltered(path, identifier, protobufBufferArray)) return true;
for (var filter : filters) {
var filtered = filter.isFiltered(path, identifier, protobufBufferArray);
LogHelper.printDebug(() ->
String.format("%s (ID: %s): %s", filtered ? "Filtered" : "Unfiltered", identifier, path)
);
if (filtered) return true;
}
return false;
}
}
}