refactor(YouTube/Video information): include playback speed

This commit is contained in:
inotia00 2024-04-06 01:36:03 +09:00
parent d6bdef650f
commit 7ba43b6f4c
33 changed files with 685 additions and 721 deletions

View File

@ -8,7 +8,7 @@ import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.litho.LithoFilterPatch
import app.revanced.patches.youtube.utils.fingerprints.QualityMenuViewInflateFingerprint
import app.revanced.patches.youtube.utils.fingerprints.VideoQualitySetterFingerprint
import app.revanced.patches.youtube.utils.fingerprints.QualitySetterFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.COMPONENTS_PATH
import app.revanced.patches.youtube.utils.integrations.Constants.FLYOUT_PANEL_CLASS_DESCRIPTOR
@ -38,7 +38,7 @@ object OldQualityLayoutPatch : BaseBytecodePatch(
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(
QualityMenuViewInflateFingerprint,
VideoQualitySetterFingerprint
QualitySetterFingerprint
)
) {
private const val FILTER_CLASS_DESCRIPTOR =
@ -49,7 +49,7 @@ object OldQualityLayoutPatch : BaseBytecodePatch(
/**
* Non-litho view, used in old clients and Shorts.
*/
val videoQualityClass = VideoQualitySetterFingerprint.resultOrThrow().mutableMethod.definingClass
val videoQualityClass = QualitySetterFingerprint.resultOrThrow().mutableMethod.definingClass
QualityMenuViewInflateFingerprint.resultOrThrow().let {
it.mutableMethod.apply {

View File

@ -4,7 +4,7 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patches.youtube.utils.integrations.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.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.patch.BaseBytecodePatch
@Suppress("unused")
@ -20,7 +20,7 @@ object AutoCaptionsPatch : BaseBytecodePatch(
) {
override fun execute(context: BytecodeContext) {
VideoIdPatch.injectCall("$GENERAL_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
VideoIdPatch.hookBackgroundPlayVideoId("$GENERAL_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
/**
* Add settings

View File

@ -6,9 +6,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.youtube.overlaybutton.alwaysrepeat.fingerprints.AutoNavInformerFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
import app.revanced.util.getTargetIndexReversed
import app.revanced.util.getWalkerMethod
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.TwoRegisterInstruction
object AlwaysRepeatPatch : BytecodePatch(
setOf(AutoNavInformerFingerprint)
@ -18,8 +21,8 @@ object AlwaysRepeatPatch : BytecodePatch(
AutoNavInformerFingerprint.resultOrThrow().let {
val walkerMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex)
walkerMethod.apply {
val index = implementation!!.instructions.size - 2
val register = getInstruction<OneRegisterInstruction>(index).registerA
val index = getTargetIndexReversed(Opcode.IGET_BOOLEAN)
val register = getInstruction<TwoRegisterInstruction>(index).registerA
addInstructions(
index + 1, """

View File

@ -9,11 +9,9 @@ import app.revanced.patches.youtube.overlaybutton.fullscreen.FullscreenButtonPat
import app.revanced.patches.youtube.utils.fix.fullscreen.FullscreenButtonViewStubPatch
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.OVERLAY_BUTTONS_PATH
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.playercontrols.PlayerControlsPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode
@ -31,11 +29,9 @@ object OverlayButtonsPatch : BaseResourcePatch(
DownloadButtonHookPatch::class,
FullscreenButtonPatch::class,
FullscreenButtonViewStubPatch::class,
OverrideSpeedHookPatch::class,
PlayerControlsPatch::class,
SettingsPatch::class,
SharedResourceIdPatch::class,
VideoIdPatch::class
VideoInformationPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE
) {

View File

@ -7,9 +7,9 @@ import app.revanced.patches.youtube.utils.fingerprints.TotalTimeFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.SEEKBAR_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.overridequality.OverrideQualityHookPatch
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.getTargetIndexWithMethodReferenceName
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
@ -22,9 +22,9 @@ object AppendTimeStampInformationPatch : BaseBytecodePatch(
description = "Adds an option to add the current video quality or playback speed in brackets next to the current time.",
dependencies = setOf(
OverrideQualityHookPatch::class,
OverrideSpeedHookPatch::class,
SettingsPatch::class,
SharedResourceIdPatch::class
SharedResourceIdPatch::class,
VideoInformationPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(TotalTimeFingerprint)

View File

@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object NewVideoQualityChangedFingerprint : MethodFingerprint(
internal object QualityChangedFromRecyclerViewFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),

View File

@ -4,7 +4,7 @@ import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object VideoQualitySetterFingerprint : MethodFingerprint(
internal object QualitySetterFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),

View File

@ -16,10 +16,10 @@ import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardT
import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailParentFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH
import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -30,9 +30,9 @@ object SpoofPlayerParameterPatch : BaseBytecodePatch(
description = "Adds options to spoof player parameters to prevent playback issues.",
dependencies = setOf(
PlayerTypeHookPatch::class,
PlayerResponsePatch::class,
VideoIdPatch::class,
SettingsPatch::class
PlayerResponseMethodHookPatch::class,
SettingsPatch::class,
VideoInformationPatch::class,
),
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(
@ -52,7 +52,7 @@ object SpoofPlayerParameterPatch : BaseBytecodePatch(
override fun execute(context: BytecodeContext) {
// Hook the player parameters.
PlayerResponsePatch += PlayerResponsePatch.Hook.PlayerParameter(
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.PlayerParameter(
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;"
)

View File

@ -1,150 +0,0 @@
package app.revanced.patches.youtube.utils.overridespeed
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
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.MethodFingerprintResult
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.overridespeed.fingerprints.PlaybackSpeedChangedFingerprint
import app.revanced.patches.youtube.utils.overridespeed.fingerprints.PlaybackSpeedParentFingerprint
import app.revanced.patches.youtube.utils.overridespeed.fingerprints.PlaybackSpeedPatchFingerprint
import app.revanced.patches.youtube.utils.overridespeed.fingerprints.SpeedClassFingerprint
import app.revanced.util.getWalkerMethod
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
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.reference.FieldReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
object OverrideSpeedHookPatch : BytecodePatch(
setOf(
PlaybackSpeedPatchFingerprint,
PlaybackSpeedParentFingerprint,
SpeedClassFingerprint
)
) {
private const val INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR =
"$VIDEO_PATH/PlaybackSpeedPatch;"
private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$INTEGRATIONS_PATH/utils/VideoUtils;"
lateinit var playbackSpeedChangedResult: MethodFingerprintResult
private lateinit var objectClass: String
override fun execute(context: BytecodeContext) {
PlaybackSpeedParentFingerprint.resultOrThrow().let { parentResult ->
val parentClassDef = parentResult.classDef
PlaybackSpeedChangedFingerprint.also {
it.resolve(
context,
parentClassDef
)
}.resultOrThrow().let {
it.mutableMethod.apply {
playbackSpeedChangedResult = it
val startIndex = it.scanResult.patternScanResult!!.startIndex
val endIndex = it.scanResult.patternScanResult!!.endIndex
val reference1 = getInstruction<ReferenceInstruction>(startIndex).reference
val reference2 = getInstruction<ReferenceInstruction>(endIndex - 1).reference
val reference3 = getInstruction<ReferenceInstruction>(endIndex).reference
val fieldReference = reference2 as FieldReference
val parentMutableClass = parentResult.mutableClass
parentMutableClass.methods.add(
ImmutableMethod(
parentMutableClass.type,
"overrideSpeed",
listOf(ImmutableMethodParameter("F", annotations, null)),
"V",
AccessFlags.PUBLIC or AccessFlags.PUBLIC,
annotations,
null,
ImmutableMethodImplementation(
4, """
const/4 v0, 0x0
cmpg-float v0, v3, v0
if-lez v0, :cond_0
iget-object v0, v2, $reference1
check-cast v0, ${fieldReference.definingClass}
iget-object v1, v0, $reference2
invoke-virtual {v1, v3}, $reference3
:cond_0
return-void
""".toInstructions(), null, null
)
).toMutable()
)
val walkerMethod = getWalkerMethod(context, endIndex)
walkerMethod.apply {
addInstruction(
this.implementation!!.instructions.size - 1,
"sput p1, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->currentSpeed:F"
)
}
}
}
}
SpeedClassFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val index = it.scanResult.patternScanResult!!.endIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA
objectClass = this.returnType
replaceInstruction(
index,
"sput-object v$register, $INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->speedClass:$objectClass"
)
addInstruction(
index + 1,
"return-object v$register"
)
}
}
PlaybackSpeedPatchFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
it.mutableClass.staticFields.add(
ImmutableField(
definingClass,
"speedClass",
objectClass,
AccessFlags.PUBLIC or AccessFlags.STATIC,
null,
annotations,
null
).toMutable()
)
addInstructions(
0, """
sget-object v0, $INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->speedClass:$objectClass
invoke-virtual {v0, p0}, $objectClass->overrideSpeed(F)V
"""
)
}
}
}
}

View File

@ -1,16 +0,0 @@
package app.revanced.patches.youtube.utils.overridespeed.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PlaybackSpeedChangedFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.IF_EQZ,
Opcode.IF_EQZ,
Opcode.IGET,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL
),
customFingerprint = { methodDef, _ -> methodDef.name == "onItemClick" }
)

View File

@ -1,18 +0,0 @@
package app.revanced.patches.youtube.utils.overridespeed.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object PlaybackSpeedParentFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L", "L", "[L", "I"),
opcodes = listOf(
Opcode.ARRAY_LENGTH,
Opcode.IF_GE,
Opcode.AGET_OBJECT,
Opcode.NEW_INSTANCE
)
)

View File

@ -1,102 +0,0 @@
package app.revanced.patches.youtube.utils.playerresponse
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.playerresponse.fingerprints.PlayerParameterBuilderFingerprint
import app.revanced.util.resultOrThrow
import java.io.Closeable
object PlayerResponsePatch : BytecodePatch(
setOf(PlayerParameterBuilderFingerprint)
), Closeable, MutableSet<PlayerResponsePatch.Hook> by mutableSetOf() {
private var VIDEO_ID_PARAMETER = 1
private var PLAYER_PARAMETER = 3
private var IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
private var freeRegister = 0
private var shouldApplyNewMethod = false
private lateinit var playerResponseMethod: MutableMethod
override fun execute(context: BytecodeContext) {
playerResponseMethod = PlayerParameterBuilderFingerprint.resultOrThrow().mutableMethod
playerResponseMethod.apply {
freeRegister = implementation!!.registerCount - parameters.size - 2
shouldApplyNewMethod = freeRegister > 2
if (shouldApplyNewMethod) {
IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = freeRegister
PLAYER_PARAMETER = freeRegister - 1
VIDEO_ID_PARAMETER = freeRegister - 2
}
}
}
override fun close() {
fun hookVideoId(hook: Hook) {
playerResponseMethod.apply {
val instruction =
if (shouldApplyNewMethod)
"invoke-static {v$VIDEO_ID_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
else
"invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
addInstruction(
0,
instruction
)
}
}
fun hookPlayerParameter(hook: Hook) {
playerResponseMethod.apply {
val instruction =
if (shouldApplyNewMethod)
"""
invoke-static {v$VIDEO_ID_PARAMETER, v$PLAYER_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p3
"""
else
"""
invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p$PLAYER_PARAMETER
"""
addInstructions(
0,
instruction
)
}
}
// Reverse the order in order to preserve insertion order of the hooks.
val beforeVideoIdHooks = filterIsInstance<Hook.PlayerBeforeVideoId>().asReversed()
val videoIdHooks = filterIsInstance<Hook.VideoId>().asReversed()
val afterVideoIdHooks = filterIsInstance<Hook.PlayerParameter>().asReversed()
// Add the hooks in this specific order as they insert instructions at the beginning of the method.
afterVideoIdHooks.forEach(::hookPlayerParameter)
videoIdHooks.forEach(::hookVideoId)
beforeVideoIdHooks.forEach(::hookPlayerParameter)
if (shouldApplyNewMethod) {
playerResponseMethod.addInstructions(
0, """
move-object v$VIDEO_ID_PARAMETER, p1
move-object v$PLAYER_PARAMETER, p3
move/from16 v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER, p11
"""
)
}
}
internal abstract class Hook(private val methodDescriptor: String) {
internal class VideoId(methodDescriptor: String) : Hook(methodDescriptor)
internal class PlayerParameter(methodDescriptor: String) : Hook(methodDescriptor)
internal class PlayerBeforeVideoId(methodDescriptor: String) : Hook(methodDescriptor)
override fun toString() = methodDescriptor
}
}

View File

@ -8,7 +8,6 @@ import app.revanced.patches.shared.litho.LithoFilterPatch
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.COMPONENTS_PATH
import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch
import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.DislikeFingerprint
import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.LikeFingerprint
import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.RemoveLikeFingerprint
@ -17,7 +16,7 @@ import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerpri
import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.ReturnYouTubeDislikeRollingNumberPatch
import app.revanced.patches.youtube.utils.returnyoutubedislike.shorts.ReturnYouTubeDislikeShortsPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.getTargetIndexWithFieldReferenceType
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
@ -30,7 +29,6 @@ object ReturnYouTubeDislikePatch : BaseBytecodePatch(
description = "Shows the dislike count of videos using the Return YouTube Dislike API.",
dependencies = setOf(
LithoFilterPatch::class,
PlayerResponsePatch::class,
ReturnYouTubeDislikeRollingNumberPatch::class,
ReturnYouTubeDislikeShortsPatch::class,
SettingsPatch::class,
@ -91,14 +89,22 @@ object ReturnYouTubeDislikePatch : BaseBytecodePatch(
}
}
VideoIdPatch.injectCall("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V")
VideoIdPatch.injectPlayerResponseVideoId("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->preloadVideoId(Ljava/lang/String;Z)V")
// region Inject newVideoLoaded event handler to update dislikes when a new video is loaded.
VideoIdPatch.hookVideoId("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V")
// Hook the player response video id, to start loading RYD sooner in the background.
VideoIdPatch.hookPlayerResponseVideoId("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->preloadVideoId(Ljava/lang/String;Z)V")
// endregion
// Player response video id is needed to search for the video ids in Shorts litho components.
if (SettingsPatch.upward1834) {
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
VideoIdPatch.injectPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
VideoIdPatch.hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
}
// endregion
/**
* Add ReVanced Extended Settings
*/

View File

@ -12,14 +12,13 @@ import app.revanced.patches.youtube.utils.fingerprints.SeekbarOnDrawFingerprint
import app.revanced.patches.youtube.utils.fingerprints.TotalTimeFingerprint
import app.revanced.patches.youtube.utils.fingerprints.YouTubeControlsOverlayFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.playercontrols.PlayerControlsPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.InsetOverlayViewLayout
import app.revanced.patches.youtube.utils.sponsorblock.fingerprints.RectangleFieldInvalidatorFingerprint
import app.revanced.patches.youtube.utils.sponsorblock.fingerprints.SegmentPlaybackControllerFingerprint
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.utils.videoid.withoutshorts.VideoIdWithoutShortsPatch
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
@ -34,11 +33,10 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch(
dependencies = [
OverrideSpeedHookPatch::class,
PlayerControlsPatch::class,
SharedResourceIdPatch::class,
VideoIdPatch::class,
VideoIdWithoutShortsPatch::class
VideoInformationPatch::class
]
)
object SponsorBlockBytecodePatch : BytecodePatch(
@ -60,13 +58,13 @@ object SponsorBlockBytecodePatch : BytecodePatch(
override fun execute(context: BytecodeContext) {
VideoIdPatch.apply {
VideoInformationPatch.apply {
// Hook the video time method
videoTimeHook(
INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR,
"setVideoTime"
)
// Initialize SponsorBlock
// Initialize the player controller
onCreateHook(
INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR,
"initialize"
@ -163,7 +161,7 @@ object SponsorBlockBytecodePatch : BytecodePatch(
}
}
// Inject VideoIdPatch
VideoIdWithoutShortsPatch.injectCall("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
// Set current video id
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setCurrentVideoId(Ljava/lang/String;)V")
}
}

View File

@ -1,33 +0,0 @@
package app.revanced.patches.youtube.utils.videocpn
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.fingerprints.OrganicPlaybackContextModelFingerprint
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.util.resultOrThrow
@Patch(dependencies = [VideoIdPatch::class])
object VideoCpnPatch : BytecodePatch(
setOf(OrganicPlaybackContextModelFingerprint)
) {
override fun execute(context: BytecodeContext) {
insertMethod = OrganicPlaybackContextModelFingerprint.resultOrThrow().mutableMethod
}
private lateinit var insertMethod: MutableMethod
internal fun injectCall(
methodDescriptor: String
) {
insertMethod.addInstructions(
2,
"invoke-static {p1}, $methodDescriptor"
)
}
}

View File

@ -1,247 +0,0 @@
package app.revanced.patches.youtube.utils.videoid.general
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.fingerprints.OrganicPlaybackContextModelFingerprint
import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.videoid.general.fingerprint.PlayerControllerSetTimeReferenceFingerprint
import app.revanced.patches.youtube.utils.videoid.general.fingerprint.VideoIdFingerprint
import app.revanced.patches.youtube.utils.videoid.general.fingerprint.VideoIdParentFingerprint
import app.revanced.patches.youtube.utils.videoid.general.fingerprint.VideoLengthFingerprint
import app.revanced.util.getWalkerMethod
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
@Patch(
dependencies = [
PlayerTypeHookPatch::class,
PlayerResponsePatch::class
]
)
object VideoIdPatch : BytecodePatch(
setOf(
OrganicPlaybackContextModelFingerprint,
PlayerControllerSetTimeReferenceFingerprint,
VideoEndFingerprint,
VideoIdParentFingerprint,
VideoLengthFingerprint
)
) {
override fun execute(context: BytecodeContext) {
VideoEndFingerprint.resultOrThrow().let {
playerInitMethod =
it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) }
// hook the player controller for use through integrations
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
it.mutableMethod.apply {
val seekHelperMethod = ImmutableMethod(
definingClass,
"seekTo",
listOf(ImmutableMethodParameter("J", annotations, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
annotations, null,
MutableMethodImplementation(4)
).toMutable()
val seekSourceEnumType = parameterTypes[1].toString()
seekHelperMethod.addInstructions(
0, """
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
invoke-virtual {p0, p1, p2, v0}, ${definingClass}->${name}(J$seekSourceEnumType)Z
move-result p1
return p1
"""
)
it.mutableClass.methods.add(seekHelperMethod)
val videoEndMethod = getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex + 1)
videoEndMethod.apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->videoEnded()Z
move-result v0
if-eqz v0, :end
return-void
""", ExternalLabel("end", getInstruction(0))
)
}
}
}
/**
* Set current video time
*/
PlayerControllerSetTimeReferenceFingerprint.resultOrThrow().let {
timeMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex)
}
/**
* Hook the methods which set the time
*/
videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime")
/**
* Set current video is livestream
*/
OrganicPlaybackContextModelFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
addInstruction(
2,
"sput-boolean p2, $INTEGRATIONS_CLASS_DESCRIPTOR->isLiveStream:Z"
)
}
}
/**
* 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"
)
}
}
VideoIdParentFingerprint.resultOrThrow().let { parentResult ->
VideoIdFingerprint.also {
it.resolve(
context,
parentResult.classDef
)
}.resultOrThrow().let {
it.mutableMethod.apply {
insertMethod = this
insertIndex = it.scanResult.patternScanResult!!.endIndex
videoIdRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
}
offset++ // offset so setVideoId is called before any injected call
}
}
injectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
injectPlayerResponseVideoId("$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.
PlayerResponsePatch += PlayerResponsePatch.Hook.PlayerBeforeVideoId(
"$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;"
)
}
const val INTEGRATIONS_CLASS_DESCRIPTOR = "$VIDEO_PATH/VideoInformation;"
private var offset = 0
private var playerInitInsertIndex = 4
private var timeInitInsertIndex = 2
private var insertIndex: Int = 0
private var videoIdRegister: Int = 0
private lateinit var insertMethod: MutableMethod
private lateinit var playerInitMethod: MutableMethod
private lateinit var timeMethod: MutableMethod
/**
* Adds an invoke-static instruction, called with the new id when the video changes
* @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;`
*/
internal fun injectCall(
methodDescriptor: String
) {
insertMethod.addInstructions(
insertIndex + offset, // move-result-object offset
"invoke-static {v$videoIdRegister}, $methodDescriptor"
)
}
/**
* Hooks the video id of every video when loaded.
* Supports all videos and functions in all situations.
*
* First parameter is the video id.
* Second parameter is if the video is a Short AND it is being opened or is currently playing.
*
* Hook is always called off the main thread.
*
* This hook is called as soon as the player response is parsed,
* and called before many other hooks are updated such as [PlayerTypeHookPatch].
*
* Note: The video id returned here may not be the current video that's being played.
* It's common for multiple Shorts to load at once in preparation
* for the user swiping to the next Short.
*
* Be aware, this can be called multiple times for the same video id.
*
* @param methodDescriptor which method to call. Params must be `Ljava/lang/String;Z`
*/
internal fun injectPlayerResponseVideoId(
methodDescriptor: String
) {
PlayerResponsePatch += PlayerResponsePatch.Hook.VideoId(
methodDescriptor
)
}
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.
*
* Note: This hook is called very early and is called before the video id, video time, video length,
* and many other data fields are set.
*
* @param targetMethodClass The descriptor for the class 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 onCreateHook(targetMethodClass: String, targetMethodName: String) =
playerInitMethod.insert(
playerInitInsertIndex++,
"v0",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
)
/**
* Hook the video time.
* The hook is usually called once per second.
*
* @param targetMethodClass The descriptor for 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) =
timeMethod.insertTimeHook(
timeInitInsertIndex++,
"$targetMethodClass->$targetMethodName(J)V"
)
}

View File

@ -1,52 +0,0 @@
package app.revanced.patches.youtube.utils.videoid.withoutshorts
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.videoid.withoutshorts.fingerprint.VideoIdWithoutShortsFingerprint
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
object VideoIdWithoutShortsPatch : BytecodePatch(
setOf(VideoIdWithoutShortsFingerprint)
) {
override fun execute(context: BytecodeContext) {
VideoIdWithoutShortsFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
insertMethod = this
insertIndex = getTargetIndex(Opcode.INVOKE_INTERFACE)
insertRegister = getInstruction<OneRegisterInstruction>(insertIndex + 1).registerA
}
}
injectCall("$VIDEO_PATH/VideoInformation;->setVideoId(Ljava/lang/String;)V")
}
private var offset = 2
private var insertIndex: Int = 0
private var insertRegister: Int = 0
private lateinit var insertMethod: MutableMethod
/**
* Adds an invoke-static instruction, called with the new id when the video changes
* @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;`
*/
internal fun injectCall(
methodDescriptor: String
) {
insertMethod.addInstructions(
insertIndex + offset, // move-result-object offset
"invoke-static {v$insertRegister}, $methodDescriptor"
)
}
}

View File

@ -3,8 +3,8 @@ package app.revanced.patches.youtube.video.customspeed
import app.revanced.patcher.data.ResourceContext
import app.revanced.patches.youtube.flyoutpanel.oldspeedlayout.OldSpeedLayoutPatch
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.patch.BaseResourcePatch
@Suppress("unused")
@ -14,8 +14,8 @@ object CustomPlaybackSpeedPatch : BaseResourcePatch(
dependencies = setOf(
CustomPlaybackSpeedBytecodePatch::class,
OldSpeedLayoutPatch::class,
OverrideSpeedHookPatch::class,
SettingsPatch::class
SettingsPatch::class,
VideoInformationPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE
) {

View File

@ -0,0 +1,313 @@
package app.revanced.patches.youtube.video.information
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.youtube.utils.fingerprints.OrganicPlaybackContextModelFingerprint
import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.video.information.fingerprints.OnPlaybackSpeedItemClickFingerprint
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.VideoInformationPatchFingerprint
import app.revanced.patches.youtube.video.information.fingerprints.VideoLengthFingerprint
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.getTargetIndex
import app.revanced.util.getTargetIndexReversed
import app.revanced.util.getWalkerMethod
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
@Patch(
description = "Hooks YouTube to get information about the current playing video.",
dependencies = [
PlayerResponseMethodHookPatch::class,
PlayerTypeHookPatch::class,
VideoIdPatch::class
]
)
object VideoInformationPatch : BytecodePatch(
setOf(
OnPlaybackSpeedItemClickFingerprint,
OrganicPlaybackContextModelFingerprint,
PlaybackSpeedClassFingerprint,
PlayerControllerSetTimeReferenceFingerprint,
VideoEndFingerprint,
VideoInformationPatchFingerprint,
VideoLengthFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$VIDEO_PATH/VideoInformation;"
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
// Used by other patches.
internal lateinit var speedSelectionInsertMethod: MutableMethod
override fun execute(context: BytecodeContext) {
VideoEndFingerprint.resultOrThrow().let {
playerConstructorMethod =
it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) }
// hook the player controller for use through integrations
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
it.mutableMethod.apply {
val seekSourceEnumType = parameterTypes[1].toString()
it.mutableClass.methods.add(
ImmutableMethod(
definingClass,
"seekTo",
listOf(ImmutableMethodParameter("J", annotations, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
annotations,
null,
ImmutableMethodImplementation(
4, """
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
invoke-virtual {p0, p1, p2, v0}, ${definingClass}->${name}(J$seekSourceEnumType)Z
move-result p1
return p1
""".toInstructions(),
null,
null
)
).toMutable()
)
val videoEndMethod = getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex + 1)
videoEndMethod.apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->videoEnded()Z
move-result v0
if-eqz v0, :end
return-void
""", ExternalLabel("end", getInstruction(0))
)
}
}
}
/**
* Set current video time method
*/
PlayerControllerSetTimeReferenceFingerprint.resultOrThrow().let {
videoTimeConstructorMethod =
it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex)
}
/**
* Set current video time
*/
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;")
/**
* Hook the user playback speed selection
*/
OnPlaybackSpeedItemClickFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
speedSelectionInsertMethod = this
val speedSelectionValueInstructionIndex = getTargetIndex(Opcode.IGET)
val setPlaybackSpeedContainerClassFieldIndex = getTargetIndexReversed(speedSelectionValueInstructionIndex, Opcode.IGET_OBJECT)
val setPlaybackSpeedContainerClassFieldReference =
getInstruction<ReferenceInstruction>(setPlaybackSpeedContainerClassFieldIndex).reference.toString()
val setPlaybackSpeedClassFieldReference =
getInstruction<ReferenceInstruction>(speedSelectionValueInstructionIndex + 1).reference.toString()
val setPlaybackSpeedMethodReference =
getInstruction<ReferenceInstruction>(speedSelectionValueInstructionIndex + 2).reference.toString()
it.mutableClass.methods.add(
ImmutableMethod(
definingClass,
"overridePlaybackSpeed",
listOf(ImmutableMethodParameter("F", annotations, null)),
"V",
AccessFlags.PUBLIC or AccessFlags.PUBLIC,
annotations,
null,
ImmutableMethodImplementation(
4, """
const/4 v0, 0x0
cmpg-float v0, v3, v0
if-lez v0, :ignore
# Get the container class field.
iget-object v0, v2, $setPlaybackSpeedContainerClassFieldReference
# Get the field from its class.
iget-object v1, v0, $setPlaybackSpeedClassFieldReference
# Invoke setPlaybackSpeed on that class.
invoke-virtual {v1, v3}, $setPlaybackSpeedMethodReference
:ignore
return-void
""".toInstructions(), null, null
)
).toMutable()
)
val walkerMethod = getWalkerMethod(context, speedSelectionValueInstructionIndex + 2)
walkerMethod.apply {
addInstruction(
this.implementation!!.instructions.size - 1,
"invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->setPlaybackSpeed(F)V"
)
}
}
}
PlaybackSpeedClassFingerprint.resultOrThrow().let { result ->
result.mutableMethod.apply {
val index = result.scanResult.patternScanResult!!.endIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA
val playbackSpeedClass = this.returnType
replaceInstruction(
index,
"sput-object v$register, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackSpeedClass:$playbackSpeedClass"
)
addInstruction(
index + 1,
"return-object v$register"
)
VideoInformationPatchFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
it.mutableClass.staticFields.add(
ImmutableField(
definingClass,
"playbackSpeedClass",
playbackSpeedClass,
AccessFlags.PUBLIC or AccessFlags.STATIC,
null,
annotations,
null
).toMutable()
)
addInstructions(
0, """
sget-object v0, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackSpeedClass:$playbackSpeedClass
invoke-virtual {v0, p0}, $playbackSpeedClass->overridePlaybackSpeed(F)V
return-void
"""
)
}
}
}
}
}
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.
*
* Note: This hook is called very early and is called before the video id, video time, video length,
* and many other data fields are set.
*
* @param targetMethodClass The descriptor for the class 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 onCreateHook(targetMethodClass: String, targetMethodName: String) =
playerConstructorMethod.insert(
playerConstructorInsertIndex++,
"v0",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
)
/**
* Hook the video time.
* The hook is usually called once per second.
*
* @param targetMethodClass The descriptor for 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) =
videoTimeConstructorMethod.insertTimeHook(
videoTimeConstructorInsertIndex++,
"$targetMethodClass->$targetMethodName(J)V"
)
internal fun cpnHook(descriptor: String) =
videoCpnConstructorMethod.insert(
videoCpnConstructorInsertIndex++,
"p1, p2",
descriptor
)
}

View File

@ -0,0 +1,20 @@
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal object OnPlaybackSpeedItemClickFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = listOf("Landroid/widget/AdapterView;", "Landroid/view/View;", "I", "J"),
customFingerprint = { methodDef, _ ->
methodDef.name == "onItemClick" && methodDef.implementation?.instructions?.find {
it.opcode == Opcode.IGET_OBJECT &&
it.getReference<FieldReference>()!!.type == "Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"
} != null
}
)

View File

@ -1,11 +1,11 @@
package app.revanced.patches.youtube.utils.overridespeed.fingerprints
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
import com.android.tools.smali.dexlib2.Opcode
internal object SpeedClassFingerprint : MethodFingerprint(
internal object PlaybackSpeedClassFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L"),

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.utils.videoid.general.fingerprint
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode

View File

@ -1,16 +1,16 @@
package app.revanced.patches.youtube.utils.overridespeed.fingerprints
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlaybackSpeedPatchFingerprint : MethodFingerprint(
internal object VideoInformationPatchFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("F"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "$VIDEO_PATH/PlaybackSpeedPatch;"
&& methodDef.name == "overrideSpeed"
methodDef.definingClass == "$VIDEO_PATH/VideoInformation;"
&& methodDef.name == "overridePlaybackSpeed"
}
)

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.utils.videoid.general.fingerprint
package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.fingerprint.LiteralValueFingerprint

View File

@ -0,0 +1,106 @@
package app.revanced.patches.youtube.video.playerresponse
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.video.playerresponse.fingerprint.PlayerParameterBuilderFingerprint
import app.revanced.util.resultOrThrow
import java.io.Closeable
object PlayerResponseMethodHookPatch :
BytecodePatch(setOf(PlayerParameterBuilderFingerprint)),
Closeable,
MutableSet<PlayerResponseMethodHookPatch.Hook> by mutableSetOf() {
// Parameter numbers of the patched method.
private var PARAMETER_VIDEO_ID = 1
private var PARAMETER_PLAYER_PARAMETER = 3
private var PARAMETER_PLAYLIST_ID = 4
private var PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING = 11
private var freeRegister = 0
private var shouldApplyNewMethod = false
private lateinit var playerResponseMethod: MutableMethod
override fun execute(context: BytecodeContext) {
playerResponseMethod = PlayerParameterBuilderFingerprint.resultOrThrow().mutableMethod
playerResponseMethod.apply {
freeRegister = implementation!!.registerCount - parameters.size - 2
shouldApplyNewMethod = freeRegister > 2
if (shouldApplyNewMethod) {
PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING = freeRegister
PARAMETER_PLAYLIST_ID = freeRegister - 1
PARAMETER_PLAYER_PARAMETER = freeRegister - 2
PARAMETER_VIDEO_ID = freeRegister - 3
}
}
}
override fun close() {
fun hookVideoId(hook: Hook) {
val instruction =
if (shouldApplyNewMethod)
"invoke-static {v$PARAMETER_VIDEO_ID, v$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook"
else
"invoke-static {p$PARAMETER_VIDEO_ID, p$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING}, $hook"
playerResponseMethod.addInstruction(
0, instruction
)
}
fun hookPlayerParameter(hook: Hook) {
val instruction =
if (shouldApplyNewMethod)
"""
invoke-static {v$PARAMETER_VIDEO_ID, v$PARAMETER_PLAYER_PARAMETER, 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
move-result-object p$PARAMETER_PLAYER_PARAMETER
"""
playerResponseMethod.addInstructions(
0,
instruction
)
}
// Reverse the order in order to preserve insertion order of the hooks.
val beforeVideoIdHooks = filterIsInstance<Hook.PlayerParameterBeforeVideoId>().asReversed()
val videoIdHooks = filterIsInstance<Hook.VideoId>().asReversed()
val afterVideoIdHooks = filterIsInstance<Hook.PlayerParameter>().asReversed()
// Add the hooks in this specific order as they insert instructions at the beginning of the method.
afterVideoIdHooks.forEach(::hookPlayerParameter)
videoIdHooks.forEach(::hookVideoId)
beforeVideoIdHooks.forEach(::hookPlayerParameter)
if (shouldApplyNewMethod) {
playerResponseMethod.addInstructions(
0, """
move-object v$PARAMETER_VIDEO_ID, p1
move-object v$PARAMETER_PLAYER_PARAMETER, p3
move-object v$PARAMETER_PLAYLIST_ID, p4
move/from16 v$PARAMETER_IS_SHORT_AND_OPENING_OR_PLAYING, p11
"""
)
}
}
internal abstract class Hook(private val methodDescriptor: String) {
internal class VideoId(methodDescriptor: String) : Hook(methodDescriptor)
internal class PlayerParameter(methodDescriptor: String) : Hook(methodDescriptor)
internal class PlayerParameterBeforeVideoId(methodDescriptor: String) : Hook(methodDescriptor)
override fun toString() = methodDescriptor
}
}

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.utils.playerresponse.fingerprints
package app.revanced.patches.youtube.video.playerresponse.fingerprint
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
@ -11,7 +11,7 @@ internal object PlayerParameterBuilderFingerprint : MethodFingerprint(
"Ljava/lang/String;", // VideoId.
"[B",
"Ljava/lang/String;", // Player parameters proto buffer.
"Ljava/lang/String;",
"Ljava/lang/String;", // PlaylistId.
"I",
"I",
"Ljava/util/Set;",

View File

@ -4,18 +4,17 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patches.youtube.utils.fingerprints.NewVideoQualityChangedFingerprint
import app.revanced.patches.youtube.utils.fingerprints.VideoQualitySetterFingerprint
import app.revanced.patches.youtube.utils.fingerprints.QualityChangedFromRecyclerViewFingerprint
import app.revanced.patches.youtube.utils.fingerprints.QualitySetterFingerprint
import app.revanced.patches.youtube.utils.fix.shortsplayback.ShortsPlaybackPatch
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.overridequality.OverrideQualityHookPatch
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch.contexts
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.utils.videoid.withoutshorts.VideoIdWithoutShortsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.copyXmlNode
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
@ -27,22 +26,27 @@ object VideoQualityPatch : BaseBytecodePatch(
description = "Adds an option to set the default video quality.",
dependencies = setOf(
OverrideQualityHookPatch::class,
OverrideSpeedHookPatch::class,
PlayerTypeHookPatch::class,
SettingsPatch::class,
ShortsPlaybackPatch::class,
VideoIdPatch::class,
VideoIdWithoutShortsPatch::class
VideoInformationPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(
NewVideoQualityChangedFingerprint,
VideoQualitySetterFingerprint
QualityChangedFromRecyclerViewFingerprint,
QualitySetterFingerprint
)
) {
private const val INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR =
"$VIDEO_PATH/VideoQualityPatch;"
private const val INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR =
"$VIDEO_PATH/ReloadVideoPatch;"
override fun execute(context: BytecodeContext) {
NewVideoQualityChangedFingerprint.resultOrThrow().let {
// Remember video quality from recyclerview (litho view).
QualityChangedFromRecyclerViewFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val index = it.scanResult.patternScanResult!!.startIndex
val qualityRegister = getInstruction<TwoRegisterInstruction>(index).registerA
@ -55,7 +59,7 @@ object VideoQualityPatch : BaseBytecodePatch(
}
}
VideoQualitySetterFingerprint.resultOrThrow().let {
QualitySetterFingerprint.resultOrThrow().let {
val onItemClickMethod =
it.mutableClass.methods.find { method -> method.name == "onItemClick" }
@ -69,10 +73,8 @@ object VideoQualityPatch : BaseBytecodePatch(
} ?: throw PatchException("Failed to find onItemClick method")
}
VideoIdPatch.injectCall("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
VideoIdWithoutShortsPatch.injectCall("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
VideoIdWithoutShortsPatch.injectCall("$INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
VideoIdPatch.hookBackgroundPlayVideoId("$INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
/**
* Copy arrays
@ -94,9 +96,4 @@ object VideoQualityPatch : BaseBytecodePatch(
SettingsPatch.updatePatchStatus("Default video quality")
}
private const val INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR =
"$VIDEO_PATH/VideoQualityPatch;"
private const val INTEGRATIONS_RELOAD_VIDEO_CLASS_DESCRIPTOR =
"$VIDEO_PATH/ReloadVideoPatch;"
}

View File

@ -4,34 +4,35 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patches.youtube.utils.fingerprints.NewVideoQualityChangedFingerprint
import app.revanced.patches.youtube.utils.fingerprints.QualityChangedFromRecyclerViewFingerprint
import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.videocpn.VideoCpnPatch
import app.revanced.patches.youtube.video.speed.fingerprints.NewPlaybackSpeedChangedFingerprint
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch.speedSelectionInsertMethod
import app.revanced.patches.youtube.video.speed.fingerprints.PlaybackSpeedChangedFromRecyclerViewFingerprint
import app.revanced.patches.youtube.video.speed.fingerprints.PlaybackSpeedInitializeFingerprint
import app.revanced.util.getTargetIndex
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import app.revanced.util.updatePatchStatus
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Suppress("unused")
object PlaybackSpeedPatch : BaseBytecodePatch(
name = "Default playback speed",
description = "Adds an option to set the default playback speed.",
dependencies = setOf(
OverrideSpeedHookPatch::class,
SettingsPatch::class,
VideoCpnPatch::class
VideoInformationPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(
NewVideoQualityChangedFingerprint,
QualityChangedFromRecyclerViewFingerprint,
VideoEndFingerprint
)
) {
@ -40,24 +41,27 @@ object PlaybackSpeedPatch : BaseBytecodePatch(
override fun execute(context: BytecodeContext) {
NewVideoQualityChangedFingerprint.resultOrThrow().let { parentResult ->
NewPlaybackSpeedChangedFingerprint.also {
it.resolve(
context,
parentResult.classDef
)
}.resultOrThrow().let { result ->
arrayOf(result, OverrideSpeedHookPatch.playbackSpeedChangedResult).forEach {
it.mutableMethod.apply {
val index = it.scanResult.patternScanResult!!.endIndex
val register = getInstruction<FiveRegisterInstruction>(index).registerD
PlaybackSpeedChangedFromRecyclerViewFingerprint.resolve(
context,
QualityChangedFromRecyclerViewFingerprint.resultOrThrow().classDef
)
addInstruction(
index,
"invoke-static {v$register}, $INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->userChangedSpeed(F)V"
)
}
}
val newMethod = PlaybackSpeedChangedFromRecyclerViewFingerprint.resultOrThrow().mutableMethod
arrayOf(
newMethod,
speedSelectionInsertMethod
).forEach {
it.apply {
val speedSelectionValueInstructionIndex = getTargetIndex(Opcode.IGET)
val speedSelectionValueRegister =
getInstruction<TwoRegisterInstruction>(speedSelectionValueInstructionIndex).registerA
addInstruction(
speedSelectionValueInstructionIndex + 1,
"invoke-static {v$speedSelectionValueRegister}, " +
"$INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->userSelectedPlaybackSpeed(F)V"
)
}
}
@ -82,7 +86,7 @@ object PlaybackSpeedPatch : BaseBytecodePatch(
}
}
VideoCpnPatch.injectCall("$INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;)V")
VideoInformationPatch.cpnHook("$INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->newVideoStarted(Ljava/lang/String;Z)V")
/**
* Add settings

View File

@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object NewPlaybackSpeedChangedFingerprint : MethodFingerprint(
internal object PlaybackSpeedChangedFromRecyclerViewFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),

View File

@ -0,0 +1,136 @@
package app.revanced.patches.youtube.video.videoid
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdFingerprint
import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdParentFingerprint
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.fingerprints.VideoIdFingerprintBackgroundPlay
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(
description = "Hooks to detect when the video id changes",
dependencies = [PlayerResponseMethodHookPatch::class],
)
object VideoIdPatch : BytecodePatch(
setOf(
VideoIdParentFingerprint,
VideoIdFingerprintBackgroundPlay
)
) {
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) {
/**
* Supplies the method and register index of the video id register.
*
* @param consumer Consumer that receives the method, insert index and video id register index.
*/
fun MethodFingerprint.setFields(consumer: (MutableMethod, Int, Int) -> Unit) = resultOrThrow().let { result ->
val videoIdRegisterIndex = result.scanResult.patternScanResult!!.endIndex
result.mutableMethod.let {
val videoIdRegister = it.getInstruction<OneRegisterInstruction>(videoIdRegisterIndex).registerA
val insertIndex = videoIdRegisterIndex + 1
consumer(it, insertIndex, videoIdRegister)
}
}
VideoIdFingerprint.resolve(context, VideoIdParentFingerprint.resultOrThrow().classDef)
VideoIdFingerprint.setFields { method, index, register ->
videoIdMethod = method
videoIdInsertIndex = index
videoIdRegister = register
}
VideoIdFingerprintBackgroundPlay.resultOrThrow().let {
it.mutableMethod.apply {
backgroundPlaybackMethod = this
backgroundPlaybackInsertIndex = getTargetIndex(Opcode.INVOKE_INTERFACE) + 2
backgroundPlaybackVideoIdRegister = getInstruction<OneRegisterInstruction>(backgroundPlaybackInsertIndex - 1).registerA
}
}
}
/**
* Hooks the new video id when the video changes.
*
* Supports all videos (regular videos and Shorts).
*
* _Does not function if playing in the background with no video visible_.
*
* Be aware, this can be called multiple times for the same video id.
*
* @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;`
*/
fun hookVideoId(
methodDescriptor: String
) = videoIdMethod.addInstruction(
videoIdInsertIndex++,
"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.
*
* First parameter is the video id.
* Second parameter is if the video is a Short AND it is being opened or is currently playing.
*
* Hook is always called off the main thread.
*
* This hook is called as soon as the player response is parsed,
* and called before many other hooks are updated such as [PlayerTypeHookPatch].
*
* Note: The video id returned here may not be the current video that's being played.
* 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.
*
* Be aware, this can be called multiple times for the same video id.
*
* @param methodDescriptor which method to call. Params must be `Ljava/lang/String;Z`
*/
fun hookPlayerResponseVideoId(methodDescriptor: String) {
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.VideoId(
methodDescriptor
)
}
}

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.utils.videoid.general.fingerprint
package app.revanced.patches.youtube.video.videoid.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@ -1,11 +1,14 @@
package app.revanced.patches.youtube.utils.videoid.withoutshorts.fingerprint
package app.revanced.patches.youtube.video.videoid.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object VideoIdWithoutShortsFingerprint : MethodFingerprint(
/**
* Renamed from VideoIdWithoutShortsFingerprint
*/
internal object VideoIdFingerprintBackgroundPlay : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.DECLARED_SYNCHRONIZED,
parameters = listOf("L"),

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.utils.videoid.general.fingerprint
package app.revanced.patches.youtube.video.videoid.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint