diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt index 5ad90b129..a85969b2f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt @@ -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 - """ - ) - } - } ?: throw PlayerParameterBuilderFingerprint.exception + PlayerResponsePatch += PlayerResponsePatch.Hook.PlayerParameter( + "$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;" + ) /** * Forces the SeekBar thumbnail preview container to be shown diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt index 9e09e1f54..dc7a7d4dc 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/playerresponse/PlayerResponsePatch.kt @@ -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 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().asReversed() + val videoIdHooks = filterIsInstance().asReversed() + val afterVideoIdHooks = filterIsInstance().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 + } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt index 36cedcca0..c29deb6de 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt @@ -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 */ diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/general/VideoIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/general/VideoIdPatch.kt index a4bb06f4a..1f7d8d5da 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/general/VideoIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/videoid/general/VideoIdPatch.kt @@ -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")