diff --git a/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlResourcePatch.kt index c802399b..7720265e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/interaction/copyvideourl/CopyVideoUrlResourcePatch.kt @@ -14,8 +14,8 @@ import app.revanced.util.resources.ResourceUtils.mergeStrings @Patch( dependencies = [ - BottomControlsResourcePatch::class, - SettingsPatch::class + SettingsPatch::class, + BottomControlsResourcePatch::class ] ) object CopyVideoUrlResourcePatch : ResourcePatch() { diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt index e2462fda..235c0e3f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt @@ -51,7 +51,9 @@ object ReturnYouTubeDislikePatch : BytecodePatch( override fun execute(context: BytecodeContext) { // region Inject newVideoLoaded event handler to update dislikes when a new video is loaded. - VideoIdPatch.injectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V") + // This patch needs a few adjustments and lots of testing before it can change to the new video id hook. + // There's a few corner cases and some weirdness when loading new videos (specifically with detecting shorts). + VideoIdPatch.legacyInjectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V") // endregion diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch.kt index 877a7d6f..0fb4b265 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockBytecodePatch.kt @@ -94,9 +94,12 @@ object SponsorBlockBytecodePatch : BytecodePatch( } /* - * Set current video id + * Set current video id. + * + * The new video id hook seems to work without issues, + * but it's easier to keep using this hook as it's well tested and has no known problems. */ - VideoIdPatch.injectCallBackgroundPlay("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V") + VideoIdPatch.legacyInjectCallBackgroundPlay("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V") /* * Seekbar drawing diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt index 003f3a07..1f2eb399 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt @@ -9,26 +9,26 @@ import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen +import app.revanced.patches.shared.settings.preference.impl.StringResource +import app.revanced.patches.shared.settings.preference.impl.SwitchPreference import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch -import app.revanced.patches.youtube.video.information.VideoInformationPatch +import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( description = "Spoofs the signature to prevent playback issues.", dependencies = [ - SpoofSignatureResourcePatch::class, - IntegrationsPatch::class, + SettingsPatch::class, PlayerTypeHookPatch::class, - VideoInformationPatch::class, + PlayerResponseMethodHookPatch::class, ] ) object SpoofSignaturePatch : BytecodePatch( setOf( - ProtobufParameterBuilderFingerprint, PlayerResponseModelImplFingerprint, StoryboardThumbnailParentFingerprint, StoryboardRendererSpecFingerprint, @@ -39,29 +39,60 @@ object SpoofSignaturePatch : BytecodePatch( "Lapp/revanced/integrations/patches/spoof/SpoofSignaturePatch;" override fun execute(context: BytecodeContext) { - // Hook parameter. - ProtobufParameterBuilderFingerprint.result?.let { - val setParamMethod = context - .toMethodWalker(it.method) - .nextMethod(it.scanResult.patternScanResult!!.startIndex, true).getMethod() as MutableMethod - - setParamMethod.apply { - val protobufParameterRegister = 3 - - addInstructions( - 0, - """ - invoke-static {p$protobufParameterRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;)Ljava/lang/String; - move-result-object p$protobufParameterRegister - """ + SettingsPatch.PreferenceScreen.MISC.addPreferences( + PreferenceScreen( + key = "revanced_spoof_signature_verification", + title = StringResource( + "revanced_spoof_signature_verification_title", + "Spoof app signature" + ), + preferences = listOf( + SwitchPreference( + "revanced_spoof_signature_verification_enabled", + StringResource("revanced_spoof_signature_verification_enabled_title", "Spoof app signature"), + StringResource( + "revanced_spoof_signature_verification_enabled_summary_on", + "App signature spoofed\\n\\n" + + "Side effects include:\\n" + + "• Enhanced bitrate is not available\\n" + + "• Videos cannot be downloaded\\n" + + "• No seekbar thumbnails for paid or age restricted videos" + ), + StringResource( + "revanced_spoof_signature_verification_enabled_summary_off", + "App signature not spoofed\\n\\nVideo playback may not work" + ), + StringResource( + "revanced_spoof_signature_verification_enabled_user_dialog_message", + "Turning off this setting will cause video playback issues." + ) + ), + SwitchPreference( + "revanced_spoof_signature_in_feed_enabled", + StringResource("revanced_spoof_signature_in_feed_enabled_title", "Spoof app signature in feed"), + StringResource( + "revanced_spoof_signature_in_feed_enabled_summary_on", + "App signature spoofed\\n\\n" + + "Side effects include:\\n" + + "• Feed videos are missing subtitles\\n" + + "• Automatically played feed videos will show up in your watch history" + ), + StringResource( + "revanced_spoof_signature_in_feed_enabled_summary_off", + "App signature not spoofed for feed videos\n\n" + + "Feed videos will play for less than 1 minute before encountering playback issues" + ) + ) ) - } - } ?: throw ProtobufParameterBuilderFingerprint.exception + ) + ) - // When signature spoofing is enabled, the seekbar when tapped does not show - // the video time, chapter names, or the video thumbnail. - // Changing the value returned of this method forces all of these to show up, - // except the thumbnails are blank, which is handled with the patch below. + // Hook the player parameters. + PlayerResponseMethodHookPatch.injectProtoBufferHook("$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;)Ljava/lang/String;") + + // Force the seekbar thumbnails to show up. + // This is only required to show the seekbar time and chapters + // if the storyboard spec fetch fails. StoryboardThumbnailParentFingerprint.result?.classDef?.let { classDef -> StoryboardThumbnailFingerprint.also { it.resolve( @@ -89,58 +120,58 @@ object SpoofSignaturePatch : BytecodePatch( """ ) } ?: throw StoryboardThumbnailFingerprint.exception + } - /** - * Hook StoryBoard renderer url - */ - PlayerResponseModelImplFingerprint.result?.let { - it.mutableMethod.apply { - val getStoryBoardIndex = it.scanResult.patternScanResult!!.endIndex - val getStoryBoardRegister = getInstruction(getStoryBoardIndex).registerA + /** + * Hook StoryBoard renderer url + */ + PlayerResponseModelImplFingerprint.result?.let { + it.mutableMethod.apply { + val getStoryBoardIndex = it.scanResult.patternScanResult!!.endIndex + val getStoryBoardRegister = getInstruction(getStoryBoardIndex).registerA - addInstructions( - getStoryBoardIndex, - """ + addInstructions( + getStoryBoardIndex, + """ invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; move-result-object v$getStoryBoardRegister """ - ) - } - } ?: throw PlayerResponseModelImplFingerprint.exception + ) + } + } ?: throw PlayerResponseModelImplFingerprint.exception - StoryboardRendererSpecFingerprint.result?.let { - it.mutableMethod.apply { - val storyBoardUrlParams = 0 + StoryboardRendererSpecFingerprint.result?.let { + it.mutableMethod.apply { + val storyBoardUrlParams = 0 - addInstructionsWithLabels( - 0, - """ + addInstructionsWithLabels( + 0, + """ if-nez p$storyBoardUrlParams, :ignore invoke-static { p$storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; move-result-object p$storyBoardUrlParams """, - ExternalLabel("ignore", getInstruction(0)) - ) - } - } ?: throw StoryboardRendererSpecFingerprint.exception + ExternalLabel("ignore", getInstruction(0)) + ) + } + } ?: throw StoryboardRendererSpecFingerprint.exception - // Hook recommended value - StoryboardRendererInitFingerprint.result?.let { - val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex + // Hook recommended value + StoryboardRendererInitFingerprint.result?.let { + val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex - val originalValueRegister = it.mutableMethod - .getInstruction(moveOriginalRecommendedValueIndex).registerA + val originalValueRegister = it.mutableMethod + .getInstruction(moveOriginalRecommendedValueIndex).registerA - it.mutableMethod.apply { - addInstructions( - moveOriginalRecommendedValueIndex + 1, - """ + it.mutableMethod.apply { + addInstructions( + moveOriginalRecommendedValueIndex + 1, + """ invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I move-result v$originalValueRegister """ - ) - } - } ?: throw StoryboardRendererInitFingerprint.exception - } + ) + } + } ?: throw StoryboardRendererInitFingerprint.exception } -} \ No newline at end of file +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt index f89d9e00..e69de29b 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt @@ -1,68 +0,0 @@ -package app.revanced.patches.youtube.misc.fix.playback - -import app.revanced.patcher.data.ResourceContext -import app.revanced.patcher.patch.ResourcePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.shared.mapping.misc.ResourceMappingPatch -import app.revanced.patches.shared.settings.preference.impl.PreferenceScreen -import app.revanced.patches.shared.settings.preference.impl.StringResource -import app.revanced.patches.shared.settings.preference.impl.SwitchPreference -import app.revanced.patches.youtube.misc.settings.SettingsPatch - -@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class]) -object SpoofSignatureResourcePatch : ResourcePatch() { - internal var scrubbedPreviewThumbnailResourceId: Long = -1 - - override fun execute(context: ResourceContext) { - SettingsPatch.PreferenceScreen.MISC.addPreferences( - PreferenceScreen( - key = "revanced_spoof_signature_verification", - title = StringResource( - "revanced_spoof_signature_verification_title", - "Spoof app signature" - ), - preferences = listOf( - SwitchPreference( - "revanced_spoof_signature_verification_enabled", - StringResource("revanced_spoof_signature_verification_enabled_title", "Spoof app signature"), - StringResource( - "revanced_spoof_signature_verification_enabled_summary_on", - "App signature spoofed\\n\\n" - + "Side effects include:\\n" - + "• No ambient mode\\n" - + "• Videos cannot be downloaded" - ), - StringResource( - "revanced_spoof_signature_verification_enabled_summary_off", - "App signature not spoofed\\n\\nVideo playback may not work" - ), - StringResource( - "revanced_spoof_signature_verification_enabled_user_dialog_message", - "Turning off this setting will cause video playback issues." - ) - ), - SwitchPreference( - "revanced_spoof_signature_in_feed_enabled", - StringResource("revanced_spoof_signature_in_feed_enabled_title", "Spoof app signature in feed"), - StringResource( - "revanced_spoof_signature_in_feed_enabled_summary_on", - "App signature spoofed\\n\\n" - + "Side effects include:\\n" - + "• Feed videos are missing subtitles\\n" - + "• Automatically played feed videos will show up in your watch history" - ), - StringResource( - "revanced_spoof_signature_in_feed_enabled_summary_off", - "App signature not spoofed for feed videos\n\n" - + "Feed videos will play for less than 1 minute before encountering playback issues" - ) - ) - ) - ) - ) - - scrubbedPreviewThumbnailResourceId = ResourceMappingPatch.resourceMappings.single { - it.type == "id" && it.name == "thumbnail" - }.id - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt deleted file mode 100644 index 037485d5..00000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt +++ /dev/null @@ -1,13 +0,0 @@ -package app.revanced.patches.youtube.misc.fix.playback.fingerprints - -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import com.android.tools.smali.dexlib2.Opcode - -object ProtobufParameterBuilderFingerprint : MethodFingerprint( - opcodes = listOf( - Opcode.INVOKE_VIRTUAL_RANGE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IPUT_OBJECT - ), - strings = listOf("Unexpected empty videoId.", "Prefetch request are disabled.") -) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch.kt index 31b7d6d9..f3962f82 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch.kt @@ -23,6 +23,7 @@ object PlayerControlsBytecodePatch : BytecodePatch( private var moveToRegisterInstructionIndex: Int = 0 private var viewRegister: Int = 0 + private lateinit var inflateFingerprintResult: MethodFingerprintResult override fun execute(context: BytecodeContext) { LayoutConstructorFingerprint.result?.let { @@ -31,17 +32,13 @@ object PlayerControlsBytecodePatch : BytecodePatch( } ?: throw LayoutConstructorFingerprint.exception showPlayerControlsFingerprintResult = PlayerControlsVisibilityFingerprint.result!! - inflateFingerprintResult = BottomControlsInflateFingerprint.result!! - } - private var inflateFingerprintResult: MethodFingerprintResult? = null - set(fingerprint) { - field = fingerprint!!.also { - moveToRegisterInstructionIndex = it.scanResult.patternScanResult!!.endIndex - viewRegister = - (it.mutableMethod.implementation!!.instructions[moveToRegisterInstructionIndex] as OneRegisterInstruction).registerA - } + inflateFingerprintResult = BottomControlsInflateFingerprint.result!!.also { + moveToRegisterInstructionIndex = it.scanResult.patternScanResult!!.endIndex + viewRegister = + (it.mutableMethod.implementation!!.instructions[moveToRegisterInstructionIndex] as OneRegisterInstruction).registerA } + } /** * Injects the code to change the visibility of controls. diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt index a0e4f739..4e0a6970 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt @@ -111,9 +111,7 @@ object VideoInformationPatch : BytecodePatch( /* * Inject call for video id */ - val videoIdMethodDescriptor = "$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V" - VideoIdPatch.injectCall(videoIdMethodDescriptor) - VideoIdPatch.injectCallBackgroundPlay(videoIdMethodDescriptor) + VideoIdPatch.injectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V") /* * Set the video time method diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt new file mode 100644 index 00000000..53670c37 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt @@ -0,0 +1,72 @@ +package app.revanced.patches.youtube.video.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.patch.annotation.Patch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.misc.fix.playback.SpoofSignaturePatch +import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.video.playerresponse.fingerprint.PlayerParameterBuilderFingerprint +import app.revanced.patches.youtube.video.videoid.VideoIdPatch + +@Patch( + dependencies = [IntegrationsPatch::class], +) +object PlayerResponseMethodHookPatch : BytecodePatch( + setOf( + PlayerParameterBuilderFingerprint, + ) +) { + private const val playerResponseVideoIdParameter = 1 + private const val playerResponseProtoBufferParameter = 3 + /** + * Insert index when adding a video id hook. + */ + private var playerResponseVideoIdInsertIndex = 0 + /** + * Insert index when adding a proto buffer override. + * Must be after all video id hooks in the same method. + */ + private var playerResponseProtoBufferInsertIndex = 0 + private lateinit var playerResponseMethod: MutableMethod + + override fun execute(context: BytecodeContext) { + + // Hook player parameter. + PlayerParameterBuilderFingerprint.result?.let { + playerResponseMethod = it.mutableMethod + } ?: throw PlayerParameterBuilderFingerprint.exception + } + + /** + * Modify the player parameter proto buffer value. + * Used exclusively by [SpoofSignaturePatch]. + */ + fun injectProtoBufferHook(methodDescriptor: String) { + playerResponseMethod.addInstructions( + playerResponseProtoBufferInsertIndex, + """ + invoke-static {p$playerResponseProtoBufferParameter}, $methodDescriptor + move-result-object p$playerResponseProtoBufferParameter + """ + ) + playerResponseProtoBufferInsertIndex += 2 + } + + /** + * Used by [VideoIdPatch]. + */ + internal fun injectVideoIdHook(methodDescriptor: String) { + playerResponseMethod.addInstruction( + // Keep injection calls in the order they're added, + // and all video id hooks run before proto buffer hooks. + playerResponseVideoIdInsertIndex++, + "invoke-static {p$playerResponseVideoIdParameter}, $methodDescriptor" + ) + playerResponseProtoBufferInsertIndex++ + } +} + diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/fingerprint/PlayerParameterBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/fingerprint/PlayerParameterBuilderFingerprint.kt new file mode 100644 index 00000000..38f7fb32 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/fingerprint/PlayerParameterBuilderFingerprint.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.youtube.video.playerresponse.fingerprint + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +object PlayerParameterBuilderFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "L", + parameters = listOf( + "Ljava/lang/String;", // VideoId. + "[B", + "Ljava/lang/String;", // Player parameters proto buffer. + "Ljava/lang/String;", + "I", + "I", + "Ljava/util/Set;", + "Ljava/lang/String;", + "Ljava/lang/String;", + "L", + "Z", + "Z", + "Z" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt index 76721143..eccc4775 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt @@ -9,16 +9,21 @@ import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch +import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import app.revanced.patches.youtube.video.videoid.fingerprint.VideoIdFingerprint import app.revanced.patches.youtube.video.videoid.fingerprint.VideoIdFingerprintBackgroundPlay import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( description = "Hooks to detect when the video id changes", - dependencies = [IntegrationsPatch::class], + dependencies = [IntegrationsPatch::class, PlayerResponseMethodHookPatch::class], ) object VideoIdPatch : BytecodePatch( - setOf(VideoIdFingerprint, VideoIdFingerprintBackgroundPlay) + setOf( + VideoIdFingerprint, + VideoIdFingerprintBackgroundPlay + ) ) { private var videoIdRegister = 0 private var insertIndex = 0 @@ -29,6 +34,7 @@ object VideoIdPatch : BytecodePatch( private lateinit var backgroundPlaybackMethod: MutableMethod override fun execute(context: BytecodeContext) { + /** * Supplies the method and register index of the video id register. * @@ -45,10 +51,10 @@ object VideoIdPatch : BytecodePatch( } } ?: throw VideoIdFingerprint.exception - VideoIdFingerprint.setFields { method, insertIndex, videoIdRegister -> + VideoIdFingerprint.setFields { method, index, register -> insertMethod = method - VideoIdPatch.insertIndex = insertIndex - VideoIdPatch.videoIdRegister = videoIdRegister + insertIndex = index + videoIdRegister = register } VideoIdFingerprintBackgroundPlay.setFields { method, insertIndex, videoIdRegister -> @@ -58,42 +64,53 @@ object VideoIdPatch : BytecodePatch( } } + /** + * Adds an invoke-static instruction, called with the new id when the video changes. + * + * Called as soon as the player response is parsed, and called before many other hooks are + * updated such as [PlayerTypeHookPatch]. + * + * Supports all videos and functions in all situations. + * + * Be aware, this can be called multiple times for the same video id. + * + * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` + */ + fun injectCall(methodDescriptor: String) = PlayerResponseMethodHookPatch.injectVideoIdHook(methodDescriptor) - /** - * Adds an invoke-static instruction, called with the new id when the video changes. - * - * Supports all videos (regular videos, Shorts and Stories). - * - * _Does not function if playing in the background with no video visible_. - * - * Be aware, this can be called multiple times for the same video id. - * - * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` - */ - fun injectCall( - methodDescriptor: String - ) = insertMethod.addInstruction( - // Keep injection calls in the order they're added: - // Increment index. So if additional injection calls are added, those calls run after this injection call. - insertIndex++, - "invoke-static {v$videoIdRegister}, $methodDescriptor" - ) + /** + * Adds an invoke-static instruction, called with the new id when the video changes. + * + * Supports all videos (regular videos and Shorts). + * + * _Does not function if playing in the background with no video visible_. + * + * Be aware, this can be called multiple times for the same video id. + * + * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` + */ + fun legacyInjectCall( + methodDescriptor: String + ) = insertMethod.addInstruction( + insertIndex++, + "invoke-static {v$videoIdRegister}, $methodDescriptor" + ) - /** - * Alternate hook that supports only regular videos, but hook supports changing to new video - * during background play when no video is visible. - * - * _Does not support Shorts or Stories_. - * - * Be aware, the hook can be called multiple times for the same video id. - * - * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` - */ - fun injectCallBackgroundPlay( - methodDescriptor: String - ) = backgroundPlaybackMethod.addInstruction( - backgroundPlaybackInsertIndex++, // move-result-object offset - "invoke-static {v$backgroundPlaybackVideoIdRegister}, $methodDescriptor" - ) + /** + * Alternate hook that supports only regular videos, but hook supports changing to new video + * during background play when no video is visible. + * + * _Does not support Shorts_. + * + * Be aware, the hook can be called multiple times for the same video id. + * + * @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;` + */ + fun legacyInjectCallBackgroundPlay( + methodDescriptor: String + ) = backgroundPlaybackMethod.addInstruction( + backgroundPlaybackInsertIndex++, // move-result-object offset + "invoke-static {v$backgroundPlaybackVideoIdRegister}, $methodDescriptor" + ) }