refactor(YouTube/Video information): change invoke method

- Different methods are invoked depending on whether the video being played is a regular video or Shorts.
- The following information is invoked in VideoInformation: Channel Id, Channel Name, Video Title, Video Length, LiveStream, Playlist Id.
- Also, video time is invoked every 100ms, not 1000ms.
This commit is contained in:
inotia00 2024-04-30 23:23:56 +09:00
parent a71d1870c4
commit e5c180ba84
19 changed files with 333 additions and 130 deletions

View File

@ -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.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.GENERAL_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.integrations.Constants.GENERAL_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.settings.SettingsPatch 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 import app.revanced.util.patch.BaseBytecodePatch
@Suppress("unused") @Suppress("unused")
@ -14,13 +14,13 @@ object AutoCaptionsPatch : BaseBytecodePatch(
dependencies = setOf( dependencies = setOf(
AutoCaptionsBytecodePatch::class, AutoCaptionsBytecodePatch::class,
SettingsPatch::class, SettingsPatch::class,
VideoIdPatch::class VideoInformationPatch::class
), ),
compatiblePackages = COMPATIBLE_PACKAGE compatiblePackages = COMPATIBLE_PACKAGE
) { ) {
override fun execute(context: BytecodeContext) { 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 * Add settings

View File

@ -53,7 +53,7 @@ object SpoofPlayerParameterPatch : BaseBytecodePatch(
// Hook the player parameters. // Hook the player parameters.
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameter( 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. // Force the seekbar time and chapters to always show up.

View File

@ -66,6 +66,7 @@ object SharedResourceIdPatch : ResourcePatch() {
var ModernMiniPlayerForwardButton = -1L var ModernMiniPlayerForwardButton = -1L
var ModernMiniPlayerRewindButton = -1L var ModernMiniPlayerRewindButton = -1L
var MusicAppDeeplinkButtonView = -1L var MusicAppDeeplinkButtonView = -1L
var NotificationBigPictureIconWidth = -1L
var PanelSubHeader = -1L var PanelSubHeader = -1L
var PlayerCollapseButton = -1L var PlayerCollapseButton = -1L
var PosterArtWidthDefault = -1L var PosterArtWidthDefault = -1L
@ -152,6 +153,7 @@ object SharedResourceIdPatch : ResourcePatch() {
ModernMiniPlayerForwardButton = getId(ID, "modern_miniplayer_forward_button") ModernMiniPlayerForwardButton = getId(ID, "modern_miniplayer_forward_button")
ModernMiniPlayerRewindButton = getId(ID, "modern_miniplayer_rewind_button") ModernMiniPlayerRewindButton = getId(ID, "modern_miniplayer_rewind_button")
MusicAppDeeplinkButtonView = getId(ID, "music_app_deeplink_button_view") MusicAppDeeplinkButtonView = getId(ID, "music_app_deeplink_button_view")
NotificationBigPictureIconWidth = getId(DIMEN, "notification_big_picture_icon_width")
PanelSubHeader = getId(ID, "panel_subheader") PanelSubHeader = getId(ID, "panel_subheader")
PlayerCollapseButton = getId(ID, "player_collapse_button") PlayerCollapseButton = getId(ID, "player_collapse_button")
PosterArtWidthDefault = getId(DIMEN, "poster_art_width_default") PosterArtWidthDefault = getId(DIMEN, "poster_art_width_default")

View File

@ -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.RectangleFieldInvalidatorFingerprint
import app.revanced.patches.youtube.utils.sponsorblock.fingerprints.SegmentPlaybackControllerFingerprint import app.revanced.patches.youtube.utils.sponsorblock.fingerprints.SegmentPlaybackControllerFingerprint
import app.revanced.patches.youtube.video.information.VideoInformationPatch 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.getTargetIndex
import app.revanced.util.getTargetIndexWithFieldReferenceTypeReversed import app.revanced.util.getTargetIndexWithFieldReferenceTypeReversed
import app.revanced.util.getTargetIndexWithMethodReferenceName import app.revanced.util.getTargetIndexWithMethodReferenceName
@ -35,7 +34,6 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
dependencies = [ dependencies = [
PlayerControlsPatch::class, PlayerControlsPatch::class,
SharedResourceIdPatch::class, SharedResourceIdPatch::class,
VideoIdPatch::class,
VideoInformationPatch::class VideoInformationPatch::class
] ]
) )
@ -173,6 +171,6 @@ object SponsorBlockBytecodePatch : BytecodePatch(
) )
// Set current video id // 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")
} }
} }

View File

@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch 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.integrations.Constants.SHARED_PATH
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch 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.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.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.VideoLengthFingerprint
import app.revanced.patches.youtube.video.information.fingerprints.VideoQualityListFingerprint 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.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.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.addFieldAndInstructions import app.revanced.util.addFieldAndInstructions
import app.revanced.util.getReference
import app.revanced.util.getTargetIndex import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexReversed import app.revanced.util.getTargetIndexReversed
import app.revanced.util.getWalkerMethod import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction 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.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.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@ -53,27 +64,54 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
) )
object VideoInformationPatch : BytecodePatch( object VideoInformationPatch : BytecodePatch(
setOf( setOf(
ChannelIdFingerprint,
ChannelNameFingerprint,
OnPlaybackSpeedItemClickFingerprint, OnPlaybackSpeedItemClickFingerprint,
OrganicPlaybackContextModelFingerprint, OrganicPlaybackContextModelFingerprint,
PlaybackInitializationFingerprint,
PlaybackSpeedClassFingerprint, PlaybackSpeedClassFingerprint,
PlayerControllerSetTimeReferenceFingerprint,
VideoEndFingerprint, VideoEndFingerprint,
VideoIdFingerprint,
VideoIdFingerprintBackgroundPlay,
VideoIdFingerprintShorts,
VideoLengthFingerprint, VideoLengthFingerprint,
VideoQualityListFingerprint, VideoQualityListFingerprint,
VideoQualityTextFingerprint VideoQualityTextFingerprint,
VideoTitleFingerprint,
) )
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$SHARED_PATH/VideoInformation;" "$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 lateinit var playerConstructorMethod: MutableMethod
private var playerConstructorInsertIndex = 4 private var playerConstructorInsertIndex = 4
private lateinit var videoTimeConstructorMethod: MutableMethod private lateinit var videoTimeMethod: MutableMethod
private var videoTimeConstructorInsertIndex = 2 private var videoTimeIndex = 1
private lateinit var videoCpnConstructorMethod: MutableMethod
private var videoCpnConstructorInsertIndex = 2
// Used by other patches. // Used by other patches.
internal lateinit var speedSelectionInsertMethod: MutableMethod internal lateinit var speedSelectionInsertMethod: MutableMethod
@ -83,6 +121,10 @@ object VideoInformationPatch : BytecodePatch(
val videoInformationMutableClass = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!.mutableClass val videoInformationMutableClass = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!.mutableClass
VideoEndFingerprint.resultOrThrow().let { VideoEndFingerprint.resultOrThrow().let {
// resolve video time fingerprint
VideoTimeFingerprint.resolve(context, it.classDef)
playerConstructorMethod = playerConstructorMethod =
it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) } 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<MethodReference>()?.returnType == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
} + 1
if (targetIndex == 0) throw PatchException("Could not find instruction index.")
val targetRegister = getInstruction<OneRegisterInstruction>(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<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
}
if (targetIndex < 0) throw PatchException("Could not find instruction index.")
val targetRegister = getInstruction<FiveRegisterInstruction>(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<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
}
if (targetIndex < 0) throw PatchException("Could not find instruction index.")
val targetRegister = getInstruction<FiveRegisterInstruction>(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 * Set current video time method
*/ */
PlayerControllerSetTimeReferenceFingerprint.resultOrThrow().let { VideoTimeFingerprint.resultOrThrow().mutableMethod.apply {
videoTimeConstructorMethod = videoTimeMethod = this
it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex) addInstruction(
0,
"move-wide/from16 v0, p5"
)
} }
/** /**
@ -162,40 +276,15 @@ object VideoInformationPatch : BytecodePatch(
*/ */
videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime") videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime")
/**
* Set current video length
*/
VideoLengthFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val startIndex = it.scanResult.patternScanResult!!.startIndex
val primaryRegister = getInstruction<OneRegisterInstruction>(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 * Set current video id
*/ */
val videoIdMethodDescriptor = "$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V"
VideoIdPatch.hookVideoId(videoIdMethodDescriptor)
VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor)
VideoIdPatch.hookPlayerResponseVideoId( VideoIdPatch.hookPlayerResponseVideoId(
"$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V") "$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V")
// Call before any other video id hooks, // Call before any other video id hooks,
// so they can use VideoInformation and check if the video id is for a Short. // so they can use VideoInformation and check if the video id is for a Short.
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameterBeforeVideoId( 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 * 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. * 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. * @param targetMethodName The name of the static method to invoke when the player controller is created.
*/ */
internal fun videoTimeHook(targetMethodClass: String, targetMethodName: String) = internal fun videoTimeHook(targetMethodClass: String, targetMethodName: String) =
videoTimeConstructorMethod.insertTimeHook( videoTimeMethod.addInstruction(
videoTimeConstructorInsertIndex++, videoTimeIndex++,
"$targetMethodClass->$targetMethodName(J)V" "invoke-static { v0, v1 }, $targetMethodClass->$targetMethodName(J)V"
) )
internal fun cpnHook(descriptor: String) = private fun MethodFingerprint.getMethodName(returnType : String) :String {
videoCpnConstructorMethod.insert( resultOrThrow().mutableMethod.apply {
videoCpnConstructorInsertIndex++, val targetIndex = indexOfFirstInstruction {
"p1, p2", opcode == Opcode.INVOKE_INTERFACE
descriptor && getReference<MethodReference>()?.definingClass == PLAYER_RESPONSE_MODEL_CLASS_DESCRIPTOR
) && getReference<MethodReference>()?.returnType == returnType
}
if (targetIndex < 0) throw PatchException("Could not find instruction index.")
val targetReference = getInstruction<ReferenceInstruction>(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
)
}
} }

View File

@ -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")
)

View File

@ -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",
)
)

View File

@ -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.")
)

View File

@ -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: ")
)

View File

@ -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")
)

View File

@ -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.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@ -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 }
)

View File

@ -1,18 +1,11 @@
package app.revanced.patches.youtube.video.information.fingerprints package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or 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.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object VideoLengthFingerprint : LiteralValueFingerprint( internal object VideoLengthFingerprint : MethodFingerprint(
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(), strings = listOf("Gaplessly transitioning away from an Ad before it ends.")
opcodes = listOf( )
Opcode.MOVE_RESULT_WIDE,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL
),
literalSupplier = { 45388753 }
)

View File

@ -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")
)

View File

@ -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 }
)

View File

@ -67,7 +67,6 @@ object VideoPlaybackPatch : BaseBytecodePatch(
DeviceDimensionsModelToStringFingerprint, DeviceDimensionsModelToStringFingerprint,
HDRCapabilityFingerprint, HDRCapabilityFingerprint,
PlaybackSpeedChangedFromRecyclerViewFingerprint, PlaybackSpeedChangedFromRecyclerViewFingerprint,
PlaybackSpeedInitializeFingerprint,
QualityChangedFromRecyclerViewFingerprint, QualityChangedFromRecyclerViewFingerprint,
QualityMenuViewInflateFingerprint, QualityMenuViewInflateFingerprint,
QualitySetterFingerprint, 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") context.updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "RememberPlaybackSpeed")
@ -195,8 +194,8 @@ object VideoPlaybackPatch : BaseBytecodePatch(
} ?: throw PatchException("Failed to find onItemClick method") } ?: throw PatchException("Failed to find onItemClick method")
} }
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(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")
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V") VideoInformationPatch.hookBackgroundPlay("$INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
// endregion // endregion
@ -212,7 +211,7 @@ object VideoPlaybackPatch : BaseBytecodePatch(
addInstruction( addInstruction(
insertIndex + 1, insertIndex + 1,
"invoke-static { v$insertRegister }, " + "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 = val onItemClickMethod =
@ -229,7 +228,7 @@ object VideoPlaybackPatch : BaseBytecodePatch(
addInstructionsWithLabels( addInstructionsWithLabels(
insertIndex, """ 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 move-result v$insertRegister
if-nez v$insertRegister, :show if-nez v$insertRegister, :show
""", ExternalLabel("show", getInstruction(jumpIndex)) """, ExternalLabel("show", getInstruction(jumpIndex))

View File

@ -57,12 +57,12 @@ object PlayerResponseMethodHookPatch :
val instruction = val instruction =
if (shouldApplyNewMethod) 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 move-result-object p3
""" """
else 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 move-result-object p$PARAMETER_PLAYER_PARAMETER
""" """

View File

@ -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.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch 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.VideoIdFingerprint
import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdFingerprintBackgroundPlay
import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdParentFingerprint import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdParentFingerprint
import app.revanced.util.getTargetIndex
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch( @Patch(
@ -22,19 +19,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
dependencies = [PlayerResponseMethodHookPatch::class], dependencies = [PlayerResponseMethodHookPatch::class],
) )
object VideoIdPatch : BytecodePatch( object VideoIdPatch : BytecodePatch(
setOf( setOf(VideoIdParentFingerprint)
VideoIdParentFingerprint,
VideoIdFingerprintBackgroundPlay
)
) { ) {
private var videoIdRegister = 0 private var videoIdRegister = 0
private var videoIdInsertIndex = 0 private var videoIdInsertIndex = 0
private lateinit var videoIdMethod: MutableMethod private lateinit var videoIdMethod: MutableMethod
private var backgroundPlaybackVideoIdRegister = 0
private var backgroundPlaybackInsertIndex = 0
private lateinit var backgroundPlaybackMethod: MutableMethod
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
/** /**
@ -59,14 +49,6 @@ object VideoIdPatch : BytecodePatch(
videoIdInsertIndex = index videoIdInsertIndex = index
videoIdRegister = register videoIdRegister = register
} }
VideoIdFingerprintBackgroundPlay.resultOrThrow().let {
it.mutableMethod.apply {
backgroundPlaybackMethod = this
backgroundPlaybackInsertIndex = getTargetIndex(Opcode.INVOKE_INTERFACE) + 2
backgroundPlaybackVideoIdRegister = getInstruction<OneRegisterInstruction>(backgroundPlaybackInsertIndex - 1).registerA
}
}
} }
/** /**
@ -87,23 +69,6 @@ object VideoIdPatch : BytecodePatch(
"invoke-static {v$videoIdRegister}, $methodDescriptor" "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. * Hooks the video id of every video when loaded.
* Supports all videos and functions in all situations. * 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 * It's common for multiple Shorts to load at once in preparation
* for the user swiping to the next Short. * for the user swiping to the next Short.
* *
* For most use cases, you probably want to use * For most use cases, you probably want to use [hookVideoId] instead.
* [hookVideoId] or [hookBackgroundPlayVideoId] instead.
* *
* Be aware, this can be called multiple times for the same video id. * Be aware, this can be called multiple times for the same video id.
* *

View File

@ -1041,7 +1041,7 @@ Limitation: Official headers in search results will be hidden."</string>
<string name="revanced_skip_preloaded_buffer_title">Skip preloaded buffer</string> <string name="revanced_skip_preloaded_buffer_title">Skip preloaded buffer</string>
<string name="revanced_skip_preloaded_buffer_summary">"Skip preloaded buffer at video start to bypass default video quality enforcement delay. <string name="revanced_skip_preloaded_buffer_summary">"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."</string> • Does not apply to HDR videos, live stream videos, videos shorter than 15 seconds."</string>
<string name="revanced_skip_preloaded_buffer_toast_title">Show a toast when skipped</string> <string name="revanced_skip_preloaded_buffer_toast_title">Show a toast when skipped</string>
<string name="revanced_skip_preloaded_buffer_toast_summary_on">Toast is shown.</string> <string name="revanced_skip_preloaded_buffer_toast_summary_on">Toast is shown.</string>