Skip to content

Commit 2e8d5c6

Browse files
author
LisoUseInAIKyrios
authored
fix(YouTube - SponsorBlock): Improve create segment manual seek accuracy (#3491)
1 parent 025766b commit 2e8d5c6

File tree

8 files changed

+119
-54
lines changed

8 files changed

+119
-54
lines changed

api/revanced-patches.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2104,6 +2104,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
21042104
}
21052105

21062106
public final class app/revanced/util/BytecodeUtilsKt {
2107+
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;
21072108
public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
21082109
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;
21092110
public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;

src/main/kotlin/app/revanced/patches/youtube/misc/imageurlhook/CronetImageUrlHook.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.reques
1616
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
1717
import app.revanced.patches.youtube.misc.imageurlhook.fingerprints.cronet.request.callback.OnSucceededFingerprint
1818
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
19+
import app.revanced.util.alsoResolve
1920
import app.revanced.util.resultOrThrow
2021
import com.android.tools.smali.dexlib2.AccessFlags
2122
import com.android.tools.smali.dexlib2.Opcode
@@ -84,17 +85,14 @@ object CronetImageUrlHook : BytecodePatch(
8485
}
8586

8687
override fun execute(context: BytecodeContext) {
87-
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
88-
also { resolve(context, fingerprint.resultOrThrow().classDef) }.resultOrThrow()
89-
9088
loadImageUrlMethod = MessageDigestImageUrlFingerprint
91-
.alsoResolve(MessageDigestImageUrlParentFingerprint).mutableMethod
89+
.alsoResolve(context, MessageDigestImageUrlParentFingerprint).mutableMethod
9290

9391
loadImageSuccessCallbackMethod = OnSucceededFingerprint
94-
.alsoResolve(OnResponseStartedFingerprint).mutableMethod
92+
.alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
9593

9694
loadImageErrorCallbackMethod = OnFailureFingerprint
97-
.alsoResolve(OnResponseStartedFingerprint).mutableMethod
95+
.alsoResolve(context, OnResponseStartedFingerprint).mutableMethod
9896

9997
// The URL is required for the failure callback hook, but the URL field is obfuscated.
10098
// Add a helper get method that returns the URL field.

src/main/kotlin/app/revanced/patches/youtube/video/information/VideoInformationPatch.kt

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
77
import app.revanced.patcher.extensions.or
88
import app.revanced.patcher.patch.BytecodePatch
99
import app.revanced.patcher.patch.annotation.Patch
10+
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
1011
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
1112
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
1213
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
1314
import app.revanced.patches.youtube.video.information.fingerprints.*
1415
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
1516
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
17+
import app.revanced.util.alsoResolve
1618
import app.revanced.util.exception
1719
import app.revanced.util.getReference
1820
import app.revanced.util.indexOfFirstInstructionOrThrow
@@ -45,9 +47,11 @@ object VideoInformationPatch : BytecodePatch(
4547
)
4648
) {
4749
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/VideoInformation;"
50+
private const val INTEGRATIONS_PLAYER_INTERFACE = "Lapp/revanced/integrations/youtube/patches/VideoInformation${'$'}PlaybackController;"
4851

4952
private lateinit var playerInitMethod: MutableMethod
50-
private var playerInitInsertIndex = 4
53+
private var playerInitInsertIndex = -1
54+
private var playerInitInsertRegister = -1
5155

5256
private lateinit var mdxInitMethod: MutableMethod
5357
private var mdxInitInsertIndex = -1
@@ -70,42 +74,43 @@ object VideoInformationPatch : BytecodePatch(
7074
with(PlayerInitFingerprint.resultOrThrow()) {
7175
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
7276

73-
// hook the player controller for use through integrations
77+
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference.
78+
val initThisIndex = playerInitMethod.indexOfFirstInstructionOrThrow {
79+
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
80+
}
81+
playerInitInsertRegister = playerInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
82+
playerInitInsertIndex = initThisIndex + 1
83+
84+
// Hook the player controller for use through integrations.
7485
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize")
7586

76-
// seek method
7787
val seekFingerprintResultMethod =
78-
SeekFingerprint.also { it.resolve(context, classDef) }.resultOrThrow().method
79-
80-
// create helper method
81-
val seekHelperMethod = generateSeekMethodHelper(seekFingerprintResultMethod)
88+
SeekFingerprint.alsoResolve(context, PlayerInitFingerprint).method
89+
val seekRelativeFingerprintResultMethod =
90+
SeekRelativeFingerprint.alsoResolve(context, PlayerInitFingerprint).method
8291

83-
// add the seekTo method to the class for the integrations to call
84-
mutableClass.methods.add(seekHelperMethod)
92+
// Create integrations interface methods.
93+
addSeekInterfaceMethods(mutableClass, seekFingerprintResultMethod, seekRelativeFingerprintResultMethod)
8594
}
8695

8796
with(MdxPlayerDirectorSetVideoStageFingerprint.resultOrThrow()) {
8897
mdxInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
8998

90-
// find the location of the first invoke-direct call and extract the register storing the 'this' object reference
9199
val initThisIndex = mdxInitMethod.indexOfFirstInstructionOrThrow {
92100
opcode == Opcode.INVOKE_DIRECT && getReference<MethodReference>()?.name == "<init>"
93101
}
94102
mdxInitInsertRegister = mdxInitMethod.getInstruction<FiveRegisterInstruction>(initThisIndex).registerC
95103
mdxInitInsertIndex = initThisIndex + 1
96104

97-
// hook the MDX director for use through integrations
105+
// Hook the MDX director for use through integrations.
98106
onCreateHookMdx(INTEGRATIONS_CLASS_DESCRIPTOR, "initializeMdx")
99107

100-
// MDX seek method
101108
val mdxSeekFingerprintResultMethod =
102-
MdxSeekFingerprint.apply { resolve(context, classDef) }.resultOrThrow().method
109+
MdxSeekFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
110+
val mdxSeekRelativeFingerprintResultMethod =
111+
MdxSeekRelativeFingerprint.alsoResolve(context, MdxPlayerDirectorSetVideoStageFingerprint).method
103112

104-
// create helper method
105-
val mdxSeekHelperMethod = generateSeekMethodHelper(mdxSeekFingerprintResultMethod)
106-
107-
// add the seekTo method to the class for the integrations to call
108-
mutableClass.methods.add(mdxSeekHelperMethod)
113+
addSeekInterfaceMethods(mutableClass, mdxSeekFingerprintResultMethod, mdxSeekRelativeFingerprintResultMethod)
109114
}
110115

111116
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
@@ -173,33 +178,42 @@ object VideoInformationPatch : BytecodePatch(
173178
userSelectedPlaybackSpeedHook(INTEGRATIONS_CLASS_DESCRIPTOR, "userSelectedPlaybackSpeed")
174179
}
175180

176-
private fun generateSeekMethodHelper(seekMethod: Method): MutableMethod {
177-
178-
// create helper method
179-
val generatedMethod = ImmutableMethod(
180-
seekMethod.definingClass,
181-
"seekTo",
182-
listOf(ImmutableMethodParameter("J", null, "time")),
183-
"Z",
184-
AccessFlags.PUBLIC or AccessFlags.FINAL,
185-
null, null,
186-
MutableMethodImplementation(4)
187-
).toMutable()
188-
189-
// get enum type for the seek helper method
190-
val seekSourceEnumType = seekMethod.parameterTypes[1].toString()
191-
192-
// insert helper method instructions
193-
generatedMethod.addInstructions(
194-
0,
195-
"""
196-
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
197-
invoke-virtual { p0, p1, p2, v0 }, $seekMethod
198-
move-result p1
199-
return p1
200-
"""
201-
)
202-
return generatedMethod
181+
private fun addSeekInterfaceMethods(targetClass: MutableClass, seekToMethod: Method, seekToRelativeMethod: Method) {
182+
// Add the interface and methods that integrations calls.
183+
targetClass.interfaces.add(INTEGRATIONS_PLAYER_INTERFACE)
184+
185+
arrayOf(
186+
seekToMethod to "seekTo",
187+
seekToRelativeMethod to "seekToRelative"
188+
).forEach { (method, name) ->
189+
// Add interface method.
190+
// Get enum type for the seek helper method.
191+
val seekSourceEnumType = method.parameterTypes[1].toString()
192+
193+
val interfaceImplementation = ImmutableMethod(
194+
targetClass.type,
195+
name,
196+
listOf(ImmutableMethodParameter("J", null, "time")),
197+
"Z",
198+
AccessFlags.PUBLIC or AccessFlags.FINAL,
199+
null, null,
200+
MutableMethodImplementation(4)
201+
).toMutable()
202+
203+
// Insert helper method instructions.
204+
interfaceImplementation.addInstructions(
205+
0,
206+
"""
207+
# first enum (field a) is SEEK_SOURCE_UNKNOWN
208+
sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType
209+
invoke-virtual { p0, p1, p2, v0 }, $method
210+
move-result p1
211+
return p1
212+
"""
213+
)
214+
215+
targetClass.methods.add(interfaceImplementation)
216+
}
203217
}
204218

205219
private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) =
@@ -220,8 +234,8 @@ object VideoInformationPatch : BytecodePatch(
220234
internal fun onCreateHook(targetMethodClass: String, targetMethodName: String) =
221235
playerInitMethod.insert(
222236
playerInitInsertIndex++,
223-
"v0",
224-
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
237+
"v$playerInitInsertRegister",
238+
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
225239
)
226240

227241
/**
@@ -234,7 +248,7 @@ object VideoInformationPatch : BytecodePatch(
234248
mdxInitMethod.insert(
235249
mdxInitInsertIndex++,
236250
"v$mdxInitInsertRegister",
237-
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
251+
"$targetMethodClass->$targetMethodName($INTEGRATIONS_PLAYER_INTERFACE)V"
238252
)
239253

240254
/**

src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/MdxSeekFingerprint.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
55
import com.android.tools.smali.dexlib2.AccessFlags
66
import com.android.tools.smali.dexlib2.Opcode
77

8+
/**
9+
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
10+
*/
811
internal object MdxSeekFingerprint : MethodFingerprint(
912
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
1013
returnType = "Z",
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package app.revanced.patches.youtube.video.information.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import com.android.tools.smali.dexlib2.AccessFlags
6+
import com.android.tools.smali.dexlib2.Opcode
7+
8+
/**
9+
* Resolves using class found in [MdxPlayerDirectorSetVideoStageFingerprint].
10+
*/
11+
internal object MdxSeekRelativeFingerprint : MethodFingerprint(
12+
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
13+
returnType = "Z",
14+
parameters = listOf("J", "L"),
15+
opcodes = listOf(
16+
Opcode.IGET_OBJECT,
17+
Opcode.INVOKE_INTERFACE
18+
)
19+
)

src/main/kotlin/app/revanced/patches/youtube/video/information/fingerprints/SeekFingerprint.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package app.revanced.patches.youtube.video.information.fingerprints
33

44
import app.revanced.patcher.fingerprint.MethodFingerprint
55

6+
/**
7+
* Resolves using class found in [PlayerInitFingerprint].
8+
*/
69
internal object SeekFingerprint : MethodFingerprint(
710
strings = listOf("Attempting to seek during an ad")
811
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package app.revanced.patches.youtube.video.information.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import com.android.tools.smali.dexlib2.AccessFlags
6+
import com.android.tools.smali.dexlib2.Opcode
7+
8+
/**
9+
* Resolves using class found in [PlayerInitFingerprint].
10+
*/
11+
internal object SeekRelativeFingerprint : MethodFingerprint(
12+
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
13+
returnType = "Z",
14+
parameters = listOf("J", "L"),
15+
opcodes = listOf(
16+
Opcode.ADD_LONG_2ADDR,
17+
Opcode.INVOKE_VIRTUAL,
18+
Opcode.MOVE_RESULT,
19+
Opcode.RETURN
20+
)
21+
)

src/main/kotlin/app/revanced/util/BytecodeUtils.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,9 @@ fun Iterable<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { f
249249
fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) = forEach { fingerprint ->
250250
fingerprint.returnEarly(bool)
251251
}
252+
253+
/**
254+
* Resolves this fingerprint using the classDef of a parent fingerprint.
255+
*/
256+
fun MethodFingerprint.alsoResolve(context: BytecodeContext, parentFingerprint: MethodFingerprint) =
257+
also { resolve(context, parentFingerprint.resultOrThrow().classDef) }.resultOrThrow()

0 commit comments

Comments
 (0)