diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/utils/AlwaysRepeatPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/utils/AlwaysRepeatPatch.java index 93ad1be461..6dd5f176b8 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/utils/AlwaysRepeatPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/utils/AlwaysRepeatPatch.java @@ -14,7 +14,7 @@ public class AlwaysRepeatPatch { * @return video is repeated. */ public static boolean alwaysRepeat() { - return alwaysRepeatEnabled() && VideoInformation.overrideVideoTime(0); + return alwaysRepeatEnabled() && VideoInformation.seekTo(0); } public static boolean alwaysRepeatEnabled() { diff --git a/app/src/main/java/app/revanced/integrations/youtube/patches/video/ReloadVideoPatch.java b/app/src/main/java/app/revanced/integrations/youtube/patches/video/ReloadVideoPatch.java index 8126846a81..6386f929f7 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/patches/video/ReloadVideoPatch.java +++ b/app/src/main/java/app/revanced/integrations/youtube/patches/video/ReloadVideoPatch.java @@ -42,8 +42,8 @@ private static void reloadVideo(final long videoLength) { final long lastVideoTime = VideoInformation.getVideoTime(); final float playbackSpeed = VideoInformation.getPlaybackSpeed(); final long speedAdjustedTimeThreshold = (long) (playbackSpeed * 300); - VideoInformation.overrideVideoTime(videoLength); - VideoInformation.overrideVideoTime(lastVideoTime + speedAdjustedTimeThreshold); + VideoInformation.seekTo(videoLength); + VideoInformation.seekTo(lastVideoTime + speedAdjustedTimeThreshold); if (!Settings.SKIP_PRELOADED_BUFFER_TOAST.get()) return; diff --git a/app/src/main/java/app/revanced/integrations/youtube/shared/VideoInformation.java b/app/src/main/java/app/revanced/integrations/youtube/shared/VideoInformation.java index ff74db5fa6..aa7405c588 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/shared/VideoInformation.java +++ b/app/src/main/java/app/revanced/integrations/youtube/shared/VideoInformation.java @@ -6,9 +6,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.lang.ref.WeakReference; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import app.revanced.integrations.shared.utils.Logger; import app.revanced.integrations.shared.utils.Utils; @@ -23,11 +26,17 @@ public final class VideoInformation { private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f; private static final int DEFAULT_YOUTUBE_VIDEO_QUALITY = -2; private static final String DEFAULT_YOUTUBE_VIDEO_QUALITY_STRING = getString("quality_auto"); + private static final String SEEK_METHOD_NAME = "seekTo"; /** * Prefix present in all Short player parameters signature. */ private static final String SHORTS_PLAYER_PARAMETERS = "8AEB"; + private static WeakReference playerControllerRef; + private static WeakReference mdxPlayerDirectorRef; + private static Method seekMethod; + private static Method mdxSeekMethod; + @NonNull private static String channelId = ""; @NonNull @@ -65,13 +74,39 @@ public final class VideoInformation { /** * Injection point. + * + * @param playerController player controller object. */ - public static void initialize() { - videoLength = 0; - videoTime = -1; + public static void initialize(@NonNull Object playerController) { + try { + playerControllerRef = new WeakReference<>(Objects.requireNonNull(playerController)); + videoTime = -1; + videoLength = 0; + playbackSpeed = DEFAULT_YOUTUBE_PLAYBACK_SPEED; + + seekMethod = playerController.getClass().getMethod(SEEK_METHOD_NAME, Long.TYPE); + seekMethod.setAccessible(true); + } catch (Exception ex) { + Logger.printException(() -> "Failed to initialize", ex); + } + } + + /** + * Injection point. + * + * @param mdxPlayerDirector MDX player director object (casting mode). + */ + public static void initializeMdx(@NonNull Object mdxPlayerDirector) { + try { + mdxPlayerDirectorRef = new WeakReference<>(Objects.requireNonNull(mdxPlayerDirector)); + + mdxSeekMethod = mdxPlayerDirector.getClass().getMethod(SEEK_METHOD_NAME, Long.TYPE); + mdxSeekMethod.setAccessible(true); + } catch (Exception ex) { + Logger.printException(() -> "Failed to initialize MDX", ex); + } } - @Deprecated public static boolean seekTo(final long seekTime) { return seekTo(seekTime, getVideoLength()); } @@ -89,10 +124,34 @@ public static boolean seekTo(final long seekTime) { public static boolean seekTo(final long seekTime, final long videoLength) { Utils.verifyOnMainThread(); try { + final long videoTime = getVideoTime(); final long adjustedSeekTime = getAdjustedSeekTime(seekTime, videoLength); Logger.printDebug(() -> "Seeking to " + getFormattedTimeStamp(adjustedSeekTime)); - return overrideVideoTime(adjustedSeekTime); + try { + //noinspection DataFlowIssue + if ((Boolean) seekMethod.invoke(playerControllerRef.get(), adjustedSeekTime)) { + return true; + } // Else the video is loading or changing videos, or video is casting to a different device. + } catch (Exception ex) { + Logger.printInfo(() -> "seekTo method call failed", ex); + } + + // 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 + // as the Lounge API only supports seconds. + if ((adjustedSeekTime / 1000) == (videoTime / 1000)) { + Logger.printDebug(() -> "Skipping seekTo for MDX because seek time is too small (" + + (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; + } } catch (Exception ex) { Logger.printException(() -> "Failed to seek", ex); return false; diff --git a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java index df4e93bb96..8b8808637d 100644 --- a/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java +++ b/app/src/main/java/app/revanced/integrations/youtube/sponsorblock/SegmentPlaybackController.java @@ -189,7 +189,7 @@ public static void clearData() { * Injection point. * Initializes SponsorBlock when the video player starts playing a new video. */ - public static void initialize() { + public static void initialize(@NonNull Object ignoredPlayerController) { try { Utils.verifyOnMainThread(); SponsorBlockSettings.initialize();