From 95f290f1139cc8679beecac53c623847668f885e Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 2 Jun 2024 17:43:41 +0200 Subject: [PATCH] fix(YouTube - Spoof client): Restore playback speed menu when spoofing to an iOS client --- .../misc/fix/playback/SpoofClientPatch.kt | 91 ++++++++++++------- .../CreatePlaybackSpeedMenuItemFingerprint.kt | 22 +++++ ...PlayerGestureConfigSyntheticFingerprint.kt | 28 +++--- .../resources/addresources/values/strings.xml | 2 +- 4 files changed, 92 insertions(+), 51 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt index 36fc572c..c008f673 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt @@ -14,14 +14,8 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen -import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildPlayerRequestURIFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithModelFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.SetPlayerRequestClientTypeFingerprint +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow @@ -86,6 +80,9 @@ object SpoofClientPatch : BytecodePatch( // Player gesture config. PlayerGestureConfigSyntheticFingerprint, + + // Player speed menu item. + CreatePlaybackSpeedMenuItemFingerprint, ), ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = @@ -99,7 +96,7 @@ object SpoofClientPatch : BytecodePatch( SettingsPatch.PreferenceScreen.MISC.addPreferences( PreferenceScreen( key = "revanced_spoof_client_screen", - sorting = Sorting.UNSORTED, + sorting = PreferenceScreen.Sorting.UNSORTED, preferences = setOf( SwitchPreference("revanced_spoof_client"), SwitchPreference("revanced_spoof_client_use_ios"), @@ -127,33 +124,6 @@ object SpoofClientPatch : BytecodePatch( // endregion - // region fix player gesture. - - PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let { - val endIndex = it.scanResult.patternScanResult!!.endIndex - - arrayOf(3, 9).forEach { offSet -> - (context.toMethodWalker(it.mutableMethod) - .nextMethod(endIndex - offSet, true) - .getMethod() as MutableMethod) - .apply { - - val index = implementation!!.instructions.lastIndex - val register = getInstruction(index).registerA - - addInstructions( - index, - """ - invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z - move-result v$register - """ - ) - } - } - } - - // endregion - // region Block /get_watch requests to fall back to /player requests. BuildPlayerRequestURIFingerprint.resultOrThrow().let { @@ -281,5 +251,56 @@ object SpoofClientPatch : BytecodePatch( // endregion + // region Fix player gesture if spoofing to iOS. + + PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let { + val endIndex = it.scanResult.patternScanResult!!.endIndex + val downAndOutLandscapeAllowedIndex = endIndex - 3 + val downAndOutPortraitAllowedIndex = endIndex - 9 + + arrayOf( + downAndOutLandscapeAllowedIndex, + downAndOutPortraitAllowedIndex, + ).forEach { index -> + val gestureAllowedMethod = context.toMethodWalker(it.mutableMethod) + .nextMethod(index, true) + .getMethod() as MutableMethod + + gestureAllowedMethod.apply { + val isAllowedIndex = getInstructions().lastIndex + val isAllowed = getInstruction(isAllowedIndex).registerA + + addInstructions( + isAllowedIndex, + """ + invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z + move-result v$isAllowed + """, + ) + } + } + } + + // endregion + + // Fix playback speed menu item if spoofing to iOS. + + CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let { + val shouldCreateMenuIndex = it.scanResult.patternScanResult!!.endIndex + + it.mutableMethod.apply { + val shouldCreateMenuRegister = getInstruction(shouldCreateMenuIndex).registerA + + addInstructions( + shouldCreateMenuIndex, + """ + invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z + move-result v$shouldCreateMenuRegister + """, + ) + } + } + + // endregion } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt new file mode 100644 index 00000000..389977dd --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("[L", "F"), + opcodes = listOf( + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT, // Return value controls the creation of the playback speed menu item. + Opcode.IF_EQZ, // If the return value is false, the playback speed menu item is not created. + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerGestureConfigSyntheticFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerGestureConfigSyntheticFingerprint.kt index bbf66cb3..d691d7cf 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerGestureConfigSyntheticFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerGestureConfigSyntheticFingerprint.kt @@ -1,9 +1,7 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod import app.revanced.patcher.fingerprint.MethodFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint.indexOfDownAndOutAllowedInstruction import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.AccessFlags @@ -24,28 +22,28 @@ internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint( Opcode.IGET_OBJECT, Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed + Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed. Opcode.MOVE_RESULT, Opcode.CHECK_CAST, Opcode.IPUT_BOOLEAN, Opcode.INVOKE_INTERFACE, Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed + Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed. Opcode.MOVE_RESULT, Opcode.IPUT_BOOLEAN, Opcode.RETURN_VOID, ), customFingerprint = { methodDef, classDef -> - // This method is always called "a" because this kind of class always has a single method. - methodDef.name == "a" && classDef.methods.count() == 2 && - indexOfDownAndOutAllowedInstruction(methodDef) >= 0 - } -) { - fun indexOfDownAndOutAllowedInstruction(methodDef: Method) = - methodDef.indexOfFirstInstruction { - val reference = getReference() - reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" && + fun indexOfDownAndOutAllowedInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" && reference.parameterTypes.isEmpty() && reference.returnType == "Z" - } -} \ No newline at end of file + } + + // This method is always called "a" because this kind of class always has a single method. + methodDef.name == "a" && classDef.methods.count() == 2 && + indexOfDownAndOutAllowedInstruction(methodDef) >= 0 + }, +) diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index cae84e50..34b2a380 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -1096,7 +1096,7 @@ Client is not spoofed\n\nVideo playback may not work Turning off this setting may cause video playback issues. Spoof client to iOS - Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Speed menu is missing\n• Watch history may not work\n• Live streams cannot play as audio only\n• Live streams not available on older devices + Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Watch history may not work\n• Live streams cannot play as audio only\n• Live streams not available on older devices Client is currently spoofed to Android VR\n\nSide effects include:\n• No HDR video\n• Kids videos do not playback\n• Paused videos can randomly resume Spoof client thumbnails not available (API timed out) Spoof client thumbnails temporarily not available: %s