SponsorBlock: Made changes requested in code review

See: TeamNewPipe#3205
This commit is contained in:
polymorphicshade 2020-05-14 22:16:09 -06:00
parent d30178f68f
commit 193a19d9d1
10 changed files with 27 additions and 324 deletions

View file

@ -1,6 +1,5 @@
package org.schabi.newpipe; package org.schabi.newpipe;
import android.annotation.SuppressLint;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@ -13,39 +12,18 @@ import org.json.JSONObject;
import org.schabi.newpipe.util.SponsorTimeInfo; import org.schabi.newpipe.util.SponsorTimeInfo;
import org.schabi.newpipe.util.TimeFrame; import org.schabi.newpipe.util.TimeFrame;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class SponsorBlockApiTask extends AsyncTask<String, Void, JSONObject> { public class SponsorBlockApiTask extends AsyncTask<String, Void, JSONObject> {
private static final Application APP = App.getApp(); private static final Application APP = App.getApp();
private static final String SPONSOR_BLOCK_API_URL = "https://api.sponsor.ajay.app/api/"; private static final String SPONSOR_BLOCK_API_URL = "https://sponsor.ajay.app/api/";
private static final int TIMEOUT_PERIOD = 30;
private static final String TAG = SponsorBlockApiTask.class.getSimpleName(); private static final String TAG = SponsorBlockApiTask.class.getSimpleName();
private static final boolean DEBUG = MainActivity.DEBUG; private static final boolean DEBUG = MainActivity.DEBUG;
private OkHttpClient client;
// api methods public SponsorTimeInfo getYouTubeVideoSponsorTimes(final String videoId)
public SponsorTimeInfo getVideoSponsorTimes(final String url) throws ExecutionException, throws ExecutionException, InterruptedException, JSONException {
InterruptedException, JSONException {
String videoId = parseIdFromUrl(url);
String apiSuffix = "getVideoSponsorTimes?videoID=" + videoId;
JSONObject obj = execute(apiSuffix).get(); JSONObject obj = execute("getVideoSponsorTimes?videoID=" + videoId).get();
JSONArray arrayObj = obj.getJSONArray("sponsorTimes"); JSONArray arrayObj = obj.getJSONArray("sponsorTimes");
SponsorTimeInfo result = new SponsorTimeInfo(); SponsorTimeInfo result = new SponsorTimeInfo();
@ -64,26 +42,6 @@ public class SponsorBlockApiTask extends AsyncTask<String, Void, JSONObject> {
return result; return result;
} }
public void postVideoSponsorTimes(final String url, final double startTime,
final double endTime, final String userId) {
double dStartTime = startTime / 1000.0;
double dEndTime = endTime / 1000.0;
String videoId = parseIdFromUrl(url);
String apiSuffix = "postVideoSponsorTimes?videoID="
+ videoId
+ "&startTime="
+ dStartTime
+ "&endTime="
+ dEndTime
+ "&userID=" + (userId == null
? getRandomUserId()
: userId);
execute(apiSuffix);
}
// task methods
@Override @Override
protected JSONObject doInBackground(final String... strings) { protected JSONObject doInBackground(final String... strings) {
if (isCancelled() || !isConnected()) { if (isCancelled() || !isConnected()) {
@ -91,23 +49,13 @@ public class SponsorBlockApiTask extends AsyncTask<String, Void, JSONObject> {
} }
try { try {
if (client == null) { String responseBody =
client = getUnsafeOkHttpClient() DownloaderImpl
.newBuilder() .getInstance()
.readTimeout(TIMEOUT_PERIOD, TimeUnit.SECONDS) .get(SPONSOR_BLOCK_API_URL + strings[0])
.build(); .responseBody();
}
Request request = new Request.Builder() return new JSONObject(responseBody);
.url(SPONSOR_BLOCK_API_URL + strings[0])
.build();
Response response = client.newCall(request).execute();
ResponseBody responseBody = response.body();
return responseBody == null
? null
: new JSONObject(responseBody.string());
} catch (Exception ex) { } catch (Exception ex) {
if (DEBUG) { if (DEBUG) {
@ -118,70 +66,10 @@ public class SponsorBlockApiTask extends AsyncTask<String, Void, JSONObject> {
return null; return null;
} }
// helper methods
private boolean isConnected() { private boolean isConnected() {
ConnectivityManager cm = ConnectivityManager cm =
(ConnectivityManager) APP.getSystemService(Context.CONNECTIVITY_SERVICE); (ConnectivityManager) APP.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null return cm.getActiveNetworkInfo() != null
&& cm.getActiveNetworkInfo().isConnected(); && cm.getActiveNetworkInfo().isConnected();
} }
private OkHttpClient getUnsafeOkHttpClient()
throws NoSuchAlgorithmException, KeyManagementException {
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkClientTrusted(final java.security.cert.X509Certificate[] chain,
final String authType) {
}
@SuppressLint("TrustAllX509TrustManager")
@Override
public void checkServerTrusted(final java.security.cert.X509Certificate[] chain,
final String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
return new OkHttpClient
.Builder()
.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
.hostnameVerifier((hostname, session) -> true)
.build();
}
private String parseIdFromUrl(final String youTubeUrl) {
String pattern = "(?<=youtu.be/|watch\\?v=|/videos/|embed/)[^#&?]*";
Pattern compiledPattern = Pattern.compile(pattern);
Matcher matcher = compiledPattern.matcher(youTubeUrl);
if (matcher.find()) {
return matcher.group();
} else {
return null;
}
}
private String getRandomUserId() {
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder salt = new StringBuilder();
Random random = new Random();
while (salt.length() < 36) {
int index = (int) (random.nextFloat() * chars.length());
salt.append(chars.charAt(index));
}
return salt.toString();
}
} }

View file

@ -1060,11 +1060,13 @@ public abstract class BasePlayer implements
initThumbnail(info.getThumbnailUrl()); initThumbnail(info.getThumbnailUrl());
registerView(); registerView();
if (mPrefs.getBoolean(context.getString(R.string.sponsorblock_enable), false)) { if (info.getUrl().startsWith("https://www.youtube.com")
&& mPrefs.getBoolean(context.getString(R.string.sponsorblock_enable), false)) {
try { try {
sponsorTimeInfo = new SponsorBlockApiTask().getVideoSponsorTimes(getVideoUrl()); sponsorTimeInfo = new SponsorBlockApiTask()
.getYouTubeVideoSponsorTimes(info.getId());
} catch (Exception e) { } catch (Exception e) {
Log.e("SPONSOR_BLOCK", "Error getting video sponsor times.", e); Log.e("SPONSOR_BLOCK", "Error getting YouTube video sponsor times.", e);
} }
} }
} }

View file

@ -20,7 +20,6 @@
package org.schabi.newpipe.player; package org.schabi.newpipe.player;
import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -71,7 +70,6 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.exoplayer2.ui.SubtitleView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.SponsorBlockApiTask;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
import org.schabi.newpipe.player.helper.PlaybackParameterDialog; import org.schabi.newpipe.player.helper.PlaybackParameterDialog;
@ -89,15 +87,10 @@ import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.SponsorTimeInfo;
import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.TimeFrame;
import org.schabi.newpipe.views.FocusOverlayView; import org.schabi.newpipe.views.FocusOverlayView;
import org.schabi.newpipe.views.MarkableSeekBar;
import org.schabi.newpipe.views.SeekBarMarker;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.UUID; import java.util.UUID;
@ -557,7 +550,6 @@ public final class MainVideoPlayer extends AppCompatActivity
private ImageButton switchPopupButton; private ImageButton switchPopupButton;
private ImageButton switchBackgroundButton; private ImageButton switchBackgroundButton;
private ImageButton muteButton; private ImageButton muteButton;
private ImageButton submitSponsorTimesButton;
private RelativeLayout windowRootLayout; private RelativeLayout windowRootLayout;
private View secondaryControls; private View secondaryControls;
@ -595,17 +587,6 @@ public final class MainVideoPlayer extends AppCompatActivity
this.toggleOrientationButton = view.findViewById(R.id.toggleOrientation); this.toggleOrientationButton = view.findViewById(R.id.toggleOrientation);
this.switchBackgroundButton = view.findViewById(R.id.switchBackground); this.switchBackgroundButton = view.findViewById(R.id.switchBackground);
this.muteButton = view.findViewById(R.id.switchMute); this.muteButton = view.findViewById(R.id.switchMute);
this.submitSponsorTimesButton = view.findViewById(R.id.submitSponsorTimes);
this.submitSponsorTimesButton.setTag(false);
this.submitSponsorTimesButton.setOnLongClickListener(v -> {
onSponsorBlockButtonLongClicked();
return true;
});
if (!defaultPreferences.getBoolean(getString(R.string.sponsorblock_enable), false)) {
this.submitSponsorTimesButton.setVisibility(View.GONE);
}
this.switchPopupButton = view.findViewById(R.id.switchPopup); this.switchPopupButton = view.findViewById(R.id.switchPopup);
this.queueLayout = findViewById(R.id.playQueuePanel); this.queueLayout = findViewById(R.id.playQueuePanel);
@ -655,7 +636,6 @@ public final class MainVideoPlayer extends AppCompatActivity
toggleOrientationButton.setOnClickListener(this); toggleOrientationButton.setOnClickListener(this);
switchBackgroundButton.setOnClickListener(this); switchBackgroundButton.setOnClickListener(this);
muteButton.setOnClickListener(this); muteButton.setOnClickListener(this);
submitSponsorTimesButton.setOnClickListener(this);
switchPopupButton.setOnClickListener(this); switchPopupButton.setOnClickListener(this);
getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> { getRootView().addOnLayoutChangeListener((view, l, t, r, b, ol, ot, or, ob) -> {
@ -836,110 +816,6 @@ public final class MainVideoPlayer extends AppCompatActivity
setMuteButton(muteButton, playerImpl.isMuted()); setMuteButton(muteButton, playerImpl.isMuted());
} }
private void onSponsorBlockButtonClicked() {
if (DEBUG) {
Log.d(TAG, "onSponsorBlockButtonClicked() called");
}
if (playerImpl.getPlayer() == null) {
return;
}
if ((boolean) submitSponsorTimesButton.getTag()) {
TimeFrame customTimeFrame =
new TimeFrame(customSponsorStartTime, simpleExoPlayer.getCurrentPosition());
customTimeFrame.tag = "custom";
SponsorTimeInfo sponsorTimeInfo = getSponsorTimeInfo();
if (sponsorTimeInfo == null) {
sponsorTimeInfo = new SponsorTimeInfo();
setSponsorTimeInfo(sponsorTimeInfo);
}
sponsorTimeInfo.timeFrames.add(customTimeFrame);
SeekBarMarker marker =
new SeekBarMarker(customTimeFrame.startTime, customTimeFrame.endTime,
(int) simpleExoPlayer.getDuration(), Color.BLUE);
marker.tag = "custom";
MarkableSeekBar markableSeekBar = (MarkableSeekBar) getPlaybackSeekBar();
markableSeekBar.seekBarMarkers.add(marker);
markableSeekBar.invalidate();
submitSponsorTimesButton.setTag(false);
submitSponsorTimesButton.setImageResource(R.drawable.ic_sponsor_block);
} else {
customSponsorStartTime = (int) simpleExoPlayer.getCurrentPosition();
submitSponsorTimesButton.setTag(true);
submitSponsorTimesButton.setImageResource(R.drawable.ic_sponsor_block_stop);
}
}
private void onSponsorBlockButtonLongClicked() {
if (DEBUG) {
Log.d(TAG, "onSponsorBlockButtonLongClicked() called");
}
if (playerImpl.getPlayer() == null) {
return;
}
ArrayList<SeekBarMarker> customMarkers = new ArrayList<>();
ArrayList<TimeFrame> customTimeFrames = new ArrayList<>();
for (SeekBarMarker marker : ((MarkableSeekBar) getPlaybackSeekBar()).seekBarMarkers) {
if (marker.tag == "custom") {
customMarkers.add(marker);
}
}
if (customMarkers.size() == 0) {
return;
}
SponsorTimeInfo sponsorTimeInfo = getSponsorTimeInfo();
if (sponsorTimeInfo != null) {
for (TimeFrame timeFrame : sponsorTimeInfo.timeFrames) {
if (timeFrame.tag == "custom") {
customTimeFrames.add(timeFrame);
}
}
}
new AlertDialog
.Builder(context)
.setMessage("Submit " + customMarkers.size() + " sponsor time segment(s)?")
.setPositiveButton("Yes", (dialog, id) -> {
String username = defaultPreferences
.getString(getString(R.string.sponsorblock_username), null);
for (TimeFrame timeFrame : customTimeFrames) {
try {
new SponsorBlockApiTask()
.postVideoSponsorTimes(getVideoUrl(), timeFrame.startTime,
timeFrame.endTime, username);
} catch (Exception e) {
Log.e("SPONSOR_BLOCK", "Error getting video sponsor times.", e);
}
}
})
.setNegativeButton("No", null)
.setNeutralButton("Clear", (dialog, id) -> {
for (SeekBarMarker marker : customMarkers) {
((MarkableSeekBar) getPlaybackSeekBar()).seekBarMarkers.remove(marker);
}
if (sponsorTimeInfo != null) {
for (TimeFrame timeFrame : customTimeFrames) {
sponsorTimeInfo.timeFrames.remove(timeFrame);
}
}
})
.create()
.show();
}
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
super.onClick(v); super.onClick(v);
@ -970,10 +846,6 @@ public final class MainVideoPlayer extends AppCompatActivity
onPlayBackgroundButtonClicked(); onPlayBackgroundButtonClicked();
} else if (v.getId() == muteButton.getId()) { } else if (v.getId() == muteButton.getId()) {
onMuteUnmuteButtonClicked(); onMuteUnmuteButtonClicked();
} else if (v.getId() == submitSponsorTimesButton.getId()) {
onSponsorBlockButtonClicked();
} else if (v.getId() == closeButton.getId()) { } else if (v.getId() == closeButton.getId()) {
onPlaybackShutdown(); onPlaybackShutdown();
return; return;

View file

@ -625,7 +625,7 @@ public abstract class VideoPlayer extends BasePlayer
super.onPrepared(playWhenReady); super.onPrepared(playWhenReady);
tryMarkSponsorTimes(); markSponsorTimes();
if (simpleExoPlayer.getCurrentPosition() != 0 && !isControlsVisible()) { if (simpleExoPlayer.getCurrentPosition() != 0 && !isControlsVisible()) {
controlsVisibilityHandler.removeCallbacksAndMessages(null); controlsVisibilityHandler.removeCallbacksAndMessages(null);
@ -634,7 +634,7 @@ public abstract class VideoPlayer extends BasePlayer
} }
} }
private void tryMarkSponsorTimes() { private void markSponsorTimes() {
SponsorTimeInfo sponsorTimeInfo = getSponsorTimeInfo(); SponsorTimeInfo sponsorTimeInfo = getSponsorTimeInfo();
if (sponsorTimeInfo == null) { if (sponsorTimeInfo == null) {

View file

@ -148,15 +148,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
startActivity(i); startActivity(i);
return true; return true;
}); });
Preference sponsorblockLeaderboardsPreference =
findPreference(getString(R.string.sponsorblock_leaderboards));
sponsorblockLeaderboardsPreference.setOnPreferenceClickListener((Preference p) -> {
Intent i = new Intent(Intent.ACTION_VIEW,
Uri.parse("https://api.sponsor.ajay.app/stats"));
startActivity(i);
return true;
});
} }
@Override @Override

View file

@ -414,22 +414,6 @@
android:contentDescription="@string/switch_to_background" android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
<ImageButton
android:id="@+id/submitSponsorTimes"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/switchMute"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_sponsor_block"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/submit_sponsor_times"
tools:ignore="RtlHardcoded"/>
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout

View file

@ -406,22 +406,6 @@
android:contentDescription="@string/switch_to_background" android:contentDescription="@string/switch_to_background"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
<ImageButton
android:id="@+id/submitSponsorTimes"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/switchMute"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="true"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_sponsor_block"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/submit_sponsor_times"
tools:ignore="RtlHardcoded"/>
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout

View file

@ -223,21 +223,9 @@
<string name="downloads_storage_ask" translatable="false">downloads_storage_ask</string> <string name="downloads_storage_ask" translatable="false">downloads_storage_ask</string>
<string name="storage_use_saf" translatable="false">storage_use_saf</string> <string name="storage_use_saf" translatable="false">storage_use_saf</string>
<string name="sponsorblock_enable">skip_sponsors</string> <string name="sponsorblock_enable" translatable="false">skip_sponsors</string>
<string name="sponsorblock_enable_title">Skip Sponsors</string> <string name="sponsorblock_notifications" translatable="false">sponsorblock_notifications</string>
<string name="sponsorblock_enable_summary">Use the SponsorBlock API to automatically skip sponsors in videos.</string> <string name="sponsorblock_status" translatable="false">sponsorblock_status</string>
<string name="sponsorblock_notifications">sponsorblock_notifications</string>
<string name="sponsorblock_notifications_title">Notify when sponsors are skipped</string>
<string name="sponsorblock_notifications_summary">Send a notification to your device when a sponsor is automatically skipped.</string>
<string name="sponsorblock_username">sponsorblock_username</string>
<string name="sponsorblock_username_title">Set Username</string>
<string name="sponsorblock_username_summary">Set a username used when submitting sponsor times. Leave empty to use a default randomized username.</string>
<string name="sponsorblock_status">sponsorblock_status</string>
<string name="sponsorblock_status_title">View Online Status</string>
<string name="sponsorblock_status_summary">View the online status of the SponsorBlock servers.</string>
<string name="sponsorblock_leaderboards">sponsorblock_leaderboards</string>
<string name="sponsorblock_leaderboards_title">View Leaderboards</string>
<string name="sponsorblock_leaderboards_summary">View the online leaderboards for SponsorBlock.</string>
<!-- FileName Downloads --> <!-- FileName Downloads -->
<string name="settings_file_charset_key" translatable="false">file_rename_charset</string> <string name="settings_file_charset_key" translatable="false">file_rename_charset</string>

View file

@ -661,4 +661,11 @@
<string name="channel_created_by">Created by %s</string> <string name="channel_created_by">Created by %s</string>
<string name="video_detail_by">By %s</string> <string name="video_detail_by">By %s</string>
<string name="playlist_page_summary">Playlist page</string> <string name="playlist_page_summary">Playlist page</string>
<!-- SponsorBlock -->
<string name="sponsorblock_enable_title">Skip Sponsors</string>
<string name="sponsorblock_enable_summary">Use the SponsorBlock API to automatically skip sponsors in videos.</string>
<string name="sponsorblock_notifications_title">Notify when sponsors are skipped</string>
<string name="sponsorblock_notifications_summary">Send a notification to your device when a sponsor is automatically skipped.</string>
<string name="sponsorblock_status_title">View Online Status</string>
<string name="sponsorblock_status_summary">View the online status of the SponsorBlock servers.</string>
</resources> </resources>

View file

@ -137,23 +137,10 @@
android:title="@string/sponsorblock_notifications_title" android:title="@string/sponsorblock_notifications_title"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<EditTextPreference
android:dependency="@string/sponsorblock_enable"
android:key="@string/sponsorblock_username"
android:summary="@string/sponsorblock_username_summary"
android:title="@string/sponsorblock_username_title"
app:iconSpaceReserved="false" />
<Preference <Preference
android:key="@string/sponsorblock_status" android:key="@string/sponsorblock_status"
android:summary="@string/sponsorblock_status_summary" android:summary="@string/sponsorblock_status_summary"
android:title="@string/sponsorblock_status_title" android:title="@string/sponsorblock_status_title"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<Preference
android:key="@string/sponsorblock_leaderboards"
android:summary="@string/sponsorblock_leaderboards_summary"
android:title="@string/sponsorblock_leaderboards_title"
app:iconSpaceReserved="false" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>