diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/bytecode/patch/SponsorBlockBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/bytecode/patch/SponsorBlockBytecodePatch.kt index 0a75529e3..b7e775ca2 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/bytecode/patch/SponsorBlockBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock/bytecode/patch/SponsorBlockBytecodePatch.kt @@ -81,7 +81,7 @@ class SponsorBlockBytecodePatch : BytecodePatch( /* Set current video id */ - VideoIdPatch.injectCall("$INTEGRATIONS_PLAYER_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V") + VideoIdPatch.injectCallBackgroundPlay("$INTEGRATIONS_PLAYER_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V") /* Seekbar drawing diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/video/information/patch/VideoInformationPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/video/information/patch/VideoInformationPatch.kt index 99708eb49..83a9333a0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/video/information/patch/VideoInformationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/video/information/patch/VideoInformationPatch.kt @@ -98,7 +98,9 @@ class VideoInformationPatch : BytecodePatch( /* Inject call for video id */ - VideoIdPatch.injectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V") + val videoIdMethodDescriptor = "$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V" + VideoIdPatch.injectCall(videoIdMethodDescriptor) + VideoIdPatch.injectCallBackgroundPlay(videoIdMethodDescriptor) /* Set the video time method diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/fingerprint/VideoIdFingerprintBackgroundPlay.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/fingerprint/VideoIdFingerprintBackgroundPlay.kt new file mode 100644 index 000000000..44c14a2c2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/fingerprint/VideoIdFingerprintBackgroundPlay.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.misc.video.videoid.fingerprint + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +object VideoIdFingerprintBackgroundPlay : MethodFingerprint( + returnType = "V", + access = AccessFlags.DECLARED_SYNCHRONIZED or AccessFlags.FINAL or AccessFlags.PUBLIC, + parameters = listOf("L"), + opcodes = listOf(Opcode.INVOKE_INTERFACE), + customFingerprint = { + it.definingClass.endsWith("PlaybackLifecycleMonitor;") + } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/patch/VideoIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/patch/VideoIdPatch.kt index 5227d9748..bf961de7b 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/patch/VideoIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/video/videoid/patch/VideoIdPatch.kt @@ -15,6 +15,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.video.videoid.annotation.VideoIdCompatibility import app.revanced.patches.youtube.misc.video.videoid.fingerprint.VideoIdFingerprint +import app.revanced.patches.youtube.misc.video.videoid.fingerprint.VideoIdFingerprintBackgroundPlay import org.jf.dexlib2.iface.instruction.OneRegisterInstruction @Name("video-id-hook") @@ -23,30 +24,50 @@ import org.jf.dexlib2.iface.instruction.OneRegisterInstruction @Version("0.0.1") @DependsOn([IntegrationsPatch::class]) class VideoIdPatch : BytecodePatch( - listOf(VideoIdFingerprint) + listOf(VideoIdFingerprint, VideoIdFingerprintBackgroundPlay) ) { override fun execute(context: BytecodeContext): PatchResult { - VideoIdFingerprint.result?.let { - val videoIdRegisterInstructionIndex = it.scanResult.patternScanResult!!.endIndex + VideoIdFingerprint.result?.let { result -> + val videoIdRegisterInstructionIndex = result.scanResult.patternScanResult!!.endIndex - with(it.mutableMethod) { - insertMethod = this + result.mutableMethod.also { + insertMethod = it + }.apply { videoIdRegister = (instruction(videoIdRegisterInstructionIndex) as OneRegisterInstruction).registerA insertIndex = videoIdRegisterInstructionIndex + 1 } } ?: return VideoIdFingerprint.toErrorResult() + VideoIdFingerprintBackgroundPlay.result?.let { result -> + val endIndex = result.scanResult.patternScanResult!!.endIndex + + result.mutableMethod.also { + backgroundPlaybackMethod = it + }.apply { + backgroundPlaybackVideoIdRegister = (instruction(endIndex + 1) as OneRegisterInstruction).registerA + backgroundPlaybackInsertIndex = endIndex + 2 + } + } ?: return VideoIdFingerprintBackgroundPlay.toErrorResult() + return PatchResultSuccess() } companion object { private var videoIdRegister = 0 private var insertIndex = 0 - private lateinit var insertMethod: MutableMethod + private var backgroundPlaybackVideoIdRegister = 0 + private var backgroundPlaybackInsertIndex = 0 + private lateinit var backgroundPlaybackMethod: MutableMethod + /** * 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;` @@ -54,12 +75,28 @@ class VideoIdPatch : BytecodePatch( fun injectCall( methodDescriptor: String ) = insertMethod.addInstructions( - // Keep injection calls in the order they're added. - // Order has been proven to be important for the same reason that order of patch execution is important - // such as for the VideoInformation patch. + // 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" ) + + /** + * 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.addInstructions( + backgroundPlaybackInsertIndex++, // move-result-object offset + "invoke-static {v$backgroundPlaybackVideoIdRegister}, $methodDescriptor" + ) } }