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 b812a90a..a70f61e5 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 @@ -10,6 +10,7 @@ import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch +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 @@ -19,6 +20,7 @@ import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlay 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.util.getReference @@ -69,11 +71,15 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter ) object SpoofClientPatch : BytecodePatch( setOf( + // Client type spoof. BuildInitPlaybackRequestFingerprint, BuildPlayerRequestURIFingerprint, SetPlayerRequestClientTypeFingerprint, CreatePlayerRequestBodyFingerprint, CreatePlayerRequestBodyWithModelFingerprint, + + // Player gesture config. + PlayerGestureConfigSyntheticFingerprint, ), ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = @@ -115,6 +121,33 @@ 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 { 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 new file mode 100644 index 00000000..bbf66cb3 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerGestureConfigSyntheticFingerprint.kt @@ -0,0 +1,51 @@ +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 +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Ljava/lang/Object;"), + opcodes = listOf( + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + 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.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;" && + reference.parameterTypes.isEmpty() && + reference.returnType == "Z" + } +} \ No newline at end of file diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index fb45208e..cae84e50 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -1097,7 +1097,7 @@ 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 Android VR\n\nSide effects include:\n• No HDR video\n• Swipe to enter/exit fullscreen does not work\n• Kids videos do not playback\n• Paused videos can randomly resume + 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