diff --git a/src/main/kotlin/app/revanced/patches/youtube/general/autocaptions/AutoCaptionsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/general/autocaptions/AutoCaptionsPatch.kt index a3054702f..84ac32968 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/general/autocaptions/AutoCaptionsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/general/autocaptions/AutoCaptionsPatch.kt @@ -4,7 +4,7 @@ import app.revanced.patcher.data.BytecodeContext import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.integrations.Constants.GENERAL_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.settings.SettingsPatch -import app.revanced.patches.youtube.video.videoid.VideoIdPatch +import app.revanced.patches.youtube.video.information.VideoInformationPatch import app.revanced.util.patch.BaseBytecodePatch @Suppress("unused") @@ -14,13 +14,13 @@ object AutoCaptionsPatch : BaseBytecodePatch( dependencies = setOf( AutoCaptionsBytecodePatch::class, SettingsPatch::class, - VideoIdPatch::class + VideoInformationPatch::class ), compatiblePackages = COMPATIBLE_PACKAGE ) { override fun execute(context: BytecodeContext) { - VideoIdPatch.hookBackgroundPlayVideoId("$GENERAL_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V") + VideoInformationPatch.hookBackgroundPlay("$GENERAL_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") /** * Add settings 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 35e74a2d4..d200e6bc3 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 @@ -53,7 +53,7 @@ object SpoofPlayerParameterPatch : BaseBytecodePatch( // Hook the player parameters. PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameter( - "$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;" + "$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;" ) // Force the seekbar time and chapters to always show up. diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt index 5e3266a3c..b63570438 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/SharedResourceIdPatch.kt @@ -66,6 +66,7 @@ object SharedResourceIdPatch : ResourcePatch() { var ModernMiniPlayerForwardButton = -1L var ModernMiniPlayerRewindButton = -1L var MusicAppDeeplinkButtonView = -1L + var NotificationBigPictureIconWidth = -1L var PanelSubHeader = -1L var PlayerCollapseButton = -1L var PosterArtWidthDefault = -1L @@ -152,6 +153,7 @@ object SharedResourceIdPatch : ResourcePatch() { ModernMiniPlayerForwardButton = getId(ID, "modern_miniplayer_forward_button") ModernMiniPlayerRewindButton = getId(ID, "modern_miniplayer_rewind_button") MusicAppDeeplinkButtonView = getId(ID, "music_app_deeplink_button_view") + NotificationBigPictureIconWidth = getId(DIMEN, "notification_big_picture_icon_width") PanelSubHeader = getId(ID, "panel_subheader") PlayerCollapseButton = getId(ID, "player_collapse_button") PosterArtWidthDefault = getId(DIMEN, "poster_art_width_default") diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/sponsorblock/SponsorBlockBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/sponsorblock/SponsorBlockBytecodePatch.kt index 8c8639501..359a7e376 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/sponsorblock/SponsorBlockBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/sponsorblock/SponsorBlockBytecodePatch.kt @@ -18,7 +18,6 @@ import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.Inset import app.revanced.patches.youtube.utils.sponsorblock.fingerprints.RectangleFieldInvalidatorFingerprint import app.revanced.patches.youtube.utils.sponsorblock.fingerprints.SegmentPlaybackControllerFingerprint import app.revanced.patches.youtube.video.information.VideoInformationPatch -import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.util.getTargetIndex import app.revanced.util.getTargetIndexWithFieldReferenceTypeReversed import app.revanced.util.getTargetIndexWithMethodReferenceName @@ -35,7 +34,6 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference dependencies = [ PlayerControlsPatch::class, SharedResourceIdPatch::class, - VideoIdPatch::class, VideoInformationPatch::class ] ) @@ -173,6 +171,6 @@ object SponsorBlockBytecodePatch : BytecodePatch( ) // Set current video id - VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V") + VideoInformationPatch.hook("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") } } \ No newline at end of file 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 cb3150163..a535eeee0 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 @@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.Patch @@ -18,18 +19,27 @@ import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint import app.revanced.patches.youtube.utils.integrations.Constants.SHARED_PATH import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch +import app.revanced.patches.youtube.video.information.fingerprints.ChannelIdFingerprint +import app.revanced.patches.youtube.video.information.fingerprints.ChannelNameFingerprint import app.revanced.patches.youtube.video.information.fingerprints.OnPlaybackSpeedItemClickFingerprint +import app.revanced.patches.youtube.video.information.fingerprints.PlaybackInitializationFingerprint import app.revanced.patches.youtube.video.information.fingerprints.PlaybackSpeedClassFingerprint -import app.revanced.patches.youtube.video.information.fingerprints.PlayerControllerSetTimeReferenceFingerprint +import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprint +import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprintBackgroundPlay +import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprintShorts import app.revanced.patches.youtube.video.information.fingerprints.VideoLengthFingerprint import app.revanced.patches.youtube.video.information.fingerprints.VideoQualityListFingerprint import app.revanced.patches.youtube.video.information.fingerprints.VideoQualityTextFingerprint +import app.revanced.patches.youtube.video.information.fingerprints.VideoTimeFingerprint +import app.revanced.patches.youtube.video.information.fingerprints.VideoTitleFingerprint import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.util.addFieldAndInstructions +import app.revanced.util.getReference import app.revanced.util.getTargetIndex import app.revanced.util.getTargetIndexReversed import app.revanced.util.getWalkerMethod +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -37,6 +47,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.immutable.ImmutableMethod import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter @@ -53,27 +64,54 @@ import com.android.tools.smali.dexlib2.util.MethodUtil ) object VideoInformationPatch : BytecodePatch( setOf( + ChannelIdFingerprint, + ChannelNameFingerprint, OnPlaybackSpeedItemClickFingerprint, OrganicPlaybackContextModelFingerprint, + PlaybackInitializationFingerprint, PlaybackSpeedClassFingerprint, - PlayerControllerSetTimeReferenceFingerprint, VideoEndFingerprint, + VideoIdFingerprint, + VideoIdFingerprintBackgroundPlay, + VideoIdFingerprintShorts, VideoLengthFingerprint, VideoQualityListFingerprint, - VideoQualityTextFingerprint + VideoQualityTextFingerprint, + VideoTitleFingerprint, ) ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = "$SHARED_PATH/VideoInformation;" + private const val PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR = + "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;" + + private const val REGISTER_PLAYER_RESPONSE_MODEL = 8 + + private const val REGISTER_CHANNEL_ID = 0 + private const val REGISTER_CHANNEL_NAME = 1 + private const val REGISTER_VIDEO_ID = 2 + private const val REGISTER_VIDEO_TITLE = 3 + private const val REGISTER_VIDEO_LENGTH = 4 + private const val REGISTER_VIDEO_LENGTH_DUMMY = 5 + private const val REGISTER_VIDEO_IS_LIVE = 6 + + private lateinit var channelIdMethodCall: String + private lateinit var channelNameMethodCall: String + private lateinit var videoIdMethodCall: String + private lateinit var videoTitleMethodCall: String + private lateinit var videoLengthMethodCall: String + private lateinit var videoIsLiveMethodCall: String + + private lateinit var videoInformationMethod: MutableMethod + private lateinit var backgroundVideoInformationMethod: MutableMethod + private lateinit var shortsVideoInformationMethod: MutableMethod + private lateinit var playerConstructorMethod: MutableMethod private var playerConstructorInsertIndex = 4 - private lateinit var videoTimeConstructorMethod: MutableMethod - private var videoTimeConstructorInsertIndex = 2 - - private lateinit var videoCpnConstructorMethod: MutableMethod - private var videoCpnConstructorInsertIndex = 2 + private lateinit var videoTimeMethod: MutableMethod + private var videoTimeIndex = 1 // Used by other patches. internal lateinit var speedSelectionInsertMethod: MutableMethod @@ -83,6 +121,10 @@ object VideoInformationPatch : BytecodePatch( val videoInformationMutableClass = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!.mutableClass VideoEndFingerprint.resultOrThrow().let { + + // resolve video time fingerprint + VideoTimeFingerprint.resolve(context, it.classDef) + playerConstructorMethod = it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) } @@ -149,12 +191,84 @@ object VideoInformationPatch : BytecodePatch( } } + /** + * Set current video information + */ + channelIdMethodCall = ChannelIdFingerprint.getMethodName("Ljava/lang/String;") + channelNameMethodCall = ChannelNameFingerprint.getMethodName("Ljava/lang/String;") + videoIdMethodCall = VideoIdFingerprint.getMethodName("Ljava/lang/String;") + videoTitleMethodCall = VideoTitleFingerprint.getMethodName("Ljava/lang/String;") + videoLengthMethodCall = VideoLengthFingerprint.getMethodName("J") + videoIsLiveMethodCall = ChannelIdFingerprint.getMethodName("Z") + + PlaybackInitializationFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val targetIndex = indexOfFirstInstruction { + opcode == Opcode.INVOKE_DIRECT + && getReference()?.returnType == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR + } + 1 + if (targetIndex == 0) throw PatchException("Could not find instruction index.") + val targetRegister = getInstruction(targetIndex).registerA + + addInstruction( + targetIndex + 1, + "invoke-direct {p0, v$targetRegister}, $definingClass->setVideoInformation($PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR)V" + ) + + videoInformationMethod = getVideoInformationMethod() + it.mutableClass.methods.add(videoInformationMethod) + + hook("$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoInformation(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") + } + } + + VideoIdFingerprintBackgroundPlay.resultOrThrow().let { + it.mutableMethod.apply { + val targetIndex = indexOfFirstInstruction { + opcode == Opcode.INVOKE_INTERFACE + && getReference()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR + } + if (targetIndex < 0) throw PatchException("Could not find instruction index.") + val targetRegister = getInstruction(targetIndex).registerC + + addInstruction( + targetIndex, + "invoke-direct {p0, v$targetRegister}, $definingClass->setVideoInformation($PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR)V" + ) + + backgroundVideoInformationMethod = getVideoInformationMethod() + it.mutableClass.methods.add(backgroundVideoInformationMethod) + } + } + + VideoIdFingerprintShorts.resultOrThrow().let { + it.mutableMethod.apply { + val targetIndex = indexOfFirstInstruction { + opcode == Opcode.INVOKE_INTERFACE + && getReference()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR + } + if (targetIndex < 0) throw PatchException("Could not find instruction index.") + val targetRegister = getInstruction(targetIndex).registerC + + addInstruction( + targetIndex, + "invoke-direct {p0, v$targetRegister}, $definingClass->setVideoInformation($PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR)V" + ) + + shortsVideoInformationMethod = getVideoInformationMethod() + it.mutableClass.methods.add(shortsVideoInformationMethod) + } + } + /** * Set current video time method */ - PlayerControllerSetTimeReferenceFingerprint.resultOrThrow().let { - videoTimeConstructorMethod = - it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex) + VideoTimeFingerprint.resultOrThrow().mutableMethod.apply { + videoTimeMethod = this + addInstruction( + 0, + "move-wide/from16 v0, p5" + ) } /** @@ -162,40 +276,15 @@ object VideoInformationPatch : BytecodePatch( */ videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime") - /** - * Set current video length - */ - VideoLengthFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val startIndex = it.scanResult.patternScanResult!!.startIndex - val primaryRegister = getInstruction(startIndex).registerA - val secondaryRegister = primaryRegister + 1 - - addInstruction( - startIndex + 2, - "invoke-static {v$primaryRegister, v$secondaryRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V" - ) - } - } - - /** - * Set current video is livestream - */ - videoCpnConstructorMethod = OrganicPlaybackContextModelFingerprint.resultOrThrow().mutableMethod - cpnHook("$INTEGRATIONS_CLASS_DESCRIPTOR->setLiveStreamState(Ljava/lang/String;Z)V") - /** * Set current video id */ - val videoIdMethodDescriptor = "$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V" - VideoIdPatch.hookVideoId(videoIdMethodDescriptor) - VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor) VideoIdPatch.hookPlayerResponseVideoId( "$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. PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameterBeforeVideoId( - "$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;") + "$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseParameter(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;") /** * Hook current playback speed @@ -346,12 +435,6 @@ object VideoInformationPatch : BytecodePatch( } } - private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) = - addInstruction(insertIndex, "invoke-static { $register }, $descriptor") - - private fun MutableMethod.insertTimeHook(insertIndex: Int, descriptor: String) = - insert(insertIndex, "p1, p2", descriptor) - /** * Hook the player controller. Called when a video is opened or the current video is changed. * @@ -375,15 +458,97 @@ object VideoInformationPatch : BytecodePatch( * @param targetMethodName The name of the static method to invoke when the player controller is created. */ internal fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = - videoTimeConstructorMethod.insertTimeHook( - videoTimeConstructorInsertIndex++, - "$targetMethodClass->$targetMethodName(J)V" + videoTimeMethod.addInstruction( + videoTimeIndex++, + "invoke-static { v0, v1 }, $targetMethodClass->$targetMethodName(J)V" ) - internal fun cpnHook(descriptor: String) = - videoCpnConstructorMethod.insert( - videoCpnConstructorInsertIndex++, - "p1, p2", - descriptor - ) + private fun MethodFingerprint.getMethodName(returnType : String) :String { + resultOrThrow().mutableMethod.apply { + val targetIndex = indexOfFirstInstruction { + opcode == Opcode.INVOKE_INTERFACE + && getReference()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR + && getReference()?.returnType == returnType + } + if (targetIndex < 0) throw PatchException("Could not find instruction index.") + val targetReference = getInstruction(targetIndex).reference + + return "invoke-interface {v${REGISTER_PLAYER_RESPONSE_MODEL}}, $targetReference" + } + } + + private fun MutableMethod.getVideoInformationMethod(): MutableMethod = + ImmutableMethod( + definingClass, + "setVideoInformation", + listOf(ImmutableMethodParameter(PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR, annotations, null)), + "V", + AccessFlags.PRIVATE or AccessFlags.FINAL, + annotations, + null, + ImmutableMethodImplementation( + 9, """ + $channelIdMethodCall + move-result-object v$REGISTER_CHANNEL_ID + $channelNameMethodCall + move-result-object v$REGISTER_CHANNEL_NAME + $videoIdMethodCall + move-result-object v$REGISTER_VIDEO_ID + $videoTitleMethodCall + move-result-object v$REGISTER_VIDEO_TITLE + $videoLengthMethodCall + move-result-wide v$REGISTER_VIDEO_LENGTH + $videoIsLiveMethodCall + move-result v$REGISTER_VIDEO_IS_LIVE + return-void + """.toInstructions(), + null, + null + ) + ).toMutable() + + private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) = + addInstruction(insertIndex, "invoke-static/range { $register }, $descriptor") + + /** + * This method is invoked on both regular videos and Shorts. + */ + internal fun hook(descriptor: String) = + videoInformationMethod.apply { + val index = implementation!!.instructions.size - 1 + + insert( + index, + "v${REGISTER_CHANNEL_ID} .. v${REGISTER_VIDEO_IS_LIVE}", + descriptor + ) + } + + /** + * This method is invoked only in regular videos. + */ + internal fun hookBackgroundPlay(descriptor: String) = + backgroundVideoInformationMethod.apply { + val index = implementation!!.instructions.size - 1 + + insert( + index, + "v${REGISTER_CHANNEL_ID} .. v${REGISTER_VIDEO_IS_LIVE}", + descriptor + ) + } + + /** + * This method is invoked only in shorts videos. + */ + internal fun hookShorts(descriptor: String) = + shortsVideoInformationMethod.apply { + val index = implementation!!.instructions.size - 1 + + insert( + index, + "v${REGISTER_CHANNEL_ID} .. v${REGISTER_VIDEO_IS_LIVE}", + descriptor + ) + } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/ChannelIdFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/ChannelIdFingerprint.kt new file mode 100644 index 000000000..928aaf4b3 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/ChannelIdFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object ChannelIdFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Ljava/lang/Object;"), + strings = listOf("com.google.android.apps.youtube.mdx.watch.LAST_MEALBAR_PROMOTED_LIVE_FEED_CHANNELS") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/ChannelNameFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/ChannelNameFingerprint.kt new file mode 100644 index 000000000..7f2f928cb --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/ChannelNameFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object ChannelNameFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("L"), + strings = listOf( + "setMetadata may only be called once", + "Person", + ) +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlaybackInitializationFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlaybackInitializationFingerprint.kt new file mode 100644 index 000000000..39b05a5d2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlaybackInitializationFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object PlaybackInitializationFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + strings = listOf("play() called when the player wasn\'t loaded.") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlayerControllerSetTimeReferenceFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlayerControllerSetTimeReferenceFingerprint.kt deleted file mode 100644 index 0e6c57de5..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/PlayerControllerSetTimeReferenceFingerprint.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.patches.youtube.video.information.fingerprints - -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.Opcode - -internal object PlayerControllerSetTimeReferenceFingerprint : MethodFingerprint( - opcodes = listOf( - Opcode.INVOKE_DIRECT_RANGE, - Opcode.IGET_OBJECT - ), - strings = listOf("Media progress reported outside media playback: ") -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprint.kt new file mode 100644 index 000000000..c69648f99 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object VideoIdFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + strings = listOf("Failed to download video (IllegalStateException): %s") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/videoid/fingerprints/VideoIdFingerprintBackgroundPlay.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprintBackgroundPlay.kt similarity index 92% rename from src/main/kotlin/app/revanced/patches/youtube/video/videoid/fingerprints/VideoIdFingerprintBackgroundPlay.kt rename to src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprintBackgroundPlay.kt index 643b25562..0b360f7cd 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/videoid/fingerprints/VideoIdFingerprintBackgroundPlay.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprintBackgroundPlay.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.video.videoid.fingerprints +package app.revanced.patches.youtube.video.information.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprintShorts.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprintShorts.kt new file mode 100644 index 000000000..a306de9b4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoIdFingerprintShorts.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.util.fingerprint.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.Opcode + +/** + * This fingerprint is compatible with all versions of YouTube starting from v18.29.38 to supported versions. + * This method is invoked only in Shorts. + * Accurate video information is invoked even when the user moves Shorts upward or downward. + */ +internal object VideoIdFingerprintShorts : LiteralValueFingerprint( + returnType = "V", + parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"), + opcodes = listOf( + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT + ), + literalSupplier = { 45365621 } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoLengthFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoLengthFingerprint.kt index db2d60174..34b4eb74a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoLengthFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoLengthFingerprint.kt @@ -1,18 +1,11 @@ package app.revanced.patches.youtube.video.information.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.util.fingerprint.LiteralValueFingerprint +import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -internal object VideoLengthFingerprint : LiteralValueFingerprint( +internal object VideoLengthFingerprint : MethodFingerprint( returnType = "V", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - parameters = emptyList(), - opcodes = listOf( - Opcode.MOVE_RESULT_WIDE, - Opcode.CONST_4, - Opcode.INVOKE_VIRTUAL - ), - literalSupplier = { 45388753 } -) \ No newline at end of file + strings = listOf("Gaplessly transitioning away from an Ad before it ends.") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoTimeFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoTimeFingerprint.kt new file mode 100644 index 000000000..19cf177a0 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoTimeFingerprint.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object VideoTimeFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("L", "I", "J", "J", "J", "J") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoTitleFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoTitleFingerprint.kt new file mode 100644 index 000000000..d22a96998 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/VideoTitleFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.video.information.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.NotificationBigPictureIconWidth +import app.revanced.util.fingerprint.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object VideoTitleFingerprint : LiteralValueFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + literalSupplier = { NotificationBigPictureIconWidth } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt index b2e1c0c21..24667ccdf 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/playback/VideoPlaybackPatch.kt @@ -67,7 +67,6 @@ object VideoPlaybackPatch : BaseBytecodePatch( DeviceDimensionsModelToStringFingerprint, HDRCapabilityFingerprint, PlaybackSpeedChangedFromRecyclerViewFingerprint, - PlaybackSpeedInitializeFingerprint, QualityChangedFromRecyclerViewFingerprint, QualityMenuViewInflateFingerprint, QualitySetterFingerprint, @@ -163,7 +162,7 @@ object VideoPlaybackPatch : BaseBytecodePatch( } } - VideoInformationPatch.cpnHook("$INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Z)V") + VideoInformationPatch.hookBackgroundPlay("$INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") context.updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "RememberPlaybackSpeed") @@ -195,8 +194,8 @@ object VideoPlaybackPatch : BaseBytecodePatch( } ?: throw PatchException("Failed to find onItemClick method") } - VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V") - VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V") + VideoInformationPatch.hook("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") + VideoInformationPatch.hookBackgroundPlay("$INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V") // endregion @@ -212,7 +211,7 @@ object VideoPlaybackPatch : BaseBytecodePatch( addInstruction( insertIndex + 1, "invoke-static { v$insertRegister }, " + - "$INTEGRATIONS_RESTORE_OLD_VIDEO_QUALITY_MENU_CLASS_DESCRIPTOR->showOldVideoQualityMenu(Landroid/widget/ListView;)V" + "$INTEGRATIONS_RESTORE_OLD_VIDEO_QUALITY_MENU_CLASS_DESCRIPTOR->restoreOldVideoQualityMenu(Landroid/widget/ListView;)V" ) } val onItemClickMethod = @@ -229,7 +228,7 @@ object VideoPlaybackPatch : BaseBytecodePatch( addInstructionsWithLabels( insertIndex, """ - invoke-static {}, $INTEGRATIONS_RESTORE_OLD_VIDEO_QUALITY_MENU_CLASS_DESCRIPTOR->showOldVideoQualityMenu()Z + invoke-static {}, $INTEGRATIONS_RESTORE_OLD_VIDEO_QUALITY_MENU_CLASS_DESCRIPTOR->restoreOldVideoQualityMenu()Z move-result v$insertRegister if-nez v$insertRegister, :show """, ExternalLabel("show", getInstruction(jumpIndex)) 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 index 3d79f2073..e8678f727 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt @@ -57,12 +57,12 @@ object PlayerResponseMethodHookPatch : val instruction = if (shouldApplyNewMethod) """ - invoke-static {v$PARAMETER_VIDEO_ID, v$PARAMETER_PLAYER_PARAMETER, v$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook + invoke-static {v$PARAMETER_VIDEO_ID, v$PARAMETER_PLAYER_PARAMETER, v$PARAMETER_PLAYLIST_ID, v$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook move-result-object p3 """ else """ - invoke-static {p$PARAMETER_VIDEO_ID, p$PARAMETER_PLAYER_PARAMETER, p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook + invoke-static {p$PARAMETER_VIDEO_ID, p$PARAMETER_PLAYER_PARAMETER, p$PARAMETER_PLAYLIST_ID, p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook move-result-object p$PARAMETER_PLAYER_PARAMETER """ 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 e9805328d..a9a8e3681 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 @@ -10,11 +10,8 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdFingerprint -import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdFingerprintBackgroundPlay import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdParentFingerprint -import app.revanced.util.getTargetIndex import app.revanced.util.resultOrThrow -import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( @@ -22,19 +19,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction dependencies = [PlayerResponseMethodHookPatch::class], ) object VideoIdPatch : BytecodePatch( - setOf( - VideoIdParentFingerprint, - VideoIdFingerprintBackgroundPlay - ) + setOf(VideoIdParentFingerprint) ) { private var videoIdRegister = 0 private var videoIdInsertIndex = 0 private lateinit var videoIdMethod: MutableMethod - private var backgroundPlaybackVideoIdRegister = 0 - private var backgroundPlaybackInsertIndex = 0 - private lateinit var backgroundPlaybackMethod: MutableMethod - override fun execute(context: BytecodeContext) { /** @@ -59,14 +49,6 @@ object VideoIdPatch : BytecodePatch( videoIdInsertIndex = index videoIdRegister = register } - - VideoIdFingerprintBackgroundPlay.resultOrThrow().let { - it.mutableMethod.apply { - backgroundPlaybackMethod = this - backgroundPlaybackInsertIndex = getTargetIndex(Opcode.INVOKE_INTERFACE) + 2 - backgroundPlaybackVideoIdRegister = getInstruction(backgroundPlaybackInsertIndex - 1).registerA - } - } } /** @@ -87,23 +69,6 @@ object VideoIdPatch : BytecodePatch( "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_. - * - * 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 hookBackgroundPlayVideoId( - methodDescriptor: String - ) = backgroundPlaybackMethod.addInstruction( - backgroundPlaybackInsertIndex++, // move-result-object offset - "invoke-static {v$backgroundPlaybackVideoIdRegister}, $methodDescriptor" - ) - /** * Hooks the video id of every video when loaded. * Supports all videos and functions in all situations. @@ -120,8 +85,7 @@ object VideoIdPatch : BytecodePatch( * It's common for multiple Shorts to load at once in preparation * for the user swiping to the next Short. * - * For most use cases, you probably want to use - * [hookVideoId] or [hookBackgroundPlayVideoId] instead. + * For most use cases, you probably want to use [hookVideoId] instead. * * Be aware, this can be called multiple times for the same video id. * diff --git a/src/main/resources/youtube/settings/host/values/strings.xml b/src/main/resources/youtube/settings/host/values/strings.xml index 9ba0d62be..1e90be4db 100644 --- a/src/main/resources/youtube/settings/host/values/strings.xml +++ b/src/main/resources/youtube/settings/host/values/strings.xml @@ -1041,7 +1041,7 @@ Limitation: Official headers in search results will be hidden." Skip preloaded buffer "Skip preloaded buffer at video start to bypass default video quality enforcement delay. -• When the video starts, there is a delay of approximately 0.7 seconds, but the default video quality is applied immediately. +• When the video starts, there is a delay of approximately 0.3 seconds, but the default video quality is applied immediately. • Does not apply to HDR videos, live stream videos, videos shorter than 15 seconds." Show a toast when skipped Toast is shown.