Skip to content

Commit

Permalink
fix(YouTube Music/SponsorBlock): SponsorBlock segments at the end of …
Browse files Browse the repository at this point in the history
…a song cause the player to get stuck inotia00/ReVanced_Extended#2360
  • Loading branch information
inotia00 committed Sep 5, 2024
1 parent f658778 commit ed6fdb8
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.revanced.integrations.music.shared;

import static app.revanced.integrations.shared.utils.ResourceUtils.getString;
import static app.revanced.integrations.shared.utils.Utils.getFormattedTimeStamp;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down Expand Up @@ -45,6 +46,23 @@ public final class VideoInformation {
@Nullable
private static List<Integer> videoQualities;

/**
* Injection point.
*/
public static void initialize() {
videoTime = -1;
videoLength = 0;
playbackSpeed = DEFAULT_YOUTUBE_MUSIC_PLAYBACK_SPEED;
Logger.printDebug(() -> "Initialized Player");
}

/**
* Injection point.
*/
public static void initializeMdx() {
Logger.printDebug(() -> "Initialized Mdx Player");
}

/**
* Id of the current video playing. Includes Shorts and YouTube Stories.
*
Expand Down Expand Up @@ -75,20 +93,52 @@ public static void setVideoId(@NonNull String newlyLoadedVideoId) {
* Caution: If called from a videoTimeHook() callback,
* this will cause a recursive call into the same videoTimeHook() callback.
*
* @param millisecond The millisecond to seek the video to.
* @param seekTime The millisecond to seek the video to.
* @return if the seek was successful
*/
public static boolean seekTo(final long millisecond) {
public static boolean seekTo(final long seekTime) {
Utils.verifyOnMainThread();
try {
Logger.printDebug(() -> "Seeking to " + millisecond);
return overrideVideoTime(millisecond);
final long videoLength = getVideoLength();
final long videoTime = getVideoTime();
final long adjustedSeekTime = getAdjustedSeekTime(seekTime, videoLength);

Logger.printDebug(() -> "Seeking to: " + getFormattedTimeStamp(adjustedSeekTime));

// Try regular playback controller first, and it will not succeed if casting.
if (overrideVideoTime(adjustedSeekTime)) return true;
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).
// 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;
}

return overrideMDXVideoTime(adjustedSeekTime);
} catch (Exception ex) {
Logger.printException(() -> "Failed to seek", ex);
return false;
}
}

// Prevent issues such as play/pause button or autoplay not working.
private static long getAdjustedSeekTime(final long seekTime, final long videoLength) {
// If the user skips to a section that is 500 ms before the video length,
// it will get stuck in a loop.
if (videoLength - seekTime > 500) {
return seekTime;
} else {
// Otherwise, just skips to a time longer than the video length.
// Paradoxically, if user skips to a section much longer than the video length, does not get stuck in a loop.
return Integer.MAX_VALUE;
}
}

/**
* @return The current playback speed.
*/
Expand Down Expand Up @@ -252,4 +302,14 @@ public static boolean overrideVideoTime(final long seekTime) {
return false;
}

/**
* Overrides the current video time by seeking. (MDX player)
* Rest of the implementation added by patch.
*/
public static boolean overrideMDXVideoTime(final long seekTime) {
// These instructions are ignored by patch.
Logger.printDebug(() -> "Seeking to " + seekTime);
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import java.lang.ref.WeakReference;
import java.text.Bidi;
import java.time.Duration;
import java.util.Locale;
import java.util.Objects;
import java.util.SortedMap;
Expand Down Expand Up @@ -349,6 +350,40 @@ public static void setClipboard(@NonNull String text, @Nullable String toastMess
}
}

public static String getFormattedTimeStamp(long videoTime) {
return "'" + videoTime +
"' (" +
getTimeStamp(videoTime) +
")\n";
}

@SuppressLint("DefaultLocale")
public static String getTimeStamp(long time) {
long hours;
long minutes;
long seconds;

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
final Duration duration = Duration.ofMillis(time);

hours = duration.toHours();
minutes = duration.toMinutes() % 60;
seconds = duration.getSeconds() % 60;
} else {
final long currentVideoTimeInSeconds = time / 1000;

hours = currentVideoTimeInSeconds / (60 * 60);
minutes = (currentVideoTimeInSeconds / 60) % 60;
seconds = currentVideoTimeInSeconds % 60;
}

if (hours > 0) {
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
} else {
return String.format("%02d:%02d", minutes, seconds);
}
}

public static void setPreferenceIcon(Preference preference, String str) {
final int iconResourceId = ResourceUtils.getDrawableIdentifier(str);
if (iconResourceId == 0) return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package app.revanced.integrations.youtube.shared;

import static app.revanced.integrations.shared.utils.ResourceUtils.getString;
import static app.revanced.integrations.youtube.utils.VideoUtils.getFormattedTimeStamp;
import static app.revanced.integrations.shared.utils.Utils.getFormattedTimeStamp;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import static app.revanced.integrations.shared.utils.StringRef.str;
import static app.revanced.integrations.youtube.patches.video.PlaybackSpeedPatch.userSelectedPlaybackSpeed;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.Context;
import android.media.AudioManager;
Expand All @@ -14,7 +12,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;

Expand Down Expand Up @@ -169,30 +166,6 @@ public static void showFlyoutMenu() {
}
}

public static String getFormattedTimeStamp(long videoTime) {
return "'" + videoTime +
"' (" +
getTimeStamp(videoTime) +
")\n";
}

@TargetApi(26)
@SuppressLint("DefaultLocale")
public static String getTimeStamp(long time) {
final Duration duration = Duration.ofMillis(time);

final long hours = duration.toHours();
final long minutes = duration.toMinutes() % 60;
final long seconds = duration.getSeconds() % 60;
final long millis = duration.toMillis() % 1000;

if (hours > 0) {
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
} else {
return String.format("%02d:%02d", minutes, seconds);
}
}

public static long getVideoTime(String str) {
if (str == null || str.isEmpty())
return 0;
Expand Down

0 comments on commit ed6fdb8

Please sign in to comment.