From a029c0ef9eb18ecfeb6d0ba1db53a2b899c9b3e6 Mon Sep 17 00:00:00 2001 From: polymorphicshade Date: Tue, 4 Aug 2020 17:56:21 -0600 Subject: [PATCH] SponsorBlock: Added segment categories Added category preferences with customizable colors. Also did some related preference/strings refactoring and updated the API calls to SponsorBlock to no longer use legacy versions. --- .../schabi/newpipe/SponsorBlockApiTask.java | 82 +++++++--- .../org/schabi/newpipe/player/BasePlayer.java | 94 ++++++++--- .../schabi/newpipe/player/VideoPlayer.java | 89 ++++++++++- .../settings/ContentSettingsFragment.java | 80 +++++----- .../newpipe/settings/NewPipeSettings.java | 1 + ...ponsorBlockCategoriesSettingsFragment.java | 12 ++ .../schabi/newpipe/util/SponsorTimeInfo.java | 27 ---- .../org/schabi/newpipe/util/TimeFrame.java | 12 -- .../org/schabi/newpipe/util/VideoSegment.java | 13 ++ app/src/main/res/values/colors.xml | 7 + app/src/main/res/values/settings_keys.xml | 23 ++- app/src/main/res/values/strings.xml | 20 ++- app/src/main/res/xml/content_settings.xml | 44 ++--- .../xml/sponsor_block_category_settings.xml | 151 ++++++++++++++++++ 14 files changed, 508 insertions(+), 147 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SponsorBlockCategoriesSettingsFragment.java delete mode 100644 app/src/main/java/org/schabi/newpipe/util/SponsorTimeInfo.java delete mode 100644 app/src/main/java/org/schabi/newpipe/util/TimeFrame.java create mode 100644 app/src/main/java/org/schabi/newpipe/util/VideoSegment.java create mode 100644 app/src/main/res/xml/sponsor_block_category_settings.xml diff --git a/app/src/main/java/org/schabi/newpipe/SponsorBlockApiTask.java b/app/src/main/java/org/schabi/newpipe/SponsorBlockApiTask.java index 97dc73ee6..503967b3d 100644 --- a/app/src/main/java/org/schabi/newpipe/SponsorBlockApiTask.java +++ b/app/src/main/java/org/schabi/newpipe/SponsorBlockApiTask.java @@ -4,18 +4,21 @@ import android.app.Application; import android.content.Context; import android.net.ConnectivityManager; import android.os.AsyncTask; +import android.text.TextUtils; import android.util.Log; import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; -import org.schabi.newpipe.util.SponsorTimeInfo; -import org.schabi.newpipe.util.TimeFrame; +import org.schabi.newpipe.util.VideoSegment; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; import java.util.concurrent.ExecutionException; -public class SponsorBlockApiTask extends AsyncTask { +public class SponsorBlockApiTask extends AsyncTask { private static final Application APP = App.getApp(); private String apiUrl; private static final String TAG = SponsorBlockApiTask.class.getSimpleName(); @@ -25,30 +28,67 @@ public class SponsorBlockApiTask extends AsyncTask { this.apiUrl = apiUrl; } - public SponsorTimeInfo getYouTubeVideoSponsorTimes(final String videoId) - throws ExecutionException, InterruptedException { + public VideoSegment[] getYouTubeVideoSegments(final String videoId, + final boolean includeSponsorCategory, + final boolean includeIntroCategory, + final boolean includeOutroCategory, + final boolean includeInteractionCategory, + final boolean includeSelfPromoCategory, + final boolean includeMusicCategory) + throws ExecutionException, InterruptedException, UnsupportedEncodingException { - JsonObject obj = execute("getVideoSponsorTimes?videoID=" + videoId).get(); - JsonArray arrayObj = obj.getArray("sponsorTimes"); + ArrayList categoryParamList = new ArrayList<>(); - SponsorTimeInfo result = new SponsorTimeInfo(); - - for (int i = 0; i < arrayObj.size(); i++) { - JsonArray subArrayObj = arrayObj.getArray(i); - - double startTime = subArrayObj.getDouble(0) * 1000; - double endTime = subArrayObj.getDouble(1) * 1000; - - TimeFrame timeFrame = new TimeFrame(startTime, endTime); - - result.timeFrames.add(timeFrame); + if (includeSponsorCategory) { + categoryParamList.add("sponsor"); + } + if (includeIntroCategory) { + categoryParamList.add("intro"); + } + if (includeOutroCategory) { + categoryParamList.add("outro"); + } + if (includeInteractionCategory) { + categoryParamList.add("interaction"); + } + if (includeSelfPromoCategory) { + categoryParamList.add("selfpromo"); + } + if (includeMusicCategory) { + categoryParamList.add("music_offtopic"); } - return result; + if (categoryParamList.size() == 0) { + return null; + } + + String categoryParams = "[\"" + TextUtils.join("\",\"", categoryParamList) + "\"]"; + categoryParams = URLEncoder.encode(categoryParams, "utf-8"); + + String params = "skipSegments?videoID=" + videoId + "&categories=" + categoryParams; + + JsonArray arrayObj = execute(params).get(); + + ArrayList result = new ArrayList<>(); + + for (int i = 0; i < arrayObj.size(); i++) { + JsonObject obj = (JsonObject) arrayObj.get(i); + JsonArray segments = (JsonArray) obj.get("segment"); + + double startTime = segments.getDouble(0) * 1000; + double endTime = segments.getDouble(1) * 1000; + String category = obj.getString("category"); + + VideoSegment segment = new VideoSegment(startTime, endTime, category); + + result.add(segment); + } + + return result.toArray(new VideoSegment[0]); } @Override - protected JsonObject doInBackground(final String... strings) { + protected JsonArray doInBackground(final String... strings) { if (isCancelled() || !isConnected()) { return null; } @@ -60,7 +100,7 @@ public class SponsorBlockApiTask extends AsyncTask { .get(apiUrl + strings[0]) .responseBody(); - return JsonParser.object().from(responseBody); + return JsonParser.array().from(responseBody); } catch (Exception ex) { if (DEBUG) { 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 61e897578..25788a153 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -77,7 +77,7 @@ 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.SponsorTimeInfo; +import org.schabi.newpipe.util.VideoSegment; import java.io.IOException; @@ -114,9 +114,9 @@ public abstract class BasePlayer implements public static final int STATE_COMPLETED = 128; @NonNull - private final SharedPreferences mPrefs; + protected final SharedPreferences mPrefs; - private SponsorTimeInfo sponsorTimeInfo; + private VideoSegment[] videoSegments; /*////////////////////////////////////////////////////////////////////////// // Intent @@ -716,9 +716,8 @@ public abstract class BasePlayer implements simpleExoPlayer.getBufferedPercentage() ); - if (mPrefs.getBoolean(context.getString(R.string.sponsorblock_enable), false) - && sponsorTimeInfo != null) { - int skipTo = sponsorTimeInfo.getSponsorEndTimeFromProgress(currentProgress); + if (mPrefs.getBoolean(context.getString(R.string.sponsorblock_enable_key), false)) { + int skipTo = getSponsorEndTimeFromProgress(currentProgress); if (skipTo == 0) { return; @@ -726,8 +725,9 @@ public abstract class BasePlayer implements seekTo(skipTo); - if (mPrefs.getBoolean(context.getString(R.string.sponsorblock_notifications), false)) { - String toastText = context.getString(R.string.sponsorblock_skipped_sponsor); + if (mPrefs.getBoolean( + context.getString(R.string.sponsorblock_notifications_key), false)) { + String toastText = context.getString(R.string.sponsorblock_skipped_segment); Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show(); } @@ -738,6 +738,26 @@ public abstract class BasePlayer implements } } + private int getSponsorEndTimeFromProgress(final int progress) { + if (videoSegments == null) { + return 0; + } + + for (VideoSegment segment : videoSegments) { + if (progress < segment.startTime) { + continue; + } + + if (progress > segment.endTime) { + continue; + } + + return (int) Math.ceil((segment.endTime)); + } + + return 0; + } + private Disposable getProgressReactor() { return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, MILLISECONDS, mainThread()) .observeOn(mainThread()) @@ -1101,14 +1121,56 @@ public abstract class BasePlayer implements if (info.getUrl().startsWith("https://www.youtube.com")) { String apiUrl = mPrefs - .getString(context.getString(R.string.sponsorblock_api_url), null); + .getString(context.getString(R.string.sponsorblock_api_url_key), null); boolean isSponsorBlockEnabled = mPrefs - .getBoolean(context.getString(R.string.sponsorblock_enable), false); + .getBoolean(context.getString(R.string.sponsorblock_enable_key), false); if (apiUrl != null && !apiUrl.isEmpty() && isSponsorBlockEnabled) { try { - sponsorTimeInfo = new SponsorBlockApiTask(apiUrl) - .getYouTubeVideoSponsorTimes(info.getId()); + boolean includeSponsorCategory = + mPrefs.getBoolean( + context.getString( + R.string.sponsorblock_category_sponsor_key), + false); + + boolean includeIntroCategory = + mPrefs.getBoolean( + context.getString( + R.string.sponsorblock_category_intro_key), + false); + + boolean includeOutroCategory = + mPrefs.getBoolean( + context.getString( + R.string.sponsorblock_category_outro_key), + false); + + boolean includeInteractionCategory = + mPrefs.getBoolean( + context.getString( + R.string.sponsorblock_category_interaction_key), + false); + + boolean includeSelfPromoCategory = + mPrefs.getBoolean( + context.getString( + R.string.sponsorblock_category_self_promo_key), + false); + + boolean includeMusicCategory = + mPrefs.getBoolean( + context.getString( + R.string.sponsorblock_category_music_key), + false); + + videoSegments = new SponsorBlockApiTask(apiUrl) + .getYouTubeVideoSegments(info.getId(), + includeSponsorCategory, + includeIntroCategory, + includeOutroCategory, + includeInteractionCategory, + includeSelfPromoCategory, + includeMusicCategory); } catch (Exception e) { Log.e("SPONSOR_BLOCK", "Error getting YouTube video sponsor times.", e); } @@ -1653,11 +1715,7 @@ public abstract class BasePlayer implements && prefs.getBoolean(context.getString(R.string.enable_playback_resume_key), true); } - public SponsorTimeInfo getSponsorTimeInfo() { - return sponsorTimeInfo; - } - - public void setSponsorTimeInfo(final SponsorTimeInfo sponsorTimeInfo) { - this.sponsorTimeInfo = sponsorTimeInfo; + public VideoSegment[] getVideoSegments() { + return videoSegments; } } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index 1628daca1..44839cdb5 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -69,9 +69,8 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; import org.schabi.newpipe.util.AnimationUtils; +import org.schabi.newpipe.util.VideoSegment; import org.schabi.newpipe.views.ExpandableSurfaceView; -import org.schabi.newpipe.util.SponsorTimeInfo; -import org.schabi.newpipe.util.TimeFrame; import org.schabi.newpipe.views.MarkableSeekBar; import org.schabi.newpipe.views.SeekBarMarker; @@ -625,9 +624,9 @@ public abstract class VideoPlayer extends BasePlayer } private void markSponsorTimes() { - SponsorTimeInfo sponsorTimeInfo = getSponsorTimeInfo(); + VideoSegment[] segments = getVideoSegments(); - if (sponsorTimeInfo == null) { + if (segments == null || segments.length == 0) { return; } @@ -637,10 +636,17 @@ public abstract class VideoPlayer extends BasePlayer MarkableSeekBar markableSeekBar = (MarkableSeekBar) playbackSeekBar; - for (TimeFrame timeFrame : sponsorTimeInfo.timeFrames) { + for (VideoSegment segment : segments) { + Integer color = parseSegmentCategory(segment.category); + + // if null, then this category should not be marked + if (color == null) { + continue; + } + SeekBarMarker seekBarMarker = - new SeekBarMarker(timeFrame.startTime, timeFrame.endTime, - (int) simpleExoPlayer.getDuration(), Color.GREEN); + new SeekBarMarker(segment.startTime, segment.endTime, + (int) simpleExoPlayer.getDuration(), color); markableSeekBar.seekBarMarkers.add(seekBarMarker); markableSeekBar.invalidate(); @@ -649,6 +655,75 @@ public abstract class VideoPlayer extends BasePlayer } } + private Integer parseSegmentCategory(final String category) { + String key; + String colorStr; + switch (category) { + case "sponsor": + key = context.getString(R.string.sponsorblock_category_sponsor_key); + if (mPrefs.getBoolean(key, false)) { + key = context.getString(R.string.sponsorblock_category_sponsor_color_key); + colorStr = mPrefs.getString(key, null); + return colorStr == null + ? context.getResources().getColor(R.color.sponsor_segment) + : Color.parseColor(colorStr); + } + break; + case "intro": + key = context.getString(R.string.sponsorblock_category_intro_key); + if (mPrefs.getBoolean(key, false)) { + key = context.getString(R.string.sponsorblock_category_intro_color_key); + colorStr = mPrefs.getString(key, null); + return colorStr == null + ? context.getResources().getColor(R.color.intro_segment) + : Color.parseColor(colorStr); + } + break; + case "outro": + key = context.getString(R.string.sponsorblock_category_outro_key); + if (mPrefs.getBoolean(key, false)) { + key = context.getString(R.string.sponsorblock_category_outro_color_key); + colorStr = mPrefs.getString(key, null); + return colorStr == null + ? context.getResources().getColor(R.color.outro_segment) + : Color.parseColor(colorStr); + } + break; + case "interaction": + key = context.getString(R.string.sponsorblock_category_interaction_key); + if (mPrefs.getBoolean(key, false)) { + key = context.getString(R.string.sponsorblock_category_interaction_color_key); + colorStr = mPrefs.getString(key, null); + return colorStr == null + ? context.getResources().getColor(R.color.interaction_segment) + : Color.parseColor(colorStr); + } + break; + case "selfpromo": + key = context.getString(R.string.sponsorblock_category_self_promo_key); + if (mPrefs.getBoolean(key, false)) { + key = context.getString(R.string.sponsorblock_category_self_promo_color_key); + colorStr = mPrefs.getString(key, null); + return colorStr == null + ? context.getResources().getColor(R.color.self_promo_segment) + : Color.parseColor(colorStr); + } + break; + case "music_offtopic": + key = context.getString(R.string.sponsorblock_category_music_key); + if (mPrefs.getBoolean(key, false)) { + key = context.getString(R.string.sponsorblock_category_music_color_key); + colorStr = mPrefs.getString(key, null); + return colorStr == null + ? context.getResources().getColor(R.color.music_offtopic_segment) + : Color.parseColor(colorStr); + } + break; + } + + return null; + } + @Override public void destroy() { super.destroy(); 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 ec2a79aa1..b5f977f41 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -82,22 +82,26 @@ public class ContentSettingsFragment extends BasePreferenceFragment { @Override public boolean onPreferenceTreeClick(final Preference preference) { - if (preference.getKey().equals(thumbnailLoadToggleKey)) { - final ImageLoader imageLoader = ImageLoader.getInstance(); - imageLoader.stop(); - imageLoader.clearDiskCache(); - imageLoader.clearMemoryCache(); - imageLoader.resume(); - Toast.makeText(preference.getContext(), R.string.thumbnail_cache_wipe_complete_notice, - Toast.LENGTH_SHORT).show(); - } + String key = preference.getKey(); + if (key != null) { + if (key.equals(thumbnailLoadToggleKey)) { + final ImageLoader imageLoader = ImageLoader.getInstance(); + imageLoader.stop(); + imageLoader.clearDiskCache(); + imageLoader.clearMemoryCache(); + imageLoader.resume(); + Toast.makeText(preference.getContext(), + R.string.thumbnail_cache_wipe_complete_notice, + Toast.LENGTH_SHORT).show(); + } - if (preference.getKey().equals(youtubeRestrictedModeEnabledKey)) { - Context context = getContext(); - if (context != null) { - DownloaderImpl.getInstance().updateYoutubeRestrictedModeCookies(context); - } else { - Log.w(TAG, "onPreferenceTreeClick: null context"); + if (key.equals(youtubeRestrictedModeEnabledKey)) { + Context context = getContext(); + if (context != null) { + DownloaderImpl.getInstance().updateYoutubeRestrictedModeCookies(context); + } else { + Log.w(TAG, "onPreferenceTreeClick: null context"); + } } } @@ -142,7 +146,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { }); Preference sponsorBlockWebsitePreference = - findPreference(getString(R.string.sponsorblock_home_page)); + findPreference(getString(R.string.sponsorblock_home_page_key)); sponsorBlockWebsitePreference.setOnPreferenceClickListener((Preference p) -> { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.sponsorblock_homepage_url))); @@ -151,7 +155,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { }); Preference sponsorBlockPrivacyPreference = - findPreference(getString(R.string.sponsorblock_privacy)); + findPreference(getString(R.string.sponsorblock_privacy_key)); sponsorBlockPrivacyPreference.setOnPreferenceClickListener((Preference p) -> { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.sponsorblock_privacy_policy_url))); @@ -160,17 +164,10 @@ public class ContentSettingsFragment extends BasePreferenceFragment { }); Preference sponsorBlockApiUrlPreference = - findPreference(getString(R.string.sponsorblock_api_url)); - - // workaround to force dependency updates due to using a custom preference + findPreference(getString(R.string.sponsorblock_api_url_key)); sponsorBlockApiUrlPreference .setOnPreferenceChangeListener((preference, newValue) -> { - findPreference(getString(R.string.sponsorblock_enable)) - .onDependencyChanged(preference, - newValue == null || newValue.equals("")); - findPreference(getString(R.string.sponsorblock_notifications)) - .onDependencyChanged(preference, - newValue == null || newValue.equals("")); + updateDependencies(preference, newValue); return true; }); } @@ -179,21 +176,13 @@ public class ContentSettingsFragment extends BasePreferenceFragment { public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - // workaround to force dependency updates due to using a custom preference Preference sponsorBlockApiUrlPreference = - findPreference(getString(R.string.sponsorblock_api_url)); + findPreference(getString(R.string.sponsorblock_api_url_key)); String sponsorBlockApiUrlPreferenceValue = getPreferenceManager() .getSharedPreferences() - .getString(getString(R.string.sponsorblock_api_url), null); - findPreference(getString(R.string.sponsorblock_enable)) - .onDependencyChanged(sponsorBlockApiUrlPreference, - sponsorBlockApiUrlPreferenceValue == null - || sponsorBlockApiUrlPreferenceValue.equals("")); - findPreference(getString(R.string.sponsorblock_notifications)) - .onDependencyChanged(sponsorBlockApiUrlPreference, - sponsorBlockApiUrlPreferenceValue == null - || sponsorBlockApiUrlPreferenceValue.equals("")); + .getString(getString(R.string.sponsorblock_api_url_key), null); + updateDependencies(sponsorBlockApiUrlPreference, sponsorBlockApiUrlPreferenceValue); } @Override @@ -394,6 +383,23 @@ public class ContentSettingsFragment extends BasePreferenceFragment { } } + private void updateDependencies(final Preference preference, final Object newValue) { + // This is a workaround to force dependency updates for custom preferences. + + // sponsorblock_api_url_key + if (preference.getKey().equals(getString(R.string.sponsorblock_api_url_key))) { + findPreference(getString(R.string.sponsorblock_enable_key)) + .onDependencyChanged(preference, + newValue == null || newValue.equals("")); + findPreference(getString(R.string.sponsorblock_notifications_key)) + .onDependencyChanged(preference, + newValue == null || newValue.equals("")); + findPreference(getString(R.string.sponsorblock_categories_key)) + .onDependencyChanged(preference, + newValue == null || newValue.equals("")); + } + } + /*////////////////////////////////////////////////////////////////////////// // Error //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 47a16f6f3..7eb15831a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -45,6 +45,7 @@ public final class NewPipeSettings { PreferenceManager.setDefaultValues(context, R.xml.main_settings, true); PreferenceManager.setDefaultValues(context, R.xml.video_audio_settings, true); PreferenceManager.setDefaultValues(context, R.xml.debug_settings, true); + PreferenceManager.setDefaultValues(context, R.xml.sponsor_block_category_settings, true); getVideoDownloadFolder(context); getAudioDownloadFolder(context); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SponsorBlockCategoriesSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SponsorBlockCategoriesSettingsFragment.java new file mode 100644 index 000000000..d66fe5b4b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SponsorBlockCategoriesSettingsFragment.java @@ -0,0 +1,12 @@ +package org.schabi.newpipe.settings; + +import android.os.Bundle; + +import org.schabi.newpipe.R; + +public class SponsorBlockCategoriesSettingsFragment extends BasePreferenceFragment { + @Override + public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { + addPreferencesFromResource(R.xml.sponsor_block_category_settings); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/SponsorTimeInfo.java b/app/src/main/java/org/schabi/newpipe/util/SponsorTimeInfo.java deleted file mode 100644 index 864dc6499..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/SponsorTimeInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.schabi.newpipe.util; - -import java.util.ArrayList; - -public class SponsorTimeInfo { - public ArrayList timeFrames = new ArrayList<>(); - - public int getSponsorEndTimeFromProgress(final int progress) { - if (timeFrames == null) { - return 0; - } - - for (TimeFrame t : timeFrames) { - if (progress < t.startTime) { - continue; - } - - if (progress > t.endTime) { - continue; - } - - return (int) Math.ceil((t.endTime)); - } - - return 0; - } -} diff --git a/app/src/main/java/org/schabi/newpipe/util/TimeFrame.java b/app/src/main/java/org/schabi/newpipe/util/TimeFrame.java deleted file mode 100644 index 206528b6c..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/TimeFrame.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.schabi.newpipe.util; - -public class TimeFrame { - public double startTime; - public double endTime; - public Object tag; - - public TimeFrame(final double startTime, final double endTime) { - this.startTime = startTime; - this.endTime = endTime; - } -} diff --git a/app/src/main/java/org/schabi/newpipe/util/VideoSegment.java b/app/src/main/java/org/schabi/newpipe/util/VideoSegment.java new file mode 100644 index 000000000..e639993d1 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/VideoSegment.java @@ -0,0 +1,13 @@ +package org.schabi.newpipe.util; + +public class VideoSegment { + public double startTime; + public double endTime; + public String category; + + public VideoSegment(final double startTime, final double endTime, final String category) { + this.startTime = startTime; + this.endTime = endTime; + this.category = category; + } +} diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b23a0a50d..a4ad7a13b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -83,4 +83,11 @@ #000 #be757575 + + #00d400 + #00ffff + #0202ed + #cc00ff + #ffff00 + #ff9900 diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index f93a809cc..87e1dc1c7 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -239,11 +239,24 @@ downloads_storage_ask storage_use_saf - sponsorblock_home_page - sponsorblock_enable - sponsorblock_api_url - sponsorblock_notifications - sponsorblock_privacy + sponsorblock_home_page + sponsorblock_enable + sponsorblock_api_url + sponsorblock_notifications + sponsorblock_privacy + sponsorblock_categories + sponsorblock_category_sponsor + sponsorblock_category_sponsor_color + sponsorblock_category_intro + sponsorblock_category_intro_color + sponsorblock_category_outro + sponsorblock_category_outro_color + sponsorblock_category_interaction + sponsorblock_category_interaction_color + sponsorblock_category_self_promo + sponsorblock_category_self_promo_color + sponsorblock_category_music + sponsorblock_category_music_color file_rename_charset diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f7292030b..f994d3979 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,7 +132,23 @@ Other Debug Updates - SponsorBlock (beta, third party service) + SponsorBlock (beta, third party service) + SponsorBlock Categories + Customize which video segments to skip, along with their color markings on the seek bar. + Enable + Seek Bar Color + Sponsor + Paid promotion, paid referrals and direct advertisements. Not for self-promotion or free shoutouts to causes/creators/websites/products they like. + Intermission/Intro Animation + An interval without actual content. Could be a pause, static frame, repeating animation. This should not be used for transitions containing information or be used on music videos. + Endcards/Credits + Credits or when the YouTube endcards appear. Not for spoken conclusions. This should not include useful content. This should not be used on music videos. + Interaction Reminds (Subscribe) + When there is a short reminder to like, subscribe or follow them in the middle of content. If it is long or about something specific, it should be under self promotion instead. + Unpaid/Self Promotion + Similar to "sponsor" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with. + Music: Non-Music Section + Only for use in music videos. This includes introductions or outros in music videos. Playing in background Playing in popup mode Queued on background player @@ -617,7 +633,7 @@ Videos that have been watched before and after being added to the playlist will be removed.\nAre you sure? This cannot be undone! Yes, and partially watched videos Due to ExoPlayer constraints the seek duration was set to %d seconds - Sponsor skipped + Segment skipped %d second diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index ced92db47..c0f9fdcd7 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -120,41 +120,49 @@ + android:title="@string/settings_category_sponsorblock_title"> + android:title="@string/sponsorblock_home_page_title"/> + android:title="@string/sponsorblock_privacy_title"/> + android:title="@string/sponsorblock_api_url_title"/> + android:title="@string/sponsorblock_enable_title"/> + android:title="@string/sponsorblock_notifications_title"/> + + diff --git a/app/src/main/res/xml/sponsor_block_category_settings.xml b/app/src/main/res/xml/sponsor_block_category_settings.xml new file mode 100644 index 000000000..677cb98d0 --- /dev/null +++ b/app/src/main/res/xml/sponsor_block_category_settings.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file