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.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

View File

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

View File

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

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

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.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<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
*/
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<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
*/
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<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.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
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 }
strings = listOf("Gaplessly transitioning away from an Ad before it ends.")
)

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

View File

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

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.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<OneRegisterInstruction>(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.
*

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_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>
<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>