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.
This commit is contained in:
polymorphicshade 2020-08-04 17:56:21 -06:00
parent ed831376ff
commit a029c0ef9e
14 changed files with 508 additions and 147 deletions

View file

@ -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<String, Void, JsonObject> {
public class SponsorBlockApiTask extends AsyncTask<String, Void, JsonArray> {
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<String, Void, JsonObject> {
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<String> 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<VideoSegment> 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<String, Void, JsonObject> {
.get(apiUrl + strings[0])
.responseBody();
return JsonParser.object().from(responseBody);
return JsonParser.array().from(responseBody);
} catch (Exception ex) {
if (DEBUG) {

View file

@ -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;
}
}

View file

@ -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();

View file

@ -82,17 +82,20 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
@Override
public boolean onPreferenceTreeClick(final Preference preference) {
if (preference.getKey().equals(thumbnailLoadToggleKey)) {
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.makeText(preference.getContext(),
R.string.thumbnail_cache_wipe_complete_notice,
Toast.LENGTH_SHORT).show();
}
if (preference.getKey().equals(youtubeRestrictedModeEnabledKey)) {
if (key.equals(youtubeRestrictedModeEnabledKey)) {
Context context = getContext();
if (context != null) {
DownloaderImpl.getInstance().updateYoutubeRestrictedModeCookies(context);
@ -100,6 +103,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
Log.w(TAG, "onPreferenceTreeClick: null context");
}
}
}
return super.onPreferenceTreeClick(preference);
}
@ -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
//////////////////////////////////////////////////////////////////////////*/

View file

@ -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);

View file

@ -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);
}
}

View file

@ -1,27 +0,0 @@
package org.schabi.newpipe.util;
import java.util.ArrayList;
public class SponsorTimeInfo {
public ArrayList<TimeFrame> 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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -83,4 +83,11 @@
<color name="black">#000</color>
<color name="gray_transparent">#be757575</color>
<!-- default SponsorBlock segment colors -->
<color name="sponsor_segment">#00d400</color>
<color name="intro_segment">#00ffff</color>
<color name="outro_segment">#0202ed</color>
<color name="interaction_segment">#cc00ff</color>
<color name="self_promo_segment">#ffff00</color>
<color name="music_offtopic_segment">#ff9900</color>
</resources>

View file

@ -239,11 +239,24 @@
<string name="downloads_storage_ask" translatable="false">downloads_storage_ask</string>
<string name="storage_use_saf" translatable="false">storage_use_saf</string>
<string name="sponsorblock_home_page" translatable="false">sponsorblock_home_page</string>
<string name="sponsorblock_enable" translatable="false">sponsorblock_enable</string>
<string name="sponsorblock_api_url" translatable="false">sponsorblock_api_url</string>
<string name="sponsorblock_notifications" translatable="false">sponsorblock_notifications</string>
<string name="sponsorblock_privacy" translatable="false">sponsorblock_privacy</string>
<string name="sponsorblock_home_page_key" translatable="false">sponsorblock_home_page</string>
<string name="sponsorblock_enable_key" translatable="false">sponsorblock_enable</string>
<string name="sponsorblock_api_url_key" translatable="false">sponsorblock_api_url</string>
<string name="sponsorblock_notifications_key" translatable="false">sponsorblock_notifications</string>
<string name="sponsorblock_privacy_key" translatable="false">sponsorblock_privacy</string>
<string name="sponsorblock_categories_key" translatable="false">sponsorblock_categories</string>
<string name="sponsorblock_category_sponsor_key" translatable="false">sponsorblock_category_sponsor</string>
<string name="sponsorblock_category_sponsor_color_key" translatable="false">sponsorblock_category_sponsor_color</string>
<string name="sponsorblock_category_intro_key" translatable="false">sponsorblock_category_intro</string>
<string name="sponsorblock_category_intro_color_key" translatable="false">sponsorblock_category_intro_color</string>
<string name="sponsorblock_category_outro_key" translatable="false">sponsorblock_category_outro</string>
<string name="sponsorblock_category_outro_color_key" translatable="false">sponsorblock_category_outro_color</string>
<string name="sponsorblock_category_interaction_key" translatable="false">sponsorblock_category_interaction</string>
<string name="sponsorblock_category_interaction_color_key" translatable="false">sponsorblock_category_interaction_color</string>
<string name="sponsorblock_category_self_promo_key" translatable="false">sponsorblock_category_self_promo</string>
<string name="sponsorblock_category_self_promo_color_key" translatable="false">sponsorblock_category_self_promo_color</string>
<string name="sponsorblock_category_music_key" translatable="false">sponsorblock_category_music</string>
<string name="sponsorblock_category_music_color_key" translatable="false">sponsorblock_category_music_color</string>
<!-- FileName Downloads -->
<string name="settings_file_charset_key" translatable="false">file_rename_charset</string>

View file

@ -132,7 +132,23 @@
<string name="settings_category_other_title">Other</string>
<string name="settings_category_debug_title">Debug</string>
<string name="settings_category_updates_title">Updates</string>
<string name="settings_category_sponsorblock">SponsorBlock (beta, third party service)</string>
<string name="settings_category_sponsorblock_title">SponsorBlock (beta, third party service)</string>
<string name="settings_category_sponsorblock_categories_title">SponsorBlock Categories</string>
<string name="settings_category_sponsorblock_categories_summary">Customize which video segments to skip, along with their color markings on the seek bar.</string>
<string name="settings_category_sponsorblock_category_enable">Enable</string>
<string name="settings_category_sponsorblock_category_color">Seek Bar Color</string>
<string name="settings_category_sponsorblock_category_sponsor_title">Sponsor</string>
<string name="settings_category_sponsorblock_category_sponsor_summary">Paid promotion, paid referrals and direct advertisements. Not for self-promotion or free shoutouts to causes/creators/websites/products they like.</string>
<string name="settings_category_sponsorblock_category_intro_title">Intermission/Intro Animation</string>
<string name="settings_category_sponsorblock_category_intro_summary">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.</string>
<string name="settings_category_sponsorblock_category_outro_title">Endcards/Credits</string>
<string name="settings_category_sponsorblock_category_outro_summary">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.</string>
<string name="settings_category_sponsorblock_category_interaction_title">Interaction Reminds (Subscribe)</string>
<string name="settings_category_sponsorblock_category_interaction_summary">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.</string>
<string name="settings_category_sponsorblock_category_self_promo_title">Unpaid/Self Promotion</string>
<string name="settings_category_sponsorblock_category_self_promo_summary">Similar to "sponsor" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with.</string>
<string name="settings_category_sponsorblock_category_music_title">Music: Non-Music Section</string>
<string name="settings_category_sponsorblock_category_music_summary">Only for use in music videos. This includes introductions or outros in music videos.</string>
<string name="background_player_playing_toast">Playing in background</string>
<string name="popup_playing_toast">Playing in popup mode</string>
<string name="background_player_append">Queued on background player</string>
@ -617,7 +633,7 @@
<string name="remove_watched_popup_warning">Videos that have been watched before and after being added to the playlist will be removed.\nAre you sure? This cannot be undone!</string>
<string name="remove_watched_popup_yes_and_partially_watched_videos">Yes, and partially watched videos</string>
<string name="new_seek_duration_toast">Due to ExoPlayer constraints the seek duration was set to %d seconds</string>
<string name="sponsorblock_skipped_sponsor">Sponsor skipped</string>
<string name="sponsorblock_skipped_segment">Segment skipped</string>
<!-- Time duration plurals -->
<plurals name="seconds">
<item quantity="one">%d second</item>

View file

@ -120,41 +120,49 @@
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock">
android:title="@string/settings_category_sponsorblock_title">
<Preference
android:key="@string/sponsorblock_home_page"
app:iconSpaceReserved="false"
android:key="@string/sponsorblock_home_page_key"
android:summary="@string/sponsorblock_home_page_summary"
android:title="@string/sponsorblock_home_page_title"
app:iconSpaceReserved="false" />
android:title="@string/sponsorblock_home_page_title"/>
<Preference
android:key="@string/sponsorblock_privacy"
app:iconSpaceReserved="false"
android:key="@string/sponsorblock_privacy_key"
android:summary="@string/sponsorblock_privacy_summary"
android:title="@string/sponsorblock_privacy_title"
app:iconSpaceReserved="false" />
android:title="@string/sponsorblock_privacy_title"/>
<org.schabi.newpipe.settings.custom.SponsorBlockApiUrlPreference
android:key="@string/sponsorblock_api_url"
app:iconSpaceReserved="false"
android:key="@string/sponsorblock_api_url_key"
android:summary="@string/sponsorblock_api_url_summary"
android:title="@string/sponsorblock_api_url_title"
app:iconSpaceReserved="false" />
android:title="@string/sponsorblock_api_url_title"/>
<SwitchPreference
android:dependency="@string/sponsorblock_api_url"
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_api_url_key"
android:defaultValue="false"
android:key="@string/sponsorblock_enable"
android:key="@string/sponsorblock_enable_key"
android:summary="@string/sponsorblock_enable_summary"
android:title="@string/sponsorblock_enable_title"
app:iconSpaceReserved="false" />
android:title="@string/sponsorblock_enable_title"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="true"
android:dependency="@string/sponsorblock_api_url"
android:key="@string/sponsorblock_notifications"
android:dependency="@string/sponsorblock_api_url_key"
android:key="@string/sponsorblock_notifications_key"
android:summary="@string/sponsorblock_notifications_summary"
android:title="@string/sponsorblock_notifications_title"
app:iconSpaceReserved="false" />
android:title="@string/sponsorblock_notifications_title"/>
<PreferenceScreen
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_api_url_key"
android:fragment="org.schabi.newpipe.settings.SponsorBlockCategoriesSettingsFragment"
android:key="@string/sponsorblock_categories_key"
android:title="@string/settings_category_sponsorblock_categories_title"
android:summary="@string/settings_category_sponsorblock_categories_summary"/>
</PreferenceCategory>
</PreferenceScreen>

View file

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/settings_category_sponsorblock_categories_title">
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock_category_sponsor_title">
<Preference
app:iconSpaceReserved="false"
android:summary="@string/settings_category_sponsorblock_category_sponsor_summary"
android:selectable="false"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="true"
android:key="@string/sponsorblock_category_sponsor_key"
android:title="@string/settings_category_sponsorblock_category_enable"/>
<EditTextPreference
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_category_sponsor_key"
android:defaultValue="@color/sponsor_segment"
android:key="@string/sponsorblock_category_sponsor_color_key"
android:title="@string/settings_category_sponsorblock_category_color"/>
</PreferenceCategory>
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock_category_intro_title">
<Preference
app:iconSpaceReserved="false"
android:summary="@string/settings_category_sponsorblock_category_intro_summary"
android:selectable="false"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="false"
android:key="@string/sponsorblock_category_intro_key"
android:title="@string/settings_category_sponsorblock_category_enable"/>
<EditTextPreference
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_category_intro_key"
android:defaultValue="@color/intro_segment"
android:key="@string/sponsorblock_category_intro_color_key"
android:title="@string/settings_category_sponsorblock_category_color"/>
</PreferenceCategory>
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock_category_outro_title">
<Preference
app:iconSpaceReserved="false"
android:summary="@string/settings_category_sponsorblock_category_outro_summary"
android:selectable="false"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="false"
android:key="@string/sponsorblock_category_outro_key"
android:title="@string/settings_category_sponsorblock_category_enable"/>
<EditTextPreference
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_category_outro_key"
android:defaultValue="@color/outro_segment"
android:key="@string/sponsorblock_category_outro_color_key"
android:title="@string/settings_category_sponsorblock_category_color"/>
</PreferenceCategory>
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock_category_interaction_title">
<Preference
app:iconSpaceReserved="false"
android:summary="@string/settings_category_sponsorblock_category_interaction_summary"
android:selectable="false"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="false"
android:key="@string/sponsorblock_category_interaction_key"
android:title="@string/settings_category_sponsorblock_category_enable"/>
<EditTextPreference
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_category_interaction_key"
android:defaultValue="@color/interaction_segment"
android:key="@string/sponsorblock_category_interaction_color_key"
android:title="@string/settings_category_sponsorblock_category_color"/>
</PreferenceCategory>
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock_category_self_promo_title">
<Preference
app:iconSpaceReserved="false"
android:summary="@string/settings_category_sponsorblock_category_self_promo_summary"
android:selectable="false"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="false"
android:key="@string/sponsorblock_category_self_promo_key"
android:title="@string/settings_category_sponsorblock_category_enable"/>
<EditTextPreference
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_category_self_promo_key"
android:defaultValue="@color/self_promo_segment"
android:key="@string/sponsorblock_category_self_promo_color_key"
android:title="@string/settings_category_sponsorblock_category_color"/>
</PreferenceCategory>
<PreferenceCategory
android:layout="@layout/settings_category_header_layout"
android:title="@string/settings_category_sponsorblock_category_music_title">
<Preference
app:iconSpaceReserved="false"
android:summary="@string/settings_category_sponsorblock_category_music_summary"
android:selectable="false"/>
<SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="false"
android:key="@string/sponsorblock_category_music_key"
android:title="@string/settings_category_sponsorblock_category_enable"/>
<EditTextPreference
app:iconSpaceReserved="false"
android:dependency="@string/sponsorblock_category_music_key"
android:defaultValue="@color/music_offtopic_segment"
android:key="@string/sponsorblock_category_music_color_key"
android:title="@string/settings_category_sponsorblock_category_color"/>
</PreferenceCategory>
</PreferenceScreen>