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 1/2] 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() From 43982a7dfdeb7477690171a008547ae6d630654f Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:46:30 +0400 Subject: [PATCH 2/2] refactor: Cleanup, internalize fields that should not have been api --- .../youtube/patches/VideoInformation.java | 20 ++------- .../speed/RememberPlaybackSpeedPatch.java | 8 +--- patches/api/patches.api | 5 +-- .../information/VideoInformationPatch.kt | 44 ++++++++----------- 4 files changed, 26 insertions(+), 51 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 62377337b2..6df9a90951 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 @@ -1,14 +1,14 @@ package app.revanced.extension.youtube.patches; import androidx.annotation.NonNull; -import app.revanced.extension.youtube.patches.playback.speed.RememberPlaybackSpeedPatch; -import app.revanced.extension.youtube.shared.VideoState; -import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.Utils; import java.lang.ref.WeakReference; import java.util.Objects; +import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.Utils; +import app.revanced.extension.youtube.shared.VideoState; + /** * Hooking class for the current playing video. * @noinspection unused @@ -141,18 +141,6 @@ public static void userSelectedPlaybackSpeed(float userSelectedPlaybackSpeed) { playbackSpeed = userSelectedPlaybackSpeed; } - /** - * Overrides the current playback speed. - *

- * Used exclusively by {@link RememberPlaybackSpeedPatch} - */ - public static void overridePlaybackSpeed(float speedOverride) { - if (speedOverride <= 0) throw new IllegalArgumentException("Invalid speed override: " + speedOverride); - - Logger.printDebug(() -> "Overriding playback speed to: " + speedOverride); - playbackSpeed = speedOverride; - } - /** * Injection point. * 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 28623e72b5..a6c86477c1 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 @@ -20,12 +20,8 @@ public final class RememberPlaybackSpeedPatch { * Injection point. */ public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) { - try { - Logger.printDebug(() -> "newVideoStarted"); - newVideoStarted = true; - } catch (Exception ex) { - Logger.printException(() -> "newVideoStarted failure", ex); - } + Logger.printDebug(() -> "newVideoStarted"); + newVideoStarted = true; } /** diff --git a/patches/api/patches.api b/patches/api/patches.api index 2546145df4..2ba4d9a4e0 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1370,13 +1370,10 @@ public final class app/revanced/patches/youtube/shared/FingerprintsKt { } public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt { - public static final fun getSetPlaybackSpeedClassFieldReference ()Ljava/lang/String; - public static final fun getSetPlaybackSpeedContainerClassFieldReference ()Ljava/lang/String; - public static final fun getSetPlaybackSpeedMethodReference ()Ljava/lang/String; 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 videoSpeedChangedHook (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 a7b5cfc865..a22934d1ca 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 @@ -20,13 +20,13 @@ import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.builder.BuilderInstruction import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter @@ -62,11 +62,11 @@ private lateinit var setPlaybackSpeedMethod: MutableMethod private var setPlaybackSpeedMethodIndex = -1 // Used by other patches. -lateinit var setPlaybackSpeedContainerClassFieldReference: String +internal lateinit var setPlaybackSpeedContainerClassFieldReference: FieldReference private set -lateinit var setPlaybackSpeedClassFieldReference: String +internal lateinit var setPlaybackSpeedClassFieldReference: FieldReference private set -lateinit var setPlaybackSpeedMethodReference: String +internal lateinit var setPlaybackSpeedMethodReference: MethodReference private set val videoInformationPatch = bytecodePatch( @@ -168,29 +168,27 @@ val videoInformationPatch = bytecodePatch( videoTimeHook(EXTENSION_CLASS_DESCRIPTOR, "setVideoTime") /* - * Hook the user playback speed selection + * Hook the user playback speed selection. */ onPlaybackSpeedItemClickFingerprint.method.apply { - val speedSelectionMethodInstructions = implementation!!.instructions - val speedSelectionValueInstructionIndex = speedSelectionMethodInstructions.indexOfFirst { - it.opcode == Opcode.IGET - } + val speedSelectionValueInstructionIndex = indexOfFirstInstructionOrThrow(Opcode.IGET) + legacySpeedSelectionInsertMethod = this legacySpeedSelectionInsertIndex = speedSelectionValueInstructionIndex + 1 legacySpeedSelectionValueRegister = getInstruction(speedSelectionValueInstructionIndex).registerA - val setPlaybackSpeedClassReference = + setPlaybackSpeedMethodReference = 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() + getInstruction(speedSelectionValueInstructionIndex + 1).reference as FieldReference setPlaybackSpeedContainerClassFieldReference = - getReference(speedSelectionMethodInstructions, -1, Opcode.IF_EQZ) + getInstruction(indexOfFirstInstructionOrThrow(Opcode.IF_EQZ) - 1).reference as FieldReference + + setPlaybackSpeedMethod = + proxy(classes.first { it.type == setPlaybackSpeedMethodReference.definingClass }) + .mutableClass.methods.first { it.name == setPlaybackSpeedMethodReference.name } + setPlaybackSpeedMethodIndex = 0 } // Handle new playback speed menu. @@ -204,7 +202,7 @@ val videoInformationPatch = bytecodePatch( speedSelectionValueRegister = getInstruction(index).registerA } - videoVideoSpeedChanged(EXTENSION_CLASS_DESCRIPTOR, "videoSpeedChanged") + videoSpeedChangedHook(EXTENSION_CLASS_DESCRIPTOR, "videoSpeedChanged") userSelectedPlaybackSpeedHook(EXTENSION_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed") } } @@ -306,9 +304,9 @@ fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = ) /** - * Hook when the video speed is changed, either by the user or any other reason. + * Hook when the video speed is changed for any reason _except when the user manually selects a new speed_. */ -fun videoVideoSpeedChanged(targetMethodClass: String, targetMethodName: String) = +fun videoSpeedChangedHook(targetMethodClass: String, targetMethodName: String) = setPlaybackSpeedMethod.addInstruction( setPlaybackSpeedMethodIndex++, "invoke-static { p1 }, $targetMethodClass->$targetMethodName(F)V" @@ -327,8 +325,4 @@ fun userSelectedPlaybackSpeedHook(targetMethodClass: String, targetMethodName: S speedSelectionInsertIndex++, "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() +} \ No newline at end of file