Merge pull request #42 from caneleex/feature/show-stats

show user stats
This commit is contained in:
caneleex 2021-07-24 00:34:32 +02:00 committed by GitHub
commit 9be95a105b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 626 additions and 436 deletions

View File

@ -1,20 +1,16 @@
package fi.vanced.libraries.youtube.sponsors.player.ui;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

View File

@ -3,6 +3,7 @@ package pl.jakubweg;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Handler;
@ -21,6 +22,11 @@ import java.util.Timer;
import java.util.TimerTask;
import fi.vanced.libraries.youtube.player.VideoInformation;
import pl.jakubweg.objects.SponsorSegment;
import pl.jakubweg.requests.Requester;
import static pl.jakubweg.SponsorBlockSettings.skippedSegments;
import static pl.jakubweg.SponsorBlockSettings.skippedTime;
@SuppressLint({"LongLogTag"})
public class PlayerController {
@ -38,12 +44,9 @@ public class PlayerController {
private static String currentVideoId;
private static long currentVideoLength = 1L;
private static long lastKnownVideoTime = -1L;
private static final Runnable findAndSkipSegmentRunnable = new Runnable() {
@Override
public void run() {
private static final Runnable findAndSkipSegmentRunnable = () -> {
// Log.d(TAG, "findAndSkipSegmentRunnable");
findAndSkipSegment(false);
}
findAndSkipSegment(false);
};
private static float sponsorBarLeft = 1f;
private static float sponsorBarRight = 1f;
@ -122,7 +125,7 @@ public class PlayerController {
}
public static void executeDownloadSegments(String videoId) {
SponsorSegment[] segments = SponsorBlockUtils.getSegmentsForVideo(videoId);
SponsorSegment[] segments = Requester.getSegments(videoId);
Arrays.sort(segments);
if (VERBOSE)
@ -266,15 +269,21 @@ public class PlayerController {
}
private static void sendViewRequestAsync(final long millis, final SponsorSegment segment) {
new Thread(new Runnable() {
@Override
public void run() {
if (SponsorBlockSettings.countSkips &&
segment.category != SponsorBlockSettings.SegmentInfo.Unsubmitted &&
millis - segment.start < 2000) {
// Only skips from the start should count as a view
SponsorBlockUtils.sendViewCountRequest(segment);
}
if (segment.category != SponsorBlockSettings.SegmentInfo.UNSUBMITTED) {
Context context = YouTubeTikTokRoot_Application.getAppContext();
if (context != null) {
SharedPreferences preferences = SponsorBlockSettings.getPreferences(context);
long newSkippedTime = skippedTime + (segment.end - segment.start);
preferences.edit().putInt(SponsorBlockSettings.PREFERENCES_KEY_SKIPPED_SEGMENTS, skippedSegments + 1).apply();
preferences.edit().putLong(SponsorBlockSettings.PREFERENCES_KEY_SKIPPED_SEGMENTS_TIME, newSkippedTime).apply();
}
}
new Thread(() -> {
if (SponsorBlockSettings.countSkips &&
segment.category != SponsorBlockSettings.SegmentInfo.UNSUBMITTED &&
millis - segment.start < 2000) {
// Only skips from the start should count as a view
Requester.sendViewCountRequest(segment);
}
}).start();
}
@ -372,13 +381,10 @@ public class PlayerController {
if (VERBOSE)
Log.d(TAG, "addSkipSponsorView15: view=" + view.toString());
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
final ViewGroup viewGroup = (ViewGroup) ((ViewGroup) view).getChildAt(2);
Activity context = ((Activity) viewGroup.getContext());
NewSegmentHelperLayout.context = context;
}
new Handler(Looper.getMainLooper()).postDelayed(() -> {
final ViewGroup viewGroup = (ViewGroup) ((ViewGroup) view).getChildAt(2);
Activity context = ((Activity) viewGroup.getContext());
NewSegmentHelperLayout.context = context;
}, 500);
}
@ -386,13 +392,10 @@ public class PlayerController {
playerActivity = new WeakReference<>((Activity) view.getContext());
if (VERBOSE)
Log.d(TAG, "addSkipSponsorView14: view=" + view.toString());
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
final ViewGroup viewGroup = (ViewGroup) view.getParent();
Activity activity = (Activity) viewGroup.getContext();
NewSegmentHelperLayout.context = activity;
}
new Handler(Looper.getMainLooper()).postDelayed(() -> {
final ViewGroup viewGroup = (ViewGroup) view.getParent();
Activity activity = (Activity) viewGroup.getContext();
NewSegmentHelperLayout.context = activity;
}, 500);
}
@ -456,18 +459,15 @@ public class PlayerController {
Log.d(TAG, String.format("Requesting skip to millis=%d on thread %s", millisecond, Thread.currentThread().toString()));
final long finalMillisecond = millisecond;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
try {
if (VERBOSE)
Log.i(TAG, "Skipping to millis=" + finalMillisecond);
lastKnownVideoTime = finalMillisecond;
VideoInformation.lastKnownVideoTime = lastKnownVideoTime;
setMillisecondMethod.invoke(currentObj, finalMillisecond);
} catch (Exception e) {
Log.e(TAG, "Cannot skip to millisecond", e);
}
new Handler(Looper.getMainLooper()).post(() -> {
try {
if (VERBOSE)
Log.i(TAG, "Skipping to millis=" + finalMillisecond);
lastKnownVideoTime = finalMillisecond;
VideoInformation.lastKnownVideoTime = lastKnownVideoTime;
setMillisecondMethod.invoke(currentObj, finalMillisecond);
} catch (Exception e) {
Log.e(TAG, "Cannot skip to millisecond", e);
}
});
}
@ -509,7 +509,7 @@ public class PlayerController {
skipToMillisecond(segment.end + 2);
SkipSegmentView.hide();
if (segment.category == SponsorBlockSettings.SegmentInfo.Unsubmitted) {
if (segment.category == SponsorBlockSettings.SegmentInfo.UNSUBMITTED) {
SponsorSegment[] newSegments = new SponsorSegment[sponsorSegmentsOfCurrentVideo.length - 1];
int i = 0;
for (SponsorSegment sponsorSegment : sponsorSegmentsOfCurrentVideo) {

View File

@ -1,7 +1,6 @@
package pl.jakubweg;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;

View File

@ -8,6 +8,8 @@ import android.widget.Toast;
import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application;
import pl.jakubweg.objects.SponsorSegment;
import static fi.vanced.libraries.youtube.sponsors.player.ui.SponsorBlockView.hideSkipButton;
import static fi.vanced.libraries.youtube.sponsors.player.ui.SponsorBlockView.showSkipButton;
import static pl.jakubweg.PlayerController.VERBOSE;

View File

@ -3,7 +3,6 @@ package pl.jakubweg;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@ -18,8 +17,11 @@ import android.preference.SwitchPreference;
import android.text.InputType;
import android.widget.Toast;
import java.text.DecimalFormat;
import java.util.ArrayList;
import pl.jakubweg.requests.Requester;
import static pl.jakubweg.SponsorBlockSettings.DefaultBehaviour;
import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP;
import static pl.jakubweg.SponsorBlockSettings.PREFERENCES_KEY_COUNT_SKIPS;
@ -40,8 +42,9 @@ import static pl.jakubweg.StringRef.str;
@SuppressWarnings({"unused", "deprecation"}) // injected
public class SponsorBlockPreferenceFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
private ArrayList<Preference> preferencesToDisableWhenSBDisabled = new ArrayList<>();
public static final DecimalFormat FORMATTER = new DecimalFormat("#,###,###");
public static final String SAVED_TEMPLATE = "%dh %.1f minutes";
private final ArrayList<Preference> preferencesToDisableWhenSBDisabled = new ArrayList<>();
@Override
public void onCreate(Bundle savedInstanceState) {
@ -65,13 +68,10 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
preference.setChecked(SponsorBlockSettings.isSponsorBlockEnabled);
preference.setTitle(str("enable_sb"));
preference.setSummary(str("enable_sb_sum"));
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean value = (Boolean) newValue;
enableCategoriesIfNeeded(value);
return true;
}
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
final boolean value = (Boolean) newValue;
enableCategoriesIfNeeded(value);
return true;
});
}
@ -84,25 +84,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
preference.setTitle(str("enable_segmadding"));
preference.setSummary(str("enable_segmadding_sum"));
preferencesToDisableWhenSBDisabled.add(preference);
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
final boolean value = (Boolean) o;
if (value && !SponsorBlockSettings.seenGuidelinesPopup) {
new AlertDialog.Builder(preference.getContext())
.setTitle(str("sb_guidelines_popup_title"))
.setMessage(str("sb_guidelines_popup_content"))
.setNegativeButton(str("sb_guidelines_popup_already_read"), null)
.setPositiveButton(str("sb_guidelines_popup_open"), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
openGuidelines();
}
})
.show();
}
return true;
preference.setOnPreferenceChangeListener((preference12, o) -> {
final boolean value = (Boolean) o;
if (value && !SponsorBlockSettings.seenGuidelinesPopup) {
new AlertDialog.Builder(preference12.getContext())
.setTitle(str("sb_guidelines_popup_title"))
.setMessage(str("sb_guidelines_popup_content"))
.setNegativeButton(str("sb_guidelines_popup_already_read"), null)
.setPositiveButton(str("sb_guidelines_popup_open"), (dialogInterface, i) -> openGuidelines())
.show();
}
return true;
});
}
@ -119,6 +111,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
addGeneralCategory(context, preferenceScreen);
addSegmentsCategory(context, preferenceScreen);
addStatsCategory(context, preferenceScreen);
addAboutCategory(context, preferenceScreen);
enableCategoriesIfNeeded(SponsorBlockSettings.isSponsorBlockEnabled);
@ -173,6 +166,21 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
}
private void addStatsCategory(Context context, PreferenceScreen screen) {
PreferenceCategory category = new PreferenceCategory(context);
screen.addPreference(category);
category.setTitle(str("stats"));
preferencesToDisableWhenSBDisabled.add(category);
{
Preference preference = new Preference(context);
category.addPreference(preference);
preference.setTitle(str("stats_loading"));
Requester.retrieveUserStats(category, preference);
}
}
private void addAboutCategory(Context context, PreferenceScreen screen) {
PreferenceCategory category = new PreferenceCategory(context);
screen.addPreference(category);
@ -183,14 +191,11 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
screen.addPreference(preference);
preference.setTitle(str("about_api"));
preference.setSummary(str("about_api_sum"));
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("http://sponsor.ajay.app"));
preference.getContext().startActivity(i);
return false;
}
preference.setOnPreferenceClickListener(preference1 -> {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("https://sponsor.ajay.app"));
preference1.getContext().startActivity(i);
return false;
});
}
@ -212,12 +217,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
Preference preference = new Preference(context);
preference.setTitle(str("sb_guidelines_preference_title"));
preference.setSummary(str("sb_guidelines_preference_sum"));
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
openGuidelines();
return false;
}
preference.setOnPreferenceClickListener(preference1 -> {
openGuidelines();
return false;
});
screen.addPreference(preference);
}
@ -228,12 +230,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment implement
preference.setSummary(str("general_skiptoast_sum"));
preference.setKey(PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP);
preference.setDefaultValue(showToastWhenSkippedAutomatically);
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Toast.makeText(preference.getContext(), str("skipped_sponsor"), Toast.LENGTH_SHORT).show();
return false;
}
preference.setOnPreferenceClickListener(preference12 -> {
Toast.makeText(preference12.getContext(), str("skipped_sponsor"), Toast.LENGTH_SHORT).show();
return false;
});
preferencesToDisableWhenSBDisabled.add(preference);
screen.addPreference(preference);

View File

@ -25,12 +25,11 @@ public class SponsorBlockSettings {
public static final String PREFERENCES_KEY_SEEN_GUIDELINES = "sb-seen-gl";
public static final String PREFERENCES_KEY_NEW_SEGMENT_ENABLED = "sb-new-segment-enabled";
public static final String PREFERENCES_KEY_VOTING_ENABLED = "sb-voting-enabled";
public static final String sponsorBlockSkipSegmentsUrl = "https://sponsor.ajay.app/api/skipSegments";
public static final String sponsorBlockViewedUrl = "https://sponsor.ajay.app/api/viewedVideoSponsorTime";
public static final String sponsorBlockVoteUrl = "https://sponsor.ajay.app/api/voteOnSponsorTime";
public static final String PREFERENCES_KEY_SKIPPED_SEGMENTS = "sb-skipped-segments";
public static final String PREFERENCES_KEY_SKIPPED_SEGMENTS_TIME = "sb-skipped-segments-time";
public static final String PREFERENCES_KEY_SHOW_TIME_WITHOUT_SEGMENTS = "sb-length-without-segments";
public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SkipAutomatically;
public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SKIP_AUTOMATICALLY;
public static boolean isSponsorBlockEnabled = false;
public static boolean seenGuidelinesPopup = false;
@ -41,7 +40,9 @@ public class SponsorBlockSettings {
public static boolean showTimeWithoutSegments = true;
public static int adjustNewSegmentMillis = 150;
public static String uuid = "<invalid>";
private static String sponsorBlockUrlCategories = "[]";
public static String sponsorBlockUrlCategories = "[]";
public static int skippedSegments;
public static long skippedTime;
@SuppressWarnings("unused")
@Deprecated
@ -49,22 +50,6 @@ public class SponsorBlockSettings {
Log.e("jakubweg.Settings", "Do not call SponsorBlockSettings constructor!");
}
public static String getSponsorBlockUrlWithCategories(String videoId) {
return sponsorBlockSkipSegmentsUrl + "?videoID=" + videoId + "&categories=" + sponsorBlockUrlCategories;
}
public static String getSponsorBlockViewedUrl(String UUID) {
return sponsorBlockViewedUrl + "?UUID=" + UUID;
}
public static String getSponsorBlockVoteUrl(String uuid, String userId, int type) {
return sponsorBlockVoteUrl + "?UUID=" + uuid + "&userID=" + userId + "&type=" + type;
}
public static String getSponsorBlockVoteUrl(String uuid, String userId, String category) {
return sponsorBlockVoteUrl + "?UUID=" + uuid + "&userID=" + userId + "&category=" + category;
}
public static SharedPreferences getPreferences(Context context) {
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
}
@ -134,6 +119,8 @@ public class SponsorBlockSettings {
else
sponsorBlockUrlCategories = "[%22" + TextUtils.join("%22,%22", enabledCategories) + "%22]";
skippedSegments = preferences.getInt(PREFERENCES_KEY_SKIPPED_SEGMENTS, skippedSegments);
skippedTime = preferences.getLong(PREFERENCES_KEY_SKIPPED_SEGMENTS_TIME, skippedTime);
showToastWhenSkippedAutomatically = preferences.getBoolean(PREFERENCES_KEY_SHOW_TOAST_WHEN_SKIP, showToastWhenSkippedAutomatically);
String tmp1 = preferences.getString(PREFERENCES_KEY_ADJUST_NEW_SEGMENT_STEP, null);
@ -154,9 +141,9 @@ public class SponsorBlockSettings {
}
public enum SegmentBehaviour {
SkipAutomatically("skip", sf("skip_automatically"), true, true),
ManualSkip("manual-skip", sf("skip_showbutton"), false, true),
Ignore("ignore", sf("skip_ignore"), false, false);
SKIP_AUTOMATICALLY("skip", sf("skip_automatically"), true, true),
MANUAL_SKIP("manual-skip", sf("skip_showbutton"), false, true),
IGNORE("ignore", sf("skip_ignore"), false, false);
public final String key;
public final StringRef name;
@ -175,26 +162,25 @@ public class SponsorBlockSettings {
}
public enum SegmentInfo {
Sponsor("sponsor", sf("segments_sponsor"), sf("skipped_sponsor"), sf("segments_sponsor_sum"), null, 0xFF00d400),
Intro("intro", sf("segments_intermission"), sf("skipped_intermission"), sf("segments_intermission_sum"), null, 0xFF00ffff),
Outro("outro", sf("segments_endcards"), sf("skipped_endcard"), sf("segments_endcards_sum"), null, 0xFF0202ed),
Interaction("interaction", sf("segments_subscribe"), sf("skipped_subscribe"), sf("segments_subscribe_sum"), null, 0xFFcc00ff),
SelfPromo("selfpromo", sf("segments_selfpromo"), sf("skipped_selfpromo"), sf("segments_selfpromo_sum"), null, 0xFFffff00),
MusicOfftopic("music_offtopic", sf("segments_nomusic"), sf("skipped_nomusic"), sf("segments_nomusic_sum"), null, 0xFFff9900),
Preview("preview", sf("segments_preview"), sf("skipped_preview"), sf("segments_preview_sum"), null, 0xFF008fd6),
Unsubmitted("unsubmitted", StringRef.empty, sf("skipped_unsubmitted"), StringRef.empty, SegmentBehaviour.SkipAutomatically, 0xFFFFFFFF),
;
SPONSOR("sponsor", sf("segments_sponsor"), sf("skipped_sponsor"), sf("segments_sponsor_sum"), null, 0xFF00d400),
INTRO("intro", sf("segments_intermission"), sf("skipped_intermission"), sf("segments_intermission_sum"), null, 0xFF00ffff),
OUTRO("outro", sf("segments_endcards"), sf("skipped_endcard"), sf("segments_endcards_sum"), null, 0xFF0202ed),
INTERACTION("interaction", sf("segments_subscribe"), sf("skipped_subscribe"), sf("segments_subscribe_sum"), null, 0xFFcc00ff),
SELF_PROMO("selfpromo", sf("segments_selfpromo"), sf("skipped_selfpromo"), sf("segments_selfpromo_sum"), null, 0xFFffff00),
MUSIC_OFFTOPIC("music_offtopic", sf("segments_nomusic"), sf("skipped_nomusic"), sf("segments_nomusic_sum"), null, 0xFFff9900),
PREVIEW("preview", sf("segments_preview"), sf("skipped_preview"), sf("segments_preview_sum"), null, 0xFF008fd6),
UNSUBMITTED("unsubmitted", StringRef.empty, sf("skipped_unsubmitted"), StringRef.empty, SegmentBehaviour.SKIP_AUTOMATICALLY, 0xFFFFFFFF);
private static SegmentInfo[] mValuesWithoutUnsubmitted = new SegmentInfo[]{
Sponsor,
Intro,
Outro,
Interaction,
SelfPromo,
MusicOfftopic,
Preview
private static final SegmentInfo[] mValuesWithoutUnsubmitted = new SegmentInfo[]{
SPONSOR,
INTRO,
OUTRO,
INTERACTION,
SELF_PROMO,
MUSIC_OFFTOPIC,
PREVIEW
};
private static Map<String, SegmentInfo> mValuesMap = new HashMap<>(8);
private static final Map<String, SegmentInfo> mValuesMap = new HashMap<>(values().length);
static {
for (SegmentInfo value : valuesWithoutUnsubmitted())

View File

@ -4,52 +4,46 @@ import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Looper;
import android.content.Intent;
import android.net.Uri;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.apps.youtube.app.YouTubeTikTokRoot_Application;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import fi.razerman.youtube.Helpers.XSwipeHelper;
import fi.razerman.youtube.XGlobals;
import pl.jakubweg.objects.SponsorSegment;
import pl.jakubweg.objects.UserStats;
import pl.jakubweg.requests.Requester;
import static android.text.Html.fromHtml;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fi.razerman.youtube.XGlobals.debug;
import static pl.jakubweg.PlayerController.VERBOSE;
import static pl.jakubweg.PlayerController.getCurrentVideoId;
import static pl.jakubweg.PlayerController.getLastKnownVideoTime;
import static pl.jakubweg.PlayerController.sponsorSegmentsOfCurrentVideo;
import static pl.jakubweg.SponsorBlockSettings.getSponsorBlockVoteUrl;
import static pl.jakubweg.SponsorBlockSettings.sponsorBlockSkipSegmentsUrl;
import static pl.jakubweg.SponsorBlockSettings.uuid;
import static pl.jakubweg.SponsorBlockPreferenceFragment.FORMATTER;
import static pl.jakubweg.SponsorBlockPreferenceFragment.SAVED_TEMPLATE;
import static pl.jakubweg.SponsorBlockSettings.skippedSegments;
import static pl.jakubweg.SponsorBlockSettings.skippedTime;
import static pl.jakubweg.StringRef.str;
import static pl.jakubweg.requests.Requester.voteForSegment;
@SuppressWarnings({"LongLogTag"})
public abstract class SponsorBlockUtils {
@ -61,26 +55,20 @@ public abstract class SponsorBlockUtils {
public static final SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_FORMAT);
public static final SimpleDateFormat withoutSegmentsFormatter = new SimpleDateFormat(WITHOUT_SEGMENTS_FORMAT);
public static final SimpleDateFormat withoutSegmentsFormatterH = new SimpleDateFormat(WITHOUT_SEGMENTS_FORMAT_H);
private static boolean videoHasSegments = false;
private static String timeWithoutSegments = "";
public static boolean videoHasSegments = false;
public static String timeWithoutSegments = "";
private static final int sponsorBtnId = 1234;
public static final View.OnClickListener sponsorBlockBtnListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (debug) {
Log.d(TAG, "Shield button clicked");
}
NewSegmentHelperLayout.toggle();
public static final View.OnClickListener sponsorBlockBtnListener = v -> {
if (debug) {
Log.d(TAG, "Shield button clicked");
}
NewSegmentHelperLayout.toggle();
};
public static final View.OnClickListener voteButtonListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (debug) {
Log.d(TAG, "Vote button clicked");
}
SponsorBlockUtils.onVotingClicked(v.getContext());
public static final View.OnClickListener voteButtonListener = v -> {
if (debug) {
Log.d(TAG, "Vote button clicked");
}
SponsorBlockUtils.onVotingClicked(v.getContext());
};
private static int shareBtnId = -1;
private static long newSponsorSegmentDialogShownMillis;
@ -171,134 +159,88 @@ public abstract class SponsorBlockUtils {
new Thread(submitRunnable).start();
}
};
private static String messageToToast = "";
private static EditByHandSaveDialogListener editByHandSaveDialogListener = new EditByHandSaveDialogListener();
private static final DialogInterface.OnClickListener editByHandDialogListener = new DialogInterface.OnClickListener() {
@SuppressLint("DefaultLocale")
@Override
public void onClick(DialogInterface dialog, int which) {
Context context = ((AlertDialog) dialog).getContext();
public static String messageToToast = "";
private static final EditByHandSaveDialogListener editByHandSaveDialogListener = new EditByHandSaveDialogListener();
private static final DialogInterface.OnClickListener editByHandDialogListener = (dialog, which) -> {
Context context = ((AlertDialog) dialog).getContext();
final boolean isStart = DialogInterface.BUTTON_NEGATIVE == which;
final boolean isStart = DialogInterface.BUTTON_NEGATIVE == which;
final EditText textView = new EditText(context);
textView.setHint(DATE_FORMAT);
if (isStart) {
if (newSponsorSegmentStartMillis >= 0)
textView.setText(dateFormatter.format(new Date(newSponsorSegmentStartMillis)));
} else {
if (newSponsorSegmentEndMillis >= 0)
textView.setText(dateFormatter.format(new Date(newSponsorSegmentEndMillis)));
}
editByHandSaveDialogListener.settingStart = isStart;
editByHandSaveDialogListener.editText = new WeakReference<>(textView);
new AlertDialog.Builder(context)
.setTitle(str(isStart ? "new_segment_time_start" : "new_segment_time_end"))
.setView(textView)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(str("new_segment_now"), editByHandSaveDialogListener)
.setPositiveButton(android.R.string.ok, editByHandSaveDialogListener)
.show();
dialog.dismiss();
final EditText textView = new EditText(context);
textView.setHint(DATE_FORMAT);
if (isStart) {
if (newSponsorSegmentStartMillis >= 0)
textView.setText(dateFormatter.format(new Date(newSponsorSegmentStartMillis)));
} else {
if (newSponsorSegmentEndMillis >= 0)
textView.setText(dateFormatter.format(new Date(newSponsorSegmentEndMillis)));
}
editByHandSaveDialogListener.settingStart = isStart;
editByHandSaveDialogListener.editText = new WeakReference<>(textView);
new AlertDialog.Builder(context)
.setTitle(str(isStart ? "new_segment_time_start" : "new_segment_time_end"))
.setView(textView)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(str("new_segment_now"), editByHandSaveDialogListener)
.setPositiveButton(android.R.string.ok, editByHandSaveDialogListener)
.show();
dialog.dismiss();
};
private static final DialogInterface.OnClickListener segmentVoteClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final Context context = ((AlertDialog) dialog).getContext();
final SponsorSegment segment = sponsorSegmentsOfCurrentVideo[which];
final VoteOption[] voteOptions = VoteOption.values();
String[] items = new String[voteOptions.length];
for (int i = 0; i < voteOptions.length; i++) {
items[i] = voteOptions[i].title;
}
new AlertDialog.Builder(context)
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
appContext = new WeakReference<>(context.getApplicationContext());
switch (voteOptions[which]) {
case UPVOTE:
voteForSegment(segment, VoteOption.UPVOTE);
break;
case DOWNVOTE:
voteForSegment(segment, VoteOption.DOWNVOTE);
break;
case CATEGORY_CHANGE:
onNewCategorySelect(segment, context);
break;
}
}
})
.show();
}
private static final Runnable toastRunnable = () -> {
Context context = appContext.get();
if (context != null && messageToToast != null)
Toast.makeText(context, messageToToast, Toast.LENGTH_LONG).show();
};
private static Runnable toastRunnable = new Runnable() {
@Override
public void run() {
Context context = appContext.get();
if (context != null && messageToToast != null)
Toast.makeText(context, messageToToast, Toast.LENGTH_LONG).show();
private static final DialogInterface.OnClickListener segmentVoteClickListener = (dialog, which) -> {
final Context context = ((AlertDialog) dialog).getContext();
final SponsorSegment segment = sponsorSegmentsOfCurrentVideo[which];
final VoteOption[] voteOptions = VoteOption.values();
String[] items = new String[voteOptions.length];
for (int i = 0; i < voteOptions.length; i++) {
items[i] = voteOptions[i].title;
}
new AlertDialog.Builder(context)
.setItems(items, (dialog1, which1) -> {
appContext = new WeakReference<>(context.getApplicationContext());
switch (voteOptions[which1]) {
case UPVOTE:
voteForSegment(segment, VoteOption.UPVOTE, appContext.get(), toastRunnable);
break;
case DOWNVOTE:
voteForSegment(segment, VoteOption.DOWNVOTE, appContext.get(), toastRunnable);
break;
case CATEGORY_CHANGE:
onNewCategorySelect(segment, context);
break;
}
})
.show();
};
private static final Runnable submitRunnable = new Runnable() {
@Override
public void run() {
messageToToast = null;
final String uuid = SponsorBlockSettings.uuid;
final long start = newSponsorSegmentStartMillis;
final long end = newSponsorSegmentEndMillis;
final String videoId = getCurrentVideoId();
final SponsorBlockSettings.SegmentInfo segmentType = SponsorBlockUtils.newSponsorBlockSegmentType;
try {
if (start < 0 || end < 0 || start >= end || segmentType == null || videoId == null || uuid == null) {
Log.e(TAG, "Unable to submit times, invalid parameters");
return;
}
URL url = new URL(String.format(Locale.US,
sponsorBlockSkipSegmentsUrl + "?videoID=%s&userID=%s&startTime=%.3f&endTime=%.3f&category=%s",
videoId, uuid, ((float) start) / 1000f, ((float) end) / 1000f, segmentType.key));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
switch (connection.getResponseCode()) {
default:
messageToToast = String.format(str("submit_failed_unknown_error"), connection.getResponseCode(), connection.getResponseMessage());
break;
case 429:
messageToToast = str("submit_failed_rate_limit");
break;
case 403:
messageToToast = str("submit_failed_forbidden");
break;
case 409:
messageToToast = str("submit_failed_duplicate");
break;
case 200:
messageToToast = str("submit_succeeded");
break;
}
Log.i(TAG, "Segment submitted with status: " + connection.getResponseCode() + ", " + messageToToast);
new Handler(Looper.getMainLooper()).post(toastRunnable);
connection.disconnect();
newSponsorSegmentEndMillis = newSponsorSegmentStartMillis = -1;
} catch (Exception e) {
Log.e(TAG, "Unable to submit segment", e);
private static final Runnable submitRunnable = () -> {
messageToToast = null;
final String uuid = SponsorBlockSettings.uuid;
final long start = newSponsorSegmentStartMillis;
final long end = newSponsorSegmentEndMillis;
final String videoId = getCurrentVideoId();
final SponsorBlockSettings.SegmentInfo segmentType = SponsorBlockUtils.newSponsorBlockSegmentType;
try {
if (start < 0 || end < 0 || start >= end || segmentType == null || videoId == null || uuid == null) {
Log.e(TAG, "Unable to submit times, invalid parameters");
return;
}
if (videoId != null)
PlayerController.executeDownloadSegments(videoId);
Requester.submitSegments(videoId, uuid, ((float) start) / 1000f, ((float) end) / 1000f, segmentType.key, toastRunnable);
newSponsorSegmentEndMillis = newSponsorSegmentStartMillis = -1;
} catch (Exception e) {
Log.e(TAG, "Unable to submit segment", e);
}
if (videoId != null)
PlayerController.executeDownloadSegments(videoId);
};
static {
@ -344,7 +286,7 @@ public abstract class SponsorBlockUtils {
new AlertDialog.Builder(context)
.setTitle(str("new_segment_title"))
.setMessage(String.format(str("new_segment_mark_time_as_question"),
.setMessage(str("new_segment_mark_time_as_question",
newSponsorSegmentDialogShownMillis / 60000,
newSponsorSegmentDialogShownMillis / 1000 % 60,
newSponsorSegmentDialogShownMillis % 1000))
@ -362,7 +304,7 @@ public abstract class SponsorBlockUtils {
long end = (newSponsorSegmentEndMillis) / 1000;
new AlertDialog.Builder(context)
.setTitle(str("new_segment_confirm_title"))
.setMessage(String.format(str("new_segment_confirm_content"),
.setMessage(str("new_segment_confirm_content",
start / 60, start % 60,
end / 60, end % 60,
length / 60, length % 60))
@ -383,7 +325,7 @@ public abstract class SponsorBlockUtils {
List<CharSequence> titles = new ArrayList<>(segmentAmount); // I've replaced an array with a list to prevent null elements in the array as unsubmitted segments get filtered out
for (int i = 0; i < segmentAmount; i++) {
SponsorSegment segment = sponsorSegmentsOfCurrentVideo[i];
if (segment.category == SponsorBlockSettings.SegmentInfo.Unsubmitted) {
if (segment.category == SponsorBlockSettings.SegmentInfo.UNSUBMITTED) {
continue;
}
@ -411,12 +353,7 @@ public abstract class SponsorBlockUtils {
new AlertDialog.Builder(context)
.setTitle(str("new_segment_choose_category"))
.setItems(titles, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
voteForSegment(segment, VoteOption.CATEGORY_CHANGE, values[which].key);
}
})
.setItems(titles, (dialog, which) -> voteForSegment(segment, VoteOption.CATEGORY_CHANGE, appContext.get(), toastRunnable, values[which].key))
.show();
}
@ -431,7 +368,7 @@ public abstract class SponsorBlockUtils {
final SponsorSegment[] segments = original == null ? new SponsorSegment[1] : Arrays.copyOf(original, original.length + 1);
segments[segments.length - 1] = new SponsorSegment(newSponsorSegmentStartMillis, newSponsorSegmentEndMillis,
SponsorBlockSettings.SegmentInfo.Unsubmitted, null);
SponsorBlockSettings.SegmentInfo.UNSUBMITTED, null);
Arrays.sort(segments);
sponsorSegmentsOfCurrentVideo = segments;
@ -461,131 +398,6 @@ public abstract class SponsorBlockUtils {
}
}
public synchronized static SponsorSegment[] getSegmentsForVideo(String videoId) {
newSponsorSegmentEndMillis = newSponsorSegmentStartMillis = -1;
ArrayList<SponsorSegment> sponsorSegments = new ArrayList<>();
try {
if (VERBOSE)
Log.i(TAG, "Trying to download segments for videoId=" + videoId);
URL url = new URL(SponsorBlockSettings.getSponsorBlockUrlWithCategories(videoId));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
videoHasSegments = false;
timeWithoutSegments = "";
switch (connection.getResponseCode()) {
default:
Log.e(TAG, "Unable to download segments: Status: " + connection.getResponseCode() + " " + connection.getResponseMessage());
break;
case 404:
Log.w(TAG, "No segments for this video (ERR404)");
break;
case 200:
if (VERBOSE)
Log.i(TAG, "Received status 200 OK, parsing response...");
StringBuilder stringBuilder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
connection.getInputStream().close();
JSONArray responseArray = new JSONArray(stringBuilder.toString());
int length = responseArray.length();
for (int i = 0; i < length; i++) {
JSONObject obj = ((JSONObject) responseArray.get(i));
JSONArray segments = obj.getJSONArray("segment");
long start = (long) (segments.getDouble(0) * 1000);
long end = (long) (segments.getDouble(1) * 1000);
String category = obj.getString("category");
String UUID = obj.getString("UUID");
SponsorBlockSettings.SegmentInfo segmentCategory = SponsorBlockSettings.SegmentInfo.byCategoryKey(category);
if (segmentCategory != null && segmentCategory.behaviour.showOnTimeBar) {
SponsorSegment segment = new SponsorSegment(start, end, segmentCategory, UUID);
sponsorSegments.add(segment);
}
}
if (VERBOSE)
Log.v(TAG, "Parsing done");
videoHasSegments = true;
break;
}
connection.disconnect();
} catch (Exception e) {
Log.e(TAG, "download segments failed", e);
}
timeWithoutSegments = getTimeWithoutSegments(sponsorSegments);
return sponsorSegments.toArray(new SponsorSegment[0]);
}
private static int getIdentifier(String name, String defType) {
Context context = YouTubeTikTokRoot_Application.getAppContext();
return context.getResources().getIdentifier(name, defType, context.getPackageName());
}
public static void sendViewCountRequest(SponsorSegment segment) {
try {
URL url = new URL(SponsorBlockSettings.getSponsorBlockViewedUrl(segment.UUID));
Log.d("sponsorblock", "requesting: " + url.getPath());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void voteForSegment(SponsorSegment segment, VoteOption voteOption, String... args) {
messageToToast = null;
try {
String voteUrl = voteOption == VoteOption.CATEGORY_CHANGE
? getSponsorBlockVoteUrl(segment.UUID, uuid, args[0])
: getSponsorBlockVoteUrl(segment.UUID, uuid, voteOption == VoteOption.UPVOTE ? 1 : 0);
URL url = new URL(voteUrl);
Toast.makeText(appContext.get(), str("vote_started"), Toast.LENGTH_SHORT).show();
Log.d("sponsorblock", "requesting: " + url.getPath());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
switch (connection.getResponseCode()) {
default:
messageToToast = String.format(str("vote_failed_unknown_error"), connection.getResponseCode(), connection.getResponseMessage());
break;
case 429:
messageToToast = str("vote_failed_rate_limit");
break;
case 403:
messageToToast = str("vote_failed_forbidden");
break;
case 200:
messageToToast = str("vote_succeeded");
break;
}
Log.i(TAG, "Voted for segment with status: " + connection.getResponseCode() + ", " + messageToToast);
new Handler(Looper.getMainLooper()).post(toastRunnable);
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String appendTimeWithoutSegments(String totalTime) {
if (videoHasSegments && SponsorBlockSettings.showTimeWithoutSegments && !TextUtils.isEmpty(totalTime)) {
return totalTime + timeWithoutSegments;
@ -594,7 +406,7 @@ public abstract class SponsorBlockUtils {
return totalTime;
}
public static String getTimeWithoutSegments(ArrayList<SponsorSegment> sponsorSegmentsOfCurrentVideo) {
public static String getTimeWithoutSegments(List<SponsorSegment> sponsorSegmentsOfCurrentVideo) {
if (!SponsorBlockSettings.isSponsorBlockEnabled || !SponsorBlockSettings.showTimeWithoutSegments || sponsorSegmentsOfCurrentVideo == null) {
return "";
}
@ -610,7 +422,6 @@ public abstract class SponsorBlockUtils {
try {
if (videoHasSegments && (playerType.equalsIgnoreCase("NONE"))) {
PlayerController.setCurrentVideoId(null);
return;
}
}
catch (Exception ex) {
@ -618,7 +429,76 @@ public abstract class SponsorBlockUtils {
}
}
private enum VoteOption {
public static int countMatches(CharSequence seq, char c) {
int count = 0;
for (int i = 0; i < seq.length(); i++) {
if (seq.charAt(i) == c)
count++;
}
return count;
}
@SuppressWarnings("deprecation")
public static void addUserStats(PreferenceCategory category, Preference loadingPreference, UserStats stats) {
category.removePreference(loadingPreference);
Context context = category.getContext();
{
EditTextPreference preference = new EditTextPreference(context);
category.addPreference(preference);
String userName = stats.getUserName();
preference.setTitle(fromHtml(str("stats_username", userName)));
preference.setSummary(str("stats_username_change"));
preference.setText(userName);
preference.setOnPreferenceChangeListener((preference1, newUsername) -> {
Requester.setUsername((String) newUsername);
return false;
});
}
{
Preference preference = new Preference(context);
category.addPreference(preference);
String formatted = FORMATTER.format(stats.getSegmentCount());
preference.setTitle(fromHtml(str("stats_submissions", formatted)));
}
{
Preference preference = new Preference(context);
category.addPreference(preference);
String formatted = FORMATTER.format(stats.getViewCount());
double saved = stats.getMinutesSaved();
int hoursSaved = (int) (saved / 60);
double minutesSaved = saved % 60;
String formattedSaved = String.format(SAVED_TEMPLATE, hoursSaved, minutesSaved);
preference.setTitle(fromHtml(str("stats_saved", formatted)));
preference.setSummary(fromHtml(str("stats_saved_sum", formattedSaved)));
preference.setOnPreferenceClickListener(preference1 -> {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("https://sponsor.ajay.app/stats/"));
preference1.getContext().startActivity(i);
return false;
});
}
{
Preference preference = new Preference(context);
category.addPreference(preference);
String formatted = FORMATTER.format(skippedSegments);
long hoursSaved = skippedTime / 3600000;
double minutesSaved = (skippedTime / 60000d) % 60;
String formattedSaved = String.format(SAVED_TEMPLATE, hoursSaved, minutesSaved);
preference.setTitle(fromHtml(str("stats_self_saved", formatted)));
preference.setSummary(fromHtml(str("stats_self_saved_sum", formattedSaved)));
}
}
public enum VoteOption {
UPVOTE(str("vote_upvote")),
DOWNVOTE(str("vote_downvote")),
CATEGORY_CHANGE(str("vote_category"));

View File

@ -24,7 +24,7 @@ public class StringRef {
packageName = context.getPackageName();
}
private static HashMap<String, StringRef> strings = new HashMap<>();
private static final HashMap<String, StringRef> strings = new HashMap<>();
/**
* Gets strings reference from shared collection or creates if not exists yet,
@ -52,6 +52,18 @@ public class StringRef {
return sf(id).toString();
}
/**
* Gets string value by string id, shorthand for <code>sf(id).toString()</code> and formats the string
* with given args.
* @param id string resource name/id
* @param args the args to format the string with
* @return String value from string.xml formatted with given args
*/
@NonNull
public static String str(@NonNull String id, Object... args) {
return String.format(str(id), args);
}
/**
* Creates a StringRef object that'll not change it's value

View File

@ -1,7 +1,6 @@
package pl.jakubweg;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;

View File

@ -1,4 +1,6 @@
package pl.jakubweg;
package pl.jakubweg.objects;
import pl.jakubweg.SponsorBlockSettings;
public class SponsorSegment implements Comparable<SponsorSegment> {
public final long start;

View File

@ -0,0 +1,31 @@
package pl.jakubweg.objects;
public class UserStats {
private final String userName;
private final double minutesSaved;
private final int segmentCount;
private final int viewCount;
public UserStats(String userName, double minutesSaved, int segmentCount, int viewCount) {
this.userName = userName;
this.minutesSaved = minutesSaved;
this.segmentCount = segmentCount;
this.viewCount = viewCount;
}
public String getUserName() {
return userName;
}
public double getMinutesSaved() {
return minutesSaved;
}
public int getSegmentCount() {
return segmentCount;
}
public int getViewCount() {
return viewCount;
}
}

View File

@ -0,0 +1,201 @@
package pl.jakubweg.requests;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import pl.jakubweg.SponsorBlockSettings;
import pl.jakubweg.SponsorBlockUtils;
import pl.jakubweg.SponsorBlockUtils.VoteOption;
import pl.jakubweg.objects.SponsorSegment;
import pl.jakubweg.objects.UserStats;
import static pl.jakubweg.SponsorBlockUtils.timeWithoutSegments;
import static pl.jakubweg.SponsorBlockUtils.videoHasSegments;
import static pl.jakubweg.StringRef.str;
public class Requester {
private static final String SPONSORBLOCK_API_URL = "https://sponsor.ajay.app/api/";
private static final String TIME_TEMPLATE = "%.3f";
private Requester() {}
public static synchronized SponsorSegment[] getSegments(String videoId) {
List<SponsorSegment> segments = new ArrayList<>();
try {
HttpURLConnection connection = getConnectionFromRoute(Route.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories);
int responseCode = connection.getResponseCode();
videoHasSegments = false;
timeWithoutSegments = "";
switch (responseCode) {
case 200:
JSONArray responseArray = new JSONArray(parseJson(connection));
int length = responseArray.length();
for (int i = 0; i < length; i++) {
JSONObject obj = ((JSONObject) responseArray.get(i));
JSONArray segment = obj.getJSONArray("segment");
long start = (long) (segment.getDouble(0) * 1000);
long end = (long) (segment.getDouble(1) * 1000);
String category = obj.getString("category");
String uuid = obj.getString("UUID");
SponsorBlockSettings.SegmentInfo segmentCategory = SponsorBlockSettings.SegmentInfo.byCategoryKey(category);
if (segmentCategory != null && segmentCategory.behaviour.showOnTimeBar) {
SponsorSegment sponsorSegment = new SponsorSegment(start, end, segmentCategory, uuid);
segments.add(sponsorSegment);
}
}
videoHasSegments = true;
timeWithoutSegments = SponsorBlockUtils.getTimeWithoutSegments(segments);
break;
case 404:
break;
}
connection.disconnect();
}
catch (Exception ex) {
ex.printStackTrace();
}
return segments.toArray(new SponsorSegment[0]);
}
public static void submitSegments(String videoId, String uuid, float startTime, float endTime, String category, Runnable toastRunnable) {
try {
String start = String.format(TIME_TEMPLATE, startTime);
String end = String.format(TIME_TEMPLATE, endTime);
HttpURLConnection connection = getConnectionFromRoute(Route.SUBMIT_SEGMENTS, videoId, uuid, start, end, category);
int responseCode = connection.getResponseCode();
switch (responseCode) {
case 200:
SponsorBlockUtils.messageToToast = str("submit_succeeded");
break;
case 409:
SponsorBlockUtils.messageToToast = str("submit_failed_duplicate");
break;
case 403:
SponsorBlockUtils.messageToToast = str("submit_failed_forbidden");
break;
case 429:
SponsorBlockUtils.messageToToast = str("submit_failed_rate_limit");
break;
}
new Handler(Looper.getMainLooper()).post(toastRunnable);
connection.disconnect();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public static void sendViewCountRequest(SponsorSegment segment) {
try {
HttpURLConnection connection = getConnectionFromRoute(Route.VIEWED_SEGMENT, segment.UUID);
connection.disconnect();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public static void voteForSegment(SponsorSegment segment, VoteOption voteOption, Context context, Runnable toastRunnable, String... args) {
try {
String segmentUuid = segment.UUID;
String uuid = SponsorBlockSettings.uuid;
String vote = Integer.toString(voteOption == VoteOption.UPVOTE ? 1 : 0);
Toast.makeText(context, str("vote_started"), Toast.LENGTH_SHORT).show();
HttpURLConnection connection = voteOption == VoteOption.CATEGORY_CHANGE
? getConnectionFromRoute(Route.VOTE_ON_SEGMENT_CATEGORY, segmentUuid, uuid, args[0])
: getConnectionFromRoute(Route.VOTE_ON_SEGMENT_QUALITY, segmentUuid, uuid, vote);
int responseCode = connection.getResponseCode();
switch (responseCode) {
case 200:
SponsorBlockUtils.messageToToast = str("vote_succeeded");
break;
case 403:
SponsorBlockUtils.messageToToast = str("vote_failed_forbidden");
break;
case 429:
SponsorBlockUtils.messageToToast = str("vote_failed_rate_limit");
break;
default:
SponsorBlockUtils.messageToToast = str("vote_failed_unknown_error", responseCode, connection.getResponseMessage());
break;
}
new Handler(Looper.getMainLooper()).post(toastRunnable);
connection.disconnect();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public static void retrieveUserStats(PreferenceCategory category, Preference loadingPreference) {
if (!SponsorBlockSettings.isSponsorBlockEnabled) {
loadingPreference.setTitle(str("stats_sb_disabled"));
return;
}
new Thread(() -> {
try {
HttpURLConnection connection = getConnectionFromRoute(Route.GET_USER_STATS, SponsorBlockSettings.uuid);
JSONObject json = new JSONObject(parseJson(connection));
connection.disconnect();
UserStats stats = new UserStats(json.getString("userName"), json.getDouble("minutesSaved"), json.getInt("segmentCount"),
json.getInt("viewCount"));
SponsorBlockUtils.addUserStats(category, loadingPreference, stats);
}
catch (Exception ex) {
ex.printStackTrace();
}
}).start();
}
public static void setUsername(String username) {
try {
HttpURLConnection connection = getConnectionFromRoute(Route.CHANGE_USERNAME, SponsorBlockSettings.uuid, username);
connection.disconnect();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private static HttpURLConnection getConnectionFromRoute(Route route, String... params) throws IOException {
String url = SPONSORBLOCK_API_URL + route.compile(params).getCompiledRoute();
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod(route.getMethod().name());
return connection;
}
private static String parseJson(HttpURLConnection connection) throws IOException {
StringBuilder jsonBuilder = new StringBuilder();
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
jsonBuilder.append(line);
}
inputStream.close();
return jsonBuilder.toString();
}
}

View File

@ -0,0 +1,73 @@
package pl.jakubweg.requests;
import pl.jakubweg.SponsorBlockUtils;
import static pl.jakubweg.requests.Route.Method.*;
public class Route {
public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}");
public static final Route VIEWED_SEGMENT = new Route(POST, "viewedVideoSponsorTime?UUID={segment_id}");
public static final Route GET_USER_STATS = new Route(GET, "userInfo?userID={user_id}&values=[\"userName\", \"minutesSaved\", \"segmentCount\", \"viewCount\"]");
public static final Route CHANGE_USERNAME = new Route(POST, "setUsername?userID={user_id}&username={username}");
public static final Route SUBMIT_SEGMENTS = new Route(POST, "skipSegments?videoID={video_id}&userID={user_id}&startTime={start_time}&endTime={end_time}&category={category}");
public static final Route VOTE_ON_SEGMENT_QUALITY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&type={type}");
public static final Route VOTE_ON_SEGMENT_CATEGORY = new Route(POST, "voteOnSponsorTime?UUID={segment_id}&userID={user_id}&category={category}");
private final String route;
private final Method method;
private final int paramCount;
private Route(Method method, String route) {
this.method = method;
this.route = route;
this.paramCount = SponsorBlockUtils.countMatches(route, '{');
if (paramCount != SponsorBlockUtils.countMatches(route, '}'))
throw new IllegalArgumentException("Not enough parameters");
}
public Method getMethod() {
return method;
}
public CompiledRoute compile(String... params) {
if (params.length != paramCount)
throw new IllegalArgumentException("Error compiling route [" + route + "], incorrect amount of parameters provided. " +
"Expected: " + paramCount + ", provided: " + params.length);
StringBuilder compiledRoute = new StringBuilder(route);
for (int i = 0; i < paramCount; i++) {
int paramStart = compiledRoute.indexOf("{");
int paramEnd = compiledRoute.indexOf("}");
compiledRoute.replace(paramStart, paramEnd + 1, params[i]);
}
return new CompiledRoute(this, compiledRoute.toString());
}
public static class CompiledRoute {
private final Route baseRoute;
private final String compiledRoute;
private CompiledRoute(Route baseRoute, String compiledRoute) {
this.baseRoute = baseRoute;
this.compiledRoute = compiledRoute;
}
public Route getBaseRoute() {
return baseRoute;
}
public String getCompiledRoute() {
return compiledRoute;
}
public Method getMethod() {
return baseRoute.method;
}
}
public enum Method {
GET,
POST
}
}

View File

@ -182,6 +182,16 @@
<string name="skip_automatically">Skip automatically</string>
<string name="skip_showbutton">Show a skip button</string>
<string name="skip_ignore">Don\'t do anything</string>
<string name="stats">Stats</string>
<string name="stats_loading">Loading..</string>
<string name="stats_sb_disabled">SponsorBlock is disabled</string>
<string name="stats_username">Your username: &lt;b&gt;%s&lt;/b&gt;</string>
<string name="stats_username_change">Click to change your username</string>
<string name="stats_submissions">Submissions: &lt;b&gt;%s&lt;/b&gt;</string>
<string name="stats_saved">You\'ve saved people from &lt;b&gt;%s&lt;/b&gt; segments.</string>
<string name="stats_saved_sum">That\'s &lt;b&gt;%s&lt;/b&gt; of their lives. Click to see the leaderboard</string>
<string name="stats_self_saved">You\'ve skipped &lt;b&gt;%s&lt;/b&gt; segments.</string>
<string name="stats_self_saved_sum">That\'s &lt;b&gt;%s&lt;/b&gt;.</string>
<string name="about">About</string>
<string name="about_api">This app uses the API from Sponsor Block</string>
<string name="about_api_sum">Tap to learn more, and see downloads for other platforms at: sponsor.ajay.app</string>

View File

@ -2,10 +2,10 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -15,7 +15,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}