From 4cadf54bc2e0ad2d6e4648bdd563ce246adf51ad Mon Sep 17 00:00:00 2001 From: polymorphicshade Date: Sun, 23 Aug 2020 15:03:48 -0600 Subject: [PATCH] SponsorBlock: Added exclusion list feature Added the ability to add uploaders to a persistent exclusion list by long-pressing the SponsorBlock icon. Segments won't be skipped, but they will still be marked. --- .../org/schabi/newpipe/player/BasePlayer.java | 46 +++++++--- .../newpipe/player/VideoPlayerImpl.java | 86 ++++++++++++++++--- .../settings/ContentSettingsFragment.java | 14 +++ .../schabi/newpipe/util/SponsorBlockMode.java | 7 ++ .../ic_sponsor_block_exclude_black_24dp.xml | 12 +++ .../ic_sponsor_block_exclude_white_24dp.xml | 13 +++ app/src/main/res/values/settings_keys.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/content_settings.xml | 6 ++ 9 files changed, 164 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/SponsorBlockMode.java create mode 100644 app/src/main/res/drawable/ic_sponsor_block_exclude_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_sponsor_block_exclude_white_24dp.xml diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 800425da6..2ebfc2b4f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -76,9 +76,11 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.SerializedCache; +import org.schabi.newpipe.util.SponsorBlockMode; import org.schabi.newpipe.util.VideoSegment; import java.io.IOException; +import java.util.Set; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -205,7 +207,7 @@ public abstract class BasePlayer implements private Disposable stateLoader; protected int currentState = STATE_PREFLIGHT; - private boolean isBlockingSponsors; + private SponsorBlockMode sponsorBlockMode = SponsorBlockMode.DISABLED; public BasePlayer(@NonNull final Context context) { this.context = context; @@ -237,9 +239,6 @@ public abstract class BasePlayer implements this.renderFactory = new DefaultRenderersFactory(context); this.mPrefs = PreferenceManager.getDefaultSharedPreferences(App.getApp()); - - isBlockingSponsors = mPrefs.getBoolean(context.getString(R.string.sponsor_block_enable_key), - false); } public void setup() { @@ -699,11 +698,25 @@ public abstract class BasePlayer implements if (DEBUG) { Log.d(TAG, "onBlockingSponsorsButtonClicked() called"); } - isBlockingSponsors = !isBlockingSponsors; + + switch (sponsorBlockMode) { + case DISABLED: + sponsorBlockMode = SponsorBlockMode.ENABLED; + break; + case ENABLED: + sponsorBlockMode = SponsorBlockMode.DISABLED; + break; + case EXCLUDE: + // ignored + } } - public boolean isBlockingSponsors() { - return isBlockingSponsors; + public SponsorBlockMode getSponsorBlockMode() { + return sponsorBlockMode; + } + + public void setSponsorBlockMode(final SponsorBlockMode mode) { + sponsorBlockMode = mode; } /*////////////////////////////////////////////////////////////////////////// @@ -731,9 +744,7 @@ public abstract class BasePlayer implements simpleExoPlayer.getBufferedPercentage() ); - if (isBlockingSponsors - && mPrefs.getBoolean( - context.getString(R.string.sponsor_block_enable_key), false)) { + if (sponsorBlockMode == SponsorBlockMode.ENABLED) { final VideoSegment segment = getSkippableSegment(currentProgress); if (segment == null) { return; @@ -1165,11 +1176,22 @@ public abstract class BasePlayer implements initThumbnail(info.getThumbnailUrl()); registerView(); + final boolean isSponsorBlockEnabled = mPrefs.getBoolean( + context.getString(R.string.sponsor_block_enable_key), false); + final Set channelExclusions = mPrefs.getStringSet( + context.getString(R.string.sponsor_block_exclusion_list_key), null); + + if (channelExclusions != null && channelExclusions.contains(info.getUploaderName())) { + sponsorBlockMode = SponsorBlockMode.EXCLUDE; + } else { + sponsorBlockMode = isSponsorBlockEnabled + ? SponsorBlockMode.ENABLED + : SponsorBlockMode.DISABLED; + } + if (info.getUrl().startsWith("https://www.youtube.com")) { final String apiUrl = mPrefs .getString(context.getString(R.string.sponsor_block_api_url_key), null); - final boolean isSponsorBlockEnabled = mPrefs - .getBoolean(context.getString(R.string.sponsor_block_enable_key), false); if (apiUrl != null && !apiUrl.isEmpty() && isSponsorBlockEnabled) { try { diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java index 209b948bb..7a69ba3d0 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java @@ -100,8 +100,11 @@ import org.schabi.newpipe.util.KoreUtil; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.ShareUtils; +import org.schabi.newpipe.util.SponsorBlockMode; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static android.content.Context.WINDOW_SERVICE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -409,7 +412,7 @@ public class VideoPlayerImpl extends VideoPlayer channelTextView.setVisibility(View.VISIBLE); } setMuteButton(muteButton, isMuted()); - setBlockSponsorsButton(blockSponsorsButton, isBlockingSponsors()); + setBlockSponsorsButton(blockSponsorsButton); animateRotation(moreOptionsButton, DEFAULT_CONTROLS_DURATION, 0); } @@ -483,6 +486,7 @@ public class VideoPlayerImpl extends VideoPlayer if (blockSponsorsButton != null) { blockSponsorsButton.setOnClickListener(this); + blockSponsorsButton.setOnLongClickListener(this); } settingsContentObserver = new ContentObserver(new Handler()) { @@ -630,6 +634,8 @@ public class VideoPlayerImpl extends VideoPlayer showHideKodiButton(); + setBlockSponsorsButton(blockSponsorsButton); + titleTextView.setText(tag.getMetadata().getName()); channelTextView.setText(tag.getMetadata().getUploaderName()); @@ -656,13 +662,18 @@ public class VideoPlayerImpl extends VideoPlayer @Override public void onBlockingSponsorsButtonClicked() { super.onBlockingSponsorsButtonClicked(); - setBlockSponsorsButton(blockSponsorsButton, isBlockingSponsors()); + setBlockSponsorsButton(blockSponsorsButton); - Toast.makeText(context, - isBlockingSponsors() - ? "SponsorBlock enabled" - : "SponsorBlock disabled", - Toast.LENGTH_SHORT).show(); + switch (getSponsorBlockMode()) { + case DISABLED: + Toast.makeText(context, "SponsorBlock disabled", Toast.LENGTH_SHORT).show(); + break; + case ENABLED: + Toast.makeText(context, "SponsorBlock enabled", Toast.LENGTH_SHORT).show(); + break; + case EXCLUDE: + // ignored + } } @Override @@ -857,6 +868,43 @@ public class VideoPlayerImpl extends VideoPlayer fragmentListener.onMoreOptionsLongClicked(); hideControls(0, 0); hideSystemUIIfNeeded(); + } else if (v.getId() == blockSponsorsButton.getId()) { + final MediaSourceTag currentMetadata = getCurrentMetadata(); + if (currentMetadata == null) { + return true; + } + + final Set channelExclusions = + mPrefs.getStringSet( + context.getString(R.string.sponsor_block_exclusion_list_key), + new HashSet<>()); + + final String toastText; + + if (getSponsorBlockMode() == SponsorBlockMode.EXCLUDE) { + if (channelExclusions != null) { + channelExclusions.remove(currentMetadata.getMetadata().getUploaderName()); + } + + setSponsorBlockMode(SponsorBlockMode.ENABLED); + toastText = "Uploader removed from SponsorBlock exclusion list"; + } else { + if (channelExclusions != null) { + channelExclusions.add(currentMetadata.getMetadata().getUploaderName()); + } + + setSponsorBlockMode(SponsorBlockMode.EXCLUDE); + toastText = "Uploader excluded from SponsorBlock"; + } + + mPrefs.edit() + .putStringSet( + context.getString(R.string.sponsor_block_exclusion_list_key), + channelExclusions) + .apply(); + + setBlockSponsorsButton(blockSponsorsButton); + Toast.makeText(context, toastText, Toast.LENGTH_LONG).show(); } return true; } @@ -1605,15 +1653,29 @@ public class VideoPlayerImpl extends VideoPlayer ? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp)); } - protected void setBlockSponsorsButton(final ImageButton button, - final boolean isBlockingSponsors) { + protected void setBlockSponsorsButton(final ImageButton button) { if (button == null) { return; } - button.setImageDrawable(AppCompatResources.getDrawable(service, isBlockingSponsors - ? R.drawable.ic_sponsor_block_enable_white_24dp - : R.drawable.ic_sponsor_block_disable_white_24dp)); + final SponsorBlockMode sponsorBlockMode = getSponsorBlockMode(); + final int resId; + + switch (sponsorBlockMode) { + case DISABLED: + resId = R.drawable.ic_sponsor_block_disable_white_24dp; + break; + case ENABLED: + resId = R.drawable.ic_sponsor_block_enable_white_24dp; + break; + case EXCLUDE: + resId = R.drawable.ic_sponsor_block_exclude_white_24dp; + break; + default: + return; + } + + button.setImageDrawable(AppCompatResources.getDrawable(service, resId)); } /** diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 9243b679f..dd2bfef01 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -40,6 +40,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.zip.ZipFile; @@ -169,6 +170,19 @@ public class ContentSettingsFragment extends BasePreferenceFragment { updateDependencies(preference, newValue); return true; }); + + final Preference sponsorBlockClearExclusionListPreference = + findPreference(getString(R.string.sponsor_block_clear_exclusion_list_key)); + sponsorBlockClearExclusionListPreference.setOnPreferenceClickListener((Preference p) -> { + getPreferenceManager() + .getSharedPreferences() + .edit() + .putStringSet( + getString(R.string.sponsor_block_exclusion_list_key), new HashSet<>()) + .apply(); + Toast.makeText(getContext(), "Exclusion list cleared", Toast.LENGTH_SHORT).show(); + return true; + }); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/util/SponsorBlockMode.java b/app/src/main/java/org/schabi/newpipe/util/SponsorBlockMode.java new file mode 100644 index 000000000..e9a75453c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/SponsorBlockMode.java @@ -0,0 +1,7 @@ +package org.schabi.newpipe.util; + +public enum SponsorBlockMode { + DISABLED, + ENABLED, + EXCLUDE +} diff --git a/app/src/main/res/drawable/ic_sponsor_block_exclude_black_24dp.xml b/app/src/main/res/drawable/ic_sponsor_block_exclude_black_24dp.xml new file mode 100644 index 000000000..f15834eed --- /dev/null +++ b/app/src/main/res/drawable/ic_sponsor_block_exclude_black_24dp.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_sponsor_block_exclude_white_24dp.xml b/app/src/main/res/drawable/ic_sponsor_block_exclude_white_24dp.xml new file mode 100644 index 000000000..8fafdd75a --- /dev/null +++ b/app/src/main/res/drawable/ic_sponsor_block_exclude_white_24dp.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 653eb9970..d77a18d2e 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -258,6 +258,8 @@ sponsor_block_category_self_promo_color sponsor_block_category_music sponsor_block_category_music_color + sponsor_block_exclusion_list + sponsor_block_clear_exclusion_list file_rename_charset diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49387a204..573efbaec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -706,4 +706,6 @@ Skipped unpaid/self promo Skipped non-music Toggle skipping sponsors + Clear Exclusion List + Clear the list of uploaders excluded from SponsorBlock. diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index 153dde113..453564be4 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -164,5 +164,11 @@ android:title="@string/settings_category_sponsor_block_categories_title" android:summary="@string/settings_category_sponsor_block_categories_summary"/> + +