robust implementation of feed filter + bloat filter
This commit is contained in:
parent
ff16079369
commit
c17c4e9050
17 changed files with 209 additions and 128 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <T> void filterFeedList(
|
||||
String source,
|
||||
List<T> list,
|
||||
AwemeExtractor<T> extractor,
|
||||
boolean verbose
|
||||
AwemeExtractor<T> extractor
|
||||
) {
|
||||
if (list == null) return;
|
||||
|
||||
int initialSize = list.size();
|
||||
int removed = 0;
|
||||
Iterator<T> 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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.ss.android.ugc.aweme.commerce.model;
|
||||
|
||||
public class ShopAdStruct {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.ss.android.ugc.aweme.commerce.model;
|
||||
|
||||
public class SimplePromotion {
|
||||
}
|
||||
|
|
@ -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<Object> productsInfo;
|
||||
public List<Object> 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<Object> 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"); }
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.ss.android.ugc.aweme.feed.model;
|
||||
|
||||
public class AwemeRawAd {
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.ss.android.ugc.aweme.feed.model;
|
||||
|
||||
public class RoomStruct {
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.ss.android.ugc.aweme.search.ecom.data;
|
||||
|
||||
public class Product {
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue