mirror of
https://github.com/revanced/revanced-patches
synced 2024-12-03 16:42:54 +01:00
fix(YouTube - SponsorBlock): Skip segments when casting (#3331)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
7a63b73aa4
commit
d9395fdbca
@ -14,6 +14,9 @@ import app.revanced.patches.youtube.video.information.fingerprints.*
|
||||
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.BuilderInstruction
|
||||
@ -24,6 +27,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
@Patch(
|
||||
description = "Hooks YouTube to get information about the current playing video.",
|
||||
@ -32,6 +38,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
object VideoInformationPatch : BytecodePatch(
|
||||
setOf(
|
||||
PlayerInitFingerprint,
|
||||
MdxPlayerDirectorSetVideoStageFingerprint,
|
||||
CreateVideoPlayerSeekbarFingerprint,
|
||||
PlayerControllerSetTimeReferenceFingerprint,
|
||||
OnPlaybackSpeedItemClickFingerprint
|
||||
@ -42,12 +49,16 @@ object VideoInformationPatch : BytecodePatch(
|
||||
private lateinit var playerInitMethod: MutableMethod
|
||||
private var playerInitInsertIndex = 4
|
||||
|
||||
private lateinit var mdxInitMethod: MutableMethod
|
||||
private var mdxInitInsertIndex = -1
|
||||
private var mdxInitInsertRegister = -1
|
||||
|
||||
private lateinit var timeMethod: MutableMethod
|
||||
private var timeInitInsertIndex = 2
|
||||
|
||||
private lateinit var speedSelectionInsertMethod: MutableMethod
|
||||
private var speedSelectionInsertIndex = 0
|
||||
private var speedSelectionValueRegister = 0
|
||||
private var speedSelectionInsertIndex = -1
|
||||
private var speedSelectionValueRegister = -1
|
||||
|
||||
// Used by other patches.
|
||||
internal lateinit var setPlaybackSpeedContainerClassFieldReference: String
|
||||
@ -55,44 +66,48 @@ object VideoInformationPatch : BytecodePatch(
|
||||
internal lateinit var setPlaybackSpeedMethodReference: String
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
with(PlayerInitFingerprint.result!!) {
|
||||
|
||||
with(PlayerInitFingerprint.resultOrThrow()) {
|
||||
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// hook the player controller for use through integrations
|
||||
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
|
||||
|
||||
// seek method
|
||||
val seekFingerprintResultMethod = SeekFingerprint.also { it.resolve(context, classDef) }.result!!.method
|
||||
val seekFingerprintResultMethod =
|
||||
SeekFingerprint.also { it.resolve(context, classDef) }.resultOrThrow().method
|
||||
|
||||
// create helper method
|
||||
val seekHelperMethod = ImmutableMethod(
|
||||
seekFingerprintResultMethod.definingClass,
|
||||
"seekTo",
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
|
||||
// get enum type for the seek helper method
|
||||
val seekSourceEnumType = seekFingerprintResultMethod.parameterTypes[1].toString()
|
||||
|
||||
// insert helper method instructions
|
||||
seekHelperMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual {p0, p1, p2, v0}, ${seekFingerprintResultMethod.definingClass}->${seekFingerprintResultMethod.name}(J$seekSourceEnumType)Z
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
val seekHelperMethod = generateSeekMethodHelper(seekFingerprintResultMethod)
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(seekHelperMethod)
|
||||
}
|
||||
|
||||
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
|
||||
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 {
|
||||
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
|
||||
}
|
||||
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
|
||||
mdxInitInsertIndex = initThisIndex + 1
|
||||
|
||||
// hook the MDX director for use through integrations
|
||||
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
|
||||
|
||||
// MDX seek method
|
||||
val mdxSeekFingerprintResultMethod =
|
||||
MdxSeekFingerprint.apply { resolve(context, classDef) }.resultOrThrow().method
|
||||
|
||||
// create helper method
|
||||
val mdxSeekHelperMethod = generateSeekMethodHelper(mdxSeekFingerprintResultMethod)
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(mdxSeekHelperMethod)
|
||||
}
|
||||
|
||||
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
|
||||
val videoLengthMethodResult = VideoLengthFingerprint.also { it.resolve(context, classDef) }.result!!
|
||||
|
||||
@ -103,7 +118,7 @@ object VideoInformationPatch : BytecodePatch(
|
||||
|
||||
addInstruction(
|
||||
videoLengthMethodResult.scanResult.patternScanResult!!.endIndex,
|
||||
"invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
|
||||
"invoke-static { v$videoLengthRegister, v$dummyRegisterForLong }, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -158,6 +173,35 @@ object VideoInformationPatch : BytecodePatch(
|
||||
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
|
||||
}
|
||||
|
||||
private fun generateSeekMethodHelper(seekMethod: Method): MutableMethod {
|
||||
|
||||
// create helper method
|
||||
val generatedMethod = ImmutableMethod(
|
||||
seekMethod.definingClass,
|
||||
"seekTo",
|
||||
listOf(ImmutableMethodParameter("J", null, "time")),
|
||||
"Z",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null, null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable()
|
||||
|
||||
// get enum type for the seek helper method
|
||||
val seekSourceEnumType = seekMethod.parameterTypes[1].toString()
|
||||
|
||||
// insert helper method instructions
|
||||
generatedMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
|
||||
invoke-virtual { p0, p1, p2, v0 }, $seekMethod
|
||||
move-result p1
|
||||
return p1
|
||||
"""
|
||||
)
|
||||
return generatedMethod
|
||||
}
|
||||
|
||||
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
|
||||
addInstruction(insertIndex, "invoke-static { $register }, $descriptor")
|
||||
|
||||
@ -180,6 +224,19 @@ object VideoInformationPatch : BytecodePatch(
|
||||
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
|
||||
)
|
||||
|
||||
/**
|
||||
* Hook the MDX player director. Called when playing videos while casting to a big screen device.
|
||||
*
|
||||
* @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 onCreateHookMdx(targetMethodClass: String, targetMethodName: String) =
|
||||
mdxInitMethod.insert(
|
||||
mdxInitInsertIndex++,
|
||||
"v$mdxInitInsertRegister",
|
||||
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
|
||||
)
|
||||
|
||||
/**
|
||||
* Hook the video time.
|
||||
* The hook is usually called once per second.
|
||||
|
@ -0,0 +1,8 @@
|
||||
package app.revanced.patches.youtube.video.information.fingerprints
|
||||
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object MdxPlayerDirectorSetVideoStageFingerprint : MethodFingerprint(
|
||||
strings = listOf("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ")
|
||||
)
|
@ -0,0 +1,23 @@
|
||||
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
|
||||
|
||||
internal object MdxSeekFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
parameters = listOf("J", "L"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.RETURN,
|
||||
),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
// The instruction count is necessary here to avoid matching the relative version
|
||||
// of the seek method we're after, which has the same function signature as the
|
||||
// regular one, is in the same class, and even has the exact same 3 opcodes pattern.
|
||||
methodDef.implementation!!.instructions.count() == 3
|
||||
}
|
||||
)
|
Loading…
Reference in New Issue
Block a user