diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java index cd90f9d9d3..bb93bfb3bf 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java @@ -2,7 +2,6 @@ package app.revanced.extension.tiktok.feedfilter; import app.revanced.extension.tiktok.settings.Settings; import com.ss.android.ugc.aweme.feed.model.Aweme; -import com.ss.android.ugc.aweme.commerce.AwemeCommerceStruct; public class AdsFilter implements IFilter { @Override @@ -13,21 +12,29 @@ public class AdsFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { - try { - // Standard Ads & Promotional Music - if (item.isAd() || item.isWithPromotionalMusic()) { - return true; - } + if (item == null) return false; - // Paid Partnerships (Branded Content) - if (item.mCommerceVideoAuthInfo != null) { - if (item.mCommerceVideoAuthInfo.isBrandedContent()) { - return true; - } - } - } catch (Throwable t) { - return false; + // TikTok's Internal Commercial Types + // Verified in AwemeExtKt: 1, 29, 30, 32, 33, 201 are commercial + int type = item.getAwemeType(); + if (type == 1 || type == 29 || type == 30 || type == 32 || type == 33 || type == 201) { + return true; } + + // Ad Flags (Hard and Soft/Sponsored) + if (item.isAd || item.isSoftAd || item.awemeRawAd != null) { + return true; + } + + // Music Marketing + if (item.isWithPromotionalMusic()) return true; + + if (item.mCommerceVideoAuthInfo != null) { + // PseudoAds (Spark Ads) and Branded Content + return item.mCommerceVideoAuthInfo.isBrandedContent() || + item.mCommerceVideoAuthInfo.isPseudoAd(); + } + return false; } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/BloatFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/BloatFilter.java new file mode 100644 index 0000000000..88f0487b9a --- /dev/null +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/BloatFilter.java @@ -0,0 +1,28 @@ +package app.revanced.extension.tiktok.feedfilter; + +import com.ss.android.ugc.aweme.feed.model.Aweme; + +public class BloatFilter implements IFilter { + @Override + public boolean getEnabled() { + return true; + } + + @Override + public boolean getFiltered(Aweme item) { + if (item == null) return false; + + // Full screen promos + if (item.isReferralFakeAweme || item.isRecBigCardFakeAweme) { + return true; + } + + // System cards (non video interrupts) + if (item.awemeType == 104 || item.awemeType == 105) return true; + + // Accounts to follow recs and overlays + if (item.recommendCardType != 0) return true; + + return false; + } +} \ No newline at end of file diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java index 5833284a25..5b85f6bd27 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java @@ -1,7 +1,5 @@ package app.revanced.extension.tiktok.feedfilter; -import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.settings.BaseSettings; import com.ss.android.ugc.aweme.feed.model.Aweme; import com.ss.android.ugc.aweme.feed.model.FeedItemList; import com.ss.android.ugc.aweme.follow.presenter.FollowFeedList; @@ -10,73 +8,53 @@ import java.util.Iterator; import java.util.List; public final class FeedItemsFilter { - public static void filter(FeedItemList feedItemList) { - boolean verbose = BaseSettings.DEBUG.get(); - if (feedItemList == null || feedItemList.items == null) return; - filterFeedList("FeedItemList", feedItemList.items, item -> item, verbose); + private static final IFilter[] FILTERS = new IFilter[] { + new AdsFilter(), + new LiveFilter(), + new ShopFilter(), + new StoryFilter(), + new ImageVideoFilter(), + new BloatFilter() + }; + + public static void filter(FeedItemList feedItemList) { + if (feedItemList == null || feedItemList.items == null) return; + filterFeedList(feedItemList.items, item -> item); } public static void filter(FollowFeedList followFeedList) { - boolean verbose = BaseSettings.DEBUG.get(); if (followFeedList == null || followFeedList.mItems == null) return; - - filterFeedList("FollowFeedList", followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null, verbose); + filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null); } private static void filterFeedList( - String source, List list, - AwemeExtractor extractor, - boolean verbose + AwemeExtractor extractor ) { if (list == null) return; - int initialSize = list.size(); - int removed = 0; Iterator iterator = list.iterator(); while (iterator.hasNext()) { T container = iterator.next(); Aweme item = extractor.extract(container); + if (item == null) continue; - String reason = getInternalizedFilterReason(item); - - if (reason != null) { - removed++; + if (shouldFilter(item)) { iterator.remove(); - if (verbose) { - logItem(item, reason); - } } } - - if (verbose && removed > 0) { - int finalRemoved = removed; - Logger.printInfo(() -> "[ReVanced FeedFilter] " + source + ": removed " + finalRemoved + " items."); - } } - private static String getInternalizedFilterReason(Aweme item) { - if (item.isAd() || item.isWithPromotionalMusic()) { - return "AdsFilter"; + private static boolean shouldFilter(Aweme item) { + for (IFilter filter : FILTERS) { + if (filter.getEnabled() && filter.getFiltered(item)) { + return true; + } } - - if (item.getLiveId() != 0 || item.getLiveType() != null || item.isLiveReplay()) { - return "LiveFilter"; - } - - String shareUrl = item.getShareUrl(); - if (shareUrl != null && shareUrl.contains("placeholder_product_id")) { - return "ShopFilter"; - } - - return null; - } - - private static void logItem(Aweme item, String reason) { - Logger.printInfo(() -> "[ReVanced FeedFilter] FILTERED: aid=" + item.getAid() + " Reason=" + reason); + return false; } @FunctionalInterface diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java index c9be3cd5e9..cefa57a882 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java @@ -11,9 +11,17 @@ public class ImageVideoFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { + if (item == null) return false; + + int type = item.getAwemeType(); + + // 2 = Standard Image, 150 = Photo Mode, 160 = Text Mode + if (type == 2 || type == 150 || type == 160) { + return true; + } + + // Fallback checks var imageInfos = item.getImageInfos(); - boolean isImage = imageInfos != null && !imageInfos.isEmpty(); - boolean isPhotoMode = item.getPhotoModeImageInfo() != null || item.getPhotoModeTextInfo() != null; - return isImage || isPhotoMode; + return imageInfos != null && !imageInfos.isEmpty(); } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java index f5336da5b4..33534668a4 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java @@ -24,7 +24,10 @@ public final class LikeCountFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { - AwemeStatistics statistics = item.getStatistics(); + AwemeStatistics statistics = item.statistics; + + if (statistics == null) statistics = item.getStatistics(); + if (statistics == null) return false; long likeCount = statistics.getDiggCount(); diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java index 7390430c28..6156f8064d 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java @@ -6,12 +6,19 @@ import com.ss.android.ugc.aweme.feed.model.Aweme; public class LiveFilter implements IFilter { @Override public boolean getEnabled() { - // HARDCODED: Always filter live streams return true; } @Override public boolean getFiltered(Aweme item) { - return item.getLiveId() != 0 || item.isLiveReplay() || item.getLiveType() != null; + if (item == null) return false; + + // awemeType 101 is the 'isLive' check in code + if (item.getAwemeType() == 101 || item.getRoom() != null) { + return true; + } + + // Fallbacks + return item.isLiveReplay() || item.getLiveId() != 0 || item.getLiveType() != null; } } \ No newline at end of file diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java index 627ab46c1d..e151906606 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java @@ -4,7 +4,6 @@ import app.revanced.extension.tiktok.settings.Settings; import com.ss.android.ugc.aweme.feed.model.Aweme; public class ShopFilter implements IFilter { - private static final String SHOP_INFO = "placeholder_product_id"; @Override public boolean getEnabled() { return true; @@ -13,7 +12,25 @@ public class ShopFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { + if (item == null) return false; + + // Attached Products (TikTok Shop) + if (item.productsInfo != null && !item.productsInfo.isEmpty()) { + return true; + } + + // Simple Promotions (Banner links) + if (item.simplePromotions != null && !item.simplePromotions.isEmpty()) { + return true; + } + + // Shop Ads + if (item.shopAdStruct != null) { + return true; + } + + // Fallback (URL check) String shareUrl = item.getShareUrl(); - return shareUrl != null && shareUrl.contains(SHOP_INFO); + return shareUrl != null && shareUrl.contains("placeholder_product_id"); } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java index 85d0a70883..fc3ce6756b 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java @@ -9,8 +9,17 @@ public class StoryFilter implements IFilter { return Settings.HIDE_STORY.get(); } - @Override public boolean getFiltered(Aweme item) { - return item.getIsTikTokStory(); + if (item == null) return false; + + if (item.isTikTokStory) return true; + + // Type 40 = Standard Story, 11 = Legacy/Region Story + int type = item.getAwemeType(); + if (type == 40 || type == 11 || item.isTikTokStory) { + return true; + } + + return false; } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java index 32627ca60d..4c30500b89 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java @@ -23,7 +23,11 @@ public class ViewCountFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { - AwemeStatistics statistics = item.getStatistics(); + AwemeStatistics statistics = item.statistics; + + // Fallback to getter if field is null + if (statistics == null) statistics = item.getStatistics(); + if (statistics == null) return false; long playCount = statistics.getPlayCount(); diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java index 3d3a6891d4..864d72ad6f 100644 --- a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java @@ -1,16 +1,17 @@ package com.ss.android.ugc.aweme.commerce; -import java.io.Serializable; - -public class AwemeCommerceStruct implements Serializable { - public long brandedContentType; - public long brandOrganicType; - +import com.ss.android.ugc.aweme.feed.model.AwemeRawAd; +public class AwemeCommerceStruct { + public boolean isBrandedContent() { - return this.brandedContentType > 0; + throw new UnsupportedOperationException("Stub"); } - public boolean isBrandOrganicContent() { - return this.brandOrganicType > 0; + public boolean isPseudoAd() { + throw new UnsupportedOperationException("Stub"); + } + + public AwemeRawAd getPseudoAdData() { + throw new UnsupportedOperationException("Stub"); } } \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/ShopAdStruct.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/ShopAdStruct.java new file mode 100644 index 0000000000..5c237f96fe --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/ShopAdStruct.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.commerce.model; + +public class ShopAdStruct { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/SimplePromotion.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/SimplePromotion.java new file mode 100644 index 0000000000..e2211e1877 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/SimplePromotion.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.commerce.model; + +public class SimplePromotion { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java index da42ea67f9..40811c02b0 100644 --- a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java @@ -1,61 +1,57 @@ package com.ss.android.ugc.aweme.feed.model; -import com.ss.android.ugc.aweme.commerce.AwemeCommerceStruct; +import com.ss.android.ugc.aweme.commerce.model.ShopAdStruct; +import com.ss.android.ugc.aweme.commerce.model.SimplePromotion; +import com.ss.android.ugc.aweme.search.ecom.data.Product; +import com.ss.android.ugc.aweme.commerce.AwemeCommerceStruct; import java.util.List; public class Aweme { - + + // Internal Feed Type Identifiers + public int awemeType; + public int adLinkType; + + // Live Stream Data + public RoomStruct room; + + // Monetization & Sponsored Traffic + public boolean isAd; + public boolean isSoftAd; + public AwemeRawAd awemeRawAd; public AwemeCommerceStruct mCommerceVideoAuthInfo; - public String getAid() { - throw new UnsupportedOperationException("Stub"); - } + // E-Commerce / Shop Data + public List productsInfo; + public List simplePromotions; + public ShopAdStruct shopAdStruct; + + // Non-Video Feed Injections (Fake Awemes) + public boolean isReferralFakeAweme; + public boolean isRecBigCardFakeAweme; - public boolean isAd() { - throw new UnsupportedOperationException("Stub"); - } + // Social & Follow Recommendations + public int recommendCardType; + public List familiarRecommendUser; - public boolean isLiveReplay() { - throw new UnsupportedOperationException("Stub"); - } + // Content Engagement Statistics + public AwemeStatistics statistics; - public long getLiveId() { - throw new UnsupportedOperationException("Stub"); - } + // Story Metadata + public boolean isTikTokStory; - public String getLiveType() { - throw new UnsupportedOperationException("Stub"); - } - - public boolean isWithPromotionalMusic() { - throw new UnsupportedOperationException("Stub"); - } - - public boolean getIsTikTokStory() { - throw new UnsupportedOperationException("Stub"); - } - - public List getImageInfos() { - throw new UnsupportedOperationException("Stub"); - } - - public PhotoModeImageInfo getPhotoModeImageInfo() { - throw new UnsupportedOperationException("Stub"); - } - - public PhotoModeTextInfo getPhotoModeTextInfo() { - throw new UnsupportedOperationException("Stub"); - } - - public AwemeStatistics getStatistics() { - throw new UnsupportedOperationException("Stub"); - } - - public String getShareUrl() { - throw new UnsupportedOperationException("Stub"); - } - - public AwemeCommerceStruct getCommerceAndAdSettingsStruct() { - throw new UnsupportedOperationException("Stub"); - } + public int getAwemeType() { throw new UnsupportedOperationException("Stub"); } + public RoomStruct getRoom() { throw new UnsupportedOperationException("Stub"); } + public boolean isAd() { throw new UnsupportedOperationException("Stub"); } + public boolean isSoftAd() { throw new UnsupportedOperationException("Stub"); } + public AwemeStatistics getStatistics() { throw new UnsupportedOperationException("Stub"); } + + // Stub methods for legacy compatibility + public String getAid() { throw new UnsupportedOperationException("Stub"); } + public boolean isLiveReplay() { throw new UnsupportedOperationException("Stub"); } + public long getLiveId() { throw new UnsupportedOperationException("Stub"); } + public String getLiveType() { throw new UnsupportedOperationException("Stub"); } + public boolean isWithPromotionalMusic() { throw new UnsupportedOperationException("Stub"); } + public String getShareUrl() { throw new UnsupportedOperationException("Stub"); } + public List getImageInfos() { throw new UnsupportedOperationException("Stub"); } } \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeRawAd.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeRawAd.java new file mode 100644 index 0000000000..14de433032 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeRawAd.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.feed.model; + +public class AwemeRawAd { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java index a9123e67cf..c4af1845f9 100644 --- a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java @@ -1,10 +1,13 @@ package com.ss.android.ugc.aweme.feed.model; public class AwemeStatistics { + + // Used by ViewCountFilter public long getPlayCount() { throw new UnsupportedOperationException("Stub"); } + public long getDiggCount() { throw new UnsupportedOperationException("Stub"); } -} +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/RoomStruct.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/RoomStruct.java new file mode 100644 index 0000000000..57e6d5cda2 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/RoomStruct.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.feed.model; + +public class RoomStruct { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/search/ecom/data/Product.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/search/ecom/data/Product.java new file mode 100644 index 0000000000..248e24a8d5 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/search/ecom/data/Product.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.search.ecom.data; + +public class Product { +} \ No newline at end of file