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 8ef61738..b812a90a 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 @@ -15,11 +15,13 @@ 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.Sorting import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.BuildInitPlaybackRequestFingerprint +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.SetPlayerRequestClientTypeFingerprint import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstruction import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -35,11 +37,9 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter name = "Spoof client", description = "Spoofs the client to allow video playback.", dependencies = [ - PlayerResponseMethodHookPatch::class, SettingsPatch::class, AddResourcesPatch::class, UserAgentClientSpoofPatch::class, - PlayerResponseMethodHookPatch::class, ], compatiblePackages = [ CompatiblePackage( @@ -69,19 +69,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter ) object SpoofClientPatch : BytecodePatch( setOf( - // Client type spoof. BuildInitPlaybackRequestFingerprint, BuildPlayerRequestURIFingerprint, SetPlayerRequestClientTypeFingerprint, CreatePlayerRequestBodyFingerprint, CreatePlayerRequestBodyWithModelFingerprint, - - // Storyboard spoof. - StoryboardRendererSpecFingerprint, - PlayerResponseModelImplRecommendedLevelFingerprint, - StoryboardRendererDecoderRecommendedLevelFingerprint, - PlayerResponseModelImplGeneralFingerprint, - StoryboardRendererDecoderSpecFingerprint, ), ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = @@ -98,7 +90,7 @@ object SpoofClientPatch : BytecodePatch( sorting = Sorting.UNSORTED, preferences = setOf( SwitchPreference("revanced_spoof_client"), - SwitchPreference("revanced_spoof_client_use_testsuite"), + SwitchPreference("revanced_spoof_client_use_ios"), ), ), ) @@ -149,11 +141,11 @@ object SpoofClientPatch : BytecodePatch( SetPlayerRequestClientTypeFingerprint.resultOrThrow().let { result -> // Field in the player request object that holds the client info object. val clientInfoField = result.mutableMethod - .getInstructions().first { instruction -> + .getInstructions().find { instruction -> // requestMessage.clientInfo = clientInfoBuilder.build(); instruction.opcode == Opcode.IPUT_OBJECT && instruction.getReference()?.type == CLIENT_INFO_CLASS_DESCRIPTOR - }.getReference() ?: throw PatchException("Could not find clientInfoField") + }?.getReference() ?: throw PatchException("Could not find clientInfoField") // Client info object's client type field. val clientInfoClientTypeField = result.mutableMethod @@ -168,20 +160,17 @@ object SpoofClientPatch : BytecodePatch( Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) } - val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().mutableMethod.let { - val instructions = it.getInstructions() - - val getClientModelIndex = it.indexOfFirstInstruction { - getReference().toString() == "Landroid/os/Build;->MODEL:Ljava/lang/String;" - } + val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().let { + val getClientModelIndex = CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction(it.method) + val instructions = it.mutableMethod.getInstructions() // The next IPUT_OBJECT instruction after getting the client model is setting the client model field. instructions.subList( getClientModelIndex, - instructions.lastIndex, - ).first { instruction -> + instructions.size, + ).find { instruction -> instruction.opcode == Opcode.IPUT_OBJECT - }.getReference() ?: throw PatchException("Could not find clientInfoClientModelField") + }?.getReference() ?: throw PatchException("Could not find clientInfoClientModelField") } // endregion @@ -243,6 +232,7 @@ object SpoofClientPatch : BytecodePatch( invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientVersion(Ljava/lang/String;)Ljava/lang/String; move-result-object v1 iput-object v1, v0, $clientInfoClientVersionField + :disabled return-void """, @@ -253,104 +243,5 @@ object SpoofClientPatch : BytecodePatch( // endregion - // region Fix storyboard if Android Testsuite is used. - - PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter( - "$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(" + - "Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", - ) - - // Hook recommended seekbar thumbnails quality level for regular videos. - StoryboardRendererDecoderRecommendedLevelFingerprint.resultOrThrow().let { - val endIndex = it.scanResult.patternScanResult!!.endIndex - - it.mutableMethod.apply { - val originalValueRegister = - getInstruction(endIndex).registerA - - addInstructions( - endIndex + 1, - """ - invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I - move-result v$originalValueRegister - """, - ) - } - } - - // Hook the recommended precise seeking thumbnails quality. - PlayerResponseModelImplRecommendedLevelFingerprint.resultOrThrow().let { - val endIndex = it.scanResult.patternScanResult!!.endIndex - - it.mutableMethod.apply { - val originalValueRegister = - getInstruction(endIndex).registerA - - addInstructions( - endIndex, - """ - invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I - move-result v$originalValueRegister - """, - ) - } - } - - // TODO: Hook the seekbar recommended level for Shorts to fix Shorts low quality seekbar thumbnails. - - /** - * Hook StoryBoard renderer url. - */ - PlayerResponseModelImplGeneralFingerprint.resultOrThrow().let { - val getStoryBoardIndex = it.scanResult.patternScanResult!!.endIndex - - it.mutableMethod.apply { - val getStoryBoardRegister = getInstruction(getStoryBoardIndex).registerA - - addInstructions( - getStoryBoardIndex, - """ - invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$getStoryBoardRegister - """, - ) - } - } - - // Hook the seekbar thumbnail decoder, required for Shorts. - StoryboardRendererDecoderSpecFingerprint.resultOrThrow().let { - val storyBoardUrlIndex = it.scanResult.patternScanResult!!.startIndex + 1 - - it.mutableMethod.apply { - val getStoryBoardRegister = getInstruction(storyBoardUrlIndex).registerA - - addInstructions( - storyBoardUrlIndex + 1, - """ - invoke-static { v$getStoryBoardRegister }, ${INTEGRATIONS_CLASS_DESCRIPTOR}->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$getStoryBoardRegister - """, - ) - } - } - - StoryboardRendererSpecFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val storyBoardUrlParams = "p0" - - addInstructions( - 0, - """ - if-nez $storyBoardUrlParams, :ignore - invoke-static { $storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; - move-result-object $storyBoardUrlParams - :ignore - nop - """, - ) - } - } - - // endregion } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt index bdf6f8ab..eb913300 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt @@ -2,8 +2,12 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.CreatePlayerRequestBodyWithModelFingerprint.indexOfBuildModelInstruction +import app.revanced.util.containsWideLiteralInstructionValue import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.reference.FieldReference internal object CreatePlayerRequestBodyWithModelFingerprint : MethodFingerprint( @@ -11,8 +15,17 @@ internal object CreatePlayerRequestBodyWithModelFingerprint : MethodFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf(), customFingerprint = { methodDef, _ -> - methodDef.implementation!!.instructions.any { - it.getReference().toString() == "Landroid/os/Build;->MODEL:Ljava/lang/String;" - } + methodDef.containsWideLiteralInstructionValue(1073741824) && + indexOfBuildModelInstruction(methodDef) >= 0 }, -) +) { + fun indexOfBuildModelInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + val reference = getReference() + reference?.definingClass == "Landroid/os/Build;" && + reference.name == "MODEL" && + reference.type == "Ljava/lang/String;" + } +} + + diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt index ab4be891..9328dd45 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt @@ -6,6 +6,7 @@ import app.revanced.util.containsWideLiteralInstructionValue import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +@Deprecated("Fingerprint is obsolete and will be deleted soon") internal object PlayerResponseModelImplGeneralFingerprint : MethodFingerprint( returnType = "Ljava/lang/String;", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt index ee54d776..11de5b7f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt @@ -6,6 +6,7 @@ import app.revanced.util.containsWideLiteralInstructionValue import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +@Deprecated("Fingerprint is obsolete and will be deleted soon") internal object PlayerResponseModelImplRecommendedLevelFingerprint : MethodFingerprint( returnType = "I", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt index e9d91e76..78c240ef 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/SetPlayerRequestClientTypeFingerprint.kt @@ -1,12 +1,13 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints -import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.Opcode -internal object SetPlayerRequestClientTypeFingerprint : MethodFingerprint( - strings = listOf("10.29"), +internal object SetPlayerRequestClientTypeFingerprint : LiteralValueFingerprint( opcodes = listOf( Opcode.IGET, Opcode.IPUT, // Sets ClientInfo.clientId. ), + strings = listOf("10.29"), + literalSupplier = { 134217728 } ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt index e56867ea..482ca51a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt @@ -8,6 +8,7 @@ import com.android.tools.smali.dexlib2.Opcode /** * Resolves to the same method as [StoryboardRendererDecoderSpecFingerprint]. */ +@Deprecated("Fingerprint is obsolete and will be deleted soon") internal object StoryboardRendererDecoderRecommendedLevelFingerprint : MethodFingerprint( returnType = "V", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt index bd934d2a..a2a31800 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt @@ -8,6 +8,7 @@ import com.android.tools.smali.dexlib2.Opcode /** * Resolves to the same method as [StoryboardRendererDecoderRecommendedLevelFingerprint]. */ +@Deprecated("Fingerprint is obsolete and will be deleted soon") internal object StoryboardRendererDecoderSpecFingerprint : MethodFingerprint( returnType = "V", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt index c453d6b5..cc00d0cc 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt @@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags +@Deprecated("Fingerprint is obsolete and will be deleted soon") internal object StoryboardRendererSpecFingerprint : MethodFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, returnType = "L", diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt index dad3eb29..88e368db 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt @@ -8,6 +8,7 @@ import com.android.tools.smali.dexlib2.Opcode /** * Resolves using the class found in [StoryboardThumbnailParentFingerprint]. */ +@Deprecated("Fingerprint is obsolete and will be deleted soon") internal object StoryboardThumbnailFingerprint : MethodFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, returnType = "Z", diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index 89ba652c..299db4ae 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -1092,10 +1092,9 @@ Client is spoofed Client is not spoofed\n\nVideo playback may not work Turning off this setting may cause video playback issues. - Spoof client to Android Testsuite - Spoof the client to Android Testsuite - Client is spoofed to an Android Testsuite client (iOS client is used for live streams)\n\nSide effects include, but are not limited to:\n• Speed flyout menu is missing\n• Captions are missing\n• Player swipe gestures may not work\n• Low quality Shorts seekbar thumbnails\n• Watch history may not work - Client is spoofed to an iOS client\n\nSide effects include:\n• No HDR video\n• Speed flyout menu is missing + 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• Player swipe gestures do not work\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