mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-13 13:47:42 +02:00
feat(YouTube): add Spoof streaming data
patch
This commit is contained in:
@ -276,6 +276,7 @@ object VisualPreferencesIconsPatch : BaseResourcePatch(
|
|||||||
"revanced_preference_screen_seekbar",
|
"revanced_preference_screen_seekbar",
|
||||||
"revanced_preference_screen_settings_menu",
|
"revanced_preference_screen_settings_menu",
|
||||||
"revanced_preference_screen_shorts_player",
|
"revanced_preference_screen_shorts_player",
|
||||||
|
"revanced_preference_screen_spoof_streaming_data",
|
||||||
"revanced_preference_screen_toolbar",
|
"revanced_preference_screen_toolbar",
|
||||||
"revanced_preference_screen_video_description",
|
"revanced_preference_screen_video_description",
|
||||||
"revanced_preference_screen_video_filter",
|
"revanced_preference_screen_video_filter",
|
||||||
|
@ -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<OneRegisterInstruction>(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<FiveRegisterInstruction>(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<FiveRegisterInstruction>(buildRequestIndex).registerC
|
||||||
|
|
||||||
|
val newRequestBuilderIndex =
|
||||||
|
BuildRequestFingerprint.indexOfNewUrlRequestBuilderInstruction(this)
|
||||||
|
val urlRegister = getInstruction<FiveRegisterInstruction>(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<FiveRegisterInstruction>(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<FieldReference>().toString()
|
||||||
|
|
||||||
|
val playerProtoClass = getInstruction(setStreamingDataIndex + 1).getReference<FieldReference>()!!.definingClass
|
||||||
|
val protobufClass = ProtobufClassParseByteBufferFingerprint.resultOrThrow().mutableMethod.definingClass
|
||||||
|
|
||||||
|
val getStreamingDataField = getInstructions().find { instruction ->
|
||||||
|
instruction.opcode == Opcode.IGET_OBJECT &&
|
||||||
|
instruction.getReference<FieldReference>()?.definingClass == playerProtoClass
|
||||||
|
}?.getReference<FieldReference>()
|
||||||
|
?: throw PatchException("Could not find getStreamingDataField")
|
||||||
|
|
||||||
|
val videoDetailsIndex = result.scanResult.patternScanResult!!.endIndex
|
||||||
|
val videoDetailsClass = getInstruction(videoDetailsIndex).getReference<FieldReference>()!!.type
|
||||||
|
|
||||||
|
val insertIndex = videoDetailsIndex + 1
|
||||||
|
val videoDetailsRegister = getInstruction<TwoRegisterInstruction>(videoDetailsIndex).registerA
|
||||||
|
|
||||||
|
val overrideRegister = getInstruction<TwoRegisterInstruction>(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)
|
||||||
|
}
|
||||||
|
}
|
@ -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")
|
@ -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",
|
||||||
|
),
|
||||||
|
)
|
@ -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;"
|
||||||
|
)
|
||||||
|
)
|
@ -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<MethodReference>().toString() == "Landroid/net/Uri;->toString()Ljava/lang/String;"
|
||||||
|
}
|
||||||
|
}
|
@ -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<MethodReference>().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<MethodReference>().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<MethodReference>().toString() == "Lorg/chromium/net/ExperimentalUrlRequest${'$'}Builder;->build()Lorg/chromium/net/ExperimentalUrlRequest;"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun indexOfEntrySetInstruction(methodDef: Method) =
|
||||||
|
methodDef.indexOfFirstInstruction {
|
||||||
|
opcode == Opcode.INVOKE_INTERFACE &&
|
||||||
|
getReference<MethodReference>().toString() == "Ljava/util/Map;->entrySet()Ljava/util/Set;"
|
||||||
|
}
|
||||||
|
}
|
@ -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<FieldReference>()?.name == "playerThreedRenderer"
|
||||||
|
} >= 0
|
||||||
|
},
|
||||||
|
)
|
@ -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=\""),
|
||||||
|
)
|
@ -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" },
|
||||||
|
)
|
@ -3,6 +3,8 @@ package app.revanced.patches.youtube.utils.gms
|
|||||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch
|
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch
|
||||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportResourcePatch.Companion.ORIGINAL_PACKAGE_NAME_YOUTUBE
|
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.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.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.utils.mainactivity.fingerprints.MainActivityFingerprint
|
import app.revanced.patches.youtube.utils.mainactivity.fingerprints.MainActivityFingerprint
|
||||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||||
@ -13,6 +15,8 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
|||||||
mainActivityOnCreateFingerprint = MainActivityFingerprint,
|
mainActivityOnCreateFingerprint = MainActivityFingerprint,
|
||||||
integrationsPatchDependency = IntegrationsPatch::class,
|
integrationsPatchDependency = IntegrationsPatch::class,
|
||||||
dependencies = setOf(
|
dependencies = setOf(
|
||||||
|
SpoofStreamingDataPatch::class,
|
||||||
|
SpoofUserAgentPatch::class,
|
||||||
SettingsPatch::class
|
SettingsPatch::class
|
||||||
),
|
),
|
||||||
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
||||||
|
@ -163,6 +163,16 @@
|
|||||||
<item>HEART_TINT</item>
|
<item>HEART_TINT</item>
|
||||||
<item>HIDDEN</item>
|
<item>HIDDEN</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="revanced_spoof_streaming_data_type_entries">
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_ios</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_unplugged</item>
|
||||||
|
<item>@string/revanced_spoof_streaming_data_type_entry_android_vr</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_spoof_streaming_data_type_entry_values">
|
||||||
|
<item>IOS</item>
|
||||||
|
<item>ANDROID_UNPLUGGED</item>
|
||||||
|
<item>ANDROID_VR</item>
|
||||||
|
</string-array>
|
||||||
<string-array name="revanced_spoof_app_version_target_entries">
|
<string-array name="revanced_spoof_app_version_target_entries">
|
||||||
<item>@string/revanced_spoof_app_version_target_entry_18_17_43</item>
|
<item>@string/revanced_spoof_app_version_target_entry_18_17_43</item>
|
||||||
<item>@string/revanced_spoof_app_version_target_entry_18_05_40</item>
|
<item>@string/revanced_spoof_app_version_target_entry_18_05_40</item>
|
||||||
|
@ -1577,6 +1577,37 @@ Tap on the continue button and disable battery optimizations."</string>
|
|||||||
<string name="revanced_extended_settings_reset">Reset</string>
|
<string name="revanced_extended_settings_reset">Reset</string>
|
||||||
<string name="revanced_share_copy_settings_success">Settings copied to clipboard.</string>
|
<string name="revanced_share_copy_settings_success">Settings copied to clipboard.</string>
|
||||||
|
|
||||||
|
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Spoof streaming data -->
|
||||||
|
<string name="revanced_preference_screen_spoof_streaming_data_title">Spoof streaming data</string>
|
||||||
|
<string name="revanced_preference_screen_spoof_streaming_data_summary">Spoof the streaming data to prevent playback issues.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_title">Spoof streaming data</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_summary_on">Streaming data is spoofed.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_summary_off">"Streaming data is not spoofed. Video playback may not work."</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_user_dialog_message">Turning off this setting may cause video playback issues.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_title">Default client</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_ios">iOS</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_android">Android</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_android_embedded_player">Android Embedded Player</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_android_testsuite">Android Testsuite</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_android_unplugged">Android TV</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_android_vr">Android VR</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_tvhtml5_simply_embedded_player">TV HTML5</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_type_entry_web">Web</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_side_effects_title">Spoofing side effects</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_side_effects_ios">• Movies or paid videos may not play.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_side_effects_android_unplugged">• Audio track menu is missing.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_side_effects_android_vr">• Audio track menu is missing.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_side_effects_unknown">• Video may not play.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_ios_force_avc_title">Force iOS AVC (H.264)</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_ios_force_avc_summary_on">iOS video codec is AVC (H.264).</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_ios_force_avc_summary_off">iOS video codec is AVC (H.264), VP9, or AV1.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_ios_force_avc_user_dialog_message">"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."</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Show in Stats for nerds</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Client used to fetch streaming data is shown in Stats for nerds.</string>
|
||||||
|
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Client used to fetch streaming data is hidden in Stats for nerds.</string>
|
||||||
|
|
||||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Watch history -->
|
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Watch history -->
|
||||||
<string name="revanced_preference_screen_watch_history_title">Watch history</string>
|
<string name="revanced_preference_screen_watch_history_title">Watch history</string>
|
||||||
<string name="revanced_preference_screen_watch_history_summary">Change settings related with watch history.</string>
|
<string name="revanced_preference_screen_watch_history_summary">Change settings related with watch history.</string>
|
||||||
|
@ -610,6 +610,15 @@
|
|||||||
<app.revanced.integrations.youtube.settings.preference.ImportExportPreference android:title="@string/revanced_extended_settings_import_export_as_text_title" android:summary="@string/revanced_extended_settings_import_export_as_text_summary" android:inputType="textMultiLine" />
|
<app.revanced.integrations.youtube.settings.preference.ImportExportPreference android:title="@string/revanced_extended_settings_import_export_as_text_title" android:summary="@string/revanced_extended_settings_import_export_as_text_summary" android:inputType="textMultiLine" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
|
<!-- SETTINGS: SPOOF_STREAMING_DATA
|
||||||
|
<PreferenceScreen android:title="@string/revanced_preference_screen_spoof_streaming_data_title" android:key="revanced_preference_screen_spoof_streaming_data" android:summary="@string/revanced_preference_screen_spoof_streaming_data_summary">
|
||||||
|
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_title" android:key="revanced_spoof_streaming_data" android:summaryOn="@string/revanced_spoof_streaming_data_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_summary_off" />
|
||||||
|
<ListPreference android:entries="@array/revanced_spoof_streaming_data_type_entries" android:title="@string/revanced_spoof_streaming_data_type_title" android:key="revanced_spoof_streaming_data_type" android:entryValues="@array/revanced_spoof_streaming_data_type_entry_values" />
|
||||||
|
<app.revanced.integrations.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference android:title="@string/revanced_spoof_streaming_data_side_effects_title" />
|
||||||
|
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_ios_force_avc_title" android:key="revanced_spoof_streaming_data_ios_force_avc" android:summaryOn="@string/revanced_spoof_streaming_data_ios_force_avc_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_ios_force_avc_summary_off" />
|
||||||
|
<SwitchPreference android:title="@string/revanced_spoof_streaming_data_stats_for_nerds_title" android:key="revanced_spoof_streaming_data_stats_for_nerds" android:summaryOn="@string/revanced_spoof_streaming_data_stats_for_nerds_summary_on" android:summaryOff="@string/revanced_spoof_streaming_data_stats_for_nerds_summary_off" />
|
||||||
|
</PreferenceScreen>SETTINGS: SPOOF_STREAMING_DATA -->
|
||||||
|
|
||||||
<!-- SETTINGS: WATCH_HISTORY
|
<!-- SETTINGS: WATCH_HISTORY
|
||||||
<PreferenceScreen android:title="@string/revanced_preference_screen_watch_history_title" android:key="revanced_preference_screen_watch_history" android:summary="@string/revanced_preference_screen_watch_history_summary">
|
<PreferenceScreen android:title="@string/revanced_preference_screen_watch_history_title" android:key="revanced_preference_screen_watch_history" android:summary="@string/revanced_preference_screen_watch_history_summary">
|
||||||
<Preference android:title="@string/revanced_watch_history_management_title" android:summary="@string/revanced_watch_history_management_summary">
|
<Preference android:title="@string/revanced_watch_history_management_title" android:summary="@string/revanced_watch_history_management_summary">
|
||||||
@ -726,6 +735,7 @@
|
|||||||
<Preference android:title="Enable OPUS codec" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
<Preference android:title="Enable OPUS codec" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||||
<Preference android:title="Remove background playback restrictions" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
<Preference android:title="Remove background playback restrictions" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||||
<Preference android:title="Sanitize sharing links" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
<Preference android:title="Sanitize sharing links" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||||
|
<Preference android:title="Spoof streaming data" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||||
<Preference android:title="Watch history" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
<Preference android:title="Watch history" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:name="vector"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<group
|
||||||
|
android:scaleX="0.5"
|
||||||
|
android:scaleY="0.5"
|
||||||
|
android:pivotX="480"
|
||||||
|
android:pivotY="480">
|
||||||
|
<path
|
||||||
|
android:name="path"
|
||||||
|
android:pathData="M 270.77 793.85 L 270.77 824.62 Q 270.77 833.85 278.46 841.54 Q 286.15 849.23 295.38 849.23 L 664.62 849.23 Q 673.85 849.23 681.54 841.54 Q 689.23 833.85 689.23 824.62 L 689.23 793.85 L 270.77 793.85 Z M 687.54 569.69 Q 669.38 566.69 656.69 558.77 Q 644 550.85 635.46 539.77 L 620.54 547.38 Q 615.54 550.15 610.42 548.77 Q 605.31 547.38 602.31 543.15 L 600.38 540.92 Q 597.38 536.69 598.94 531.55 Q 600.49 526.41 604.38 523.23 L 618.62 512.69 Q 612.08 497.08 612.08 479.54 Q 612.08 462 618.62 445.62 L 604.38 435.08 Q 600.49 432.1 598.94 426.86 Q 597.38 421.62 600.41 416.72 L 602.31 415.15 Q 604.54 410.15 609.65 409.15 Q 614.77 408.15 619.77 410.92 L 635.46 418.54 Q 644 407.46 656.46 399.54 Q 668.92 391.62 687.54 388.62 L 689.23 370.23 Q 689.16 364.45 692.93 360.84 Q 696.71 357.23 702.35 357.23 L 706.46 357.23 Q 711.83 357.23 715.27 360.93 Q 718.7 364.62 719.54 370.23 L 721.23 388.62 Q 739.85 391.62 751.92 399.54 Q 764 407.46 773.31 418.54 L 788.23 410.92 Q 793.23 408.15 798.35 409.54 Q 803.46 410.92 806.46 415.92 L 808.38 417.38 Q 810.62 421.62 809.45 426.86 Q 808.28 432.1 804.38 435.08 L 790.15 445.62 Q 796.69 462 796.69 479.54 Q 796.69 497.08 790.15 512.69 L 804.38 523.23 Q 808.28 527.18 809.45 532.32 Q 810.62 537.46 807.62 541.69 L 805.69 543.92 Q 803.46 548.15 798.35 549.15 Q 793.23 550.15 788.23 547.38 L 773.31 539.77 Q 764 550.85 751.31 558.77 Q 738.62 566.69 721.23 569.69 L 719.54 588.08 Q 718.67 593.34 715.07 596.82 Q 711.46 600.31 706.08 600.31 L 701.85 600.31 Q 696.46 600.31 692.79 596.7 Q 689.13 593.09 689.23 588.08 L 687.54 569.69 Z M 704 543.23 Q 730.85 543.23 749.85 524.62 Q 768.85 506 768.85 479.15 Q 768.85 452.31 749.95 433.69 Q 731.05 415.08 704.38 415.08 Q 677.15 415.08 658.54 433.76 Q 639.92 452.44 639.92 478.77 Q 639.92 506 658.54 524.62 Q 677.15 543.23 704 543.23 Z M 270.77 166.15 L 689.23 166.15 L 689.23 135.38 Q 689.23 126.15 681.54 118.46 Q 673.85 110.77 664.62 110.77 L 295.38 110.77 Q 286.15 110.77 278.46 118.46 Q 270.77 126.15 270.77 135.38 L 270.77 166.15 Z M 270.77 166.15 L 270.77 110.77 L 270.77 166.15 Z M 270.77 793.85 L 270.77 849.23 L 270.77 793.85 Z M 295.38 880 Q 272.94 880 256.47 863.53 Q 240 847.06 240 824.62 L 240 135.38 Q 240 112.94 256.47 96.47 Q 272.94 80 295.38 80 L 664.62 80 Q 687.06 80 703.53 96.47 Q 720 112.94 720 135.38 L 720 246 Q 720 252.58 715.54 256.98 Q 711.08 261.38 704.43 261.38 Q 697.77 261.38 693.5 256.98 Q 689.23 252.58 689.23 246 L 689.23 196.92 L 270.77 196.92 L 270.77 763.08 L 689.23 763.08 L 689.23 714 Q 689.23 707.42 693.69 703.02 Q 698.15 698.62 704.8 698.62 Q 711.46 698.62 715.73 703.02 Q 720 707.42 720 714 L 720 824.62 Q 720 847.06 703.53 863.53 Q 687.06 880 664.62 880 L 295.38 880 Z"
|
||||||
|
android:fillColor="?ytTextPrimary" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
Reference in New Issue
Block a user