diff --git a/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofUserAgentPatch.kt b/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofUserAgentPatch.kt new file mode 100644 index 000000000..686025fa4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/utils/fix/client/SpoofUserAgentPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.music.utils.fix.client + +import app.revanced.patches.shared.spoofuseragent.BaseSpoofUserAgentPatch + +object SpoofUserAgentPatch : BaseSpoofUserAgentPatch("com.google.android.apps.youtube.music") \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/utils/fix/clientspoof/ClientSpoofPatch.kt b/src/main/kotlin/app/revanced/patches/music/utils/fix/clientspoof/ClientSpoofPatch.kt deleted file mode 100644 index cbf2b5fda..000000000 --- a/src/main/kotlin/app/revanced/patches/music/utils/fix/clientspoof/ClientSpoofPatch.kt +++ /dev/null @@ -1,5 +0,0 @@ -package app.revanced.patches.music.utils.fix.clientspoof - -import app.revanced.patches.shared.clientspoof.BaseClientSpoofPatch - -object ClientSpoofPatch : BaseClientSpoofPatch("com.google.android.apps.youtube.music") \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/utils/gms/GmsCoreSupportPatch.kt b/src/main/kotlin/app/revanced/patches/music/utils/gms/GmsCoreSupportPatch.kt index 8c0f8a136..28b4b561e 100644 --- a/src/main/kotlin/app/revanced/patches/music/utils/gms/GmsCoreSupportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/utils/gms/GmsCoreSupportPatch.kt @@ -1,7 +1,7 @@ package app.revanced.patches.music.utils.gms import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE -import app.revanced.patches.music.utils.fix.clientspoof.ClientSpoofPatch +import app.revanced.patches.music.utils.fix.client.SpoofUserAgentPatch import app.revanced.patches.music.utils.fix.fileprovider.FileProviderPatch import app.revanced.patches.music.utils.integrations.IntegrationsPatch import app.revanced.patches.music.utils.mainactivity.fingerprints.MainActivityFingerprint @@ -14,7 +14,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch( fromPackageName = ORIGINAL_PACKAGE_NAME_YOUTUBE, mainActivityOnCreateFingerprint = MainActivityFingerprint, integrationsPatchDependency = IntegrationsPatch::class, - dependencies = setOf(ClientSpoofPatch::class, PackageNamePatch::class, FileProviderPatch::class), + dependencies = setOf(SpoofUserAgentPatch::class, PackageNamePatch::class, FileProviderPatch::class), gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch, compatiblePackages = COMPATIBLE_PACKAGE ) diff --git a/src/main/kotlin/app/revanced/patches/shared/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt b/src/main/kotlin/app/revanced/patches/shared/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt new file mode 100644 index 000000000..866aa3d4a --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/shared/fingerprints/CreatePlayerRequestBodyWithModelFingerprint.kt @@ -0,0 +1,33 @@ +package app.revanced.patches.shared.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint.indexOfModelInstruction +import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint.indexOfReleaseInstruction +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.FieldReference + +internal object CreatePlayerRequestBodyWithModelFingerprint : MethodFingerprint( + returnType = "L", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + opcodes = listOf(Opcode.OR_INT_LIT16), + customFingerprint = { methodDef, _ -> + indexOfModelInstruction(methodDef) >= 0 + && indexOfReleaseInstruction(methodDef) >= 0 + } +) { + fun indexOfModelInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + getReference().toString() == "Landroid/os/Build;->MODEL:Ljava/lang/String;" + } + + fun indexOfReleaseInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + getReference().toString() == "Landroid/os/Build${'$'}VERSION;->RELEASE:Ljava/lang/String;" + } +} diff --git a/src/main/kotlin/app/revanced/patches/shared/spoofappversion/BaseSpoofAppVersionPatch.kt b/src/main/kotlin/app/revanced/patches/shared/spoofappversion/BaseSpoofAppVersionPatch.kt index 5a23a3f7d..a72c71af9 100644 --- a/src/main/kotlin/app/revanced/patches/shared/spoofappversion/BaseSpoofAppVersionPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/spoofappversion/BaseSpoofAppVersionPatch.kt @@ -4,10 +4,9 @@ import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patches.shared.spoofappversion.fingerprints.ClientInfoFingerprint -import app.revanced.patches.shared.spoofappversion.fingerprints.ClientInfoParentFingerprint +import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint +import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint.indexOfReleaseInstruction import app.revanced.util.getTargetIndexReversed -import app.revanced.util.getTargetIndexWithFieldReferenceName import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -15,27 +14,21 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction abstract class BaseSpoofAppVersionPatch( private val descriptor: String ) : BytecodePatch( - setOf(ClientInfoParentFingerprint) + setOf(CreatePlayerRequestBodyWithModelFingerprint) ) { override fun execute(context: BytecodeContext) { - ClientInfoParentFingerprint.resultOrThrow().let { parentResult -> - ClientInfoFingerprint.resolve(context, parentResult.classDef) + CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().mutableMethod.apply { + val versionIndex = indexOfReleaseInstruction(this) + 1 + val insertIndex = getTargetIndexReversed(versionIndex, Opcode.IPUT_OBJECT) + val insertRegister = getInstruction(insertIndex).registerA - ClientInfoFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val versionIndex = getTargetIndexWithFieldReferenceName("RELEASE") + 1 - val insertIndex = getTargetIndexReversed(versionIndex, Opcode.IPUT_OBJECT) - val insertRegister = getInstruction(insertIndex).registerA - - addInstructions( - insertIndex, """ - invoke-static {v$insertRegister}, $descriptor - move-result-object v$insertRegister - """ - ) - } - } + addInstructions( + insertIndex, """ + invoke-static {v$insertRegister}, $descriptor + move-result-object v$insertRegister + """ + ) } } diff --git a/src/main/kotlin/app/revanced/patches/shared/spoofappversion/fingerprints/ClientInfoFingerprint.kt b/src/main/kotlin/app/revanced/patches/shared/spoofappversion/fingerprints/ClientInfoFingerprint.kt deleted file mode 100644 index 96c51053a..000000000 --- a/src/main/kotlin/app/revanced/patches/shared/spoofappversion/fingerprints/ClientInfoFingerprint.kt +++ /dev/null @@ -1,13 +0,0 @@ -package app.revanced.patches.shared.spoofappversion.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 ClientInfoFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - parameters = emptyList(), - opcodes = listOf(Opcode.OR_INT_LIT16) -) diff --git a/src/main/kotlin/app/revanced/patches/shared/spoofappversion/fingerprints/ClientInfoParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/shared/spoofappversion/fingerprints/ClientInfoParentFingerprint.kt deleted file mode 100644 index 799a153ef..000000000 --- a/src/main/kotlin/app/revanced/patches/shared/spoofappversion/fingerprints/ClientInfoParentFingerprint.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.revanced.patches.shared.spoofappversion.fingerprints - -import app.revanced.patcher.fingerprint.MethodFingerprint - -internal object ClientInfoParentFingerprint : MethodFingerprint( - returnType = "V", - strings = listOf("Android Wear") -) diff --git a/src/main/kotlin/app/revanced/patches/shared/clientspoof/BaseClientSpoofPatch.kt b/src/main/kotlin/app/revanced/patches/shared/spoofuseragent/BaseSpoofUserAgentPatch.kt similarity index 97% rename from src/main/kotlin/app/revanced/patches/shared/clientspoof/BaseClientSpoofPatch.kt rename to src/main/kotlin/app/revanced/patches/shared/spoofuseragent/BaseSpoofUserAgentPatch.kt index c21ad9bc6..309896a62 100644 --- a/src/main/kotlin/app/revanced/patches/shared/clientspoof/BaseClientSpoofPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/spoofuseragent/BaseSpoofUserAgentPatch.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.shared.clientspoof +package app.revanced.patches.shared.spoofuseragent import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction @@ -16,7 +16,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference -abstract class BaseClientSpoofPatch( +abstract class BaseSpoofUserAgentPatch( private val packageName: String ) : BaseTransformInstructionsPatch() { override fun filterMap( diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/SpoofClientPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/SpoofClientPatch.kt new file mode 100644 index 000000000..7aa753063 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/SpoofClientPatch.kt @@ -0,0 +1,239 @@ +package app.revanced.patches.youtube.utils.fix.client + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.or +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint +import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint.indexOfModelInstruction +import app.revanced.patches.youtube.utils.compatibility.Constants +import app.revanced.patches.youtube.utils.fix.client.fingerprints.BuildInitPlaybackRequestFingerprint +import app.revanced.patches.youtube.utils.fix.client.fingerprints.BuildPlayerRequestURIFingerprint +import app.revanced.patches.youtube.utils.fix.client.fingerprints.CreatePlayerRequestBodyFingerprint +import app.revanced.patches.youtube.utils.fix.client.fingerprints.SetPlayerRequestClientTypeFingerprint +import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH +import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch +import app.revanced.patches.youtube.utils.settings.SettingsPatch +import app.revanced.patches.youtube.video.information.VideoInformationPatch +import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch +import app.revanced.util.getReference +import app.revanced.util.getStringInstructionIndex +import app.revanced.util.patch.BaseBytecodePatch +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter + +object SpoofClientPatch : BaseBytecodePatch( + name = "Spoof client", + description = "Adds options to spoofs the client to allow video playback.", + dependencies = setOf( + PlayerTypeHookPatch::class, + PlayerResponseMethodHookPatch::class, + SettingsPatch::class, + VideoInformationPatch::class, + SpoofUserAgentPatch::class, + ), + compatiblePackages = Constants.COMPATIBLE_PACKAGE, + fingerprints = setOf( + BuildInitPlaybackRequestFingerprint, + BuildPlayerRequestURIFingerprint, + SetPlayerRequestClientTypeFingerprint, + CreatePlayerRequestBodyFingerprint, + CreatePlayerRequestBodyWithModelFingerprint, + ) +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "$MISC_PATH/SpoofClientPatch;" + private const val CLIENT_INFO_CLASS_DESCRIPTOR = + "Lcom/google/protos/youtube/api/innertube/InnertubeContext\$ClientInfo;" + + override fun execute(context: BytecodeContext) { + + // region Block /initplayback requests to fall back to /get_watch requests. + + BuildInitPlaybackRequestFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val moveUriStringIndex = it.scanResult.patternScanResult!!.startIndex + val targetRegister = getInstruction(moveUriStringIndex).registerA + + addInstructions( + moveUriStringIndex + 1, """ + invoke-static { v$targetRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$targetRegister + """, + ) + } + } + + // endregion + + // region Block /get_watch requests to fall back to /player requests. + + BuildPlayerRequestURIFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val invokeToStringIndex = it.scanResult.patternScanResult!!.startIndex + val uriRegister = getInstruction(invokeToStringIndex).registerC + + addInstructions( + invokeToStringIndex, """ + invoke-static { v$uriRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri; + move-result-object v$uriRegister + """, + ) + } + } + + // endregion + + // region Get field references to be used below. + + val (clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) = + SetPlayerRequestClientTypeFingerprint.resultOrThrow().let { result -> + with (result.mutableMethod) { + // Field in the player request object that holds the client info object. + val clientInfoField = 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") + + // Client info object's client type field. + val clientInfoClientTypeField = getInstruction(result.scanResult.patternScanResult!!.endIndex) + .getReference() ?: throw PatchException("Could not find clientInfoClientTypeField") + + val clientInfoVersionIndex = getStringInstructionIndex("10.29") + val clientInfoVersionRegister = getInstruction(clientInfoVersionIndex).registerA + val clientInfoClientVersionFieldIndex = implementation!!.instructions.let { + clientInfoVersionIndex + it.subList(clientInfoVersionIndex, it.size - 1).indexOfFirst { instruction -> + instruction.opcode == Opcode.IPUT_OBJECT + && (instruction as TwoRegisterInstruction).registerA == clientInfoVersionRegister + } + } + + // Client info object's client version field. + val clientInfoClientVersionField = getInstruction(clientInfoClientVersionFieldIndex) + .getReference() ?: throw PatchException("Could not find clientInfoClientVersionField") + + Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) + } + } + + val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().mutableMethod.let { + val instructions = it.getInstructions() + val getClientModelIndex = indexOfModelInstruction(it) + + // The next IPUT_OBJECT instruction after getting the client model is setting the client model field. + instructions.subList( + getClientModelIndex, + instructions.size, + ).find { instruction -> + val reference = instruction.getReference() + instruction.opcode == Opcode.IPUT_OBJECT + && reference?.definingClass == CLIENT_INFO_CLASS_DESCRIPTOR + && reference.type == "Ljava/lang/String;" + }?.getReference() ?: throw PatchException("Could not find clientInfoClientModelField") + } + + // endregion + + // region Spoof client type for /player requests. + + CreatePlayerRequestBodyFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val setClientInfoMethodName = "setClientInfo" + val checkCastIndex = it.scanResult.patternScanResult!!.startIndex + + val checkCastInstruction = getInstruction(checkCastIndex) + val requestMessageInstanceRegister = checkCastInstruction.registerA + val clientInfoContainerClassName = checkCastInstruction.getReference()!!.type + + addInstruction( + checkCastIndex + 1, + "invoke-static { v$requestMessageInstanceRegister }," + + " $definingClass->$setClientInfoMethodName($clientInfoContainerClassName)V", + ) + + // Change client info to use the spoofed values. + // Do this in a helper method, to remove the need of picking out multiple free registers from the hooked code. + it.mutableClass.methods.add( + ImmutableMethod( + definingClass, + setClientInfoMethodName, + listOf(ImmutableMethodParameter(clientInfoContainerClassName, annotations, "clientInfoContainer")), + "V", + AccessFlags.PRIVATE or AccessFlags.STATIC, + annotations, + null, + MutableMethodImplementation(3), + ).toMutable().apply { + addInstructions( + """ + invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isClientSpoofingEnabled()Z + move-result v0 + if-eqz v0, :disabled + + iget-object v0, p0, $clientInfoField + + # Set client type to the spoofed value. + iget v1, v0, $clientInfoClientTypeField + invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientTypeId(I)I + move-result v1 + iput v1, v0, $clientInfoClientTypeField + + # Set client model to the spoofed value. + iget-object v1, v0, $clientInfoClientModelField + invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientModel(Ljava/lang/String;)Ljava/lang/String; + move-result-object v1 + iput-object v1, v0, $clientInfoClientModelField + + # Set client version to the spoofed value. + iget-object v1, v0, $clientInfoClientVersionField + 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 + """, + ) + }, + ) + } + } + + // endregion + + // region check whether video is Shorts or Clips. + + PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameter( + "$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(" + + "Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", + ) + + // endregion + + /** + * Add settings + */ + SettingsPatch.addPreference( + arrayOf( + "PREFERENCE_CATEGORY: MISC_EXPERIMENTAL_FLAGS", + "SETTINGS: SPOOF_CLIENT" + ) + ) + + SettingsPatch.updatePatchStatus(this) + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/SpoofUserAgentPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/SpoofUserAgentPatch.kt new file mode 100644 index 000000000..06a2a0ca8 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/SpoofUserAgentPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.youtube.utils.fix.client + +import app.revanced.patches.shared.spoofuseragent.BaseSpoofUserAgentPatch + +object SpoofUserAgentPatch : BaseSpoofUserAgentPatch("com.google.android.youtube") \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/BuildInitPlaybackRequestFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/BuildInitPlaybackRequestFingerprint.kt new file mode 100644 index 000000000..9437ab1a0 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/BuildInitPlaybackRequestFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.utils.fix.client.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object BuildInitPlaybackRequestFingerprint : MethodFingerprint( + returnType = "Lorg/chromium/net/UrlRequest\$Builder;", + opcodes = listOf( + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, // Moves the request URI string to a register to build the request with. + ), + strings = listOf( + "Content-Type", + "Range", + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/BuildPlayerRequestURIFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/BuildPlayerRequestURIFingerprint.kt new file mode 100644 index 000000000..563fbdaea --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/BuildPlayerRequestURIFingerprint.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.youtube.utils.fix.client.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object BuildPlayerRequestURIFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + opcodes = listOf( + Opcode.INVOKE_VIRTUAL, // Register holds player request URI. + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.MONITOR_EXIT, + Opcode.RETURN_OBJECT, + ), + strings = listOf( + "key", + "asig", + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/CreatePlayerRequestBodyFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/CreatePlayerRequestBodyFingerprint.kt new file mode 100644 index 000000000..a631f4140 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/CreatePlayerRequestBodyFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.youtube.utils.fix.client.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object CreatePlayerRequestBodyFingerprint : MethodFingerprint( + returnType = "V", + parameters = listOf("L"), + opcodes = listOf( + Opcode.CHECK_CAST, + Opcode.IGET, + Opcode.AND_INT_LIT16, + ), + strings = listOf("ms"), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/SetPlayerRequestClientTypeFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/SetPlayerRequestClientTypeFingerprint.kt new file mode 100644 index 000000000..462049239 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/client/fingerprints/SetPlayerRequestClientTypeFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.utils.fix.client.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +internal object SetPlayerRequestClientTypeFingerprint : MethodFingerprint( + strings = listOf("10.29"), + opcodes = listOf( + Opcode.IGET, + Opcode.IPUT, // Sets ClientInfo.clientId. + ), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/clientspoof/ClientSpoofPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/clientspoof/ClientSpoofPatch.kt deleted file mode 100644 index 8fa655d65..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/clientspoof/ClientSpoofPatch.kt +++ /dev/null @@ -1,5 +0,0 @@ -package app.revanced.patches.youtube.utils.fix.clientspoof - -import app.revanced.patches.shared.clientspoof.BaseClientSpoofPatch - -object ClientSpoofPatch : BaseClientSpoofPatch("com.google.android.youtube") \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/gms/GmsCoreSupportPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/gms/GmsCoreSupportPatch.kt index 6ada26903..21fba2d8a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/gms/GmsCoreSupportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/gms/GmsCoreSupportPatch.kt @@ -4,7 +4,8 @@ import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch import app.revanced.patches.shared.packagename.PackageNamePatch import app.revanced.patches.shared.packagename.PackageNamePatch.ORIGINAL_PACKAGE_NAME_YOUTUBE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE -import app.revanced.patches.youtube.utils.fix.clientspoof.ClientSpoofPatch +import app.revanced.patches.youtube.utils.fix.client.SpoofClientPatch +import app.revanced.patches.youtube.utils.fix.client.SpoofUserAgentPatch import app.revanced.patches.youtube.utils.integrations.IntegrationsPatch import app.revanced.patches.youtube.utils.mainactivity.fingerprints.MainActivityFingerprint import app.revanced.patches.youtube.utils.settings.SettingsPatch @@ -14,7 +15,7 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch( fromPackageName = ORIGINAL_PACKAGE_NAME_YOUTUBE, mainActivityOnCreateFingerprint = MainActivityFingerprint, integrationsPatchDependency = IntegrationsPatch::class, - dependencies = setOf(ClientSpoofPatch::class, PackageNamePatch::class, SettingsPatch::class), + dependencies = setOf(SpoofClientPatch::class, SpoofUserAgentPatch::class, PackageNamePatch::class, SettingsPatch::class), gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch, compatiblePackages = COMPATIBLE_PACKAGE ) diff --git a/src/main/resources/youtube/settings/host/values/strings.xml b/src/main/resources/youtube/settings/host/values/strings.xml index 50425426e..79e1bff96 100644 --- a/src/main/resources/youtube/settings/host/values/strings.xml +++ b/src/main/resources/youtube/settings/host/values/strings.xml @@ -1414,6 +1414,27 @@ Tap on the continue button and disable battery optimizations." Removes tracking query parameters from the URLs when sharing links. Disable QUIC protocol "Disable CronetEngine's QUIC protocol." + + Spoof client + "Client is spoofed. + +Side effects include: +• No HDR video. +• Watch history may not work." + "Client is not spoofed. Video playback may not work." + Spoof client to iOS + "Client is currently spoofed to iOS. + +Side effects include: +• Playback speed menu is missing. +• Background playback does not work on live streams. +• Higher video qualities may be missing." + "Client is currently spoofed to Android VR. (iOS client is used for Clips or Shorts) + +Side effects include: +• Player swipe gestures do not work. +• Kids videos do not playback. +• Paused videos can randomly resume." Spoof player parameter "Spoofs player parameters to prevent playback issues. diff --git a/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/src/main/resources/youtube/settings/xml/revanced_prefs.xml index 62bd0b9a4..f1b161ed1 100644 --- a/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -564,6 +564,10 @@ + + @@ -640,6 +644,7 @@ +