fix(YouTube - SponsorBlock): Improve create segment manual seek accuracy (#3491)

This commit is contained in:
LisoUseInAIKyrios 2024-08-02 09:40:28 -04:00 committed by GitHub
parent ee021e37cc
commit 15449819ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 116 additions and 51 deletions

View file

@ -2104,6 +2104,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
} }
public final class app/revanced/util/BytecodeUtilsKt { public final class app/revanced/util/BytecodeUtilsKt {
public static final fun alsoResolve (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;
public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;

View file

@ -16,6 +16,7 @@ import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.reques
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnResponseStartedFingerprint import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnSucceededFingerprint import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnSucceededFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.util.alsoResolve
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@ -84,17 +85,14 @@ object CronetImageUrlHook : BytecodePatch(
} }
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
also { resolve(context, fingerprint.resultOrThrow().classDef) }.resultOrThrow()
loadImageUrlMethod = MessageDigestImageUrlFingerprint loadImageUrlMethod = MessageDigestImageUrlFingerprint
.alsoResolve(MessageDigestImageUrlParentFingerprint).mutableMethod .alsoResolve(context, MessageDigestImageUrlParentFingerprint).mutableMethod
loadImageSuccessCallbackMethod = OnSucceededFingerprint loadImageSuccessCallbackMethod = OnSucceededFingerprint
.alsoResolve(OnResponseStartedFingerprint).mutableMethod .alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
loadImageErrorCallbackMethod = OnFailureFingerprint loadImageErrorCallbackMethod = OnFailureFingerprint
.alsoResolve(OnResponseStartedFingerprint).mutableMethod .alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
// The URL is required for the failure callback hook, but the URL field is obfuscated. // The URL is required for the failure callback hook, but the URL field is obfuscated.
// Add a helper get method that returns the URL field. // Add a helper get method that returns the URL field.

View file

@ -7,12 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.video.information.fingerprints.* import app.revanced.patches.youtube.video.information.fingerprints.*
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.alsoResolve
import app.revanced.util.exception import app.revanced.util.exception
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
@ -45,9 +47,11 @@ object VideoInformationPatch : BytecodePatch(
) )
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/VideoInformation;" private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/VideoInformation;"
private const val INTEGRATIONS_PLAYER_INTERFACE = "Lapp/revanced/integrations/youtube/patches/VideoInformation${'$'}PlaybackController;"
private lateinit var playerInitMethod: MutableMethod private lateinit var playerInitMethod: MutableMethod
private var playerInitInsertIndex = 4 private var playerInitInsertIndex = -1
private var playerInitInsertRegister = -1
private lateinit var mdxInitMethod: MutableMethod private lateinit var mdxInitMethod: MutableMethod
private var mdxInitInsertIndex = -1 private var mdxInitInsertIndex = -1
@ -70,42 +74,43 @@ object VideoInformationPatch : BytecodePatch(
with(PlayerInitFingerprint.resultOrThrow()) { with(PlayerInitFingerprint.resultOrThrow()) {
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) } playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
// hook the player controller for use through integrations // find the location of the first invoke-direct call and extract the register storing the 'this' object reference.
val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
}
playerInitInsertRegister = playerInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
playerInitInsertIndex = initThisIndex + 1
// Hook the player controller for use through integrations.
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize") onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
// seek method
val seekFingerprintResultMethod = val seekFingerprintResultMethod =
SeekFingerprint.also { it.resolve(context, classDef) }.resultOrThrow().method SeekFingerprint.alsoResolve(context, PlayerInitFingerprint).method
val seekRelativeFingerprintResultMethod =
SeekRelativeFingerprint.alsoResolve(context, PlayerInitFingerprint).method
// create helper method // Create integrations interface methods.
val seekHelperMethod = generateSeekMethodHelper(seekFingerprintResultMethod) addSeekInterfaceMethods(mutableClass, seekFingerprintResultMethod, seekRelativeFingerprintResultMethod)
// add the seekTo method to the class for the integrations to call
mutableClass.methods.add(seekHelperMethod)
} }
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) { with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) } mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference
val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow { val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>" opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
} }
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
mdxInitInsertIndex = initThisIndex + 1 mdxInitInsertIndex = initThisIndex + 1
// hook the MDX director for use through integrations // Hook the MDX director for use through integrations.
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx") onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
// MDX seek method
val mdxSeekFingerprintResultMethod = val mdxSeekFingerprintResultMethod =
MdxSeekFingerprint.apply { resolve(context, classDef) }.resultOrThrow().method MdxSeekFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
val mdxSeekRelativeFingerprintResultMethod =
MdxSeekRelativeFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
// create helper method addSeekInterfaceMethods(mutableClass, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod)
val mdxSeekHelperMethod = generateSeekMethodHelper(mdxSeekFingerprintResultMethod)
// add the seekTo method to the class for the integrations to call
mutableClass.methods.add(mdxSeekHelperMethod)
} }
with(CreateVideoPlayerSeekbarFingerprint.result!!) { with(CreateVideoPlayerSeekbarFingerprint.result!!) {
@ -173,33 +178,42 @@ object VideoInformationPatch : BytecodePatch(
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed") userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
} }
private fun generateSeekMethodHelper(seekMethod: Method): MutableMethod { private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) {
// Add the interface and methods that integrations calls.
targetClass.interfaces.add(INTEGRATIONS_PLAYER_INTERFACE)
// create helper method arrayOf(
val generatedMethod = ImmutableMethod( seekToMethod to "seekTo",
seekMethod.definingClass, seekToRelativeMethod to "seekToRelative"
"seekTo", ).forEach { (method, name) ->
listOf(ImmutableMethodParameter("J", null, "time")), // Add interface method.
"Z", // Get enum type for the seek helper method.
AccessFlags.PUBLIC or AccessFlags.FINAL, val seekSourceEnumType = method.parameterTypes[1].toString()
null, null,
MutableMethodImplementation(4)
).toMutable()
// get enum type for the seek helper method val interfaceImplementation = ImmutableMethod(
val seekSourceEnumType = seekMethod.parameterTypes[1].toString() targetClass.type,
name,
listOf(ImmutableMethodParameter("J", null, "time")),
"Z",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null, null,
MutableMethodImplementation(4)
).toMutable()
// insert helper method instructions // Insert helper method instructions.
generatedMethod.addInstructions( interfaceImplementation.addInstructions(
0, 0,
""" """
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType # first enum (field a) is SEEK_SOURCE_UNKNOWN
invoke-virtual { p0, p1, p2, v0 }, $seekMethod sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
move-result p1 invoke-virtual { p0, p1, p2, v0 }, $method
return p1 move-result p1
""" return p1
) """
return generatedMethod )
targetClass.methods.add(interfaceImplementation)
}
} }
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) = private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
@ -220,8 +234,8 @@ object VideoInformationPatch : BytecodePatch(
internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) = internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) =
playerInitMethod.insert( playerInitMethod.insert(
playerInitInsertIndex++, playerInitInsertIndex++,
"v0", "v$playerInitInsertRegister",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V" "$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
) )
/** /**
@ -234,7 +248,7 @@ object VideoInformationPatch : BytecodePatch(
mdxInitMethod.insert( mdxInitMethod.insert(
mdxInitInsertIndex++, mdxInitInsertIndex++,
"v$mdxInitInsertRegister", "v$mdxInitInsertRegister",
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V" "$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
) )
/** /**

View file

@ -5,6 +5,9 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
/**
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
*/
internal object MdxSeekFingerprint : MethodFingerprint( internal object MdxSeekFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z", returnType = "Z",

View file

@ -0,0 +1,19 @@
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
/**
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
*/
internal object MdxSeekRelativeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf("J", "L"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE
)
)

View file

@ -3,6 +3,9 @@ package app.revanced.patches.youtube.video.information.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
/**
* Resolves using class found in [PlayerInitFingerprint].
*/
internal object SeekFingerprint : MethodFingerprint( internal object SeekFingerprint : MethodFingerprint(
strings = listOf("Attempting to seek during an ad") strings = listOf("Attempting to seek during an ad")
) )

View file

@ -0,0 +1,21 @@
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
/**
* Resolves using class found in [PlayerInitFingerprint].
*/
internal object SeekRelativeFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Z",
parameters = listOf("J", "L"),
opcodes = listOf(
Opcode.ADD_LONG_2ADDR,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.RETURN
)
)

View file

@ -249,3 +249,9 @@ fun Iterable<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { f
fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { fingerprint -> fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { fingerprint ->
fingerprint.returnEarly(bool) fingerprint.returnEarly(bool)
} }
/**
* Resolves this fingerprint using the classDef of a parent fingerprint.
*/
fun MethodFingerprint.alsoResolve(context: BytecodeContext, parentFingerprint: MethodFingerprint) =
also { resolve(context, parentFingerprint.resultOrThrow().classDef) }.resultOrThrow()