This commit is contained in:
Ajay Ramachandran 2022-01-27 00:40:56 +01:00 committed by GitHub
commit 9ed3413049
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 137 additions and 17 deletions

View File

@ -1,7 +1,7 @@
# SponsorBlock YouTube Vanced Implementation
In order to use this in YouTube/Vanced you must first apply the smali mods applied to vanced (the patching process used for this is currently automated using our closed source tools with no plans to open source it for the time being) (if you mod vanced directly it is not required)
* First make your edits in android studio
* Change the string "replaceMeWithsetMillisecondMethod" on PlayerController.java to the method name of YouTube package
* Change the string "replaceMeWithsetMillisecondMethod", "replaceMeWithsetVolumeMethod", and "replaceMeWithgetVolumeMethod" on PlayerController.java to the method name of YouTube package
* Compile debug apk
* Decompile this apk using apktool https://github.com/iBotPeaches/Apktool
* Take this decompiled folder and look for a folder labeled pl in one of your dex class folders

View File

@ -23,6 +23,7 @@ public class SkipSponsorButton extends FrameLayout {
String TAG = "SkipSponsorButton";
public CharSequence skipSponsorTextViewText;
public CharSequence skipSponsorText;
public CharSequence muteSegmentText;
public ImageView skipSponsorButtonIcon;
public TextView skipSponsorTextView;
public int currentTextColor;
@ -88,7 +89,8 @@ public class SkipSponsorButton extends FrameLayout {
Resources resources = context.getResources();
this.defaultBottomMargin = resources.getDimensionPixelSize(getIdentifier(context, "skip_button_default_bottom_margin", "dimen")); // dimen:skip_button_default_bottom_margin
this.ctaBottomMargin = resources.getDimensionPixelSize(getIdentifier(context, "skip_button_cta_bottom_margin", "dimen")); // dimen:skip_button_cta_bottom_margin
this.skipSponsorText = resources.getText(getIdentifier(context, "skip_sponsor", "string")); // string:skip_ads "Skip ads"
this.skipSponsorText = resources.getText(getIdentifier(context, "skip_sponsor", "string")); // string:skip_ads "Skip segment"
this.muteSegmentText = resources.getText(getIdentifier(context, "mute_segment", "string")); // string:mute_segment "Mute segment"
this.skipSponsorBtnContainer.setOnClickListener(new View.OnClickListener() {
@Override
@ -119,6 +121,14 @@ public class SkipSponsorButton extends FrameLayout {
super.dispatchDraw(canvas);
}
public void setMuted(boolean muted) {
if (muted) {
this.skipSponsorTextView.setText(this.muteSegmentText);
} else {
this.skipSponsorTextView.setText(this.skipSponsorText);
}
}
public static int getColor(Context context, int arg3) {
return Build.VERSION.SDK_INT < 23 ? context.getResources().getColor(arg3) : context.getColor(arg3);

View File

@ -38,8 +38,8 @@ public class SponsorBlockView {
}
}
public static void showSkipButton() {
skipSponsorButtonVisibility(true);
public static void showSkipButton(boolean mute) {
skipSponsorButtonVisibility(true, mute);
}
public static void hideSkipButton() {
skipSponsorButtonVisibility(false);
@ -105,6 +105,10 @@ public class SponsorBlockView {
}
private static void skipSponsorButtonVisibility(boolean visible) {
skipSponsorButtonVisibility(visible, false);
}
private static void skipSponsorButtonVisibility(boolean visible, boolean mute) {
SkipSponsorButton skipSponsorButton = _skipSponsorButton.get();
if (skipSponsorButton == null) {
Log.e(TAG, "Unable to skipSponsorButtonVisibility");
@ -113,6 +117,7 @@ public class SponsorBlockView {
visible &= shouldShowOnPlayerType;
skipSponsorButton.setMuted(mute);
skipSponsorButton.setVisibility(visible ? View.VISIBLE : View.GONE);
bringLayoutToFront();
}

View File

@ -40,10 +40,15 @@ public class PlayerController {
public static SponsorSegment[] sponsorSegmentsOfCurrentVideo;
private static WeakReference<Object> currentPlayerController = new WeakReference<>(null);
private static Method setMillisecondMethod;
private static Method setVolumeMethod;
private static Method getVolumeMethod;
private static long allowNextSkipRequestTime = 0L;
private static String currentVideoId;
private static long currentVideoLength = 1L;
private static long lastKnownVideoTime = -1L;
private static long lastKnownVolume = -1L;
private static boolean currentlyMuted = false;
private static long muteEndTime = -1L;
private static final Runnable findAndSkipSegmentRunnable = () -> {
// Log.d(TAG, "findAndSkipSegmentRunnable");
findAndSkipSegment(false);
@ -111,6 +116,10 @@ public class PlayerController {
try {
setMillisecondMethod = o.getClass().getMethod("replaceMeWithsetMillisecondMethod", Long.TYPE);
setMillisecondMethod.setAccessible(true);
setVolumeMethod = o.getClass().getMethod("replaceMeWithsetVolumeMethod", Long.TYPE);
setVolumeMethod.setAccessible(true);
getVolumeMethod = o.getClass().getMethod("replaceMeWithgetVolumeMethod");
getVolumeMethod.setAccessible(true);
lastKnownVideoTime = 0;
VideoInformation.lastKnownVideoTime = 0;
@ -226,7 +235,12 @@ public class PlayerController {
for (final SponsorSegment segment : segments) {
if (segment.start > millis) {
if (segment.start > startTimerAtMillis)
long scheduleTime = segment.start;
if (muteEndTime > millis && muteEndTime < segment.start) {
scheduleTime = muteEndTime;
}
if (scheduleTime > startTimerAtMillis)
break; // it's more then START_TIMER_BEFORE_SEGMENT_MILLIS far away
if (!segment.category.behaviour.skip)
break;
@ -238,12 +252,12 @@ public class PlayerController {
@Override
public void run() {
skipSponsorTask = null;
lastKnownVideoTime = segment.start + 1;
lastKnownVideoTime = scheduleTime + 1;
VideoInformation.lastKnownVideoTime = lastKnownVideoTime;
new Handler(Looper.getMainLooper()).post(findAndSkipSegmentRunnable);
}
};
sponsorTimer.schedule(skipSponsorTask, segment.start - millis);
sponsorTimer.schedule(skipSponsorTask, scheduleTime - millis);
} else {
if (VERBOSE)
Log.d(TAG, "skipSponsorTask is already scheduled...");
@ -472,6 +486,45 @@ public class PlayerController {
});
}
public static void setMute(final boolean value) {
if (setVolumeMethod == null || getVolumeMethod == null) {
Log.e(TAG, "setVolumeMethod or getVolumeMethod is null");
return;
}
final Object currentObj = currentPlayerController.get();
if (currentObj == null) {
Log.e(TAG, "currentObj is null (might have been collected by GC)");
return;
}
if (VERBOSE)
Log.d(TAG, String.format("Requesting set mute to %b on thread %s", value, Thread.currentThread().toString()));
new Handler(Looper.getMainLooper()).post(() -> {
try {
if (VERBOSE)
Log.i(TAG, "Setting to mute=" + value);
long currentVolume = getVolumeMethod.invoke(currentObj);
if (value && currentVolume > 0) {
lastKnownVolume = currentVolume;
}
if (value) {
setVolumeMethod.invoke(currentObj, 0);
} else {
setVolumeMethod.invoke(currentObj, lastKnownVolume);
}
currentlyMuted = value;
} catch (Exception e) {
Log.e(TAG, "Cannot skip to millisecond", e);
}
});
}
private static void findAndSkipSegment(boolean wasClicked) {
if (sponsorSegmentsOfCurrentVideo == null)
@ -480,13 +533,18 @@ public class PlayerController {
final long millis = lastKnownVideoTime;
for (SponsorSegment segment : sponsorSegmentsOfCurrentVideo) {
if (currentlyMuted) {
setMute(false);
muteEndTime = -1;
}
if (segment.start > millis)
break;
if (segment.end < millis)
continue;
SkipSegmentView.show();
SkipSegmentView.show(segment.actionType == SponsorBlockSettings.ActionType.MUTE);
if (!(segment.category.behaviour.skip || wasClicked))
return;
@ -501,13 +559,24 @@ public class PlayerController {
private static void skipSegment(SponsorSegment segment, boolean wasClicked) {
// if (lastSkippedSegment == segment) return;
// lastSkippedSegment = segment;
if (VERBOSE)
Log.d(TAG, "Skipping segment: " + segment.toString());
if (VERBOSE) {
if (segment.actionType == SponsorBlockSettings.ActionType.SKIP) {
Log.d(TAG, "Skipping segment: " + segment.toString());
} else if (segment.actionType == SponsorBlockSettings.ActionType.MUTE) {
Log.d(TAG, "Muting segment: " + segment.toString());
}
}
if (SponsorBlockSettings.showToastWhenSkippedAutomatically && !wasClicked)
SkipSegmentView.notifySkipped(segment);
skipToMillisecond(segment.end + 2);
if (segment.actionType == SponsorBlockSettings.ActionType.SKIP) {
skipToMillisecond(segment.end + 2);
} else if (segment.actionType == SponsorBlockSettings.ActionType.MUTE) {
setMute(true);
if (segment.end > muteEndTime) muteEndTime = segment.end;
}
SkipSegmentView.hide();
if (segment.category == SponsorBlockSettings.SegmentInfo.UNSUBMITTED) {
SponsorSegment[] newSegments = new SponsorSegment[sponsorSegmentsOfCurrentVideo.length - 1];

View File

@ -19,8 +19,8 @@ public class SkipSegmentView {
public static final String TAG = "jakubweg.SkipSegmentView";
private static SponsorSegment lastNotifiedSegment;
public static void show() {
showSkipButton();
public static void show(boolean mute) {
showSkipButton(mute);
}
public static void hide() {

View File

@ -30,6 +30,7 @@ public class SponsorBlockSettings {
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 String PREFERENCES_KEY_CATEGORY_COLOR_SUFFIX = "_color";
public static final String PREFERENCES_KEY_MUTE_ENABLED = "sb-voting-enabled";
public static final SegmentBehaviour DefaultBehaviour = SegmentBehaviour.SKIP_AUTOMATICALLY;
@ -40,9 +41,11 @@ public class SponsorBlockSettings {
public static boolean showToastWhenSkippedAutomatically = true;
public static boolean countSkips = true;
public static boolean showTimeWithoutSegments = true;
public static boolean isMuteEnabled = true;
public static int adjustNewSegmentMillis = 150;
public static String uuid = "<invalid>";
public static String sponsorBlockUrlCategories = "[]";
public static String sponsorBlockUrlActionTypes = "[%22skip%22]";
public static int skippedSegments;
public static long skippedTime;
@ -124,6 +127,11 @@ public class SponsorBlockSettings {
else
sponsorBlockUrlCategories = "[%22" + TextUtils.join("%22,%22", enabledCategories) + "%22]";
isMuteEnabled = preferences.getBoolean(PREFERENCES_KEY_MUTE_ENABLED, isMuteEnabled);
if (isMuteEnabled) {
sponsorBlockUrlMute = "[%22skip%22, %mute%22]";
}
skippedSegments = preferences.getInt(PREFERENCES_KEY_SKIPPED_SEGMENTS, skippedSegments);
skippedTime = preferences.getLong(PREFERENCES_KEY_SKIPPED_SEGMENTS_TIME, skippedTime);
@ -249,4 +257,25 @@ public class SponsorBlockSettings {
return Html.fromHtml(String.format("<font color=\"#%06X\">⬤</font> %s", color, title));
}
}
public enum ActionType {
SKIP("skip"),
MUTE("mute");
private String key;
private static final Map<String, SegmentInfo> mValuesMap = new HashMap<>(values().length);
static {
for (SegmentInfo value : valuesWithoutUnsubmitted())
mValuesMap.put(value.key, value);
}
ActionType(String key) {
this.key = key;
}
public static SegmentInfo byKey(String key) {
return mValuesMap.get(key);
}
}
}

View File

@ -6,12 +6,15 @@ public class SponsorSegment implements Comparable<SponsorSegment> {
public final long start;
public final long end;
public final SponsorBlockSettings.SegmentInfo category;
public final SponsorBlockSettings.ActionType actionType;
public final String UUID;
public SponsorSegment(long start, long end, SponsorBlockSettings.SegmentInfo category, String UUID) {
public SponsorSegment(long start, long end, SponsorBlockSettings.SegmentInfo category,
SponsorBlockSettings.ActionType actionType, String UUID) {
this.start = start;
this.end = end;
this.category = category;
this.actionType = actionType;
this.UUID = UUID;
}
@ -21,6 +24,7 @@ public class SponsorSegment implements Comparable<SponsorSegment> {
"start=" + start +
", end=" + end +
", category='" + category + '\'' +
", actionType='" + actionType + '\'' +
'}';
}

View File

@ -39,7 +39,7 @@ public class Requester {
public static synchronized SponsorSegment[] getSegments(String videoId) {
List<SponsorSegment> segments = new ArrayList<>();
try {
HttpURLConnection connection = getConnectionFromRoute(Route.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories);
HttpURLConnection connection = getConnectionFromRoute(Route.GET_SEGMENTS, videoId, SponsorBlockSettings.sponsorBlockUrlCategories, SponsorBlockSettings.sponsorBlockUrlActionTypes);
int responseCode = connection.getResponseCode();
videoHasSegments = false;
timeWithoutSegments = "";
@ -53,11 +53,13 @@ public class Requester {
long start = (long) (segment.getDouble(0) * 1000);
long end = (long) (segment.getDouble(1) * 1000);
String category = obj.getString("category");
String actionType = obj.getString("actionType");
String uuid = obj.getString("UUID");
SponsorBlockSettings.SegmentInfo segmentCategory = SponsorBlockSettings.SegmentInfo.byCategoryKey(category);
SponsorBlockSettings.ActionType segmentActionType = SponsorBlockSettings.ActionType.byKey(category);
if (segmentCategory != null && segmentCategory.behaviour.showOnTimeBar) {
SponsorSegment sponsorSegment = new SponsorSegment(start, end, segmentCategory, uuid);
SponsorSegment sponsorSegment = new SponsorSegment(start, end, segmentCategory, segmentActionType, uuid);
segments.add(sponsorSegment);
}
}

View File

@ -6,7 +6,7 @@ import static pl.jakubweg.requests.Route.Method.POST;
import pl.jakubweg.SponsorBlockUtils;
public class Route {
public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}");
public static final Route GET_SEGMENTS = new Route(GET, "skipSegments?videoID={video_id}&categories={categories}&actionTypes={actionTypes}");
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}");

View File

@ -237,6 +237,7 @@
<string name="sb_guidelines_popup_open">Show me</string>
<string name="skip_sponsor">Skip segment</string>
<string name="mute_segment">Mute segment</string>
<string name="litho_comments">Comments removal</string>
<string name="litho_comments_off">Comments removal is turned off (new comments / phones only)</string>