mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-29 13:20:19 +02:00
feat(YouTube): remove Spoof client
patch
This commit is contained in:
parent
12059581c9
commit
0d29ed623a
@ -276,7 +276,6 @@ 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_client",
|
|
||||||
"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",
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")
|
|
@ -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",
|
|
||||||
),
|
|
||||||
)
|
|
@ -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"),
|
|
||||||
)
|
|
@ -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",
|
|
||||||
),
|
|
||||||
)
|
|
@ -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")
|
|
||||||
},
|
|
||||||
)
|
|
@ -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"),
|
|
||||||
)
|
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -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=\""),
|
|
||||||
)
|
|
@ -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")
|
|
||||||
)
|
|
@ -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
|
|
||||||
},
|
|
||||||
)
|
|
@ -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,
|
|
||||||
),
|
|
||||||
)
|
|
@ -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.
|
|
||||||
),
|
|
||||||
)
|
|
@ -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 "),
|
|
||||||
)
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,8 +3,6 @@ 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.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.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
|
||||||
@ -15,8 +13,6 @@ object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
|||||||
mainActivityOnCreateFingerprint = MainActivityFingerprint,
|
mainActivityOnCreateFingerprint = MainActivityFingerprint,
|
||||||
integrationsPatchDependency = IntegrationsPatch::class,
|
integrationsPatchDependency = IntegrationsPatch::class,
|
||||||
dependencies = setOf(
|
dependencies = setOf(
|
||||||
SpoofClientPatch::class,
|
|
||||||
SpoofUserAgentPatch::class,
|
|
||||||
SettingsPatch::class
|
SettingsPatch::class
|
||||||
),
|
),
|
||||||
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
||||||
|
@ -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
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
)
|
|
@ -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)
|
|
||||||
}
|
|
||||||
)
|
|
@ -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)
|
|
||||||
}
|
|
||||||
)
|
|
@ -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#")
|
|
||||||
)
|
|
@ -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#")
|
|
||||||
)
|
|
@ -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("\\|"),
|
|
||||||
)
|
|
@ -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.
|
|
||||||
),
|
|
||||||
)
|
|
@ -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 - "),
|
|
||||||
)
|
|
@ -163,46 +163,6 @@
|
|||||||
<item>HEART_TINT</item>
|
<item>HEART_TINT</item>
|
||||||
<item>HIDDEN</item>
|
<item>HIDDEN</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="revanced_spoof_client_general_options_entries">
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_ios</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_testsuite</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_unplugged</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_vr</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_general_options_entry_values">
|
|
||||||
<item>IOS</item>
|
|
||||||
<item>ANDROID_TESTSUITE</item>
|
|
||||||
<item>ANDROID_UNPLUGGED</item>
|
|
||||||
<item>ANDROID_VR</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_livestream_options_entries">
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_ios</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_unplugged</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_vr</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_livestream_options_entry_values">
|
|
||||||
<item>IOS</item>
|
|
||||||
<item>ANDROID_UNPLUGGED</item>
|
|
||||||
<item>ANDROID_VR</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_shorts_options_entries">
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_ios</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_vr</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_shorts_options_entry_values">
|
|
||||||
<item>IOS</item>
|
|
||||||
<item>ANDROID_VR</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_fallback_options_entries">
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_ios</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_testsuite</item>
|
|
||||||
<item>@string/revanced_spoof_client_options_entry_android_vr</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="revanced_spoof_client_fallback_options_entry_values">
|
|
||||||
<item>IOS</item>
|
|
||||||
<item>ANDROID_TESTSUITE</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>
|
||||||
|
@ -1551,22 +1551,6 @@ Tap on the continue button and disable battery optimizations."</string>
|
|||||||
<string name="revanced_enable_opus_codec_title">Enable OPUS codec</string>
|
<string name="revanced_enable_opus_codec_title">Enable OPUS codec</string>
|
||||||
<string name="revanced_enable_opus_codec_summary">Enable the OPUS codec if the player response includes the OPUS codec.</string>
|
<string name="revanced_enable_opus_codec_summary">Enable the OPUS codec if the player response includes the OPUS codec.</string>
|
||||||
|
|
||||||
<string name="revanced_spoof_player_parameter_title">Spoof player parameter</string>
|
|
||||||
<string name="revanced_spoof_player_parameter_summary">"Spoofs player parameters to prevent playback issues.
|
|
||||||
|
|
||||||
Limitations:
|
|
||||||
• Enhanced bitrate is not available.
|
|
||||||
• No seekbar thumbnails for paid videos.
|
|
||||||
• Offline downloads may not work.
|
|
||||||
• Video may not start from the last watched time."</string>
|
|
||||||
<string name="revanced_spoof_player_parameter_in_feed_title">Spoof player parameter in feed</string>
|
|
||||||
<string name="revanced_spoof_player_parameter_in_feed_summary_on">"Player parameter spoofed for feed videos.
|
|
||||||
|
|
||||||
Limitation: Automatically played feed videos will show up in your watch history."</string>
|
|
||||||
<string name="revanced_spoof_player_parameter_in_feed_summary_off">"Player parameter not spoofed for feed videos.
|
|
||||||
|
|
||||||
Limitation: Feed videos will play for less than 1 minute before encountering playback issues."</string>
|
|
||||||
|
|
||||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Import / Export settings -->
|
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Import / Export settings -->
|
||||||
<string name="revanced_preference_screen_import_export_title">Import / Export settings</string>
|
<string name="revanced_preference_screen_import_export_title">Import / Export settings</string>
|
||||||
<string name="revanced_preference_screen_import_export_summary">Import or export settings.</string>
|
<string name="revanced_preference_screen_import_export_summary">Import or export settings.</string>
|
||||||
@ -1593,83 +1577,6 @@ Limitation: Feed videos will play for less than 1 minute before encountering pla
|
|||||||
<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 client -->
|
|
||||||
<string name="revanced_preference_screen_spoof_client_title">Spoof client</string>
|
|
||||||
<string name="revanced_preference_screen_spoof_client_summary">Spoof the client to prevent playback issues.</string>
|
|
||||||
<string name="revanced_spoof_client_title">Spoof client</string>
|
|
||||||
<string name="revanced_spoof_client_summary_on">Client is spoofed.</string>
|
|
||||||
<string name="revanced_spoof_client_summary_off">"Client is not spoofed. Video playback may not work."</string>
|
|
||||||
<string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
|
|
||||||
<string name="revanced_spoof_client_type_title">Spoof client type</string>
|
|
||||||
<string name="revanced_spoof_client_ios_force_avc_title">Force iOS AVC (H.264)</string>
|
|
||||||
<string name="revanced_spoof_client_ios_force_avc_summary_on">iOS video codec is AVC (H.264).</string>
|
|
||||||
<string name="revanced_spoof_client_ios_force_avc_summary_off">iOS video codec is AVC (H.264), VP9, or AV1.</string>
|
|
||||||
<string name="revanced_spoof_client_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_client_stats_for_nerds_title">Show in Stats for nerds</string>
|
|
||||||
<string name="revanced_spoof_client_stats_for_nerds_summary_on">Spoofed client is shown in Stats for nerds.</string>
|
|
||||||
<string name="revanced_spoof_client_stats_for_nerds_summary_off">Spoofed client is hidden in Stats for nerds.</string>
|
|
||||||
|
|
||||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Spoof client, PreferenceCategory: Spoof client options -->
|
|
||||||
<string name="revanced_preference_category_spoof_client_options">Spoof client options</string>
|
|
||||||
<string name="revanced_spoof_client_general_title">General</string>
|
|
||||||
<string name="revanced_spoof_client_livestream_title">Live streams</string>
|
|
||||||
<string name="revanced_spoof_client_shorts_title">Shorts, Clips</string>
|
|
||||||
<string name="revanced_spoof_client_fallback_title">Unplayable video</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_ios">iOS</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_android">Android</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_android_embedded_player">Android Embedded Player</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_android_testsuite">Android Testsuite</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_android_unplugged">Android TV</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_android_vr">Android VR</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_tvhtml5_simply_embedded_player">TV HTML5</string>
|
|
||||||
<string name="revanced_spoof_client_options_entry_web">Web</string>
|
|
||||||
|
|
||||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceScreen: Spoof client, PreferenceCategory: About -->
|
|
||||||
<string name="revanced_preference_category_spoof_client_about">About</string>
|
|
||||||
|
|
||||||
<string name="revanced_spoof_client_use_ios_title">iOS</string>
|
|
||||||
<string name="revanced_spoof_client_use_ios_legacy_summary">"Spoof client to iOS.
|
|
||||||
|
|
||||||
Side effects include:
|
|
||||||
• HDR is supported only with AV1 codec.
|
|
||||||
• Watch time is not saved in watch history on brand account."</string>
|
|
||||||
<string name="revanced_spoof_client_use_ios_summary">"Spoof client to iOS.
|
|
||||||
|
|
||||||
Side effects include:
|
|
||||||
• HDR is supported only with AV1 codec.
|
|
||||||
• Watch history does not work with a brand account."</string>
|
|
||||||
<string name="revanced_spoof_client_use_android_testsuite_title">Android Testsuite</string>
|
|
||||||
<string name="revanced_spoof_client_use_android_testsuite_summary">"Spoof client to Android Testsuite.
|
|
||||||
|
|
||||||
Side effects include:
|
|
||||||
• No HDR video.
|
|
||||||
• Audio track menu is missing.
|
|
||||||
• Captions may not be available.
|
|
||||||
• Download button is missing.
|
|
||||||
• End screen cards are missing.
|
|
||||||
• Low quality seekbar thumbnail."</string>
|
|
||||||
<string name="revanced_spoof_client_use_android_tv_title">Android TV</string>
|
|
||||||
<string name="revanced_spoof_client_use_android_tv_summary">"Spoof client to Android TV (YouTube TV).
|
|
||||||
|
|
||||||
Side effects include:
|
|
||||||
• No HDR video.
|
|
||||||
• Audio track menu is missing.
|
|
||||||
• Captions may not be available.
|
|
||||||
• Download button is missing.
|
|
||||||
• Low quality seekbar thumbnail.
|
|
||||||
• Some live streams are not supported for playback."</string>
|
|
||||||
<string name="revanced_spoof_client_use_android_vr_title">Android VR</string>
|
|
||||||
<string name="revanced_spoof_client_use_android_vr_summary">"Spoof client to Android VR.
|
|
||||||
|
|
||||||
Side effects include:
|
|
||||||
• No HDR video.
|
|
||||||
• Download button of video action bar is missing.
|
|
||||||
• Paused videos can randomly resume.
|
|
||||||
• Low quality Shorts seekbar thumbnails.
|
|
||||||
• Kids videos do not play."</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>
|
||||||
@ -1682,15 +1589,8 @@ Side effects include:
|
|||||||
<string name="revanced_watch_history_type_entry_3">Block watch history</string>
|
<string name="revanced_watch_history_type_entry_3">Block watch history</string>
|
||||||
<string name="revanced_watch_history_about_title">Status of watch history</string>
|
<string name="revanced_watch_history_about_title">Status of watch history</string>
|
||||||
<string name="revanced_watch_history_about_status_blocked">• Watch history is blocked.</string>
|
<string name="revanced_watch_history_about_status_blocked">• Watch history is blocked.</string>
|
||||||
<string name="revanced_watch_history_about_status_ios_replaced">"• Follows the watch history settings of Google account.
|
<string name="revanced_watch_history_about_status_replaced">• Follows the watch history settings of Google account.</string>
|
||||||
• Watch history may not work with a brand account.
|
<string name="revanced_watch_history_about_status_original">"• Follows the watch history settings of Google account.
|
||||||
(Spoof client setting is enabled and iOS client is selected)"</string>
|
|
||||||
<string name="revanced_watch_history_about_status_ios_original">"• Follows the watch history settings of Google account.
|
|
||||||
• Watch history may not work due to DNS or VPN.
|
|
||||||
• Watch history may not work with a brand account.
|
|
||||||
(Spoof client setting is enabled and iOS client is selected)"</string>
|
|
||||||
<string name="revanced_watch_history_about_status_android_replaced">• Follows the watch history settings of Google account.</string>
|
|
||||||
<string name="revanced_watch_history_about_status_android_original">"• Follows the watch history settings of Google account.
|
|
||||||
• Watch history may not work due to DNS or VPN."</string>
|
• Watch history may not work due to DNS or VPN."</string>
|
||||||
|
|
||||||
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceCategory: Patch information -->
|
<!-- PreferenceScreen: Miscellaneous, PreferenceCategory: Miscellaneous, PreferenceCategory: Patch information -->
|
||||||
|
@ -610,32 +610,6 @@
|
|||||||
<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_CLIENT
|
|
||||||
<PreferenceScreen android:title="@string/revanced_preference_screen_spoof_client_title" android:key="revanced_preference_screen_spoof_client" android:summary="@string/revanced_preference_screen_spoof_client_summary">
|
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_client_title" android:key="revanced_spoof_client" android:summaryOn="@string/revanced_spoof_client_summary_on" android:summaryOff="@string/revanced_spoof_client_summary_off" />
|
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_client_ios_force_avc_title" android:key="revanced_spoof_client_ios_force_avc" android:summaryOn="@string/revanced_spoof_client_ios_force_avc_summary_on" android:summaryOff="@string/revanced_spoof_client_ios_force_avc_summary_off" />
|
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_client_stats_for_nerds_title" android:key="revanced_spoof_client_stats_for_nerds" android:summaryOn="@string/revanced_spoof_client_stats_for_nerds_summary_on" android:summaryOff="@string/revanced_spoof_client_stats_for_nerds_summary_off" />
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/revanced_preference_category_spoof_client_options" android:layout="@layout/revanced_settings_preferences_category" />
|
|
||||||
<ListPreference android:entries="@array/revanced_spoof_client_general_options_entries" android:title="@string/revanced_spoof_client_general_title" android:key="revanced_spoof_client_general" android:entryValues="@array/revanced_spoof_client_general_options_entry_values" android:dependency="revanced_spoof_client" />
|
|
||||||
<ListPreference android:entries="@array/revanced_spoof_client_livestream_options_entries" android:title="@string/revanced_spoof_client_livestream_title" android:key="revanced_spoof_client_livestream" android:entryValues="@array/revanced_spoof_client_livestream_options_entry_values" android:dependency="revanced_spoof_client" />
|
|
||||||
<ListPreference android:entries="@array/revanced_spoof_client_shorts_options_entries" android:title="@string/revanced_spoof_client_shorts_title" android:key="revanced_spoof_client_shorts" android:entryValues="@array/revanced_spoof_client_shorts_options_entry_values" android:dependency="revanced_spoof_client" />
|
|
||||||
<ListPreference android:entries="@array/revanced_spoof_client_fallback_options_entries" android:title="@string/revanced_spoof_client_fallback_title" android:key="revanced_spoof_client_fallback" android:entryValues="@array/revanced_spoof_client_fallback_options_entry_values" android:dependency="revanced_spoof_client" />
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/revanced_preference_category_spoof_client_about" android:layout="@layout/revanced_settings_preferences_category" />SETTINGS: SPOOF_CLIENT -->
|
|
||||||
|
|
||||||
<!-- SETTINGS: IOS_CLIENT_SIDE_EFFECT_1838
|
|
||||||
<Preference android:title="@string/revanced_spoof_client_use_ios_title" android:key="revanced_spoof_client_use_ios" android:selectable="false" android:summary="@string/revanced_spoof_client_use_ios_legacy_summary" android:dependency="revanced_spoof_client" />SETTINGS: IOS_CLIENT_SIDE_EFFECT_1838 -->
|
|
||||||
|
|
||||||
<!-- SETTINGS: IOS_CLIENT_SIDE_EFFECT_1839
|
|
||||||
<Preference android:title="@string/revanced_spoof_client_use_ios_title" android:key="revanced_spoof_client_use_ios" android:selectable="false" android:summary="@string/revanced_spoof_client_use_ios_summary" android:dependency="revanced_spoof_client" />SETTINGS: IOS_CLIENT_SIDE_EFFECT_1839 -->
|
|
||||||
|
|
||||||
<!-- SETTINGS: SPOOF_CLIENT
|
|
||||||
<Preference android:title="@string/revanced_spoof_client_use_android_testsuite_title" android:key="revanced_spoof_client_use_android_testsuite" android:selectable="false" android:summary="@string/revanced_spoof_client_use_android_testsuite_summary" android:dependency="revanced_spoof_client" />
|
|
||||||
<Preference android:title="@string/revanced_spoof_client_use_android_tv_title" android:key="revanced_spoof_client_use_android_tv" android:selectable="false" android:summary="@string/revanced_spoof_client_use_android_tv_summary" android:dependency="revanced_spoof_client" />
|
|
||||||
<Preference android:title="@string/revanced_spoof_client_use_android_vr_title" android:key="revanced_spoof_client_use_android_vr" android:selectable="false" android:summary="@string/revanced_spoof_client_use_android_vr_summary" android:dependency="revanced_spoof_client" />
|
|
||||||
</PreferenceScreen>SETTINGS: SPOOF_CLIENT -->
|
|
||||||
|
|
||||||
<!-- 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">
|
||||||
@ -677,10 +651,6 @@
|
|||||||
<!-- SETTINGS: ENABLE_OPUS_CODEC
|
<!-- SETTINGS: ENABLE_OPUS_CODEC
|
||||||
<SwitchPreference android:title="@string/revanced_enable_opus_codec_title" android:key="revanced_enable_opus_codec" android:summary="@string/revanced_enable_opus_codec_summary" />SETTINGS: ENABLE_OPUS_CODEC -->
|
<SwitchPreference android:title="@string/revanced_enable_opus_codec_title" android:key="revanced_enable_opus_codec" android:summary="@string/revanced_enable_opus_codec_summary" />SETTINGS: ENABLE_OPUS_CODEC -->
|
||||||
|
|
||||||
<!-- SETTINGS: SPOOF_PLAYER_PARAMETER
|
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_player_parameter_title" android:key="revanced_spoof_player_parameter" android:summary="@string/revanced_spoof_player_parameter_summary" />
|
|
||||||
<SwitchPreference android:title="@string/revanced_spoof_player_parameter_in_feed_title" android:key="revanced_spoof_player_parameter_in_feed" android:summaryOn="@string/revanced_spoof_player_parameter_in_feed_summary_on" android:summaryOff="@string/revanced_spoof_player_parameter_in_feed_summary_off" android:dependency="revanced_spoof_player_parameter" />SETTINGS: SPOOF_PLAYER_PARAMETER -->
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/revanced_preference_category_patch_information" android:layout="@layout/revanced_settings_preferences_category"/>
|
<PreferenceCategory android:title="@string/revanced_preference_category_patch_information" android:layout="@layout/revanced_settings_preferences_category"/>
|
||||||
<PreferenceScreen android:title="@string/revanced_preference_screen_patch_information_title" android:key="revanced_preference_screen_patch_information" android:summary="@string/revanced_preference_screen_patch_information_summary" >
|
<PreferenceScreen android:title="@string/revanced_preference_screen_patch_information_title" android:key="revanced_preference_screen_patch_information" android:summary="@string/revanced_preference_screen_patch_information_summary" >
|
||||||
<PreferenceCategory android:title="@string/revanced_preference_category_tool_used" android:layout="@layout/revanced_settings_preferences_category" >
|
<PreferenceCategory android:title="@string/revanced_preference_category_tool_used" android:layout="@layout/revanced_settings_preferences_category" >
|
||||||
@ -756,7 +726,6 @@
|
|||||||
<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 client" 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>
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<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>
|
|
Loading…
x
Reference in New Issue
Block a user