refactor(video-id-hook): match with the official ReVanced's source code

This commit is contained in:
inotia00
2023-03-04 02:31:52 +09:00
parent ed63b53dcf
commit 50be52cfd8
5 changed files with 94 additions and 109 deletions

View File

@ -1,10 +0,0 @@
package app.revanced.patches.youtube.misc.videoid.mainstream.fingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object PlayerControllerFingerprint : MethodFingerprint(
customFingerprint = {
it.definingClass == "Lapp/revanced/integrations/sponsorblock/PlayerController;"
&& it.name == "setSponsorBarRect"
}
)

View File

@ -1,21 +0,0 @@
package app.revanced.patches.youtube.misc.videoid.mainstream.fingerprint
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object RepeatListenerFingerprint : MethodFingerprint(
returnType = "Z",
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(
Opcode.INVOKE_VIRTUAL_RANGE,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.CHECK_CAST,
Opcode.CONST_WIDE_32
),
strings = listOf(
"ppoobsa"
)
)

View File

@ -5,7 +5,7 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object VideoTimeFingerprint : MethodFingerprint (
object VideoTimeHighPrecisionFingerprint : MethodFingerprint (
returnType = "V",
access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("J", "J", "J", "J", "I", "L"),

View File

@ -2,6 +2,6 @@ package app.revanced.patches.youtube.misc.videoid.mainstream.fingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object VideoTimeParentFingerprint : MethodFingerprint(
object VideoTimeHighPrecisionParentFingerprint : MethodFingerprint(
strings = listOf("MedialibPlayerTimeInfo{currentPositionMillis=")
)

View File

@ -14,21 +14,18 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
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.shared.annotation.YouTubeCompatibility
import app.revanced.patches.shared.fingerprints.VideoEndFingerprint
import app.revanced.patches.shared.fingerprints.VideoEndParentFingerprint
import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.timebar.patch.HookTimebarPatch
import app.revanced.patches.youtube.misc.videoid.mainstream.fingerprint.*
import app.revanced.util.integrations.Constants.UTILS_PATH
import app.revanced.util.integrations.Constants.VIDEO_PATH
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
import org.jf.dexlib2.iface.reference.FieldReference
import org.jf.dexlib2.iface.reference.MethodReference
import org.jf.dexlib2.immutable.ImmutableMethod
@ -48,45 +45,34 @@ import org.jf.dexlib2.util.MethodUtil
class MainstreamVideoIdPatch : BytecodePatch(
listOf(
MainstreamVideoIdFingerprint,
PlayerControllerFingerprint,
PlayerControllerSetTimeReferenceFingerprint,
PlayerInitFingerprint,
RepeatListenerFingerprint,
SeekFingerprint,
TimebarFingerprint,
VideoTimeFingerprint,
VideoTimeParentFingerprint
VideoEndParentFingerprint,
VideoTimeHighPrecisionFingerprint,
VideoTimeHighPrecisionParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
RepeatListenerFingerprint.result?.let {
val targetIndex = it.scanResult.patternScanResult!!.startIndex - 1
val endIndex = it.scanResult.patternScanResult!!.endIndex
with (it.mutableMethod) {
val targetReference = (instruction(targetIndex) as BuilderInstruction35c).reference.toString()
val firstRegister = (instruction(targetIndex) as BuilderInstruction35c).registerC
val secondRegister = (instruction(targetIndex) as BuilderInstruction35c).registerD
val dummyRegister = (instruction(endIndex) as Instruction31i).registerA
addInstructions(
targetIndex + 1, """
invoke-static {}, $UTILS_PATH/EnableAutoRepeatPatch;->shouldAutoRepeat()Z
move-result v$dummyRegister
if-nez v$dummyRegister, :bypass
invoke-virtual {v$firstRegister, v$secondRegister}, $targetReference
""", listOf(ExternalLabel("bypass", instruction(targetIndex + 1)))
)
removeInstruction(targetIndex)
}
} ?: return RepeatListenerFingerprint.toErrorResult()
VideoEndParentFingerprint.result?.classDef?.let { classDef ->
VideoEndFingerprint.also {
it.resolve(context, classDef)
}.result?.mutableMethod?.let { method ->
method.addInstruction(
method.implementation!!.instructions.size - 1,
"invoke-static {}, $VIDEO_PATH/VideoInformation;->videoEnd()V"
)
} ?: return VideoEndFingerprint.toErrorResult()
} ?: return VideoEndParentFingerprint.toErrorResult()
PlayerInitFingerprint.result?.let { parentResult ->
playerInitMethod = parentResult.mutableClass.methods.first { MethodUtil.isConstructor(it) }
// hook the player controller for use through integrations
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "playerController_onCreateHook")
SeekFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.let {
val resultMethod = it.method
@ -118,29 +104,27 @@ class MainstreamVideoIdPatch : BytecodePatch(
} ?: return SeekFingerprint.toErrorResult()
} ?: return PlayerInitFingerprint.toErrorResult()
VideoTimeParentFingerprint.result?.let { parentResult ->
VideoTimeFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.mutableMethod?.addInstruction(
0,
"invoke-static {p1, p2}, $VideoInformation->setCurrentVideoTimeHighPrecision(J)V"
) ?: return VideoTimeFingerprint.toErrorResult()
} ?: return VideoTimeParentFingerprint.toErrorResult()
/*
* Set the high precision video time method
*/
VideoTimeHighPrecisionParentFingerprint.result?.let { parentResult ->
VideoTimeHighPrecisionFingerprint.also { it.resolve(context, parentResult.classDef) }.result?.mutableMethod?.let { method ->
highPrecisionTimeMethod = method
} ?: return VideoTimeHighPrecisionFingerprint.toErrorResult()
} ?: return VideoTimeHighPrecisionParentFingerprint.toErrorResult()
/*
Set current video time
*/
* Hook the methods which set the time
*/
highPrecisionTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime")
/*
* Set current video time
*/
PlayerControllerSetTimeReferenceFingerprint.result?.let {
with(context
.toMethodWalker(it.method)
timeMethod = context.toMethodWalker(it.method)
.nextMethod(it.scanResult.patternScanResult!!.startIndex, true)
.getMethod() as MutableMethod
) {
addInstruction(
2,
"invoke-static {p1, p2}, $VideoInformation->setCurrentVideoTime(J)V"
)
}
} ?: return PlayerControllerSetTimeReferenceFingerprint.toErrorResult()
@ -162,7 +146,7 @@ class MainstreamVideoIdPatch : BytecodePatch(
val secondaryRegister = primaryRegister + 1
addInstruction(
index + 3,
"invoke-static {v$primaryRegister, v$secondaryRegister}, $VideoInformation->setCurrentVideoLength(J)V"
"invoke-static {v$primaryRegister, v$secondaryRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
)
break
}
@ -170,22 +154,6 @@ class MainstreamVideoIdPatch : BytecodePatch(
}
PlayerControllerFingerprint.result?.mutableMethod?.let {
val instructions = it.implementation!!.instructions
for ((index, instruction) in instructions.withIndex()) {
if (instruction.opcode != Opcode.CONST_STRING) continue
val register = (instruction as OneRegisterInstruction).registerA
it.replaceInstruction(
index,
"const-string v$register, \"$reactReference\""
)
break
}
} ?: return PlayerControllerFingerprint.toErrorResult()
MainstreamVideoIdFingerprint.result?.let {
insertIndex = it.scanResult.patternScanResult!!.endIndex
@ -193,25 +161,27 @@ class MainstreamVideoIdPatch : BytecodePatch(
insertMethod = this
videoIdRegister = (implementation!!.instructions[insertIndex] as OneRegisterInstruction).registerA
}
offset++ // offset so setCurrentVideoId is called before any injected call
offset++ // offset so setVideoId is called before any injected call
} ?: return MainstreamVideoIdFingerprint.toErrorResult()
injectCall("$VideoInformation->setCurrentVideoId(Ljava/lang/String;)V")
injectCallonCreate(VideoInformation, "onCreate")
injectCall("$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V")
return PatchResultSuccess()
}
companion object {
const val VideoInformation = "$VIDEO_PATH/VideoInformation;"
const val INTEGRATIONS_CLASS_DESCRIPTOR = "$VIDEO_PATH/VideoInformation;"
internal var reactReference: String? = null
private var offset = 0
private var insertIndex: Int = 0
private var reactReference: String? = null
private var videoIdRegister: Int = 0
private lateinit var insertMethod: MutableMethod
private lateinit var playerInitMethod: MutableMethod
private lateinit var timeMethod: MutableMethod
private lateinit var highPrecisionTimeMethod: MutableMethod
/**
* Adds an invoke-static instruction, called with the new id when the video changes
@ -226,11 +196,57 @@ class MainstreamVideoIdPatch : BytecodePatch(
)
}
fun injectCallonCreate(MethodClass: String, MethodName: String) =
playerInitMethod.addInstruction(
4,
"invoke-static {v0}, $MethodClass->$MethodName(Ljava/lang/Object;)V"
private fun MutableMethod.insert(insert: InsertIndex, register: String, descriptor: String) =
addInstruction(insert.index, "invoke-static { $register }, $descriptor")
private fun MutableMethod.insertTimeHook(insert: InsertIndex, descriptor: String) =
insert(insert, "p1, p2", descriptor)
/**
* Hook the player controller.
*
* @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(
InsertIndex.CREATE,
"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(
InsertIndex.TIME,
"$targetMethodClass->$targetMethodName(J)V"
)
/**
* Hook the high precision video time.
* The hooks is called extremely often (10 to 15 times a seconds), so use with caution.
* Note: the hook is usually called _off_ the main thread
*
* @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 highPrecisionTimeHook(targetMethodClass: String, targetMethodName: String) =
highPrecisionTimeMethod.insertTimeHook(
InsertIndex.HIGH_PRECISION_TIME,
"$targetMethodClass->$targetMethodName(J)V"
)
enum class InsertIndex(internal val index: Int) {
CREATE(4),
TIME(2),
HIGH_PRECISION_TIME(0),
}
}
}