diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt index a026e50a89..78c677b0c4 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/visual/VisualPreferencesIconsPatch.kt @@ -276,6 +276,7 @@ object VisualPreferencesIconsPatch : BaseResourcePatch( "revanced_preference_screen_seekbar", "revanced_preference_screen_settings_menu", "revanced_preference_screen_shorts_player", + "revanced_preference_screen_spoof_streaming_data", "revanced_preference_screen_toolbar", "revanced_preference_screen_video_description", "revanced_preference_screen_video_filter", diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt new file mode 100644 index 0000000000..eec41d67e7 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt @@ -0,0 +1,261 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.youtube.utils.compatibility.Constants +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildInitPlaybackRequestFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildMediaDataSourceFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildPlayerRequestURIFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildRequestFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.CreateStreamingDataFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.NerdsStatsVideoFormatBuilderFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.ProtobufClassParseByteBufferFingerprint +import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH +import app.revanced.patches.youtube.utils.settings.SettingsPatch +import app.revanced.util.getReference +import app.revanced.util.patch.BaseBytecodePatch +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.Opcode +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 + +object SpoofStreamingDataPatch : BaseBytecodePatch( + name = "Spoof streaming data", + description = "Adds options to spoof the streaming data to allow video playback.", + dependencies = setOf( + SettingsPatch::class, + SpoofUserAgentPatch::class, + ), + compatiblePackages = Constants.COMPATIBLE_PACKAGE, + fingerprints = setOf( + BuildInitPlaybackRequestFingerprint, + BuildMediaDataSourceFingerprint, + BuildPlayerRequestURIFingerprint, + BuildRequestFingerprint, + CreateStreamingDataFingerprint, + ProtobufClassParseByteBufferFingerprint, + + // Nerds stats video format. + NerdsStatsVideoFormatBuilderFingerprint, + ) +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "$MISC_PATH/SpoofStreamingDataPatch;" + private const val REQUEST_CLASS_DESCRIPTOR = + "Lorg/chromium/net/UrlRequest;" + private const val REQUEST_BUILDER_CLASS_DESCRIPTOR = + "Lorg/chromium/net/UrlRequest\$Builder;" + + 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 = BuildPlayerRequestURIFingerprint.indexOfToStringInstruction(this) + 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 Fetch replacement streams. + + BuildRequestFingerprint.resultOrThrow().let { result -> + result.mutableMethod.apply { + val buildRequestIndex = + BuildRequestFingerprint.indexOfBuildUrlRequestInstruction(this) + val requestBuilderRegister = getInstruction(buildRequestIndex).registerC + + val newRequestBuilderIndex = + BuildRequestFingerprint.indexOfNewUrlRequestBuilderInstruction(this) + val urlRegister = getInstruction(newRequestBuilderIndex).registerD + + // Replace "requestBuilder.build()" with integrations call. + replaceInstruction( + buildRequestIndex, + "invoke-static { v$requestBuilderRegister }, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->" + + "buildRequest($REQUEST_BUILDER_CLASS_DESCRIPTOR)" + + REQUEST_CLASS_DESCRIPTOR + ) + + val entrySetIndex = BuildRequestFingerprint.indexOfEntrySetInstruction(this) + val mapRegister = if (entrySetIndex < 0) + urlRegister + 1 + else + getInstruction(entrySetIndex).registerC + + var smaliInstructions = + "invoke-static { v$urlRegister, v$mapRegister }, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->" + + "setHeader(Ljava/lang/String;Ljava/util/Map;)V" + + if (entrySetIndex < 0) smaliInstructions = """ + move-object/from16 v$mapRegister, p1 + + """ + smaliInstructions + + // Copy request headers for streaming data fetch. + addInstructions(newRequestBuilderIndex + 2, smaliInstructions) + } + } + + // endregion + + // region Replace the streaming data. + + CreateStreamingDataFingerprint.resultOrThrow().let { result -> + result.mutableMethod.apply { + val setStreamingDataIndex = result.scanResult.patternScanResult!!.startIndex + val setStreamingDataField = getInstruction(setStreamingDataIndex).getReference().toString() + + val playerProtoClass = getInstruction(setStreamingDataIndex + 1).getReference()!!.definingClass + val protobufClass = ProtobufClassParseByteBufferFingerprint.resultOrThrow().mutableMethod.definingClass + + val getStreamingDataField = getInstructions().find { instruction -> + instruction.opcode == Opcode.IGET_OBJECT && + instruction.getReference()?.definingClass == playerProtoClass + }?.getReference() + ?: throw PatchException("Could not find getStreamingDataField") + + val videoDetailsIndex = result.scanResult.patternScanResult!!.endIndex + val videoDetailsClass = getInstruction(videoDetailsIndex).getReference()!!.type + + val insertIndex = videoDetailsIndex + 1 + val videoDetailsRegister = getInstruction(videoDetailsIndex).registerA + + val overrideRegister = getInstruction(insertIndex).registerA + val freeRegister = implementation!!.registerCount - parameters.size - 2 + + addInstructionsWithLabels( + insertIndex, + """ + invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isSpoofingEnabled()Z + move-result v$freeRegister + if-eqz v$freeRegister, :disabled + + # Get video id. + # From YouTube 17.34.36 to YouTube 19.16.39, the field names and field types are the same. + iget-object v$freeRegister, v$videoDetailsRegister, $videoDetailsClass->c:Ljava/lang/String; + if-eqz v$freeRegister, :disabled + + # Get streaming data. + invoke-static { v$freeRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer; + move-result-object v$freeRegister + if-eqz v$freeRegister, :disabled + + # Parse streaming data. + sget-object v$overrideRegister, $playerProtoClass->a:$playerProtoClass + invoke-static { v$overrideRegister, v$freeRegister }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass + move-result-object v$freeRegister + check-cast v$freeRegister, $playerProtoClass + + # Set streaming data. + iget-object v$freeRegister, v$freeRegister, $getStreamingDataField + if-eqz v0, :disabled + iput-object v$freeRegister, p0, $setStreamingDataField + """, + ExternalLabel("disabled", getInstruction(insertIndex)) + ) + } + } + + // endregion + + // region Remove /videoplayback request body to fix playback. + // This is needed when using iOS client as streaming data source. + + BuildMediaDataSourceFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val targetIndex = getInstructions().lastIndex + + addInstructions( + targetIndex, + """ + # Field a: Stream uri. + # Field c: Http method. + # Field d: Post data. + # From YouTube 17.34.36 to YouTube 19.16.39, the field names and field types are the same. + move-object/from16 v0, p0 + iget-object v1, v0, $definingClass->a:Landroid/net/Uri; + iget v2, v0, $definingClass->c:I + iget-object v3, v0, $definingClass->d:[B + invoke-static { v1, v2, v3 }, $INTEGRATIONS_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B + move-result-object v1 + iput-object v1, v0, $definingClass->d:[B + """, + ) + } + } + + // endregion + + // region Append spoof info. + + NerdsStatsVideoFormatBuilderFingerprint.resultOrThrow().mutableMethod.apply { + for (index in implementation!!.instructions.size - 1 downTo 0) { + val instruction = getInstruction(index) + if (instruction.opcode != Opcode.RETURN_OBJECT) + continue + + val register = (instruction as OneRegisterInstruction).registerA + + addInstructions( + index, """ + invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->appendSpoofedClient(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$register + """ + ) + } + } + + // endregion + + /** + * Add settings + */ + SettingsPatch.addPreference( + arrayOf( + "SETTINGS: SPOOF_STREAMING_DATA" + ) + ) + + SettingsPatch.updatePatchStatus(this) + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofUserAgentPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofUserAgentPatch.kt new file mode 100644 index 0000000000..dd8a7b75c8 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofUserAgentPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata + +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/streamingdata/fingerprints/BuildInitPlaybackRequestFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildInitPlaybackRequestFingerprint.kt new file mode 100644 index 0000000000..494cba19b5 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildInitPlaybackRequestFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.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/streamingdata/fingerprints/BuildMediaDataSourceFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildMediaDataSourceFingerprint.kt new file mode 100644 index 0000000000..5683921b1d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildMediaDataSourceFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object BuildMediaDataSourceFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + returnType = "V", + parameters = listOf( + "Landroid/net/Uri;", + "J", + "I", + "[B", + "Ljava/util/Map;", + "J", + "J", + "Ljava/lang/String;", + "I", + "Ljava/lang/Object;" + ) +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildPlayerRequestURIFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildPlayerRequestURIFingerprint.kt new file mode 100644 index 0000000000..4ed9f4c7b5 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildPlayerRequestURIFingerprint.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildPlayerRequestURIFingerprint.indexOfToStringInstruction +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +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 BuildPlayerRequestURIFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + strings = listOf( + "key", + "asig", + ), + customFingerprint = { methodDef, _ -> + indexOfToStringInstruction(methodDef) >= 0 + }, +) { + fun indexOfToStringInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;" + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildRequestFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildRequestFingerprint.kt new file mode 100644 index 0000000000..d9f43bfe5d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/BuildRequestFingerprint.kt @@ -0,0 +1,50 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildRequestFingerprint.indexOfBuildUrlRequestInstruction +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildRequestFingerprint.indexOfEntrySetInstruction +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildRequestFingerprint.indexOfNewUrlRequestBuilderInstruction +import app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints.BuildRequestFingerprint.indexOfRequestFinishedListenerInstruction +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +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 BuildRequestFingerprint : MethodFingerprint( + customFingerprint = { methodDef, _ -> + methodDef.implementation != null && + indexOfRequestFinishedListenerInstruction(methodDef) >= 0 && + !methodDef.definingClass.startsWith("Lorg/") && + indexOfNewUrlRequestBuilderInstruction(methodDef) >= 0 && + indexOfBuildUrlRequestInstruction(methodDef) >= 0 && + // YouTube 17.34.36 ~ YouTube 18.35.36 + (indexOfEntrySetInstruction(methodDef) >= 0 || + // YouTube 18.36.39 ~ + methodDef.parameters[1].type == "Ljava/util/Map;") + } +) { + fun indexOfRequestFinishedListenerInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Lorg/chromium/net/ExperimentalUrlRequest${'$'}Builder;->setRequestFinishedListener(Lorg/chromium/net/RequestFinishedInfo${'$'}Listener;)Lorg/chromium/net/ExperimentalUrlRequest${'$'}Builder;" + } + + fun indexOfNewUrlRequestBuilderInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Lorg/chromium/net/CronetEngine;->newUrlRequestBuilder(Ljava/lang/String;Lorg/chromium/net/UrlRequest${'$'}Callback;Ljava/util/concurrent/Executor;)Lorg/chromium/net/UrlRequest${'$'}Builder;" + } + + fun indexOfBuildUrlRequestInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Lorg/chromium/net/ExperimentalUrlRequest${'$'}Builder;->build()Lorg/chromium/net/ExperimentalUrlRequest;" + } + + fun indexOfEntrySetInstruction(methodDef: Method) = + methodDef.indexOfFirstInstruction { + opcode == Opcode.INVOKE_INTERFACE && + getReference().toString() == "Ljava/util/Map;->entrySet()Ljava/util/Set;" + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/CreateStreamingDataFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/CreateStreamingDataFingerprint.kt new file mode 100644 index 0000000000..d9dbc39248 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/CreateStreamingDataFingerprint.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +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.reference.FieldReference + +internal object CreateStreamingDataFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + returnType = "V", + parameters = listOf("L"), + opcodes = listOf( + Opcode.IPUT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IPUT_OBJECT + ), + customFingerprint = { methodDef, _ -> + methodDef.indexOfFirstInstruction { + opcode == Opcode.SGET_OBJECT && + getReference()?.name == "playerThreedRenderer" + } >= 0 + }, +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/NerdsStatsVideoFormatBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/NerdsStatsVideoFormatBuilderFingerprint.kt new file mode 100644 index 0000000000..d53b6fd945 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/NerdsStatsVideoFormatBuilderFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object NerdsStatsVideoFormatBuilderFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, + parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"), + strings = listOf("codecs=\""), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/ProtobufClassParseByteBufferFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/ProtobufClassParseByteBufferFingerprint.kt new file mode 100644 index 0000000000..facdd232a4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/fingerprints/ProtobufClassParseByteBufferFingerprint.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.youtube.utils.fix.streamingdata.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 ProtobufClassParseByteBufferFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PROTECTED or AccessFlags.STATIC, + parameters = listOf("L", "Ljava/nio/ByteBuffer;"), + returnType = "L", + opcodes = listOf( + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.RETURN_OBJECT, + ), + customFingerprint = { methodDef, _ -> methodDef.name == "parseFrom" }, +) \ 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 a9345b9f31..80e989c09f 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 @@ -3,6 +3,8 @@ package app.revanced.patches.youtube.utils.gms import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch import app.revanced.patches.shared.gms.BaseGmsCoreSupportResourcePatch.Companion.ORIGINAL_PACKAGE_NAME_YOUTUBE import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE +import app.revanced.patches.youtube.utils.fix.streamingdata.SpoofStreamingDataPatch +import app.revanced.patches.youtube.utils.fix.streamingdata.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 @@ -13,6 +15,8 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch( mainActivityOnCreateFingerprint = MainActivityFingerprint, integrationsPatchDependency = IntegrationsPatch::class, dependencies = setOf( + SpoofStreamingDataPatch::class, + SpoofUserAgentPatch::class, SettingsPatch::class ), gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch, diff --git a/src/main/resources/youtube/settings/host/values/arrays.xml b/src/main/resources/youtube/settings/host/values/arrays.xml index 5351620964..a329bc5554 100644 --- a/src/main/resources/youtube/settings/host/values/arrays.xml +++ b/src/main/resources/youtube/settings/host/values/arrays.xml @@ -163,6 +163,16 @@ HEART_TINT HIDDEN + + @string/revanced_spoof_streaming_data_type_entry_ios + @string/revanced_spoof_streaming_data_type_entry_android_unplugged + @string/revanced_spoof_streaming_data_type_entry_android_vr + + + IOS + ANDROID_UNPLUGGED + ANDROID_VR + @string/revanced_spoof_app_version_target_entry_18_17_43 @string/revanced_spoof_app_version_target_entry_18_05_40 diff --git a/src/main/resources/youtube/settings/host/values/strings.xml b/src/main/resources/youtube/settings/host/values/strings.xml index fffed21921..230acf023c 100644 --- a/src/main/resources/youtube/settings/host/values/strings.xml +++ b/src/main/resources/youtube/settings/host/values/strings.xml @@ -1577,6 +1577,37 @@ Tap on the continue button and disable battery optimizations." Reset Settings copied to clipboard. + + Spoof streaming data + Spoof the streaming data to prevent playback issues. + Spoof streaming data + Streaming data is spoofed. + "Streaming data is not spoofed. Video playback may not work." + Turning off this setting may cause video playback issues. + Default client + iOS + Android + Android Embedded Player + Android Testsuite + Android TV + Android VR + TV HTML5 + Web + Spoofing side effects + • Movies or paid videos may not play. + • Audio track menu is missing. + • Audio track menu is missing. + • Video may not play. + Force iOS AVC (H.264) + iOS video codec is AVC (H.264). + iOS video codec is AVC (H.264), VP9, or AV1. + "Enabling this might improve battery life and fix playback stuttering. + +AVC (H.264) has a maximum resolution of 1080p, and video playback will use more internet data than VP9 or AV1." + Show in Stats for nerds + Client used to fetch streaming data is shown in Stats for nerds. + Client used to fetch streaming data is hidden in Stats for nerds. + Watch history Change settings related with watch history. diff --git a/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/src/main/resources/youtube/settings/xml/revanced_prefs.xml index 4d9dc746c9..487da577bf 100644 --- a/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -610,6 +610,15 @@ + +