mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2024-11-10 01:01:56 +01:00
fix(YouTube - SponsorBlock): Improve create segment manual seek accuracy (#3491)
This commit is contained in:
parent
ee021e37cc
commit
15449819ff
8 changed files with 116 additions and 51 deletions
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,12 +178,21 @@ 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) ->
|
||||||
|
// Add interface method.
|
||||||
|
// Get enum type for the seek helper method.
|
||||||
|
val seekSourceEnumType = method.parameterTypes[1].toString()
|
||||||
|
|
||||||
|
val interfaceImplementation = ImmutableMethod(
|
||||||
|
targetClass.type,
|
||||||
|
name,
|
||||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||||
"Z",
|
"Z",
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
@ -186,20 +200,20 @@ object VideoInformationPatch : BytecodePatch(
|
||||||
MutableMethodImplementation(4)
|
MutableMethodImplementation(4)
|
||||||
).toMutable()
|
).toMutable()
|
||||||
|
|
||||||
// get enum type for the seek helper method
|
// Insert helper method instructions.
|
||||||
val seekSourceEnumType = seekMethod.parameterTypes[1].toString()
|
interfaceImplementation.addInstructions(
|
||||||
|
|
||||||
// insert helper method instructions
|
|
||||||
generatedMethod.addInstructions(
|
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
|
# first enum (field a) is SEEK_SOURCE_UNKNOWN
|
||||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||||
invoke-virtual { p0, p1, p2, v0 }, $seekMethod
|
invoke-virtual { p0, p1, p2, v0 }, $method
|
||||||
move-result p1
|
move-result p1
|
||||||
return 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
|
@ -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")
|
||||||
)
|
)
|
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue