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