fix(YouTube - Spoof client): Restore playback speed menu when spoofing to an iOS client

This commit is contained in:
oSumAtrIX 2024-06-02 17:43:41 +02:00
parent 48aaef5072
commit 95f290f113
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
4 changed files with 92 additions and 51 deletions

View file

@ -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
} }
} }

View file

@ -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.
),
)

View file

@ -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,23 +22,18 @@ 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.
methodDef.name == "a" && classDef.methods.count() == 2 &&
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
}
) {
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) = fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction { methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>() val reference = getReference<MethodReference>()
@ -48,4 +41,9 @@ internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
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
},
)

View file

@ -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>