fix(youtube/video-ads): add switch to temporarily fix buffering issues

This commit is contained in:
oSumAtrIX 2022-11-01 07:11:14 +01:00
parent b13d692ef1
commit 8b4bed8ab3
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
4 changed files with 139 additions and 67 deletions

View File

@ -0,0 +1,36 @@
package app.revanced.integrations.patches;
import java.util.Timer;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
public final class FixPlaybackPatch {
private static Thread currentThread = null;
public static void newVideoLoaded(final String _videoId) {
if (!SettingsEnum.FIX_PLAYBACK.getBoolean()) return;
if (currentThread != null) {
currentThread.interrupt();
}
currentThread = new Thread(() -> {
while (true) {
var currentVideoLength = PlayerControllerPatch.getCurrentVideoLength();
if (currentVideoLength > 1) {
PlayerControllerPatch.seekTo(currentVideoLength);
PlayerControllerPatch.seekTo(1);
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
LogHelper.debug(FixPlaybackPatch.class, "Thread was interrupted");
}
}
});
currentThread.start();
}
}

View File

@ -0,0 +1,78 @@
package app.revanced.integrations.patches;
import android.os.Handler;
import android.os.Looper;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import app.revanced.integrations.utils.LogHelper;
/**
* Hooking class for the player controller.
*/
public final class PlayerControllerPatch {
private static final String SEEK_METHOD_NAME = "seekTo";
private static WeakReference<Object> playerController;
private static Method seekMethod;
private static long videoLength = 1;
/**
* Hook into PlayerController.onCreate() method.
*
* @param thisRef Reference to the player controller object.
*/
public static void playerController_onCreateHook(final Object thisRef) {
playerController = new WeakReference<>(thisRef);
videoLength = 1;
try {
seekMethod = thisRef.getClass().getMethod(SEEK_METHOD_NAME, Long.TYPE);
seekMethod.setAccessible(true);
} catch (NoSuchMethodException ex) {
LogHelper.debug(PlayerControllerPatch.class, "Failed to initialize: " + ex.getMessage());
}
}
/**
* Set the current video length.
*
* @param length The length of the video in milliseconds.
*/
public static void setCurrentVideoLength(final long length) {
LogHelper.debug(PlayerControllerPatch.class, "Setting current video length to " + length);
videoLength = length;
}
/**
* Seek on the current video.
*
* @param millisecond The millisecond to seek the video to.
*/
public static void seekTo(final long millisecond) {
new Handler(Looper.getMainLooper()).post(() -> {
if (seekMethod == null) {
LogHelper.debug(PlayerControllerPatch.class, "seekMethod was null");
return;
}
try {
LogHelper.debug(PlayerControllerPatch.class, "Seeking to " + millisecond);
seekMethod.invoke(playerController.get(), millisecond);
} catch (Exception ex) {
LogHelper.debug(PlayerControllerPatch.class, "Failed to seek: " + ex.getMessage());
}
});
}
/**
* Get the length of the current video playing.
*
* @return The length of the video in milliseconds.
*/
public static long getCurrentVideoLength() {
return videoLength;
}
}

View File

@ -80,6 +80,7 @@ public enum SettingsEnum {
HIDE_WATCH_IN_VR("revanced_hide_watch_in_vr", false, ReturnType.BOOLEAN, true),
// Misc. Settings
FIX_PLAYBACK("revanced_fix_playback", false, ReturnType.BOOLEAN, false),
CAPTIONS_ENABLED("revanced_autocaptions_enabled", false, ReturnType.BOOLEAN, false),
PREFERRED_AUTO_REPEAT("revanced_pref_auto_repeat", false, ReturnType.BOOLEAN),
USE_HDR_AUTO_BRIGHTNESS("revanced_pref_hdr_autobrightness", true, ReturnType.BOOLEAN),

View File

@ -10,24 +10,23 @@ import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import app.revanced.integrations.patches.PlayerControllerPatch;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.videoplayer.VideoInformation;
import app.revanced.integrations.whitelist.Whitelist;
import app.revanced.integrations.sponsorblock.objects.SponsorSegment;
import app.revanced.integrations.sponsorblock.requests.SBRequester;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
import app.revanced.integrations.videoplayer.VideoInformation;
import app.revanced.integrations.whitelist.Whitelist;
@SuppressLint({"LongLogTag"})
public class PlayerController {
@ -35,11 +34,8 @@ public class PlayerController {
private static final Timer sponsorTimer = new Timer("sponsor-skip-timer");
public static WeakReference<Activity> playerActivity = new WeakReference<>(null);
public static SponsorSegment[] sponsorSegmentsOfCurrentVideo;
private static WeakReference<Object> currentPlayerController = new WeakReference<>(null);
private static Method setMillisecondMethod;
private static long allowNextSkipRequestTime = 0L;
private static String currentVideoId;
private static long currentVideoLength = 1L;
private static long lastKnownVideoTime = -1L;
private static final Runnable findAndSkipSegmentRunnable = () -> {
findAndSkipSegment(false);
@ -88,29 +84,11 @@ public class PlayerController {
/**
* Called when creating some kind of youtube internal player controlled, every time when new video starts to play
*/
public static void onCreate(final Object o) {
if (o == null) {
LogHelper.printException(PlayerController.class, "onCreate called with null object");
return;
}
LogHelper.debug(PlayerController.class, String.format("onCreate called with object %s on thread %s", o.toString(), Thread.currentThread().toString()));
try {
setMillisecondMethod = o.getClass().getMethod("replaceMeWithsetMillisecondMethod", Long.TYPE);
setMillisecondMethod.setAccessible(true);
lastKnownVideoTime = 0;
VideoInformation.lastKnownVideoTime = 0;
currentVideoLength = 1;
currentPlayerController = new WeakReference<>(o);
SkipSegmentView.hide();
NewSegmentHelperLayout.hide();
} catch (Exception e) {
LogHelper.printException(PlayerController.class, "Exception while initializing skip method", e);
}
public static void initialize(Object _o) {
lastKnownVideoTime = 0;
VideoInformation.lastKnownVideoTime = 0;
SkipSegmentView.hide();
NewSegmentHelperLayout.hide();
}
public static void executeDownloadSegments(String videoId) {
@ -141,7 +119,7 @@ public class PlayerController {
if (millis <= 0) return;
//findAndSkipSegment(false);
if (millis == currentVideoLength) {
if (millis == PlayerControllerPatch.getCurrentVideoLength()) {
SponsorBlockUtils.hideShieldButton();
SponsorBlockUtils.hideVoteButton();
return;
@ -218,7 +196,7 @@ public class PlayerController {
* Called very high frequency (once every about 100ms), also in background. It sometimes triggers when a video is paused (couple times in the row with the same value)
*/
public static void setCurrentVideoTimeHighPrecision(final long millis) {
if ((millis < lastKnownVideoTime && lastKnownVideoTime >= currentVideoLength) || millis == 0) {
if ((millis < lastKnownVideoTime && lastKnownVideoTime >= PlayerControllerPatch.getCurrentVideoLength()) || millis == 0) {
SponsorBlockUtils.showShieldButton(); // skipping from end to the video will show the buttons again
SponsorBlockUtils.showVoteButton();
}
@ -230,22 +208,13 @@ public class PlayerController {
}
public static long getCurrentVideoLength() {
return currentVideoLength;
return PlayerControllerPatch.getCurrentVideoLength();
}
public static long getLastKnownVideoTime() {
return lastKnownVideoTime;
}
/**
* Called before onDraw method on time bar object, sets video length in millis
*/
public static void setVideoLength(final long length) {
LogHelper.debug(PlayerController.class, "setVideoLength: length=" + length);
currentVideoLength = length;
}
public static void setSponsorBarAbsoluteLeft(final Rect rect) {
setSponsorBarAbsoluteLeft(rect.left);
}
@ -333,7 +302,7 @@ public class PlayerController {
final float absoluteLeft = sponsorBarLeft;
final float absoluteRight = sponsorBarRight;
final float tmp1 = 1f / (float) currentVideoLength * (absoluteRight - absoluteLeft);
final float tmp1 = 1f / (float) PlayerControllerPatch.getCurrentVideoLength() * (absoluteRight - absoluteLeft);
for (SponsorSegment segment : sponsorSegmentsOfCurrentVideo) {
float left = segment.start * tmp1 + absoluteLeft;
float right = segment.end * tmp1 + absoluteLeft;
@ -360,31 +329,19 @@ public class PlayerController {
}
allowNextSkipRequestTime = now + 100;
if (setMillisecondMethod == null) {
LogHelper.printException(PlayerController.class, "setMillisecondMethod is null");
return false;
}
final Object currentObj = currentPlayerController.get();
if (currentObj == null) {
LogHelper.printException(PlayerController.class, "currentObj is null (might have been collected by GC)");
return false;
}
LogHelper.debug(PlayerController.class, String.format("Requesting skip to millis=%d on thread %s", millisecond, Thread.currentThread().toString()));
final long finalMillisecond = millisecond;
new Handler(Looper.getMainLooper()).post(() -> {
try {
LogHelper.debug(PlayerController.class, "Skipping to millis=" + finalMillisecond);
lastKnownVideoTime = finalMillisecond;
VideoInformation.lastKnownVideoTime = lastKnownVideoTime;
setMillisecondMethod.invoke(currentObj, finalMillisecond);
} catch (Exception e) {
LogHelper.printException(PlayerController.class, "Cannot skip to millisecond", e);
}
});
try {
LogHelper.debug(PlayerController.class, "Skipping to millis=" + finalMillisecond);
lastKnownVideoTime = finalMillisecond;
VideoInformation.lastKnownVideoTime = lastKnownVideoTime;
PlayerControllerPatch.seekTo(finalMillisecond);
} catch (Exception e) {
LogHelper.printException(PlayerController.class, "Cannot skip to millisecond", e);
}
return true;
}
@ -423,7 +380,7 @@ public class PlayerController {
SkipSegmentView.notifySkipped(segment);
boolean didSucceed = skipToMillisecond(segment.end + 2);
if(didSucceed && !wasClicked) {
if (didSucceed && !wasClicked) {
segment.didAutoSkipped = true;
}
SkipSegmentView.hide();