fix(YouTube - Spoof client): Restore playback speed menu when spoofing to an iOS client
This commit is contained in:
		
							parent
							
								
									48aaef5072
								
							
						
					
					
						commit
						95f290f113
					
				| 
						 | 
					@ -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.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
 | 
				
			||||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
 | 
					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
 | 
				
			||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
 | 
					 | 
				
			||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
 | 
					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.*
 | 
				
			||||||
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.settings.SettingsPatch
 | 
					import app.revanced.patches.youtube.misc.settings.SettingsPatch
 | 
				
			||||||
import app.revanced.util.getReference
 | 
					import app.revanced.util.getReference
 | 
				
			||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
 | 
					import app.revanced.util.indexOfFirstInstructionOrThrow
 | 
				
			||||||
| 
						 | 
					@ -86,6 +80,9 @@ object SpoofClientPatch : BytecodePatch(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Player gesture config.
 | 
					        // Player gesture config.
 | 
				
			||||||
        PlayerGestureConfigSyntheticFingerprint,
 | 
					        PlayerGestureConfigSyntheticFingerprint,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Player speed menu item.
 | 
				
			||||||
 | 
					        CreatePlaybackSpeedMenuItemFingerprint,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    private const val INTEGRATIONS_CLASS_DESCRIPTOR =
 | 
					    private const val INTEGRATIONS_CLASS_DESCRIPTOR =
 | 
				
			||||||
| 
						 | 
					@ -99,7 +96,7 @@ object SpoofClientPatch : BytecodePatch(
 | 
				
			||||||
        SettingsPatch.PreferenceScreen.MISC.addPreferences(
 | 
					        SettingsPatch.PreferenceScreen.MISC.addPreferences(
 | 
				
			||||||
            PreferenceScreen(
 | 
					            PreferenceScreen(
 | 
				
			||||||
                key = "revanced_spoof_client_screen",
 | 
					                key = "revanced_spoof_client_screen",
 | 
				
			||||||
                sorting = Sorting.UNSORTED,
 | 
					                sorting = PreferenceScreen.Sorting.UNSORTED,
 | 
				
			||||||
                preferences = setOf(
 | 
					                preferences = setOf(
 | 
				
			||||||
                    SwitchPreference("revanced_spoof_client"),
 | 
					                    SwitchPreference("revanced_spoof_client"),
 | 
				
			||||||
                    SwitchPreference("revanced_spoof_client_use_ios"),
 | 
					                    SwitchPreference("revanced_spoof_client_use_ios"),
 | 
				
			||||||
| 
						 | 
					@ -127,33 +124,6 @@ object SpoofClientPatch : BytecodePatch(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // endregion
 | 
					        // 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<OneRegisterInstruction>(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.
 | 
					        // region Block /get_watch requests to fall back to /player requests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        BuildPlayerRequestURIFingerprint.resultOrThrow().let {
 | 
					        BuildPlayerRequestURIFingerprint.resultOrThrow().let {
 | 
				
			||||||
| 
						 | 
					@ -281,5 +251,56 @@ object SpoofClientPatch : BytecodePatch(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // endregion
 | 
					        // 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<OneRegisterInstruction>(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<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                addInstructions(
 | 
				
			||||||
 | 
					                    shouldCreateMenuIndex,
 | 
				
			||||||
 | 
					                    """
 | 
				
			||||||
 | 
					                        invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
 | 
				
			||||||
 | 
					                        move-result v$shouldCreateMenuRegister
 | 
				
			||||||
 | 
					                    """,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // endregion
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,7 @@
 | 
				
			||||||
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
 | 
					package app.revanced.patches.youtube.misc.fix.playback.fingerprints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import app.revanced.patcher.extensions.or
 | 
					import app.revanced.patcher.extensions.or
 | 
				
			||||||
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
 | 
					 | 
				
			||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
 | 
					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.getReference
 | 
				
			||||||
import app.revanced.util.indexOfFirstInstruction
 | 
					import app.revanced.util.indexOfFirstInstruction
 | 
				
			||||||
import com.android.tools.smali.dexlib2.AccessFlags
 | 
					import com.android.tools.smali.dexlib2.AccessFlags
 | 
				
			||||||
| 
						 | 
					@ -24,28 +22,28 @@ internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
 | 
				
			||||||
        Opcode.IGET_OBJECT,
 | 
					        Opcode.IGET_OBJECT,
 | 
				
			||||||
        Opcode.INVOKE_INTERFACE,
 | 
					        Opcode.INVOKE_INTERFACE,
 | 
				
			||||||
        Opcode.MOVE_RESULT_OBJECT,
 | 
					        Opcode.MOVE_RESULT_OBJECT,
 | 
				
			||||||
        Opcode.INVOKE_VIRTUAL,      // playerGestureConfig.downAndOutLandscapeAllowed
 | 
					        Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
 | 
				
			||||||
        Opcode.MOVE_RESULT,
 | 
					        Opcode.MOVE_RESULT,
 | 
				
			||||||
        Opcode.CHECK_CAST,
 | 
					        Opcode.CHECK_CAST,
 | 
				
			||||||
        Opcode.IPUT_BOOLEAN,
 | 
					        Opcode.IPUT_BOOLEAN,
 | 
				
			||||||
        Opcode.INVOKE_INTERFACE,
 | 
					        Opcode.INVOKE_INTERFACE,
 | 
				
			||||||
        Opcode.MOVE_RESULT_OBJECT,
 | 
					        Opcode.MOVE_RESULT_OBJECT,
 | 
				
			||||||
        Opcode.INVOKE_VIRTUAL,      // playerGestureConfig.downAndOutPortraitAllowed
 | 
					        Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
 | 
				
			||||||
        Opcode.MOVE_RESULT,
 | 
					        Opcode.MOVE_RESULT,
 | 
				
			||||||
        Opcode.IPUT_BOOLEAN,
 | 
					        Opcode.IPUT_BOOLEAN,
 | 
				
			||||||
        Opcode.RETURN_VOID,
 | 
					        Opcode.RETURN_VOID,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    customFingerprint = { methodDef, classDef ->
 | 
					    customFingerprint = { methodDef, classDef ->
 | 
				
			||||||
        // This method is always called "a" because this kind of class always has a single method.
 | 
					        fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
 | 
				
			||||||
        methodDef.name == "a" && classDef.methods.count() == 2 &&
 | 
					            methodDef.indexOfFirstInstruction {
 | 
				
			||||||
                indexOfDownAndOutAllowedInstruction(methodDef) >= 0
 | 
					                val reference = getReference<MethodReference>()
 | 
				
			||||||
    }
 | 
					                reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
 | 
					 | 
				
			||||||
        methodDef.indexOfFirstInstruction {
 | 
					 | 
				
			||||||
            val reference = getReference<MethodReference>()
 | 
					 | 
				
			||||||
            reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
 | 
					 | 
				
			||||||
                    reference.parameterTypes.isEmpty() &&
 | 
					                    reference.parameterTypes.isEmpty() &&
 | 
				
			||||||
                    reference.returnType == "Z"
 | 
					                    reference.returnType == "Z"
 | 
				
			||||||
        }
 | 
					            }
 | 
				
			||||||
}
 | 
					
 | 
				
			||||||
 | 
					        // 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
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1096,7 +1096,7 @@
 | 
				
			||||||
            <string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
 | 
					            <string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
 | 
				
			||||||
            <string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
 | 
					            <string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
 | 
				
			||||||
            <string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string>
 | 
					            <string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string>
 | 
				
			||||||
            <string name="revanced_spoof_client_use_ios_summary_on">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</string>
 | 
					            <string name="revanced_spoof_client_use_ios_summary_on">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</string>
 | 
				
			||||||
            <string name="revanced_spoof_client_use_ios_summary_off">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</string>
 | 
					            <string name="revanced_spoof_client_use_ios_summary_off">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</string>
 | 
				
			||||||
            <string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
 | 
					            <string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
 | 
				
			||||||
            <string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>
 | 
					            <string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue