fix(YouTube/Return YouTube Dislike): prevent the first Short opened from freezing the UI

This commit is contained in:
inotia00 2023-12-04 08:32:26 +09:00
parent f77c62cbad
commit fa3acf8ffd
4 changed files with 103 additions and 62 deletions

View File

@ -18,8 +18,10 @@ import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardR
import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardRendererSpecRecommendedLevelFingerprint
import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailFingerprint
import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailParentFingerprint
import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.util.integrations.Constants.MISC_PATH
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -29,6 +31,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
description = "Spoofs player parameters to prevent playback issues.",
dependencies = [
PlayerTypeHookPatch::class,
PlayerResponsePatch::class,
VideoIdPatch::class,
SettingsPatch::class
],
compatiblePackages = [
@ -75,19 +79,9 @@ object SpoofPlayerParameterPatch : BytecodePatch(
/**
* Hook player parameter
*/
PlayerParameterBuilderFingerprint.result?.let {
it.mutableMethod.apply {
val videoIdRegister = 1
val playerParameterRegister = 3
addInstructions(
0, """
invoke-static {p$videoIdRegister, p$playerParameterRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object p$playerParameterRegister
"""
PlayerResponsePatch += PlayerResponsePatch.Hook.PlayerParameter(
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;"
)
}
} ?: throw PlayerParameterBuilderFingerprint.exception
/**
* Forces the SeekBar thumbnail preview container to be shown

View File

@ -2,39 +2,58 @@ package app.revanced.patches.youtube.utils.playerresponse
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
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.youtube.utils.fingerprints.PlayerParameterBuilderFingerprint
import java.io.Closeable
object PlayerResponsePatch : BytecodePatch(
setOf(PlayerParameterBuilderFingerprint)
) {
), Closeable,MutableSet<PlayerResponsePatch.Hook> by mutableSetOf() {
private const val VIDEO_ID_PARAMETER = 1
private const val VIDEO_IS_OPENING_OR_PLAYING_PARAMETER = 11
private const val PLAYER_PARAMETER = 3
private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
private lateinit var insertMethod: MutableMethod
/**
* Adds an invoke-static instruction, called with the new id when the video changes
* @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;`
*/
internal fun injectCall(
methodDescriptor: String
) {
insertMethod.addInstructions(
0, // move-result-object offset
"invoke-static {p$VIDEO_ID_PARAMETER, p$VIDEO_IS_OPENING_OR_PLAYING_PARAMETER}, $methodDescriptor"
)
}
private lateinit var playerResponseMethod: MutableMethod
override fun execute(context: BytecodeContext) {
PlayerParameterBuilderFingerprint.result?.let {
insertMethod = it.mutableMethod
} ?: throw PlayerParameterBuilderFingerprint.exception
playerResponseMethod = PlayerParameterBuilderFingerprint.result?.mutableMethod
?: throw PlayerParameterBuilderFingerprint.exception
}
override fun close() {
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
0,
"invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
)
fun hookPlayerParameter(hook: Hook) = playerResponseMethod.addInstructions(
0, """
invoke-static {p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p$PLAYER_PARAMETER
"""
)
// Reverse the order in order to preserve insertion order of the hooks.
val beforeVideoIdHooks = filterIsInstance<Hook.PlayerBeforeVideoId>().asReversed()
val videoIdHooks = filterIsInstance<Hook.VideoId>().asReversed()
val afterVideoIdHooks = filterIsInstance<Hook.PlayerParameter>().asReversed()
// Add the hooks in this specific order as they insert instructions at the beginning of the method.
afterVideoIdHooks.forEach(::hookPlayerParameter)
videoIdHooks.forEach(::hookVideoId)
beforeVideoIdHooks.forEach(::hookPlayerParameter)
}
internal abstract class Hook(private val methodDescriptor: String) {
internal class VideoId(methodDescriptor: String) : Hook(methodDescriptor)
internal class PlayerParameter(methodDescriptor: String) : Hook(methodDescriptor)
internal class PlayerBeforeVideoId(methodDescriptor: String) : Hook(methodDescriptor)
override fun toString() = methodDescriptor
}
}

View File

@ -101,13 +101,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
TextComponentConstructorFingerprint.result?.let { parentResult ->
// Resolves fingerprints
val parentClassDef = parentResult.classDef
TextComponentContextFingerprint.resolve(context, parentClassDef)
TextComponentTmpFingerprint.resolve(context, parentClassDef)
TextComponentAtomicReferenceFingerprint.resolve(context, parentClassDef)
TextComponentAtomicReferenceLegacyFingerprint.resolve(context, parentClassDef)
TextComponentContextFingerprint.also {
it.resolve(
context,
parentResult.classDef
)
}.result?.let {
TextComponentContextFingerprint.result?.let {
it.mutableMethod.apply {
val booleanIndex = it.scanResult.patternScanResult!!.endIndex
@ -127,12 +128,7 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
}
} ?: throw TextComponentContextFingerprint.exception
TextComponentTmpFingerprint.also {
it.resolve(
context,
parentResult.classDef
)
}.result?.let {
TextComponentTmpFingerprint.result?.let {
it.mutableMethod.apply {
val startIndex = it.scanResult.patternScanResult!!.startIndex
tmpRegister =
@ -142,17 +138,11 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
val textComponentAtomicReferenceResult =
TextComponentAtomicReferenceFingerprint.also {
it.resolve(context, parentResult.classDef)
}.result
?: TextComponentAtomicReferenceLegacyFingerprint.also {
it.resolve(context, parentResult.classDef)
}.result
TextComponentAtomicReferenceFingerprint.result
?: TextComponentAtomicReferenceLegacyFingerprint.result
?: throw TextComponentAtomicReferenceLegacyFingerprint.exception
TextComponentAtomicReferenceFingerprint.also {
it.resolve(context, parentResult.classDef)
}.result?.let {
TextComponentAtomicReferenceFingerprint.result?.let {
it.mutableMethod.apply {
val startIndex = it.scanResult.patternScanResult!!.startIndex
val originalRegisterA =
@ -196,14 +186,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
}
} ?: throw TextComponentConstructorFingerprint.exception
VideoIdPatch.injectCall("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V")
VideoIdPatch.injectPlayerResponseVideoId("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->preloadVideoId(Ljava/lang/String;Z)V")
if (SettingsPatch.upward1834) {
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
PlayerResponsePatch.injectCall("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
VideoIdPatch.injectPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
}
PlayerResponsePatch.injectCall("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->preloadVideoId(Ljava/lang/String;Z)V")
VideoIdPatch.injectCall("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V")
/**
* Add ReVanced Extended Settings
*/

View File

@ -13,6 +13,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.fingerprints.OrganicPlaybackContextModelFingerprint
import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.videoid.general.fingerprint.PlayerControllerSetTimeReferenceFingerprint
import app.revanced.patches.youtube.utils.videoid.general.fingerprint.VideoEndFingerprint
@ -27,7 +28,12 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
@Patch(dependencies = [PlayerTypeHookPatch::class])
@Patch(
dependencies = [
PlayerTypeHookPatch::class,
PlayerResponsePatch::class
]
)
object VideoIdPatch : BytecodePatch(
setOf(
OrganicPlaybackContextModelFingerprint,
@ -144,8 +150,12 @@ object VideoIdPatch : BytecodePatch(
} ?: throw VideoIdFingerprint.exception
} ?: throw VideoIdParentFingerprint.exception
injectCall("$VIDEO_PATH/VideoInformation;->setVideoId(Ljava/lang/String;)V")
injectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
injectPlayerResponseVideoId("$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V")
// Call before any other video id hooks,
// so they can use VideoInformation and check if the video id is for a Short.
PlayerResponsePatch += PlayerResponsePatch.Hook.PlayerBeforeVideoId(
"$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseSignature(Ljava/lang/String;Z)Ljava/lang/String;")
}
const val INTEGRATIONS_CLASS_DESCRIPTOR = "$VIDEO_PATH/VideoInformation;"
@ -173,6 +183,34 @@ object VideoIdPatch : BytecodePatch(
)
}
/**
* Hooks the video id of every video when loaded.
* Supports all videos and functions in all situations.
*
* First parameter is the video id.
* Second parameter is if the video is a Short AND it is being opened or is currently playing.
*
* Hook is always called off the main thread.
*
* This hook is called as soon as the player response is parsed,
* and called before many other hooks are updated such as [PlayerTypeHookPatch].
*
* Note: The video id returned here may not be the current video that's being played.
* It's common for multiple Shorts to load at once in preparation
* for the user swiping to the next Short.
*
* Be aware, this can be called multiple times for the same video id.
*
* @param methodDescriptor which method to call. Params must be `Ljava/lang/String;Z`
*/
internal fun injectPlayerResponseVideoId(
methodDescriptor: String
) {
PlayerResponsePatch += PlayerResponsePatch.Hook.VideoId(
methodDescriptor
)
}
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
addInstruction(insertIndex, "invoke-static { $register }, $descriptor")