mirror of
https://github.com/revanced/revanced-integrations.git
synced 2025-02-01 06:37:32 +01:00
fix(youtube/sponsorblock): fix some segments skipping slightly too late (#436)
This commit is contained in:
parent
59850b2c04
commit
f69492819e
@ -24,7 +24,7 @@ public final class VideoInformation {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private static String videoId = "";
|
private static String videoId = "";
|
||||||
private static long videoLength = 0;
|
private static long videoLength = 0;
|
||||||
private static volatile long videoTime = -1; // must be volatile. Value is set off main thread from high precision patch hook
|
private static long videoTime = -1;
|
||||||
/**
|
/**
|
||||||
* The current playback speed
|
* The current playback speed
|
||||||
*/
|
*/
|
||||||
@ -98,17 +98,17 @@ public final class VideoInformation {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
* Called off the main thread approximately every 50ms to 100ms
|
* Called on the main thread every 1000ms.
|
||||||
*
|
*
|
||||||
* @param currentPlaybackTime The current playback time of the video in milliseconds.
|
* @param currentPlaybackTime The current playback time of the video in milliseconds.
|
||||||
*/
|
*/
|
||||||
public static void setVideoTimeHighPrecision(final long currentPlaybackTime) {
|
public static void setVideoTime(final long currentPlaybackTime) {
|
||||||
videoTime = currentPlaybackTime;
|
videoTime = currentPlaybackTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Seek on the current video.
|
* Seek on the current video.
|
||||||
* Does not function for playback of Shorts or Stories.
|
* Does not function for playback of Shorts.
|
||||||
*
|
*
|
||||||
* Caution: If called from a videoTimeHook() callback,
|
* Caution: If called from a videoTimeHook() callback,
|
||||||
* this will cause a recursive call into the same videoTimeHook() callback.
|
* this will cause a recursive call into the same videoTimeHook() callback.
|
||||||
@ -118,11 +118,6 @@ public final class VideoInformation {
|
|||||||
*/
|
*/
|
||||||
public static boolean seekTo(final long millisecond) {
|
public static boolean seekTo(final long millisecond) {
|
||||||
ReVancedUtils.verifyOnMainThread();
|
ReVancedUtils.verifyOnMainThread();
|
||||||
if (seekMethod == null) {
|
|
||||||
LogHelper.printException(() -> "seekMethod was null");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LogHelper.printDebug(() -> "Seeking to " + millisecond);
|
LogHelper.printDebug(() -> "Seeking to " + millisecond);
|
||||||
return (Boolean) seekMethod.invoke(playerControllerRef.get(), millisecond);
|
return (Boolean) seekMethod.invoke(playerControllerRef.get(), millisecond);
|
||||||
@ -137,7 +132,7 @@ public final class VideoInformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Id of the current video playing. Includes Shorts and YouTube Stories.
|
* Id of the current video playing. Includes Shorts.
|
||||||
*
|
*
|
||||||
* @return The id of the video. Empty string if not set yet.
|
* @return The id of the video. Empty string if not set yet.
|
||||||
*/
|
*/
|
||||||
@ -154,7 +149,7 @@ public final class VideoInformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Length of the current video playing. Includes Shorts and YouTube Stories.
|
* Length of the current video playing. Includes Shorts.
|
||||||
*
|
*
|
||||||
* @return The length of the video in milliseconds.
|
* @return The length of the video in milliseconds.
|
||||||
* If the video is not yet loaded, or if the video is playing in the background with no video visible,
|
* If the video is not yet loaded, or if the video is playing in the background with no video visible,
|
||||||
@ -165,14 +160,14 @@ public final class VideoInformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Playback time of the current video playing.
|
* Playback time of the current video playing. Includes Shorts.
|
||||||
* Value can lag up to approximately 100ms behind the actual current video playback time.
|
|
||||||
*
|
*
|
||||||
* Note: Code inside a videoTimeHook patch callback
|
* Value will lag behind the actual playback time by a variable amount based on the playback speed.
|
||||||
* should use the callback video time and avoid using this method
|
|
||||||
* (in situations of recursive hook callbacks, the value returned here may be outdated).
|
|
||||||
*
|
*
|
||||||
* Includes Shorts and YouTube Stories.
|
* If playback speed is 2.0x, this value may be up to 2000ms behind the actual playback time.
|
||||||
|
* If playback speed is 1.0x, this value may be up to 1000ms behind the actual playback time.
|
||||||
|
* If playback speed is 0.5x, this value may be up to 500ms behind the actual playback time.
|
||||||
|
* Etc.
|
||||||
*
|
*
|
||||||
* @return The time of the video in milliseconds. -1 if not set yet.
|
* @return The time of the video in milliseconds. -1 if not set yet.
|
||||||
*/
|
*/
|
||||||
@ -192,7 +187,7 @@ public final class VideoInformation {
|
|||||||
* @see VideoState
|
* @see VideoState
|
||||||
*/
|
*/
|
||||||
public static boolean isAtEndOfVideo() {
|
public static boolean isAtEndOfVideo() {
|
||||||
return videoTime > 0 && videoLength > 0 && videoTime >= videoLength;
|
return videoTime >= videoLength && videoLength > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ public class SegmentPlaybackController {
|
|||||||
/**
|
/**
|
||||||
* Because loading can take time, show the skip to highlight for a few seconds after the segments load.
|
* Because loading can take time, show the skip to highlight for a few seconds after the segments load.
|
||||||
* This is the system time (in milliseconds) to no longer show the initial display skip to highlight.
|
* This is the system time (in milliseconds) to no longer show the initial display skip to highlight.
|
||||||
|
* Value will be zero if no highlight segment exists, or if the system time to show the highlight has passed.
|
||||||
*/
|
*/
|
||||||
private static long highlightSegmentInitialShowEndTime;
|
private static long highlightSegmentInitialShowEndTime;
|
||||||
|
|
||||||
@ -198,7 +199,7 @@ public class SegmentPlaybackController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (PlayerType.getCurrent().isNoneOrHidden()) {
|
if (PlayerType.getCurrent().isNoneOrHidden()) {
|
||||||
LogHelper.printDebug(() -> "ignoring short or story");
|
LogHelper.printDebug(() -> "ignoring Short");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!ReVancedUtils.isNetworkConnected()) {
|
if (!ReVancedUtils.isNetworkConnected()) {
|
||||||
@ -238,14 +239,20 @@ public class SegmentPlaybackController {
|
|||||||
setSegments(segments);
|
setSegments(segments);
|
||||||
|
|
||||||
final long videoTime = VideoInformation.getVideoTime();
|
final long videoTime = VideoInformation.getVideoTime();
|
||||||
// if the current video time is before the highlight
|
if (highlightSegment != null) {
|
||||||
if (highlightSegment != null && videoTime < highlightSegment.end) {
|
// If the current video time is before the highlight.
|
||||||
if (highlightSegment.shouldAutoSkip()) {
|
final long timeUntilHighlight = highlightSegment.start - videoTime;
|
||||||
skipSegment(highlightSegment, false);
|
if (timeUntilHighlight > 0) {
|
||||||
return;
|
if (highlightSegment.shouldAutoSkip()) {
|
||||||
|
skipSegment(highlightSegment, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
highlightSegmentInitialShowEndTime = System.currentTimeMillis() + Math.min(
|
||||||
|
(long) (timeUntilHighlight / VideoInformation.getPlaybackSpeed()),
|
||||||
|
DURATION_TO_SHOW_SKIP_BUTTON);
|
||||||
}
|
}
|
||||||
highlightSegmentInitialShowEndTime = System.currentTimeMillis() + DURATION_TO_SHOW_SKIP_BUTTON;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for any skips now, instead of waiting for the next update to setVideoTime()
|
// check for any skips now, instead of waiting for the next update to setVideoTime()
|
||||||
setVideoTime(videoTime);
|
setVideoTime(videoTime);
|
||||||
});
|
});
|
||||||
@ -262,7 +269,7 @@ public class SegmentPlaybackController {
|
|||||||
public static void setVideoTime(long millis) {
|
public static void setVideoTime(long millis) {
|
||||||
try {
|
try {
|
||||||
if (!SettingsEnum.SB_ENABLED.getBoolean()
|
if (!SettingsEnum.SB_ENABLED.getBoolean()
|
||||||
|| PlayerType.getCurrent().isNoneOrHidden() // shorts playback
|
|| PlayerType.getCurrent().isNoneOrHidden() // Shorts playback.
|
||||||
|| segments == null || segments.length == 0) {
|
|| segments == null || segments.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -270,11 +277,17 @@ public class SegmentPlaybackController {
|
|||||||
|
|
||||||
updateHiddenSegments(millis);
|
updateHiddenSegments(millis);
|
||||||
|
|
||||||
// to debug the timing logic, set this to a very large value (5000 or more)
|
|
||||||
// then try manually seeking just playback reaches a skip/hide of different segments
|
|
||||||
final long lookAheadMilliseconds = 1500; // must be larger than the average time between calls to this method
|
|
||||||
final float playbackSpeed = VideoInformation.getPlaybackSpeed();
|
final float playbackSpeed = VideoInformation.getPlaybackSpeed();
|
||||||
final long startTimerLookAheadThreshold = millis + (long)(playbackSpeed * lookAheadMilliseconds);
|
// Amount of time to look ahead for the next segment,
|
||||||
|
// and the threshold to determine if a scheduled show/hide is at the correct video time when it's run.
|
||||||
|
//
|
||||||
|
// This value must be greater than largest time between calls to this method (1000ms),
|
||||||
|
// and must be adjusted for the video speed.
|
||||||
|
//
|
||||||
|
// To debug the stale skip logic, set this to a very large value (5000 or more)
|
||||||
|
// then try manually seeking just before playback reaches a segment skip.
|
||||||
|
final long speedAdjustedTimeThreshold = (long)(playbackSpeed * 1200);
|
||||||
|
final long startTimerLookAheadThreshold = millis + speedAdjustedTimeThreshold;
|
||||||
|
|
||||||
SponsorSegment foundSegmentCurrentlyPlaying = null;
|
SponsorSegment foundSegmentCurrentlyPlaying = null;
|
||||||
SponsorSegment foundUpcomingSegment = null;
|
SponsorSegment foundUpcomingSegment = null;
|
||||||
@ -344,9 +357,11 @@ public class SegmentPlaybackController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (highlightSegment != null) {
|
if (highlightSegment != null) {
|
||||||
if (millis < DURATION_TO_SHOW_SKIP_BUTTON || System.currentTimeMillis() < highlightSegmentInitialShowEndTime) {
|
if (millis < DURATION_TO_SHOW_SKIP_BUTTON || (highlightSegmentInitialShowEndTime != 0
|
||||||
|
&& System.currentTimeMillis() < highlightSegmentInitialShowEndTime)) {
|
||||||
SponsorBlockViewController.showSkipHighlightButton(highlightSegment);
|
SponsorBlockViewController.showSkipHighlightButton(highlightSegment);
|
||||||
} else {
|
} else {
|
||||||
|
highlightSegmentInitialShowEndTime = 0;
|
||||||
SponsorBlockViewController.hideSkipHighlightButton();
|
SponsorBlockViewController.hideSkipHighlightButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,12 +376,9 @@ public class SegmentPlaybackController {
|
|||||||
SponsorBlockViewController.hideSkipSegmentButton();
|
SponsorBlockViewController.hideSkipSegmentButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be greater than the average time between updates to VideoInformation time
|
|
||||||
final long videoInformationTimeUpdateThresholdMilliseconds = 250;
|
|
||||||
|
|
||||||
// schedule a hide, only if the segment end is near
|
// schedule a hide, only if the segment end is near
|
||||||
final SponsorSegment segmentToHide =
|
final SponsorSegment segmentToHide =
|
||||||
(foundSegmentCurrentlyPlaying != null && foundSegmentCurrentlyPlaying.endIsNear(millis, lookAheadMilliseconds))
|
(foundSegmentCurrentlyPlaying != null && foundSegmentCurrentlyPlaying.endIsNear(millis, speedAdjustedTimeThreshold))
|
||||||
? foundSegmentCurrentlyPlaying
|
? foundSegmentCurrentlyPlaying
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@ -384,9 +396,13 @@ public class SegmentPlaybackController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scheduledHideSegment = null;
|
scheduledHideSegment = null;
|
||||||
|
if (VideoState.getCurrent() != VideoState.PLAYING) {
|
||||||
|
LogHelper.printDebug(() -> "Ignoring scheduled hide segment as video is paused: " + segmentToHide);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final long videoTime = VideoInformation.getVideoTime();
|
final long videoTime = VideoInformation.getVideoTime();
|
||||||
if (!segmentToHide.endIsNear(videoTime, videoInformationTimeUpdateThresholdMilliseconds)) {
|
if (!segmentToHide.endIsNear(videoTime, speedAdjustedTimeThreshold)) {
|
||||||
// current video time is not what's expected. User paused playback
|
// current video time is not what's expected. User paused playback
|
||||||
LogHelper.printDebug(() -> "Ignoring outdated scheduled hide: " + segmentToHide
|
LogHelper.printDebug(() -> "Ignoring outdated scheduled hide: " + segmentToHide
|
||||||
+ " videoInformation time: " + videoTime);
|
+ " videoInformation time: " + videoTime);
|
||||||
@ -419,10 +435,13 @@ public class SegmentPlaybackController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scheduledUpcomingSegment = null;
|
scheduledUpcomingSegment = null;
|
||||||
|
if (VideoState.getCurrent() != VideoState.PLAYING) {
|
||||||
|
LogHelper.printDebug(() -> "Ignoring scheduled hide segment as video is paused: " + segmentToSkip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final long videoTime = VideoInformation.getVideoTime();
|
final long videoTime = VideoInformation.getVideoTime();
|
||||||
if (!segmentToSkip.startIsNear(videoTime,
|
if (!segmentToSkip.startIsNear(videoTime, speedAdjustedTimeThreshold)) {
|
||||||
videoInformationTimeUpdateThresholdMilliseconds)) {
|
|
||||||
// current video time is not what's expected. User paused playback
|
// current video time is not what's expected. User paused playback
|
||||||
LogHelper.printDebug(() -> "Ignoring outdated scheduled segment: " + segmentToSkip
|
LogHelper.printDebug(() -> "Ignoring outdated scheduled segment: " + segmentToSkip
|
||||||
+ " videoInformation time: " + videoTime);
|
+ " videoInformation time: " + videoTime);
|
||||||
@ -488,10 +507,10 @@ public class SegmentPlaybackController {
|
|||||||
SponsorBlockViewController.hideSkipHighlightButton();
|
SponsorBlockViewController.hideSkipHighlightButton();
|
||||||
SponsorBlockViewController.hideSkipSegmentButton();
|
SponsorBlockViewController.hideSkipSegmentButton();
|
||||||
|
|
||||||
// If trying to seek to end of the video, YouTube can seek just short of the actual end.
|
// If trying to seek to end of the video, YouTube can seek just before of the actual end.
|
||||||
// (especially if the video does not end on a whole second boundary).
|
// (especially if the video does not end on a whole second boundary).
|
||||||
// This causes additional segment skip attempts, even though it cannot seek any closer to the desired time.
|
// This causes additional segment skip attempts, even though it cannot seek any closer to the desired time.
|
||||||
// Check for and ignore repeated skip attempts of the same segment over a short time period.
|
// Check for and ignore repeated skip attempts of the same segment over a small time period.
|
||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
final long minimumMillisecondsBetweenSkippingSameSegment = 500;
|
final long minimumMillisecondsBetweenSkippingSameSegment = 500;
|
||||||
if ((lastSegmentSkipped == segmentToSkip) && (now - lastSegmentSkippedTime < minimumMillisecondsBetweenSkippingSameSegment)) {
|
if ((lastSegmentSkipped == segmentToSkip) && (now - lastSegmentSkippedTime < minimumMillisecondsBetweenSkippingSameSegment)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user