diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/FixPlaybackSpeedWhilePlayingPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/FixPlaybackSpeedWhilePlayingPatch.java new file mode 100644 index 0000000000..ae60ee57d0 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/FixPlaybackSpeedWhilePlayingPatch.java @@ -0,0 +1,24 @@ +package app.revanced.extension.youtube.patches; + +import app.revanced.extension.shared.Logger; +import app.revanced.extension.youtube.shared.PlayerType; + +@SuppressWarnings("unused") +public class FixPlaybackSpeedWhilePlayingPatch { + + private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f; + + public static boolean playbackSpeedChanged(float playbackSpeed) { + if (playbackSpeed == DEFAULT_YOUTUBE_PLAYBACK_SPEED && + PlayerType.getCurrent().isMaximizedOrFullscreen()) { + + Logger.printDebug(() -> "Blocking call to change playback speed to 1.0x"); + + return true; + } + + return false; + } + +} + diff --git a/patches/api/patches.api b/patches/api/patches.api index 25d3d50601..b76994c3a6 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1296,6 +1296,10 @@ public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClien public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatchKt { + public static final fun getFixPlaybackSpeedWhilePlayingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt { public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt index d0c289a419..bf12775612 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/dns/CheckWatchHistoryDomainNameResolutionPatch.kt @@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint private const val EXTENSION_CLASS_DESCRIPTOR = @@ -13,7 +14,10 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch( name = "Check watch history domain name resolution", description = "Checks if the device DNS server is preventing user watch history from being saved.", ) { - dependsOn(addResourcesPatch) + dependsOn( + sharedExtensionPatch, + addResourcesPatch + ) compatibleWith( "com.google.android.youtube"( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt new file mode 100644 index 0000000000..4d280cd883 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatch.kt @@ -0,0 +1,61 @@ +package app.revanced.patches.youtube.misc.fix.playbackspeed + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch +import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater +import app.revanced.patches.youtube.misc.playservice.versionCheckPatch +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/FixPlaybackSpeedWhilePlayingPatch;" + +/** + * Fixes a bug in YouTube 19.34+ where the playback speed + * can incorrectly reset to 1.0x under certain conditions. + * + * Reproduction steps using 19.34+ + * 1. Open a video and start playback + * 2. Change the speed to any value that is not 1.0x. + * 3. Open the comments panel. + * 4. Tap any "N more replies" link at the bottom of a comment, or tap on a timestamp of a comment. + * 5. Pause the video + * 6. Resume the video + * 7. Playback speed will incorrectly change to 1.0x. + */ +@Suppress("unused") +val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{ + dependsOn( + sharedExtensionPatch, + playerTypeHookPatch, + versionCheckPatch, + ) + + execute { + if (!is_19_34_or_greater) { + return@execute + } + + playbackSpeedInFeedsFingerprint.method.apply { + val freeRegister = implementation!!.registerCount - parameters.size - 2 + val playbackSpeedIndex = indexOfGetPlaybackSpeedInstruction(this) + val playbackSpeedRegister = getInstruction(playbackSpeedIndex).registerA + val returnIndex = indexOfFirstInstructionOrThrow(playbackSpeedIndex, Opcode.RETURN_VOID) + + addInstructionsWithLabels( + playbackSpeedIndex + 1, + """ + invoke-static { v$playbackSpeedRegister }, $EXTENSION_CLASS_DESCRIPTOR->playbackSpeedChanged(F)Z + move-result v$freeRegister + if-nez v$freeRegister, :do_not_change + """, + ExternalLabel("do_not_change", getInstruction(returnIndex)) + ) + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/Fingerprints.kt new file mode 100644 index 0000000000..d5a255ca5f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playbackspeed/Fingerprints.kt @@ -0,0 +1,38 @@ +package app.revanced.patches.youtube.misc.fix.playbackspeed + +import app.revanced.patcher.fingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionReversed +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.FieldReference + +/** + * This method is usually used to set the initial speed (1.0x) when playback starts from the feed. + * For some reason, in the latest YouTube, it is invoked even after the video has already started. + */ +internal val playbackSpeedInFeedsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L") + opcodes( + Opcode.IGET, + Opcode.MUL_INT_LIT16, + Opcode.IGET_WIDE, + Opcode.CONST_WIDE_16, + Opcode.CMP_LONG, + Opcode.IF_EQZ, + Opcode.IF_LEZ, + Opcode.SUB_LONG_2ADDR, + ) + custom { method, _ -> + indexOfGetPlaybackSpeedInstruction(method) >= 0 + } +} + +internal fun indexOfGetPlaybackSpeedInstruction(method: Method) = + method.indexOfFirstInstructionReversed { + opcode == Opcode.IGET && + getReference()?.type == "F" + } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt index 627a70f91a..5eb29385e3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt @@ -18,6 +18,7 @@ import app.revanced.patches.shared.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch +import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch import app.revanced.util.* import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -118,6 +119,7 @@ val settingsPatch = bytecodePatch( settingsResourcePatch, addResourcesPatch, disableCairoSettingsPatch, + fixPlaybackSpeedWhilePlayingPatch, // Currently there is no easy way to make a mandatory patch, // so for now this is a dependent of this patch. checkEnvironmentPatch,