mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 05:07:41 +02:00
feat(YouTube Music): Add Spoof player parameter
patch https://github.com/inotia00/ReVanced_Extended/issues/2832
This commit is contained in:
@ -14,7 +14,8 @@ import app.revanced.patches.music.utils.settings.addSwitchPreference
|
||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.patches.music.video.information.videoIdHook
|
||||
import app.revanced.patches.music.video.information.videoInformationPatch
|
||||
import app.revanced.patches.music.video.playerresponse.hookPlayerResponse
|
||||
import app.revanced.patches.music.video.playerresponse.Hook
|
||||
import app.revanced.patches.music.video.playerresponse.addPlayerResponseMethodHook
|
||||
import app.revanced.patches.music.video.playerresponse.playerResponseMethodHookPatch
|
||||
import app.revanced.util.findMethodOrThrow
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
@ -46,7 +47,11 @@ val albumMusicVideoPatch = bytecodePatch(
|
||||
|
||||
// region hook player response
|
||||
|
||||
hookPlayerResponse("$EXTENSION_CLASS_DESCRIPTOR->newPlayerResponse(Ljava/lang/String;Ljava/lang/String;I)V")
|
||||
addPlayerResponseMethodHook(
|
||||
Hook.VideoIdAndPlaylistId(
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->newPlayerResponse(Ljava/lang/String;Ljava/lang/String;I)V"
|
||||
),
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
package app.revanced.patches.music.utils.fix.parameter
|
||||
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val subtitleWindowFingerprint = legacyFingerprint(
|
||||
name = "subtitleWindowFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("I", "I", "I", "Z", "Z"),
|
||||
strings = listOf("invalid anchorHorizontalPos: %s"),
|
||||
)
|
||||
|
||||
/**
|
||||
* If this flag is activated, a playback issue occurs in age-restricted videos.
|
||||
*/
|
||||
internal const val AGE_RESTRICTED_PLAYBACK_FEATURE_FLAG = 45651506L
|
||||
|
||||
internal val ageRestrictedPlaybackFeatureFlagFingerprint = legacyFingerprint(
|
||||
name = "ageRestrictedPlaybackFeatureFlagFingerprint",
|
||||
returnType = "Z",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = emptyList(),
|
||||
literals = listOf(AGE_RESTRICTED_PLAYBACK_FEATURE_FLAG),
|
||||
)
|
@ -0,0 +1,82 @@
|
||||
package app.revanced.patches.music.utils.fix.parameter
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.music.utils.extension.Constants.MISC_PATH
|
||||
import app.revanced.patches.music.utils.patch.PatchList.SPOOF_PLAYER_PARAMETER
|
||||
import app.revanced.patches.music.utils.settings.CategoryType
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
||||
import app.revanced.patches.music.utils.settings.addSwitchPreference
|
||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.patches.music.video.information.videoInformationPatch
|
||||
import app.revanced.patches.music.video.playerresponse.Hook
|
||||
import app.revanced.patches.music.video.playerresponse.addPlayerResponseMethodHook
|
||||
import app.revanced.patches.music.video.playerresponse.playerResponseMethodHookPatch
|
||||
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.fingerprint.resolvable
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"$MISC_PATH/SpoofPlayerParameterPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val spoofPlayerParameterPatch = bytecodePatch(
|
||||
SPOOF_PLAYER_PARAMETER.title,
|
||||
SPOOF_PLAYER_PARAMETER.summary
|
||||
) {
|
||||
compatibleWith(COMPATIBLE_PACKAGE)
|
||||
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
videoInformationPatch,
|
||||
playerResponseMethodHookPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
addPlayerResponseMethodHook(
|
||||
Hook.PlayerParameter(
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
|
||||
),
|
||||
)
|
||||
|
||||
// region fix for subtitles position
|
||||
|
||||
subtitleWindowFingerprint.methodOrThrow().addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static {p1, p2, p3, p4, p5}, $EXTENSION_CLASS_DESCRIPTOR->fixSubtitleWindowPosition(IIIZZ)[I
|
||||
move-result-object v0
|
||||
const/4 v1, 0x0
|
||||
aget p1, v0, v1 # ap, anchor position
|
||||
const/4 v1, 0x1
|
||||
aget p2, v0, v1 # ah, horizontal anchor
|
||||
const/4 v1, 0x2
|
||||
aget p3, v0, v1 # av, vertical anchor
|
||||
"""
|
||||
)
|
||||
|
||||
// endregion
|
||||
|
||||
// region fix for feature flags
|
||||
|
||||
if (ageRestrictedPlaybackFeatureFlagFingerprint.resolvable()) {
|
||||
ageRestrictedPlaybackFeatureFlagFingerprint.injectLiteralInstructionBooleanCall(
|
||||
AGE_RESTRICTED_PLAYBACK_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->forceDisableAgeRestrictedPlaybackFeatureFlag(Z)Z"
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
addSwitchPreference(
|
||||
CategoryType.MISC,
|
||||
"revanced_spoof_player_parameter",
|
||||
"true"
|
||||
)
|
||||
|
||||
updatePatchStatus(SPOOF_PLAYER_PARAMETER)
|
||||
|
||||
}
|
||||
}
|
@ -157,6 +157,10 @@ internal enum class PatchList(
|
||||
"Spoof client",
|
||||
"Adds options to spoof the client to allow playback."
|
||||
),
|
||||
SPOOF_PLAYER_PARAMETER(
|
||||
"Spoof player parameter",
|
||||
"Adds options to spoof player parameter to allow playback."
|
||||
),
|
||||
TRANSLATIONS_FOR_YOUTUBE_MUSIC(
|
||||
"Translations for YouTube Music",
|
||||
"Add translations or remove string resources."
|
||||
|
@ -13,6 +13,9 @@ import app.revanced.patches.music.utils.extension.Constants.SHARED_PATH
|
||||
import app.revanced.patches.music.utils.playbackSpeedFingerprint
|
||||
import app.revanced.patches.music.utils.playbackSpeedParentFingerprint
|
||||
import app.revanced.patches.music.utils.resourceid.sharedResourceIdPatch
|
||||
import app.revanced.patches.music.video.playerresponse.Hook
|
||||
import app.revanced.patches.music.video.playerresponse.addPlayerResponseMethodHook
|
||||
import app.revanced.patches.music.video.playerresponse.playerResponseMethodHookPatch
|
||||
import app.revanced.patches.shared.mdxPlayerDirectorSetVideoStageFingerprint
|
||||
import app.revanced.patches.shared.videoLengthFingerprint
|
||||
import app.revanced.util.addStaticFieldToExtension
|
||||
@ -71,7 +74,10 @@ private var videoTimeConstructorInsertIndex = 2
|
||||
val videoInformationPatch = bytecodePatch(
|
||||
description = "videoInformationPatch",
|
||||
) {
|
||||
dependsOn(sharedResourceIdPatch)
|
||||
dependsOn(
|
||||
playerResponseMethodHookPatch,
|
||||
sharedResourceIdPatch
|
||||
)
|
||||
|
||||
execute {
|
||||
fun addSeekInterfaceMethods(
|
||||
@ -241,7 +247,18 @@ val videoInformationPatch = bytecodePatch(
|
||||
* Set current video id
|
||||
*/
|
||||
videoIdHook("$EXTENSION_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
|
||||
|
||||
addPlayerResponseMethodHook(
|
||||
Hook.VideoId(
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;)V"
|
||||
),
|
||||
)
|
||||
// Call before any other video id hooks,
|
||||
// so they can use VideoInformation and check if the video id is for a Short.
|
||||
addPlayerResponseMethodHook(
|
||||
Hook.PlayerParameterBeforeVideoId(
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->newPlayerResponseParameter(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"
|
||||
)
|
||||
)
|
||||
/**
|
||||
* Hook current playback speed
|
||||
*/
|
||||
|
@ -11,8 +11,7 @@ private val PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST = listOf(
|
||||
"[B",
|
||||
"Ljava/lang/String;", // Player parameters proto buffer.
|
||||
"Ljava/lang/String;", // PlaylistId.
|
||||
"I", // PlaylistIndex.
|
||||
"I"
|
||||
"I" // PlaylistIndex.
|
||||
)
|
||||
|
||||
/**
|
||||
@ -30,7 +29,7 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint(
|
||||
return@custom false
|
||||
}
|
||||
|
||||
val startsWithMethodParameterList = parameterTypes.slice(0..5)
|
||||
val startsWithMethodParameterList = parameterTypes.slice(0..4)
|
||||
|
||||
parametersEqual(
|
||||
PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST,
|
||||
|
@ -1,22 +1,35 @@
|
||||
package app.revanced.patches.music.video.playerresponse
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.music.utils.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.utils.playservice.is_7_03_or_greater
|
||||
import app.revanced.patches.music.utils.playservice.versionCheckPatch
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
|
||||
private val hooks = mutableSetOf<Hook>()
|
||||
|
||||
fun addPlayerResponseMethodHook(hook: Hook) {
|
||||
hooks += hook
|
||||
}
|
||||
|
||||
private const val REGISTER_VIDEO_ID = "p1"
|
||||
private const val REGISTER_PLAYER_PARAMETER = "p3"
|
||||
private const val REGISTER_PLAYLIST_ID = "p4"
|
||||
private const val REGISTER_PLAYLIST_INDEX = "p5"
|
||||
|
||||
private lateinit var playerResponseMethod: MutableMethod
|
||||
private var numberOfInstructionsAdded = 0
|
||||
|
||||
val playerResponseMethodHookPatch = bytecodePatch(
|
||||
description = "playerResponseMethodHookPatch"
|
||||
) {
|
||||
dependsOn(versionCheckPatch)
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
playerResponseMethod = if (is_7_03_or_greater) {
|
||||
@ -25,16 +38,70 @@ val playerResponseMethodHookPatch = bytecodePatch(
|
||||
playerParameterBuilderLegacyFingerprint
|
||||
}.methodOrThrow()
|
||||
}
|
||||
|
||||
finalize {
|
||||
fun hookVideoId(hook: Hook) {
|
||||
playerResponseMethod.addInstruction(
|
||||
0,
|
||||
"invoke-static {$REGISTER_VIDEO_ID}, $hook",
|
||||
)
|
||||
numberOfInstructionsAdded++
|
||||
}
|
||||
|
||||
fun hookVideoIdAndPlaylistId(hook: Hook) {
|
||||
playerResponseMethod.addInstruction(
|
||||
0,
|
||||
"invoke-static {$REGISTER_VIDEO_ID, $REGISTER_PLAYLIST_ID, $REGISTER_PLAYLIST_INDEX}, $hook",
|
||||
)
|
||||
numberOfInstructionsAdded++
|
||||
}
|
||||
|
||||
fun hookPlayerParameter(hook: Hook) {
|
||||
playerResponseMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static {$REGISTER_VIDEO_ID, v0}, $hook
|
||||
move-result-object v0
|
||||
""",
|
||||
)
|
||||
numberOfInstructionsAdded += 2
|
||||
}
|
||||
|
||||
// Reverse the order in order to preserve insertion order of the hooks.
|
||||
val beforeVideoIdHooks =
|
||||
hooks.filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed()
|
||||
val videoIdHooks = hooks.filterIsInstance<Hook.VideoId>().asReversed()
|
||||
val videoIdAndPlaylistIdHooks = hooks.filterIsInstance<Hook.VideoIdAndPlaylistId>().asReversed()
|
||||
val afterVideoIdHooks = hooks.filterIsInstance<Hook.PlayerParameter>().asReversed()
|
||||
|
||||
// Add the hooks in this specific order as they insert instructions at the beginning of the method.
|
||||
afterVideoIdHooks.forEach(::hookPlayerParameter)
|
||||
videoIdAndPlaylistIdHooks.forEach(::hookVideoIdAndPlaylistId)
|
||||
videoIdHooks.forEach(::hookVideoId)
|
||||
beforeVideoIdHooks.forEach(::hookPlayerParameter)
|
||||
|
||||
playerResponseMethod.apply {
|
||||
addInstruction(
|
||||
0,
|
||||
"move-object/from16 v0, $REGISTER_PLAYER_PARAMETER"
|
||||
)
|
||||
numberOfInstructionsAdded++
|
||||
|
||||
// Move the modified register back.
|
||||
addInstruction(
|
||||
numberOfInstructionsAdded,
|
||||
"move-object/from16 $REGISTER_PLAYER_PARAMETER, v0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun hookPlayerResponse(
|
||||
descriptor: String,
|
||||
onlyVideoId: Boolean = false
|
||||
) {
|
||||
val smaliInstruction = if (onlyVideoId)
|
||||
"invoke-static {$REGISTER_VIDEO_ID}, $descriptor"
|
||||
else
|
||||
"invoke-static {$REGISTER_VIDEO_ID, $REGISTER_PLAYLIST_ID, $REGISTER_PLAYLIST_INDEX}, $descriptor"
|
||||
sealed class Hook(private val methodDescriptor: String) {
|
||||
class VideoId(methodDescriptor: String) : Hook(methodDescriptor)
|
||||
class VideoIdAndPlaylistId(methodDescriptor: String) : Hook(methodDescriptor)
|
||||
|
||||
playerResponseMethod.addInstruction(0, smaliInstruction)
|
||||
class PlayerParameter(methodDescriptor: String) : Hook(methodDescriptor)
|
||||
class PlayerParameterBeforeVideoId(methodDescriptor: String) : Hook(methodDescriptor)
|
||||
|
||||
override fun toString() = methodDescriptor
|
||||
}
|
||||
|
Reference in New Issue
Block a user