mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-04-30 06:34:28 +02:00
fix(YouTube - Spoof video streams): Resolve playback issues with dynamic player config (#4521)
This commit is contained in:
parent
52e4b9c43e
commit
647e7642ef
@ -362,27 +362,18 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setContext(Context appContext) {
|
public static void setContext(Context appContext) {
|
||||||
// Must initially set context as the language settings needs it.
|
// Must initially set context to check the app language.
|
||||||
context = appContext;
|
context = appContext;
|
||||||
|
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
|
||||||
|
|
||||||
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
|
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
|
||||||
if (language != AppLanguage.DEFAULT) {
|
if (language != AppLanguage.DEFAULT) {
|
||||||
// Create a new context with the desired language.
|
// Create a new context with the desired language.
|
||||||
|
Logger.printDebug(() -> "Using app language: " + language);
|
||||||
Configuration config = appContext.getResources().getConfiguration();
|
Configuration config = appContext.getResources().getConfiguration();
|
||||||
config.setLocale(language.getLocale());
|
config.setLocale(language.getLocale());
|
||||||
context = appContext.createConfigurationContext(config);
|
context = appContext.createConfigurationContext(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
|
|
||||||
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
|
|
||||||
// even though the context is already set before the call.
|
|
||||||
//
|
|
||||||
// The initialization logger methods do not directly or indirectly
|
|
||||||
// reference the Context or any Settings and are unaffected by this problem.
|
|
||||||
//
|
|
||||||
// Info level also helps debug if a patch hook is called before
|
|
||||||
// the context is set since debug logging is off by default.
|
|
||||||
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setClipboard(@NonNull String text) {
|
public static void setClipboard(@NonNull String text) {
|
||||||
|
@ -107,6 +107,21 @@ public class SpoofVideoStreamsPatch {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* Turns off a feature flag that interferes with spoofing.
|
||||||
|
*/
|
||||||
|
public static boolean useMediaFetchHotConfigReplacement(boolean original) {
|
||||||
|
if (original) {
|
||||||
|
Logger.printDebug(() -> "useMediaFetchHotConfigReplacement is set on");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SPOOF_STREAMING_DATA) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +24,7 @@ public final class EnableDebuggingPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) {
|
public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) {
|
||||||
if (LOG_FEATURE_FLAGS && value) {
|
if (LOG_FEATURE_FLAGS && value) {
|
||||||
if (featureFlags.putIfAbsent(flag, true) == null) {
|
if (featureFlags.putIfAbsent(flag, true) == null) {
|
||||||
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
|
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
|
||||||
|
@ -776,8 +776,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
|
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
|
||||||
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
|
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||||
|
@ -137,3 +137,15 @@ internal val patchIncludedExtensionMethodFingerprint = fingerprint {
|
|||||||
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
|
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Feature flag that turns on Platypus programming language code compiled to native C++.
|
||||||
|
// This code appears to replace the player config after the streams are loaded.
|
||||||
|
// Flag is present in YouTube 19.34, but is missing Platypus stream replacement code until 19.43.
|
||||||
|
// Flag and Platypus code is also present in newer versions of YouTube Music.
|
||||||
|
internal const val MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG = 45645570L
|
||||||
|
|
||||||
|
internal val mediaFetchHotConfigFingerprint = fingerprint {
|
||||||
|
literal {
|
||||||
|
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ internal const val EXTENSION_CLASS_DESCRIPTOR =
|
|||||||
|
|
||||||
fun spoofVideoStreamsPatch(
|
fun spoofVideoStreamsPatch(
|
||||||
block: BytecodePatchBuilder.() -> Unit = {},
|
block: BytecodePatchBuilder.() -> Unit = {},
|
||||||
|
applyMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false },
|
||||||
executeBlock: BytecodePatchContext.() -> Unit = {},
|
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||||
) = bytecodePatch(
|
) = bytecodePatch(
|
||||||
name = "Spoof video streams",
|
name = "Spoof video streams",
|
||||||
@ -238,6 +239,17 @@ fun spoofVideoStreamsPatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region turn off stream config replacement feature flag.
|
||||||
|
|
||||||
|
if (applyMediaFetchHotConfigChanges()) {
|
||||||
|
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
executeBlock()
|
executeBlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.misc.debugging
|
package app.revanced.patches.youtube.misc.debugging
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
@ -11,9 +12,11 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;"
|
"Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;"
|
||||||
@ -61,20 +64,18 @@ val enableDebuggingPatch = bytecodePatch(
|
|||||||
experimentalBooleanFeatureFlagFingerprint.match(
|
experimentalBooleanFeatureFlagFingerprint.match(
|
||||||
experimentalFeatureFlagParentFingerprint.originalClassDef
|
experimentalFeatureFlagParentFingerprint.originalClassDef
|
||||||
).method.apply {
|
).method.apply {
|
||||||
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT)
|
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
|
||||||
// It appears that all usage of this method has a default of 'false',
|
|
||||||
// so there's no need to pass in the default.
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex,
|
index,
|
||||||
"""
|
"""
|
||||||
move-result v0
|
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
|
||||||
invoke-static { v0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZJ)Z
|
move-result v$register
|
||||||
move-result v0
|
|
||||||
return v0
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
experimentalDoubleFeatureFlagFingerprint.match(
|
experimentalDoubleFeatureFlagFingerprint.match(
|
||||||
experimentalFeatureFlagParentFingerprint.originalClassDef
|
experimentalFeatureFlagParentFingerprint.originalClassDef
|
||||||
@ -92,7 +93,6 @@ val enableDebuggingPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
experimentalLongFeatureFlagFingerprint.match(
|
experimentalLongFeatureFlagFingerprint.match(
|
||||||
experimentalFeatureFlagParentFingerprint.originalClassDef
|
experimentalFeatureFlagParentFingerprint.originalClassDef
|
||||||
).method.apply {
|
).method.apply {
|
||||||
@ -108,6 +108,8 @@ val enableDebuggingPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
experimentalStringFeatureFlagFingerprint.match(
|
experimentalStringFeatureFlagFingerprint.match(
|
||||||
experimentalFeatureFlagParentFingerprint.originalClassDef
|
experimentalFeatureFlagParentFingerprint.originalClassDef
|
||||||
).method.apply {
|
).method.apply {
|
||||||
@ -123,7 +125,6 @@ val enableDebuggingPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// There exists other experimental accessor methods for byte[]
|
// There exists other experimental accessor methods for byte[]
|
||||||
// and wrappers for obfuscated classes, but currently none of those are hooked.
|
// and wrappers for obfuscated classes, but currently none of those are hooked.
|
||||||
|
@ -11,9 +11,9 @@ internal val experimentalFeatureFlagParentFingerprint = fingerprint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
|
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||||
returns("Z")
|
returns("Z")
|
||||||
parameters("J", "Z")
|
parameters("L", "J", "Z")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
|
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
|
||||||
@ -33,4 +33,3 @@ internal val experimentalStringFeatureFlagFingerprint = fingerprint {
|
|||||||
returns("Ljava/lang/String;")
|
returns("Ljava/lang/String;")
|
||||||
parameters("J", "Ljava/lang/String;")
|
parameters("J", "Ljava/lang/String;")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import app.revanced.patches.shared.misc.settings.preference.NonInteractivePrefer
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
|
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
|
||||||
@ -25,7 +27,10 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
|||||||
dependsOn(
|
dependsOn(
|
||||||
userAgentClientSpoofPatch,
|
userAgentClientSpoofPatch,
|
||||||
settingsPatch,
|
settingsPatch,
|
||||||
|
versionCheckPatch
|
||||||
)
|
)
|
||||||
|
}, {
|
||||||
|
is_19_34_or_greater
|
||||||
}, {
|
}, {
|
||||||
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")
|
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")
|
||||||
|
|
||||||
|
@ -408,10 +408,13 @@ internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, exten
|
|||||||
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
|
||||||
|
val operation = if (register < 16) "invoke-static { v$register }"
|
||||||
|
else "invoke-static/range { v$register .. v$register }"
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
index + 1,
|
index + 1,
|
||||||
"""
|
"""
|
||||||
invoke-static { v$register }, $extensionsMethod
|
$operation, $extensionsMethod
|
||||||
move-result v$register
|
move-result v$register
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
@ -458,7 +461,7 @@ fun MutableMethod.returnEarly(bool: Boolean = false) {
|
|||||||
return v0
|
return v0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
else -> throw Exception("This case should never happen.")
|
else -> throw Exception("Return type is not supported: $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
addInstructions(0, stringInstructions)
|
addInstructions(0, stringInstructions)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user