Add initial implementation of mute segments
This commit is contained in:
parent
92d58b7963
commit
08c33e81ab
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}");
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue