From 50be52cfd8c3035e5e59003a7aa39236d254fc79 Mon Sep 17 00:00:00 2001 From: inotia00 Date: Sat, 4 Mar 2023 02:31:52 +0900 Subject: [PATCH] refactor(video-id-hook): match with the official ReVanced's source code --- .../PlayerControllerFingerprint.kt | 10 -- .../fingerprint/RepeatListenerFingerprint.kt | 21 --- ...t => VideoTimeHighPrecisionFingerprint.kt} | 2 +- ...ideoTimeHighPrecisionParentFingerprint.kt} | 2 +- .../patch/MainstreamVideoIdPatch.kt | 168 ++++++++++-------- 5 files changed, 94 insertions(+), 109 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/PlayerControllerFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/RepeatListenerFingerprint.kt rename src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/{VideoTimeFingerprint.kt => VideoTimeHighPrecisionFingerprint.kt} (89%) rename src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/{VideoTimeParentFingerprint.kt => VideoTimeHighPrecisionParentFingerprint.kt} (76%) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/PlayerControllerFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/PlayerControllerFingerprint.kt deleted file mode 100644 index 3b956b42e..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/PlayerControllerFingerprint.kt +++ /dev/null @@ -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" - } -) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/RepeatListenerFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/RepeatListenerFingerprint.kt deleted file mode 100644 index 5b9985516..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/RepeatListenerFingerprint.kt +++ /dev/null @@ -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" - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeHighPrecisionFingerprint.kt similarity index 89% rename from src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeHighPrecisionFingerprint.kt index 69b85aedc..e58f4a07a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeHighPrecisionFingerprint.kt @@ -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"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeHighPrecisionParentFingerprint.kt similarity index 76% rename from src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeParentFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeHighPrecisionParentFingerprint.kt index 9987110c8..b5eb3e2e4 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeParentFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/fingerprint/VideoTimeHighPrecisionParentFingerprint.kt @@ -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=") ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/patch/MainstreamVideoIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/patch/MainstreamVideoIdPatch.kt index 47c0a737e..883b5ad27 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/patch/MainstreamVideoIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/videoid/mainstream/patch/MainstreamVideoIdPatch.kt @@ -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), + } } }