mirror of
https://github.com/revanced/revanced-integrations.git
synced 2024-11-27 14:26:49 +01:00
fix(YouTube - SponsorBlock): Improve create segment manual seek accuracy (#671)
This commit is contained in:
parent
d57a64b659
commit
34c02aeb2a
@ -7,7 +7,6 @@ import app.revanced.integrations.shared.Logger;
|
|||||||
import app.revanced.integrations.shared.Utils;
|
import app.revanced.integrations.shared.Utils;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,17 +14,21 @@ import java.util.Objects;
|
|||||||
* @noinspection unused
|
* @noinspection unused
|
||||||
*/
|
*/
|
||||||
public final class VideoInformation {
|
public final class VideoInformation {
|
||||||
|
|
||||||
|
public interface PlaybackController {
|
||||||
|
// Methods are added to YT classes during patching.
|
||||||
|
boolean seekTo(long videoTime);
|
||||||
|
boolean seekToRelative(long videoTimeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
|
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
|
||||||
private static final String SEEK_METHOD_NAME = "seekTo";
|
|
||||||
/**
|
/**
|
||||||
* Prefix present in all Short player parameters signature.
|
* Prefix present in all Short player parameters signature.
|
||||||
*/
|
*/
|
||||||
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
|
private static final String SHORTS_PLAYER_PARAMETERS = "8AEB";
|
||||||
|
|
||||||
private static WeakReference<Object> playerControllerRef;
|
private static WeakReference<PlaybackController> playerControllerRef = new WeakReference<>(null);
|
||||||
private static WeakReference<Object> mdxPlayerDirectorRef;
|
private static WeakReference<PlaybackController> mdxPlayerDirectorRef = new WeakReference<>(null);
|
||||||
private static Method seekMethod;
|
|
||||||
private static Method mdxSeekMethod;
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static String videoId = "";
|
private static String videoId = "";
|
||||||
@ -47,15 +50,12 @@ public final class VideoInformation {
|
|||||||
*
|
*
|
||||||
* @param playerController player controller object.
|
* @param playerController player controller object.
|
||||||
*/
|
*/
|
||||||
public static void initialize(@NonNull Object playerController) {
|
public static void initialize(@NonNull PlaybackController playerController) {
|
||||||
try {
|
try {
|
||||||
playerControllerRef = new WeakReference<>(Objects.requireNonNull(playerController));
|
playerControllerRef = new WeakReference<>(Objects.requireNonNull(playerController));
|
||||||
videoTime = -1;
|
videoTime = -1;
|
||||||
videoLength = 0;
|
videoLength = 0;
|
||||||
playbackSpeed = DEFAULT_YOUTUBE_PLAYBACK_SPEED;
|
playbackSpeed = DEFAULT_YOUTUBE_PLAYBACK_SPEED;
|
||||||
|
|
||||||
seekMethod = playerController.getClass().getMethod(SEEK_METHOD_NAME, Long.TYPE);
|
|
||||||
seekMethod.setAccessible(true);
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "Failed to initialize", ex);
|
Logger.printException(() -> "Failed to initialize", ex);
|
||||||
}
|
}
|
||||||
@ -66,12 +66,9 @@ public final class VideoInformation {
|
|||||||
*
|
*
|
||||||
* @param mdxPlayerDirector MDX player director object (casting mode).
|
* @param mdxPlayerDirector MDX player director object (casting mode).
|
||||||
*/
|
*/
|
||||||
public static void initializeMdx(@NonNull Object mdxPlayerDirector) {
|
public static void initializeMdx(@NonNull PlaybackController mdxPlayerDirector) {
|
||||||
try {
|
try {
|
||||||
mdxPlayerDirectorRef = new WeakReference<>(Objects.requireNonNull(mdxPlayerDirector));
|
mdxPlayerDirectorRef = new WeakReference<>(Objects.requireNonNull(mdxPlayerDirector));
|
||||||
|
|
||||||
mdxSeekMethod = mdxPlayerDirector.getClass().getMethod(SEEK_METHOD_NAME, Long.TYPE);
|
|
||||||
mdxSeekMethod.setAccessible(true);
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "Failed to initialize MDX", ex);
|
Logger.printException(() -> "Failed to initialize MDX", ex);
|
||||||
}
|
}
|
||||||
@ -195,42 +192,80 @@ public final class VideoInformation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.printDebug(() -> "Seeking to " + adjustedSeekTime);
|
Logger.printDebug(() -> "Seeking to: " + adjustedSeekTime);
|
||||||
|
|
||||||
try {
|
// Try regular playback controller first, and it will not succeed if casting.
|
||||||
//noinspection DataFlowIssue
|
PlaybackController controller = playerControllerRef.get();
|
||||||
if ((Boolean) seekMethod.invoke(playerControllerRef.get(), adjustedSeekTime)) {
|
if (controller == null) {
|
||||||
return true;
|
Logger.printDebug(() -> "Cannot seekTo because player controller is null");
|
||||||
} // Else the video is loading or changing videos, or video is casting to a different device.
|
} else {
|
||||||
} catch (Exception ex) {
|
if (controller.seekTo(adjustedSeekTime)) return true;
|
||||||
Logger.printInfo(() -> "seekTo method call failed", ex);
|
Logger.printDebug(() -> "seekTo did not succeeded. Trying MXD.");
|
||||||
|
// Else the video is loading or changing videos, or video is casting to a different device.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try calling the seekTo method of the MDX player director (called when casting).
|
// Try calling the seekTo method of the MDX player director (called when casting).
|
||||||
// The difference has to be a different second mark in order to avoid infinite skip loops
|
// The difference has to be a different second mark in order to avoid infinite skip loops
|
||||||
// as the Lounge API only supports seconds.
|
// as the Lounge API only supports seconds.
|
||||||
if ((adjustedSeekTime / 1000) == (videoTime / 1000)) {
|
if (adjustedSeekTime / 1000 == videoTime / 1000) {
|
||||||
Logger.printDebug(() -> "Skipping seekTo for MDX because seek time is too small ("
|
Logger.printDebug(() -> "Skipping seekTo for MDX because seek time is too small "
|
||||||
+ (adjustedSeekTime - videoTime) + "ms)");
|
+ "(" + (adjustedSeekTime - videoTime) + "ms)");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//noinspection DataFlowIssue
|
|
||||||
return (Boolean) mdxSeekMethod.invoke(mdxPlayerDirectorRef.get(), adjustedSeekTime);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printInfo(() -> "seekTo (MDX) method call failed", ex);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller = mdxPlayerDirectorRef.get();
|
||||||
|
if (controller == null) {
|
||||||
|
Logger.printDebug(() -> "Cannot seekTo MXD because player controller is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller.seekTo(adjustedSeekTime);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "Failed to seek", ex);
|
Logger.printException(() -> "Failed to seek", ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @noinspection UnusedReturnValue*/
|
/**
|
||||||
public static boolean seekToRelative(long millisecondsRelative) {
|
* Seeks a relative amount. Should always be used over {@link #seekTo(long)}
|
||||||
return seekTo(videoTime + millisecondsRelative);
|
* when the desired seek time is an offset of the current time.
|
||||||
|
*
|
||||||
|
* @noinspection UnusedReturnValue
|
||||||
|
*/
|
||||||
|
public static boolean seekToRelative(long seekTime) {
|
||||||
|
Utils.verifyOnMainThread();
|
||||||
|
try {
|
||||||
|
Logger.printDebug(() -> "Seeking relative to: " + seekTime);
|
||||||
|
|
||||||
|
// Try regular playback controller first, and it will not succeed if casting.
|
||||||
|
PlaybackController controller = playerControllerRef.get();
|
||||||
|
if (controller == null) {
|
||||||
|
Logger.printDebug(() -> "Cannot seek relative as player controller is null");
|
||||||
|
} else {
|
||||||
|
if (controller.seekToRelative(seekTime)) return true;
|
||||||
|
Logger.printDebug(() -> "seekToRelative did not succeeded. Trying MXD.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the fine adjustment function so it's at least 1 second before/after.
|
||||||
|
// Otherwise the fine adjustment will do nothing when casting.
|
||||||
|
final long adjustedSeekTime;
|
||||||
|
if (seekTime < 0) {
|
||||||
|
adjustedSeekTime = Math.min(seekTime, -1000);
|
||||||
|
} else {
|
||||||
|
adjustedSeekTime = Math.max(seekTime, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
controller = mdxPlayerDirectorRef.get();
|
||||||
|
if (controller == null) {
|
||||||
|
Logger.printDebug(() -> "Cannot seek relative as MXD player controller is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return controller.seekToRelative(adjustedSeekTime);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Failed to seek relative", ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package app.revanced.integrations.youtube.patches.playback.quality;
|
package app.revanced.integrations.youtube.patches.playback.quality;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import static app.revanced.integrations.shared.StringRef.str;
|
||||||
|
import static app.revanced.integrations.shared.Utils.NetworkType;
|
||||||
|
|
||||||
import app.revanced.integrations.shared.settings.IntegerSetting;
|
import androidx.annotation.Nullable;
|
||||||
import app.revanced.integrations.youtube.settings.Settings;
|
|
||||||
import app.revanced.integrations.shared.Logger;
|
|
||||||
import app.revanced.integrations.shared.Utils;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static app.revanced.integrations.shared.StringRef.str;
|
import app.revanced.integrations.shared.Logger;
|
||||||
import static app.revanced.integrations.shared.Utils.NetworkType;
|
import app.revanced.integrations.shared.Utils;
|
||||||
|
import app.revanced.integrations.shared.settings.IntegerSetting;
|
||||||
|
import app.revanced.integrations.youtube.patches.VideoInformation;
|
||||||
|
import app.revanced.integrations.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class RememberVideoQualityPatch {
|
public class RememberVideoQualityPatch {
|
||||||
@ -158,7 +159,7 @@ public class RememberVideoQualityPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void newVideoStarted(Object ignoredPlayerController) {
|
public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) {
|
||||||
Logger.printDebug(() -> "newVideoStarted");
|
Logger.printDebug(() -> "newVideoStarted");
|
||||||
qualityNeedsUpdating = true;
|
qualityNeedsUpdating = true;
|
||||||
videoQualities = null;
|
videoQualities = null;
|
||||||
|
@ -13,7 +13,7 @@ public final class RememberPlaybackSpeedPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void newVideoStarted(Object ignoredPlayerController) {
|
public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) {
|
||||||
Logger.printDebug(() -> "newVideoStarted");
|
Logger.printDebug(() -> "newVideoStarted");
|
||||||
VideoInformation.overridePlaybackSpeed(Settings.PLAYBACK_SPEED_DEFAULT.get());
|
VideoInformation.overridePlaybackSpeed(Settings.PLAYBACK_SPEED_DEFAULT.get());
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import app.revanced.integrations.shared.Logger;
|
import app.revanced.integrations.shared.Logger;
|
||||||
import app.revanced.integrations.shared.Utils;
|
import app.revanced.integrations.shared.Utils;
|
||||||
@ -182,7 +178,7 @@ public class SegmentPlaybackController {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
* Initializes SponsorBlock when the video player starts playing a new video.
|
* Initializes SponsorBlock when the video player starts playing a new video.
|
||||||
*/
|
*/
|
||||||
public static void initialize(Object ignoredPlayerController) {
|
public static void initialize(VideoInformation.PlaybackController ignoredPlayerController) {
|
||||||
try {
|
try {
|
||||||
Utils.verifyOnMainThread();
|
Utils.verifyOnMainThread();
|
||||||
SponsorBlockSettings.initialize();
|
SponsorBlockSettings.initialize();
|
||||||
@ -632,6 +628,7 @@ public class SegmentPlaybackController {
|
|||||||
/**
|
/**
|
||||||
* Injection point
|
* Injection point
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static void setSponsorBarRect(final Object self) {
|
public static void setSponsorBarRect(final Object self) {
|
||||||
try {
|
try {
|
||||||
Field field = self.getClass().getDeclaredField("replaceMeWithsetSponsorBarRect");
|
Field field = self.getClass().getDeclaredField("replaceMeWithsetSponsorBarRect");
|
||||||
@ -663,6 +660,7 @@ public class SegmentPlaybackController {
|
|||||||
/**
|
/**
|
||||||
* Injection point
|
* Injection point
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static void setSponsorBarThickness(int thickness) {
|
public static void setSponsorBarThickness(int thickness) {
|
||||||
if (sponsorBarThickness != thickness) {
|
if (sponsorBarThickness != thickness) {
|
||||||
Logger.printDebug(() -> "setSponsorBarThickness: " + thickness);
|
Logger.printDebug(() -> "setSponsorBarThickness: " + thickness);
|
||||||
@ -673,6 +671,7 @@ public class SegmentPlaybackController {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static String appendTimeWithoutSegments(String totalTime) {
|
public static String appendTimeWithoutSegments(String totalTime) {
|
||||||
try {
|
try {
|
||||||
if (Settings.SB_ENABLED.get() && Settings.SB_VIDEO_LENGTH_WITHOUT_SEGMENTS.get()
|
if (Settings.SB_ENABLED.get() && Settings.SB_VIDEO_LENGTH_WITHOUT_SEGMENTS.get()
|
||||||
@ -725,9 +724,9 @@ public class SegmentPlaybackController {
|
|||||||
final long minutes = (timeWithoutSegmentsValue / 60000) % 60;
|
final long minutes = (timeWithoutSegmentsValue / 60000) % 60;
|
||||||
final long seconds = (timeWithoutSegmentsValue / 1000) % 60;
|
final long seconds = (timeWithoutSegmentsValue / 1000) % 60;
|
||||||
if (hours > 0) {
|
if (hours > 0) {
|
||||||
timeWithoutSegments = String.format("\u2009(%d:%02d:%02d)", hours, minutes, seconds);
|
timeWithoutSegments = String.format(Locale.ENGLISH, "\u2009(%d:%02d:%02d)", hours, minutes, seconds);
|
||||||
} else {
|
} else {
|
||||||
timeWithoutSegments = String.format("\u2009(%d:%02d)", minutes, seconds);
|
timeWithoutSegments = String.format(Locale.ENGLISH, "\u2009(%d:%02d)", minutes, seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -744,6 +743,7 @@ public class SegmentPlaybackController {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static void drawSponsorTimeBars(final Canvas canvas, final float posY) {
|
public static void drawSponsorTimeBars(final Canvas canvas, final float posY) {
|
||||||
try {
|
try {
|
||||||
if (segments == null) return;
|
if (segments == null) return;
|
||||||
|
Loading…
Reference in New Issue
Block a user