feat(YouTube): remove Spoof client patch

This commit is contained in:
inotia00
2024-09-01 02:08:06 +09:00
parent 12059581c9
commit 0d29ed623a
30 changed files with 2 additions and 1371 deletions

View File

@ -276,7 +276,6 @@ object VisualPreferencesIconsPatch : BaseResourcePatch(
"revanced_preference_screen_seekbar",
"revanced_preference_screen_settings_menu",
"revanced_preference_screen_shorts_player",
"revanced_preference_screen_spoof_client",
"revanced_preference_screen_toolbar",
"revanced_preference_screen_video_description",
"revanced_preference_screen_video_filter",

View File

@ -1,496 +0,0 @@
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.addInstructionsWithLabels
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.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint
import app.revanced.patches.shared.fingerprints.CreatePlayerRequestBodyWithModelFingerprint.indexOfModelInstruction
import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch
import app.revanced.patches.youtube.utils.compatibility.Constants
import app.revanced.patches.youtube.utils.fingerprints.PlaybackRateBottomSheetBuilderFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.BuildInitPlaybackRequestFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.BuildPlaybackStatsRequestURIFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.BuildPlayerRequestURIFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.CreatePlaybackSpeedMenuItemFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.CreatePlayerRequestBodyFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.CreatePlayerRequestBodyWithVersionReleaseFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.CreatePlayerRequestBodyWithVersionReleaseFingerprint.indexOfBuildInstruction
import app.revanced.patches.youtube.utils.fix.client.fingerprints.NerdsStatsVideoFormatBuilderFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.OrganicPlaybackContextModelFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.PlayerGestureConfigSyntheticFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.PlayerResponseModelBackgroundAudioPlaybackFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.SetPlayerRequestClientTypeFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.UserAgentHeaderBuilderFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH
import app.revanced.patches.youtube.utils.integrations.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.storyboard.StoryboardHookPatch
import app.revanced.patches.youtube.utils.trackingurlhook.TrackingUrlHookPatch
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.getWalkerMethod
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import app.revanced.util.updatePatchStatus
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 spoof the client to allow video playback.",
dependencies = setOf(
// Required since iOS livestream fix partially enables background playback.
BackgroundPlaybackPatch::class,
PlayerTypeHookPatch::class,
TrackingUrlHookPatch::class,
PlayerResponseMethodHookPatch::class,
SettingsPatch::class,
VideoInformationPatch::class,
SpoofUserAgentPatch::class,
StoryboardHookPatch::class,
),
compatiblePackages = Constants.COMPATIBLE_PACKAGE,
fingerprints = setOf(
// Client type spoof.
BuildInitPlaybackRequestFingerprint,
BuildPlayerRequestURIFingerprint,
SetPlayerRequestClientTypeFingerprint,
CreatePlayerRequestBodyFingerprint,
CreatePlayerRequestBodyWithModelFingerprint,
CreatePlayerRequestBodyWithVersionReleaseFingerprint,
UserAgentHeaderBuilderFingerprint,
// Player gesture config.
PlayerGestureConfigSyntheticFingerprint,
// Player speed menu item.
CreatePlaybackSpeedMenuItemFingerprint,
PlaybackRateBottomSheetBuilderFingerprint,
// Livestream audio only background playback.
PlayerResponseModelBackgroundAudioPlaybackFingerprint,
// Watch history.
BuildPlaybackStatsRequestURIFingerprint,
OrganicPlaybackContextModelFingerprint,
// Nerds stats video format.
NerdsStatsVideoFormatBuilderFingerprint,
)
) {
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) {
var settingArray = arrayOf(
"SETTINGS: SPOOF_CLIENT"
)
// 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 = it.scanResult.patternScanResult!!.startIndex
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 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<FieldReference>()?.type == CLIENT_INFO_CLASS_DESCRIPTOR
}?.getReference<FieldReference>()
?: throw PatchException("Could not find clientInfoField")
// Client info object's client type field.
val clientInfoClientTypeField =
getInstruction(result.scanResult.patternScanResult!!.endIndex)
.getReference<FieldReference>()
?: throw PatchException("Could not find clientInfoClientTypeField")
val clientInfoVersionIndex = getStringInstructionIndex("10.29")
val clientInfoVersionRegister =
getInstruction<OneRegisterInstruction>(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<FieldReference>()
?: 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<FieldReference>()
instruction.opcode == Opcode.IPUT_OBJECT
&& reference?.definingClass == CLIENT_INFO_CLASS_DESCRIPTOR
&& reference.type == "Ljava/lang/String;"
}?.getReference<FieldReference>()
?: throw PatchException("Could not find clientInfoClientModelField")
}
val clientInfoOsVersionField =
CreatePlayerRequestBodyWithVersionReleaseFingerprint.resultOrThrow().mutableMethod.let {
val buildIndex = indexOfBuildInstruction(it)
val instructions = it.getInstructions()
instructions.subList(
buildIndex - 5,
buildIndex,
).find { instruction ->
val reference = instruction.getReference<FieldReference>()
instruction.opcode == Opcode.IPUT_OBJECT
&& reference?.definingClass == CLIENT_INFO_CLASS_DESCRIPTOR
&& reference.type == "Ljava/lang/String;"
}?.getReference<FieldReference>()
?: throw PatchException("Could not find clientInfoOsVersionField")
}
// 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<OneRegisterInstruction>(checkCastIndex)
val requestMessageInstanceRegister = checkCastInstruction.registerA
val clientInfoContainerClassName =
checkCastInstruction.getReference<TypeReference>()!!.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
# Set client os version to the spoofed value.
iget-object v1, v0, $clientInfoOsVersionField
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getOsVersion(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientInfoOsVersionField
:disabled
return-void
""",
)
},
)
}
}
// endregion
// region Spoof user-agent
UserAgentHeaderBuilderFingerprint.resultOrThrow().mutableMethod.apply {
val insertIndex = implementation!!.instructions.lastIndex
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex, """
invoke-static { v$insertRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getUserAgent(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$insertRegister
"""
)
}
// 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
// region fix player gesture.
PlayerGestureConfigSyntheticFingerprint.resultOrThrow().let {
val endIndex = it.scanResult.patternScanResult!!.endIndex
val downAndOutLandscapeAllowedIndex = endIndex - 3
val downAndOutPortraitAllowedIndex = endIndex - 9
arrayOf(
downAndOutLandscapeAllowedIndex,
downAndOutPortraitAllowedIndex,
).forEach { index ->
val gestureAllowedMethod = it.getWalkerMethod(context, index)
gestureAllowedMethod.apply {
val isAllowedIndex = getInstructions().lastIndex
val isAllowed = getInstruction<OneRegisterInstruction>(isAllowedIndex).registerA
addInstructions(
isAllowedIndex,
"""
invoke-static { v$isAllowed }, $INTEGRATIONS_CLASS_DESCRIPTOR->enablePlayerGesture(Z)Z
move-result v$isAllowed
""",
)
}
}
}
// endregion
// region fix playback speed menu item.
// fix for iOS, Android Testsuite
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
val scanResult = it.scanResult.patternScanResult!!
if (scanResult.startIndex != 0) throw PatchException("Unexpected start index: ${scanResult.startIndex}")
it.mutableMethod.apply {
// Find the conditional check if the playback speed menu item is not created.
val shouldCreateMenuIndex =
indexOfFirstInstructionOrThrow(scanResult.endIndex) { opcode == Opcode.IF_EQZ }
val shouldCreateMenuRegister =
getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
addInstructions(
shouldCreateMenuIndex,
"""
invoke-static { v$shouldCreateMenuRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenu(Z)Z
move-result v$shouldCreateMenuRegister
""",
)
}
}
// fix for Android TV
PlaybackRateBottomSheetBuilderFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.endIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex,
"""
invoke-static { v$targetRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->forceCreatePlaybackSpeedMenuReversed(Z)Z
move-result v$targetRegister
""",
)
}
}
// endregion
// region fix background playback in live stream, if spoofing to iOS
// This force enables audio background playback.
PlayerResponseModelBackgroundAudioPlaybackFingerprint.resultOrThrow().mutableMethod.apply {
addInstructionsWithLabels(
0, """
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBackgroundAudioPlayback()Z
move-result v0
if-eqz v0, :disabled
return v0
""", ExternalLabel("disabled", getInstruction(0))
)
}
// endregion
// region watch history if spoofing to iOS
if (!SettingsPatch.upward1839) {
BuildPlaybackStatsRequestURIFingerprint.resultOrThrow().let {
val walkerMethod =
it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex)
walkerMethod.addInstructions(
0, """
invoke-static {p0}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideTrackingUrl(Landroid/net/Uri;)Landroid/net/Uri;
move-result-object p0
"""
)
}
OrganicPlaybackContextModelFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.endIndex
val insertRegister =
getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstruction(
insertIndex,
"invoke-static { v$insertRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->setCpn(Ljava/lang/String;)V"
)
}
}
TrackingUrlHookPatch.hookTrackingUrl("$INTEGRATIONS_CLASS_DESCRIPTOR->setTrackingUriParameter(Landroid/net/Uri;)V")
}
// 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
// region hook storyboard.
StoryboardHookPatch.hook(INTEGRATIONS_CLASS_DESCRIPTOR)
// endregion
if (!SettingsPatch.upward1839) {
settingArray += "SETTINGS: IOS_CLIENT_SIDE_EFFECT_1838"
} else {
settingArray += "SETTINGS: IOS_CLIENT_SIDE_EFFECT_1839"
context.updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "SpoofClient")
}
/**
* Add settings
*/
SettingsPatch.addPreference(settingArray)
SettingsPatch.updatePatchStatus(this)
}
}

View File

@ -1,5 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client
import app.revanced.patches.shared.spoofuseragent.BaseSpoofUserAgentPatch
object SpoofUserAgentPatch : BaseSpoofUserAgentPatch("com.google.android.youtube")

View File

@ -1,16 +0,0 @@
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",
),
)

View File

@ -1,17 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.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 BuildPlaybackStatsRequestURIFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
returnType = "L",
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_STRING,
),
strings = listOf("event", "streamingstats"),
)

View File

@ -1,20 +0,0 @@
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",
),
)

View File

@ -1,34 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.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 CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
opcodes = listOf(
Opcode.IGET_OBJECT, // First instruction of the method
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
null // MOVE_RESULT or MOVE_RESULT_OBJECT, Return value controls the creation of the playback speed menu item.
),
// 19.01 and earlier is missing the second parameter.
// Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures.
customFingerprint = custom@{ methodDef, _ ->
// 19.01 and earlier parameters are: "[L"
// 19.02+ parameters are "[L", "F"
val parameterTypes = methodDef.parameterTypes
val firstParameter = parameterTypes.firstOrNull()
if (firstParameter == null || !firstParameter.startsWith("[L")) {
return@custom false
}
parameterTypes.size == 1 || (parameterTypes.size == 2 && parameterTypes[1] == "F")
},
)

View File

@ -1,15 +0,0 @@
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"),
)

View File

@ -1,30 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.fix.client.fingerprints.CreatePlayerRequestBodyWithVersionReleaseFingerprint.indexOfBuildInstruction
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.MethodReference
internal object CreatePlayerRequestBodyWithVersionReleaseFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
strings = listOf("Google Inc."),
customFingerprint = { methodDef, _ ->
indexOfBuildInstruction(methodDef) >= 0
},
) {
fun indexOfBuildInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.name == "build" &&
reference.parameterTypes.isEmpty() &&
reference.returnType.startsWith("L")
}
}

View File

@ -1,12 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.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=\""),
)

View File

@ -1,24 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.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 OrganicPlaybackContextModelFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf(
"Ljava/lang/String;", // cpn
"Z",
"Z",
"Z",
"Z"
),
opcodes = listOf(
Opcode.INVOKE_DIRECT,
Opcode.IF_EQZ,
Opcode.IPUT_OBJECT,
),
strings = listOf("Null contentCpn")
)

View File

@ -1,55 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
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.MethodReference
/**
* [FuzzyPatternScanMethod] was added to maintain compatibility for YouTube v18.29.38 to v18.32.39.
* TODO: Remove this annotation if support for YouTube v18.29.38 to v18.32.39 is dropped.
*/
@FuzzyPatternScanMethod(5)
internal object PlayerGestureConfigSyntheticFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object;"),
opcodes = listOf(
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutLandscapeAllowed.
Opcode.MOVE_RESULT,
Opcode.CHECK_CAST,
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // playerGestureConfig.downAndOutPortraitAllowed.
Opcode.MOVE_RESULT,
Opcode.IPUT_BOOLEAN,
Opcode.RETURN_VOID,
),
customFingerprint = { methodDef, classDef ->
fun indexOfDownAndOutAllowedInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;" &&
reference.parameterTypes.isEmpty() &&
reference.returnType == "Z"
}
// This method is always called "a" because this kind of class always has a single method.
methodDef.name == "a" && classDef.methods.count() == 2 &&
indexOfDownAndOutAllowedInstruction(methodDef) >= 0
},
)

View File

@ -1,43 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.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
/**
* On iOS clients, this method always returns false in live streams.
*
* This fingerprint seems to break easily because there are many [Opcode] patterns, but it is not.
* This fingerprint has been tested on all versions from YouTube 17.34.36 to YouTube 19.29.42.
*/
internal object PlayerResponseModelBackgroundAudioPlaybackFingerprint : MethodFingerprint(
returnType = "Z",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
opcodes = listOf(
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.GOTO,
Opcode.RETURN,
null, // Opcode.CONST_4 or Opcode.MOVE
Opcode.RETURN,
),
)

View File

@ -1,12 +0,0 @@
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.
),
)

View File

@ -1,19 +0,0 @@
package app.revanced.patches.youtube.utils.fix.client.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
/**
* This is the fingerprint used in the 'client-spoof' patch around 2022.
* (Integrated into 'UserAgentClientSpoofPatch' now.)
*
* This method is modified by 'UserAgentClientSpoofPatch', so the fingerprint does not check the [Opcode].
*/
internal object UserAgentHeaderBuilderFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
returnType = "Ljava/lang/String;",
parameters = listOf("Landroid/content/Context;", "Ljava/lang/String;", "Ljava/lang/String;"),
strings = listOf("(Linux; U; Android "),
)

View File

@ -1,53 +0,0 @@
package app.revanced.patches.youtube.utils.fix.parameter
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
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.utils.storyboard.StoryboardHookPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.util.patch.BaseBytecodePatch
@Suppress("unused")
@Deprecated("This patch will be removed in the future.")
object SpoofPlayerParameterPatch : BaseBytecodePatch(
// name = "Spoof player parameters",
description = "Adds options to spoof player parameters to prevent playback issues.",
dependencies = setOf(
PlayerTypeHookPatch::class,
PlayerResponseMethodHookPatch::class,
SettingsPatch::class,
VideoInformationPatch::class,
StoryboardHookPatch::class,
),
compatiblePackages = COMPATIBLE_PACKAGE,
use = false
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$MISC_PATH/SpoofPlayerParameterPatch;"
override fun execute(context: BytecodeContext) {
// Hook the player parameters.
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameter(
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;"
)
// Hook storyboard.
StoryboardHookPatch.hook(INTEGRATIONS_CLASS_DESCRIPTOR)
/**
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE_CATEGORY: MISC_EXPERIMENTAL_FLAGS",
"SETTINGS: SPOOF_PLAYER_PARAMETER"
)
)
SettingsPatch.updatePatchStatus(this)
}
}

View File

@ -3,8 +3,6 @@ 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.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
@ -15,8 +13,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
mainActivityOnCreateFingerprint = MainActivityFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
dependencies = setOf(
SpoofClientPatch::class,
SpoofUserAgentPatch::class,
SettingsPatch::class
),
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,

View File

@ -1,154 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard
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.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.storyboard.fingerprints.PlayerResponseModelGeneralStoryboardRendererFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.PlayerResponseModelLiveStreamStoryboardRendererFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.PlayerResponseModelStoryboardRecommendedLevelFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.StoryboardRendererDecoderRecommendedLevelFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.StoryboardRendererDecoderSpecFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.StoryboardRendererSpecFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.StoryboardThumbnailFingerprint
import app.revanced.patches.youtube.utils.storyboard.fingerprints.StoryboardThumbnailParentFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
description = "Force inject the Storyboard by fetching YouTube API.",
dependencies = [SharedResourceIdPatch::class],
)
object StoryboardHookPatch : BytecodePatch(
setOf(
PlayerResponseModelGeneralStoryboardRendererFingerprint,
PlayerResponseModelLiveStreamStoryboardRendererFingerprint,
PlayerResponseModelStoryboardRecommendedLevelFingerprint,
StoryboardRendererDecoderRecommendedLevelFingerprint,
StoryboardRendererDecoderSpecFingerprint,
StoryboardRendererSpecFingerprint,
StoryboardThumbnailParentFingerprint,
)
) {
private lateinit var context: BytecodeContext
override fun execute(context: BytecodeContext) {
this.context = context
}
internal fun hook(classDescriptor: String) {
// Force the seekbar time and chapters to always show up.
// This is used if the storyboard spec fetch fails, for viewing paid videos,
// or if storyboard spoofing is turned off.
StoryboardThumbnailFingerprint.resolve(
context,
StoryboardThumbnailParentFingerprint.resultOrThrow().classDef
)
StoryboardThumbnailFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.endIndex
val targetRegister =
getInstruction<OneRegisterInstruction>(targetIndex).registerA
// Since this is end of the method must replace one line then add the rest.
addInstructions(
targetIndex + 1,
"""
invoke-static {}, $classDescriptor->getSeekbarThumbnailOverrideValue()Z
move-result v$targetRegister
return v$targetRegister
"""
)
removeInstruction(targetIndex)
}
}
// Hook storyboard renderer url.
arrayOf(
PlayerResponseModelGeneralStoryboardRendererFingerprint,
PlayerResponseModelLiveStreamStoryboardRendererFingerprint
).forEach { fingerprint ->
fingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val getStoryboardIndex = it.scanResult.patternScanResult!!.endIndex
val getStoryboardRegister =
getInstruction<OneRegisterInstruction>(getStoryboardIndex).registerA
addInstructions(
getStoryboardIndex,
"""
invoke-static { v$getStoryboardRegister }, $classDescriptor->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$getStoryboardRegister
"""
)
}
}
}
// Hook recommended seekbar thumbnails quality level.
StoryboardRendererDecoderRecommendedLevelFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex
val originalValueRegister =
getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
addInstructions(
moveOriginalRecommendedValueIndex + 1, """
invoke-static { v$originalValueRegister }, $classDescriptor->getStoryboardRecommendedLevel(I)I
move-result v$originalValueRegister
"""
)
}
}
// Hook the recommended precise seeking thumbnails quality level.
PlayerResponseModelStoryboardRecommendedLevelFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex
val originalValueRegister =
getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
addInstructions(
moveOriginalRecommendedValueIndex, """
invoke-static { v$originalValueRegister }, $classDescriptor->getStoryboardRecommendedLevel(I)I
move-result v$originalValueRegister
"""
)
}
}
StoryboardRendererSpecFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val storyBoardUrlParams = 0
addInstructionsWithLabels(
0, """
if-nez p$storyBoardUrlParams, :ignore
invoke-static { p$storyBoardUrlParams }, $classDescriptor->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
move-result-object p$storyBoardUrlParams
""", ExternalLabel("ignore", getInstruction(0))
)
}
}
// Hook the seekbar thumbnail decoder and use a NULL spec for live streams.
StoryboardRendererDecoderSpecFingerprint.resultOrThrow().let {
val storyBoardUrlIndex = it.scanResult.patternScanResult!!.startIndex + 1
val storyboardUrlRegister =
it.mutableMethod.getInstruction<OneRegisterInstruction>(storyBoardUrlIndex).registerA
it.mutableMethod.addInstructions(
storyBoardUrlIndex + 1, """
invoke-static { v$storyboardUrlRegister }, $classDescriptor->getStoryboardDecoderRendererSpec(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$storyboardUrlRegister
"""
)
}
}
}

View File

@ -1,24 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.containsWideLiteralInstructionIndex
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object PlayerResponseModelGeneralStoryboardRendererFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
opcodes = listOf(
Opcode.RETURN_OBJECT,
Opcode.CONST_4,
Opcode.RETURN_OBJECT
),
customFingerprint = handler@{ methodDef, _ ->
if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;"))
return@handler false
methodDef.containsWideLiteralInstructionIndex(55735497)
}
)

View File

@ -1,24 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.containsWideLiteralInstructionIndex
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object PlayerResponseModelLiveStreamStoryboardRendererFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
opcodes = listOf(
Opcode.RETURN_OBJECT,
Opcode.CONST_4,
Opcode.RETURN_OBJECT
),
customFingerprint = handler@{ methodDef, _ ->
if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;"))
return@handler false
methodDef.containsWideLiteralInstructionIndex(70276274)
}
)

View File

@ -1,24 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.containsWideLiteralInstructionIndex
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object PlayerResponseModelStoryboardRecommendedLevelFingerprint : MethodFingerprint(
returnType = "I",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
opcodes = listOf(
Opcode.SGET_OBJECT,
Opcode.IGET,
Opcode.RETURN
),
customFingerprint = handler@{ methodDef, _ ->
if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;"))
return@handler false
methodDef.containsWideLiteralInstructionIndex(55735497)
}
)

View File

@ -1,23 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.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
/**
* Resolves to the same method as [StoryboardRendererDecoderSpecFingerprint].
*/
internal object StoryboardRendererDecoderRecommendedLevelFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT
),
strings = listOf("#-1#")
)

View File

@ -1,23 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.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
/**
* Resolves to the same method as [StoryboardRendererDecoderRecommendedLevelFingerprint].
*/
internal object StoryboardRendererDecoderSpecFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"),
opcodes = listOf(
Opcode.INVOKE_INTERFACE, // First instruction of the method.
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.CONST_4,
Opcode.IF_NEZ,
),
strings = listOf("#-1#")
)

View File

@ -1,12 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object StoryboardRendererSpecFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
returnType = "L",
parameters = listOf("Ljava/lang/String;", "J"),
strings = listOf("\\|"),
)

View File

@ -1,23 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.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
/**
* Resolves using the class found in [StoryboardThumbnailParentFingerprint].
*/
internal object StoryboardThumbnailFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf(),
opcodes = listOf(
Opcode.MOVE_RESULT,
Opcode.IF_GTZ,
Opcode.GOTO,
Opcode.CONST_4,
Opcode.RETURN,
Opcode.RETURN, // Last instruction of method.
),
)

View File

@ -1,17 +0,0 @@
package app.revanced.patches.youtube.utils.storyboard.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Here lies code that creates the seekbar thumbnails.
*
* An additional change here might force the thumbnails to be created,
* or possibly a change somewhere else (maybe involving YouTube 18.23.35 class `hte`)
*/
internal object StoryboardThumbnailParentFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/graphics/Bitmap;",
strings = listOf("Storyboard regionDecoder.decodeRegion exception - "),
)