Skip to content

Commit 61401c9

Browse files
committed
fix(YouTube - Spoof client): Restore playback speed menu when spoofing to an iOS client
1 parent cd42182 commit 61401c9

File tree

4 files changed

+92
-51
lines changed

4 files changed

+92
-51
lines changed

src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,8 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
1414
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
1515
import app.revanced.patches.all.misc.resources.AddResourcesPatch
1616
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
17-
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
1817
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
19-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint
20-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildPlayerRequestURIFingerprint
21-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyFingerprint
22-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithModelFingerprint
23-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint
24-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.SetPlayerRequestClientTypeFingerprint
18+
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
2519
import app.revanced.patches.youtube.misc.settings.SettingsPatch
2620
import app.revanced.util.getReference
2721
import app.revanced.util.indexOfFirstInstructionOrThrow
@@ -86,6 +80,9 @@ object SpoofClientPatch : BytecodePatch(
8680

8781
// Player gesture config.
8882
PlayerGestureConfigSyntheticFingerprint,
83+
84+
// Player speed menu item.
85+
CreatePlaybackSpeedMenuItemFingerprint,
8986
),
9087
) {
9188
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
@@ -99,7 +96,7 @@ object SpoofClientPatch : BytecodePatch(
9996
SettingsPatch.PreferenceScreen.MISC.addPreferences(
10097
PreferenceScreen(
10198
key = "revanced_spoof_client_screen",
102-
sorting = Sorting.UNSORTED,
99+
sorting = PreferenceScreen.Sorting.UNSORTED,
103100
preferences = setOf(
104101
SwitchPreference("revanced_spoof_client"),
105102
SwitchPreference("revanced_spoof_client_use_ios"),
@@ -127,33 +124,6 @@ object SpoofClientPatch : BytecodePatch(
127124

128125
// endregion
129126

130-
// region fix player gesture.
131-
132-
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
133-
val endIndex = it.scanResult.patternScanResult!!.endIndex
134-
135-
arrayOf(3, 9).forEach { offSet ->
136-
(context.toMethodWalker(it.mutableMethod)
137-
.nextMethod(endIndex - offSet, true)
138-
.getMethod() as MutableMethod)
139-
.apply {
140-
141-
val index = implementation!!.instructions.lastIndex
142-
val register = getInstruction<OneRegisterInstruction>(index).registerA
143-
144-
addInstructions(
145-
index,
146-
"""
147-
invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
148-
move-result v$register
149-
"""
150-
)
151-
}
152-
}
153-
}
154-
155-
// endregion
156-
157127
// region Block /get_watch requests to fall back to /player requests.
158128

159129
BuildPlayerRequestURIFingerprint.resultOrThrow().let {
@@ -281,5 +251,56 @@ object SpoofClientPatch : BytecodePatch(
281251

282252
// endregion
283253

254+
// region Fix player gesture if spoofing to iOS.
255+
256+
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
257+
val endIndex = it.scanResult.patternScanResult!!.endIndex
258+
val downAndOutLandscapeAllowedIndex = endIndex - 3
259+
val downAndOutPortraitAllowedIndex = endIndex - 9
260+
261+
arrayOf(
262+
downAndOutLandscapeAllowedIndex,
263+
downAndOutPortraitAllowedIndex,
264+
).forEach { index ->
265+
val gestureAllowedMethod = context.toMethodWalker(it.mutableMethod)
266+
.nextMethod(index, true)
267+
.getMethod() as MutableMethod
268+
269+
gestureAllowedMethod.apply {
270+
val isAllowedIndex = getInstructions().lastIndex
271+
val isAllowed = getInstruction<OneRegisterInstruction>(isAllowedIndex).registerA
272+
273+
addInstructions(
274+
isAllowedIndex,
275+
"""
276+
invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
277+
move-result v$isAllowed
278+
""",
279+
)
280+
}
281+
}
282+
}
283+
284+
// endregion
285+
286+
// Fix playback speed menu item if spoofing to iOS.
287+
288+
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
289+
val shouldCreateMenuIndex = it.scanResult.patternScanResult!!.endIndex
290+
291+
it.mutableMethod.apply {
292+
val shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
293+
294+
addInstructions(
295+
shouldCreateMenuIndex,
296+
"""
297+
invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
298+
move-result v$shouldCreateMenuRegister
299+
""",
300+
)
301+
}
302+
}
303+
304+
// endregion
284305
}
285306
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import com.android.tools.smali.dexlib2.AccessFlags
6+
import com.android.tools.smali.dexlib2.Opcode
7+
8+
internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
9+
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
10+
returnType = "V",
11+
parameters = listOf("[L", "F"),
12+
opcodes = listOf(
13+
Opcode.IGET_OBJECT,
14+
Opcode.IGET_OBJECT,
15+
Opcode.IGET_OBJECT,
16+
Opcode.CONST_4,
17+
Opcode.IF_EQZ,
18+
Opcode.INVOKE_INTERFACE,
19+
Opcode.MOVE_RESULT, // Return value controls the creation of the playback speed menu item.
20+
Opcode.IF_EQZ, // If the return value is false, the playback speed menu item is not created.
21+
),
22+
)
Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
22

33
import app.revanced.patcher.extensions.or
4-
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
54
import app.revanced.patcher.fingerprint.MethodFingerprint
6-
import app.revanced.patches.youtube.misc.fix.playback.fingerprints.PlayerGestureConfigSyntheticFingerprint.indexOfDownAndOutAllowedInstruction
75
import app.revanced.util.getReference
86
import app.revanced.util.indexOfFirstInstruction
97
import com.android.tools.smali.dexlib2.AccessFlags
@@ -24,28 +22,28 @@ internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
2422
Opcode.IGET_OBJECT,
2523
Opcode.INVOKE_INTERFACE,
2624
Opcode.MOVE_RESULT_OBJECT,
27-
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed
25+
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
2826
Opcode.MOVE_RESULT,
2927
Opcode.CHECK_CAST,
3028
Opcode.IPUT_BOOLEAN,
3129
Opcode.INVOKE_INTERFACE,
3230
Opcode.MOVE_RESULT_OBJECT,
33-
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed
31+
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
3432
Opcode.MOVE_RESULT,
3533
Opcode.IPUT_BOOLEAN,
3634
Opcode.RETURN_VOID,
3735
),
3836
customFingerprint = { methodDef, classDef ->
39-
// This method is always called "a" because this kind of class always has a single method.
40-
methodDef.name == "a" && classDef.methods.count() == 2 &&
41-
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
42-
}
43-
) {
44-
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
45-
methodDef.indexOfFirstInstruction {
46-
val reference = getReference<MethodReference>()
47-
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
37+
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
38+
methodDef.indexOfFirstInstruction {
39+
val reference = getReference<MethodReference>()
40+
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
4841
reference.parameterTypes.isEmpty() &&
4942
reference.returnType == "Z"
50-
}
51-
}
43+
}
44+
45+
// This method is always called "a" because this kind of class always has a single method.
46+
methodDef.name == "a" && classDef.methods.count() == 2 &&
47+
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
48+
},
49+
)

src/main/resources/addresources/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@
10961096
<string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
10971097
<string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
10981098
<string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string>
1099-
<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>
1099+
<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>
11001100
<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>
11011101
<string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
11021102
<string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>

0 commit comments

Comments
 (0)