fix(YouTube/SponsorBlock): improve create segment manual seek accuracy

This commit is contained in:
inotia00 2024-08-04 13:10:12 +09:00
parent e0e2a6f551
commit 0330f97c4f
3 changed files with 101 additions and 41 deletions

View File

@ -14,7 +14,6 @@ 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.patcher.util.smali.toInstructions 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.fingerprints.VideoEndFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.SHARED_PATH import app.revanced.patches.youtube.utils.integrations.Constants.SHARED_PATH
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
@ -26,6 +25,7 @@ import app.revanced.patches.youtube.video.information.fingerprints.OnPlaybackSpe
import app.revanced.patches.youtube.video.information.fingerprints.PlaybackInitializationFingerprint import app.revanced.patches.youtube.video.information.fingerprints.PlaybackInitializationFingerprint
import app.revanced.patches.youtube.video.information.fingerprints.PlaybackSpeedClassFingerprint 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.PlayerControllerSetTimeReferenceFingerprint
import app.revanced.patches.youtube.video.information.fingerprints.SeekRelativeFingerprint
import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprint import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprint
import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprintBackgroundPlay import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprintBackgroundPlay
import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprintShorts import app.revanced.patches.youtube.video.information.fingerprints.VideoIdFingerprintShorts
@ -36,6 +36,7 @@ import app.revanced.patches.youtube.video.information.fingerprints.VideoTitleFin
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.addFieldAndInstructions import app.revanced.util.addFieldAndInstructions
import app.revanced.util.alsoResolve
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.getTargetIndexOrThrow import app.revanced.util.getTargetIndexOrThrow
import app.revanced.util.getTargetIndexReversedOrThrow import app.revanced.util.getTargetIndexReversedOrThrow
@ -71,7 +72,6 @@ object VideoInformationPatch : BytecodePatch(
ChannelNameFingerprint, ChannelNameFingerprint,
MdxPlayerDirectorSetVideoStageFingerprint, MdxPlayerDirectorSetVideoStageFingerprint,
OnPlaybackSpeedItemClickFingerprint, OnPlaybackSpeedItemClickFingerprint,
OrganicPlaybackContextModelFingerprint,
PlaybackInitializationFingerprint, PlaybackInitializationFingerprint,
PlaybackSpeedClassFingerprint, PlaybackSpeedClassFingerprint,
PlayerControllerSetTimeReferenceFingerprint, PlayerControllerSetTimeReferenceFingerprint,
@ -117,10 +117,11 @@ object VideoInformationPatch : BytecodePatch(
/** /**
* Used in [VideoEndFingerprint] and [MdxPlayerDirectorSetVideoStageFingerprint]. * Used in [VideoEndFingerprint] and [MdxPlayerDirectorSetVideoStageFingerprint].
* Since both classes are inherited from the same class, * Since both classes are inherited from the same class,
* [VideoEndFingerprint] and [MdxPlayerDirectorSetVideoStageFingerprint] always have the same [seekSourceEnumType] and [seekSourceMethodName]. * [VideoEndFingerprint] and [MdxPlayerDirectorSetVideoStageFingerprint] always have the same [seekSourceEnumType], [seekSourceMethodName] and [seekRelativeSourceMethodName].
*/ */
private var seekSourceEnumType = "" private var seekSourceEnumType = ""
private var seekSourceMethodName = "" private var seekSourceMethodName = ""
private var seekRelativeSourceMethodName = ""
private lateinit var videoInformationMutableClass: MutableClass private lateinit var videoInformationMutableClass: MutableClass
private lateinit var context: BytecodeContext private lateinit var context: BytecodeContext
@ -138,28 +139,18 @@ object VideoInformationPatch : BytecodePatch(
internal lateinit var speedSelectionInsertMethod: MutableMethod internal lateinit var speedSelectionInsertMethod: MutableMethod
internal lateinit var videoEndMethod: MutableMethod internal lateinit var videoEndMethod: MutableMethod
private fun getSeekToConstructorMethod( private fun addSeekInterfaceMethods(
result: MethodFingerprintResult, result: MethodFingerprintResult,
seekMethodName: String,
methodName: String, methodName: String,
fieldMethodName: String,
fieldName: String fieldName: String
): Pair<MutableMethod, Int> { ) {
result.mutableMethod.apply { result.mutableMethod.apply {
val constructorMethod =
result.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) }
val constructorInsertIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
} + 1
if (seekSourceEnumType.isEmpty() && seekSourceMethodName.isEmpty()) {
seekSourceEnumType = parameterTypes[1].toString()
seekSourceMethodName = name
}
result.mutableClass.methods.add( result.mutableClass.methods.add(
ImmutableMethod( ImmutableMethod(
definingClass, definingClass,
"seekTo", fieldMethodName,
listOf(ImmutableMethodParameter("J", annotations, "time")), listOf(ImmutableMethodParameter("J", annotations, "time")),
"Z", "Z",
AccessFlags.PUBLIC or AccessFlags.FINAL, AccessFlags.PUBLIC or AccessFlags.FINAL,
@ -167,8 +158,9 @@ object VideoInformationPatch : BytecodePatch(
null, null,
ImmutableMethodImplementation( ImmutableMethodImplementation(
4, """ 4, """
# 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}, ${definingClass}->$seekSourceMethodName(J$seekSourceEnumType)Z invoke-virtual {p0, p1, p2, v0}, ${definingClass}->$seekMethodName(J$seekSourceEnumType)Z
move-result p1 move-result p1
return p1 return p1
""".toInstructions(), """.toInstructions(),
@ -181,7 +173,7 @@ object VideoInformationPatch : BytecodePatch(
val smaliInstructions = val smaliInstructions =
""" """
if-eqz v0, :ignore if-eqz v0, :ignore
invoke-virtual {v0, p0, p1}, $definingClass->seekTo(J)Z invoke-virtual {v0, p0, p1}, $definingClass->$fieldMethodName(J)Z
move-result v0 move-result v0
return v0 return v0
:ignore :ignore
@ -197,8 +189,6 @@ object VideoInformationPatch : BytecodePatch(
smaliInstructions, smaliInstructions,
true true
) )
return Pair(constructorMethod, constructorInsertIndex)
} }
} }
@ -208,16 +198,37 @@ object VideoInformationPatch : BytecodePatch(
context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!.mutableClass context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!.mutableClass
VideoEndFingerprint.resultOrThrow().let { VideoEndFingerprint.resultOrThrow().let {
val (playerConstructorMethod, playerConstructorInsertIndex) = it.mutableMethod.apply {
getSeekToConstructorMethod(it, "overrideVideoTime", "videoInformationClass") playerConstructorMethod =
it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) }
this.playerConstructorMethod = playerConstructorMethod playerConstructorInsertIndex = playerConstructorMethod.indexOfFirstInstructionOrThrow {
this.playerConstructorInsertIndex = playerConstructorInsertIndex opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
} + 1
// hook the player controller for use through integrations // hook the player controller for use through integrations
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize") onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
it.mutableMethod.apply { seekSourceEnumType = parameterTypes[1].toString()
seekSourceMethodName = name
seekRelativeSourceMethodName = SeekRelativeFingerprint.alsoResolve(context, VideoEndFingerprint).mutableMethod.name
// Create integrations interface methods.
addSeekInterfaceMethods(
it,
seekSourceMethodName,
"overrideVideoTime",
"seekTo",
"videoInformationClass"
)
addSeekInterfaceMethods(
it,
seekRelativeSourceMethodName,
"overrideVideoTimeRelative",
"seekToRelative",
"videoInformationClass"
)
val literalIndex = getWideLiteralInstructionIndex(45368273) val literalIndex = getWideLiteralInstructionIndex(45368273)
val walkerIndex = val walkerIndex =
getTargetIndexReversedOrThrow(literalIndex, Opcode.INVOKE_VIRTUAL_RANGE) getTargetIndexReversedOrThrow(literalIndex, Opcode.INVOKE_VIRTUAL_RANGE)
@ -228,14 +239,33 @@ object VideoInformationPatch : BytecodePatch(
} }
MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow().let { MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow().let {
val (mdxConstructorMethod, mdxConstructorInsertIndex) = it.mutableMethod.apply {
getSeekToConstructorMethod(it, "overrideMDXVideoTime", "videoInformationMDXClass") mdxConstructorMethod =
it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) }
this.mdxConstructorMethod = mdxConstructorMethod mdxConstructorInsertIndex = mdxConstructorMethod.indexOfFirstInstructionOrThrow {
this.mdxConstructorInsertIndex = mdxConstructorInsertIndex opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
} + 1
// hook the MDX director for use through integrations // hook the MDX director for use through integrations
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize") onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
// Create integrations interface methods.
addSeekInterfaceMethods(
it,
seekSourceMethodName,
"overrideMDXVideoTime",
"seekTo",
"videoInformationMDXClass"
)
addSeekInterfaceMethods(
it,
seekRelativeSourceMethodName,
"overrideMDXVideoTimeRelative",
"seekToRelative",
"videoInformationMDXClass"
)
}
} }
/** /**
@ -495,7 +525,7 @@ object VideoInformationPatch : BytecodePatch(
internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) = internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) =
playerConstructorMethod.addInstruction( playerConstructorMethod.addInstruction(
playerConstructorInsertIndex++, playerConstructorInsertIndex++,
"invoke-static/range { p0 .. p0 }, $targetMethodClass->$targetMethodName(Ljava/lang/Object;)V" "invoke-static { }, $targetMethodClass->$targetMethodName()V"
) )
/** /**
@ -507,7 +537,7 @@ object VideoInformationPatch : BytecodePatch(
internal fun onCreateHookMdx(targetMethodClass: String, targetMethodName: String) = internal fun onCreateHookMdx(targetMethodClass: String, targetMethodName: String) =
mdxConstructorMethod.addInstruction( mdxConstructorMethod.addInstruction(
mdxConstructorInsertIndex++, mdxConstructorInsertIndex++,
"invoke-static/range { p0 .. p0 }, $targetMethodClass->$targetMethodName(Ljava/lang/Object;)V" "invoke-static { }, $targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
) )
/** /**

View File

@ -0,0 +1,22 @@
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.fingerprints.VideoEndFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Resolves using class found in [VideoEndFingerprint].
*/
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

@ -19,6 +19,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
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
import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction 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.instruction.ReferenceInstruction
@ -647,15 +648,22 @@ fun MutableClass.addFieldAndInstructions(
.filter { method -> method.name == "<init>" } .filter { method -> method.name == "<init>" }
.forEach { mutableMethod -> .forEach { mutableMethod ->
mutableMethod.apply { mutableMethod.apply {
val initializeIndex = getTargetIndexWithMethodReferenceName("<init>") val initializeIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
}
val insertIndex = if (initializeIndex == -1) val insertIndex = if (initializeIndex == -1)
1 1
else else
initializeIndex + 1 initializeIndex + 1
val initializeRegister = if (initializeIndex == -1)
"p0"
else
"v${getInstruction<FiveRegisterInstruction>(initializeIndex).registerC}"
addInstruction( addInstruction(
insertIndex, insertIndex,
"sput-object p0, $objectCall" "sput-object $initializeRegister, $objectCall"
) )
} }
} }