From 1f7b3fc9e7247185b6aaeca252e83118e4355b7b Mon Sep 17 00:00:00 2001
From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Date: Tue, 26 Nov 2024 15:19:44 +0400
Subject: [PATCH] fix(YouTube - Playback speed): Allow long press 2x speed when
using a default playback speed
---
.../youtube/patches/VideoInformation.java | 18 +++-
.../speed/RememberPlaybackSpeedPatch.java | 84 ++++++++++++-------
patches/api/patches.api | 1 +
.../information/VideoInformationPatch.kt | 29 +++++--
4 files changed, 91 insertions(+), 41 deletions(-)
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
index 6b64ade12d..62377337b2 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/VideoInformation.java
@@ -120,6 +120,16 @@ public static void setPlayerResponseVideoId(@NonNull String videoId, boolean isS
}
}
+ /**
+ * Injection point.
+ */
+ public static void videoSpeedChanged(float currentVideoSpeed) {
+ if (playbackSpeed != currentVideoSpeed) {
+ Logger.printDebug(() -> "Video speed changed: " + currentVideoSpeed);
+ playbackSpeed = currentVideoSpeed;
+ }
+ }
+
/**
* Injection point.
* Called when user selects a playback speed.
@@ -137,10 +147,10 @@ public static void userSelectedPlaybackSpeed(float userSelectedPlaybackSpeed) {
* Used exclusively by {@link RememberPlaybackSpeedPatch}
*/
public static void overridePlaybackSpeed(float speedOverride) {
- if (playbackSpeed != speedOverride) {
- Logger.printDebug(() -> "Overriding playback speed to: " + speedOverride);
- playbackSpeed = speedOverride;
- }
+ if (speedOverride <= 0) throw new IllegalArgumentException("Invalid speed override: " + speedOverride);
+
+ Logger.printDebug(() -> "Overriding playback speed to: " + speedOverride);
+ playbackSpeed = speedOverride;
}
/**
diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/playback/speed/RememberPlaybackSpeedPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/playback/speed/RememberPlaybackSpeedPatch.java
index 3c504df5d9..28623e72b5 100644
--- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/playback/speed/RememberPlaybackSpeedPatch.java
+++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/playback/speed/RememberPlaybackSpeedPatch.java
@@ -12,14 +12,20 @@ public final class RememberPlaybackSpeedPatch {
private static final long TOAST_DELAY_MILLISECONDS = 750;
+ private static volatile boolean newVideoStarted;
+
private static long lastTimeSpeedChanged;
/**
* Injection point.
*/
public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) {
- Logger.printDebug(() -> "newVideoStarted");
- VideoInformation.overridePlaybackSpeed(Settings.PLAYBACK_SPEED_DEFAULT.get());
+ try {
+ Logger.printDebug(() -> "newVideoStarted");
+ newVideoStarted = true;
+ } catch (Exception ex) {
+ Logger.printException(() -> "newVideoStarted failure", ex);
+ }
}
/**
@@ -29,42 +35,56 @@ public static void newVideoStarted(VideoInformation.PlaybackController ignoredPl
* @param playbackSpeed The playback speed the user selected
*/
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
- if (Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get()) {
- // With the 0.05x menu, if the speed is set by integrations to higher than 2.0x
- // then the menu will allow increasing without bounds but the max speed is
- // still capped to under 8.0x.
- playbackSpeed = Math.min(playbackSpeed, CustomPlaybackSpeedPatch.PLAYBACK_SPEED_MAXIMUM - 0.05f);
-
- // Prevent toast spamming if using the 0.05x adjustments.
- // Show exactly one toast after the user stops interacting with the speed menu.
- final long now = System.currentTimeMillis();
- lastTimeSpeedChanged = now;
-
- final float finalPlaybackSpeed = playbackSpeed;
- Utils.runOnMainThreadDelayed(() -> {
- if (lastTimeSpeedChanged != now) {
- // The user made additional speed adjustments and this call is outdated.
- return;
- }
-
- if (Settings.PLAYBACK_SPEED_DEFAULT.get() == finalPlaybackSpeed) {
- // User changed to a different speed and immediately changed back.
- // Or the user is going past 8.0x in the glitched out 0.05x menu.
- return;
- }
- Settings.PLAYBACK_SPEED_DEFAULT.save(finalPlaybackSpeed);
-
- Utils.showToastLong(str("revanced_remember_playback_speed_toast", (finalPlaybackSpeed + "x")));
- }, TOAST_DELAY_MILLISECONDS);
+ try {
+ if (Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get()) {
+ // With the 0.05x menu, if the speed is set by integrations to higher than 2.0x
+ // then the menu will allow increasing without bounds but the max speed is
+ // still capped to under 8.0x.
+ playbackSpeed = Math.min(playbackSpeed, CustomPlaybackSpeedPatch.PLAYBACK_SPEED_MAXIMUM - 0.05f);
+
+ // Prevent toast spamming if using the 0.05x adjustments.
+ // Show exactly one toast after the user stops interacting with the speed menu.
+ final long now = System.currentTimeMillis();
+ lastTimeSpeedChanged = now;
+
+ final float finalPlaybackSpeed = playbackSpeed;
+ Utils.runOnMainThreadDelayed(() -> {
+ if (lastTimeSpeedChanged != now) {
+ // The user made additional speed adjustments and this call is outdated.
+ return;
+ }
+
+ if (Settings.PLAYBACK_SPEED_DEFAULT.get() == finalPlaybackSpeed) {
+ // User changed to a different speed and immediately changed back.
+ // Or the user is going past 8.0x in the glitched out 0.05x menu.
+ return;
+ }
+ Settings.PLAYBACK_SPEED_DEFAULT.save(finalPlaybackSpeed);
+
+ Utils.showToastLong(str("revanced_remember_playback_speed_toast", (finalPlaybackSpeed + "x")));
+ }, TOAST_DELAY_MILLISECONDS);
+ }
+ } catch (Exception ex) {
+ Logger.printException(() -> "userSelectedPlaybackSpeed failure", ex);
}
}
/**
* Injection point.
- * Overrides the video speed. Called after video loads, and immediately after user selects a different playback speed
+ * Overrides the video speed. Called after video loads,
+ * and immediately after the user selects a different playback speed.
*/
public static float getPlaybackSpeedOverride() {
- return VideoInformation.getPlaybackSpeed();
+ if (newVideoStarted) {
+ newVideoStarted = false;
+
+ final float defaultSpeed = Settings.PLAYBACK_SPEED_DEFAULT.get();
+ if (defaultSpeed > 0) {
+ return defaultSpeed;
+ }
+ }
+
+ return -2.0f;
}
-}
+}
\ No newline at end of file
diff --git a/patches/api/patches.api b/patches/api/patches.api
index 89368b9431..2546145df4 100644
--- a/patches/api/patches.api
+++ b/patches/api/patches.api
@@ -1376,6 +1376,7 @@ public final class app/revanced/patches/youtube/video/information/VideoInformati
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V
public static final fun videoTimeHook (Ljava/lang/String;Ljava/lang/String;)V
+ public static final fun videoVideoSpeedChanged (Ljava/lang/String;Ljava/lang/String;)V
}
public abstract class app/revanced/patches/youtube/video/playerresponse/Hook {
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt
index a44e371bfd..a7b5cfc865 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt
@@ -57,6 +57,10 @@ private lateinit var speedSelectionInsertMethod: MutableMethod
private var speedSelectionInsertIndex = -1
private var speedSelectionValueRegister = -1
+// Change playback speed method.
+private lateinit var setPlaybackSpeedMethod: MutableMethod
+private var setPlaybackSpeedMethodIndex = -1
+
// Used by other patches.
lateinit var setPlaybackSpeedContainerClassFieldReference: String
private set
@@ -176,10 +180,15 @@ val videoInformationPatch = bytecodePatch(
legacySpeedSelectionValueRegister =
getInstruction(speedSelectionValueInstructionIndex).registerA
+ val setPlaybackSpeedClassReference =
+ getInstruction(speedSelectionValueInstructionIndex + 2).reference as MethodReference
+ setPlaybackSpeedMethod = proxy(classes.first { it.type == setPlaybackSpeedClassReference.definingClass })
+ .mutableClass.methods.first { it.name == setPlaybackSpeedClassReference.name }
+ setPlaybackSpeedMethodIndex = 0
+
+ setPlaybackSpeedMethodReference = setPlaybackSpeedClassReference.toString()
setPlaybackSpeedClassFieldReference =
getInstruction(speedSelectionValueInstructionIndex + 1).reference.toString()
- setPlaybackSpeedMethodReference =
- getInstruction(speedSelectionValueInstructionIndex + 2).reference.toString()
setPlaybackSpeedContainerClassFieldReference =
getReference(speedSelectionMethodInstructions, -1, Opcode.IF_EQZ)
}
@@ -195,6 +204,7 @@ val videoInformationPatch = bytecodePatch(
speedSelectionValueRegister = getInstruction(index).registerA
}
+ videoVideoSpeedChanged(EXTENSION_CLASS_DESCRIPTOR, "videoSpeedChanged")
userSelectedPlaybackSpeedHook(EXTENSION_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
}
}
@@ -295,9 +305,14 @@ fun videoTimeHook(targetMethodClass: String, targetMethodName: String) =
"$targetMethodClass->$targetMethodName(J)V",
)
-private fun getReference(instructions: List, offset: Int, opcode: Opcode) =
- (instructions[instructions.indexOfFirst { it.opcode == opcode } + offset] as ReferenceInstruction)
- .reference.toString()
+/**
+ * Hook when the video speed is changed, either by the user or any other reason.
+ */
+fun videoVideoSpeedChanged(targetMethodClass: String, targetMethodName: String) =
+ setPlaybackSpeedMethod.addInstruction(
+ setPlaybackSpeedMethodIndex++,
+ "invoke-static { p1 }, $targetMethodClass->$targetMethodName(F)V"
+ )
/**
* Hook the video speed selected by the user.
@@ -313,3 +328,7 @@ fun userSelectedPlaybackSpeedHook(targetMethodClass: String, targetMethodName: S
"invoke-static { v$speedSelectionValueRegister }, $targetMethodClass->$targetMethodName(F)V",
)
}
+
+private fun getReference(instructions: List, offset: Int, opcode: Opcode) =
+ (instructions[instructions.indexOfFirst { it.opcode == opcode } + offset] as ReferenceInstruction)
+ .reference.toString()