fix: apply multiple changes from integrations, refactor package structure, class names and implementations
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.annotation
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.41.37")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class PlayerControllerCompatibility
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
|
||||
@Name("create-video-player-seekbar-fingerprint")
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object CreateVideoPlayerSeekbarFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
strings = listOf("timed_markers_width")
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("player-controller-set-time-reference-fingerprint")
|
||||
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object PlayerControllerSetTimeReferenceFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(Opcode.INVOKE_DIRECT_RANGE, Opcode.IGET_OBJECT),
|
||||
strings = listOf("Media progress reported outside media playback: ")
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
|
||||
@Name("player-init-fingerprint")
|
||||
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object PlayerInitFingerprint : MethodFingerprint(
|
||||
strings = listOf(
|
||||
"playVideo called on player response with no videoStreamingData."
|
||||
),
|
||||
)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
|
||||
@Name("seek-fingerprint")
|
||||
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object SeekFingerprint : MethodFingerprint(
|
||||
strings = listOf("Attempting to seek during an ad")
|
||||
)
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("video-length-fingerprint")
|
||||
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoLengthFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(
|
||||
Opcode.MOVE_RESULT_WIDE,
|
||||
Opcode.CMP_LONG,
|
||||
Opcode.IF_LEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_WIDE,
|
||||
Opcode.GOTO,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_WIDE,
|
||||
Opcode.CONST_4,
|
||||
Opcode.INVOKE_VIRTUAL
|
||||
)
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
|
||||
|
||||
@Name("video-time-fingerprint")
|
||||
@SponsorBlockCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoTimeFingerprint : MethodFingerprint(
|
||||
strings = listOf("MedialibPlayerTimeInfo{currentPositionMillis=")
|
||||
)
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package app.revanced.patches.youtube.misc.video.information.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.data.toMethodWalker
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.instruction
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.video.speed.remember.annotation.RememberPlaybackSpeedCompatibility
|
||||
import app.revanced.patches.youtube.misc.video.information.fingerprints.*
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||
import org.jf.dexlib2.immutable.ImmutableMethodParameter
|
||||
import org.jf.dexlib2.util.MethodUtil
|
||||
|
||||
@Name("video-information")
|
||||
@Description("Hooks YouTube to get information about the current playing video.")
|
||||
@RememberPlaybackSpeedCompatibility
|
||||
@Version("0.0.1")
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
class VideoInformationPatch : BytecodePatch(
|
||||
listOf(
|
||||
PlayerInitFingerprint,
|
||||
CreateVideoPlayerSeekbarFingerprint,
|
||||
PlayerControllerSetTimeReferenceFingerprint,
|
||||
VideoTimeFingerprint,
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
with(PlayerInitFingerprint.result!!) {
|
||||
playerInitMethod = mutableClass.methods.first { MethodUtil.isConstructor(it) }
|
||||
|
||||
// hook the player controller for use through integrations
|
||||
onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "playerController_onCreateHook")
|
||||
|
||||
// seek method
|
||||
val seekFingerprintResultMethod = SeekFingerprint.also { it.resolve(context, classDef) }.result!!.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
|
||||
"""
|
||||
)
|
||||
|
||||
// add the seekTo method to the class for the integrations to call
|
||||
mutableClass.methods.add(seekHelperMethod)
|
||||
}
|
||||
|
||||
with(CreateVideoPlayerSeekbarFingerprint.result!!) {
|
||||
val videoLengthMethodResult = VideoLengthFingerprint.also { it.resolve(context, classDef) }.result!!
|
||||
|
||||
with(videoLengthMethodResult.mutableMethod) {
|
||||
val videoLengthRegisterIndex = videoLengthMethodResult.scanResult.patternScanResult!!.endIndex - 2
|
||||
val videoLengthRegister = (instruction(videoLengthRegisterIndex) as OneRegisterInstruction).registerA
|
||||
val dummyRegisterForLong = videoLengthRegister + 1 // required for long values since they are wide
|
||||
|
||||
addInstruction(
|
||||
videoLengthMethodResult.scanResult.patternScanResult!!.endIndex,
|
||||
"invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Set the video time method
|
||||
*/
|
||||
with(PlayerControllerSetTimeReferenceFingerprint.result!!) {
|
||||
timeMethod = context.toMethodWalker(method)
|
||||
.nextMethod(scanResult.patternScanResult!!.startIndex, true)
|
||||
.getMethod() as MutableMethod
|
||||
}
|
||||
|
||||
/*
|
||||
Set the high precision video time method
|
||||
*/
|
||||
highPrecisionTimeMethod =
|
||||
(object : MethodFingerprint("V", null, listOf("J", "J", "J", "J", "I", "L"), null) {}).also {
|
||||
it.resolve(context, VideoTimeFingerprint.result!!.classDef)
|
||||
}.result!!.mutableMethod
|
||||
|
||||
/*
|
||||
Hook the methods which set the time
|
||||
*/
|
||||
highPrecisionTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime")
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/VideoInformation;"
|
||||
|
||||
private lateinit var playerInitMethod: MutableMethod
|
||||
private lateinit var timeMethod: MutableMethod
|
||||
private lateinit var highPrecisionTimeMethod: MutableMethod
|
||||
|
||||
private fun MutableMethod.insert(insert: InsertIndex, register: String, descriptor: String) =
|
||||
addInstruction(insert.index, "invoke-static { $register }, $descriptor")
|
||||
|
||||
private fun MutableMethod.insertTimeHook(insert: InsertIndex, descriptor: String) =
|
||||
insert(insert, "p1, p2", descriptor)
|
||||
|
||||
/**
|
||||
* Hook the player controller.
|
||||
*
|
||||
* @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 onCreateHook(targetMethodClass: String, targetMethodName: String) =
|
||||
playerInitMethod.insert(
|
||||
InsertIndex.CREATE,
|
||||
"v0",
|
||||
"$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V"
|
||||
)
|
||||
|
||||
/**
|
||||
* Hook the video time.
|
||||
*
|
||||
* @param targetMethodClass The descriptor for the static method 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 videoTimeHook(targetMethodClass: String, targetMethodName: String) =
|
||||
timeMethod.insertTimeHook(
|
||||
InsertIndex.TIME,
|
||||
"$targetMethodClass->$targetMethodName(J)V"
|
||||
)
|
||||
|
||||
/**
|
||||
* Hook the high precision video time.
|
||||
*
|
||||
* @param targetMethodClass The descriptor for the static method 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 highPrecisionTimeHook(targetMethodClass: String, targetMethodName: String) =
|
||||
highPrecisionTimeMethod.insertTimeHook(
|
||||
InsertIndex.HIGH_PRECISION_TIME,
|
||||
"$targetMethodClass->$targetMethodName(J)V"
|
||||
)
|
||||
|
||||
enum class InsertIndex(internal val index: Int) {
|
||||
CREATE(4),
|
||||
TIME(2),
|
||||
HIGH_PRECISION_TIME(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.video.quality.annotations
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.36.37", "17.41.37", "17.42.35", "17.43.36")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class RememberVideoQualityCompatibility
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package app.revanced.patches.youtube.misc.video.quality.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.quality.annotations.RememberVideoQualityCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("video-quality-reference-fingerprint")
|
||||
@RememberVideoQualityCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoQualityReferenceFingerprint : MethodFingerprint(
|
||||
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(
|
||||
Opcode.IPUT_OBJECT, Opcode.RETURN_VOID
|
||||
)
|
||||
)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
package app.revanced.patches.youtube.misc.video.quality.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.quality.annotations.RememberVideoQualityCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("video-quality-setter-fingerprint")
|
||||
@RememberVideoQualityCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoQualitySetterFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
listOf("[L", "I", "I", "Z", "I"),
|
||||
listOf(
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.IPUT_BOOLEAN,
|
||||
)
|
||||
)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
|
||||
package app.revanced.patches.youtube.misc.video.quality.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
|
||||
import app.revanced.patches.youtube.misc.video.quality.annotations.RememberVideoQualityCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("video-user-quality-change-fingerprint")
|
||||
@RememberVideoQualityCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoUserQualityChangeFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
listOf("L","L","I","J"),
|
||||
listOf(
|
||||
Opcode.MOVE,
|
||||
Opcode.MOVE_WIDE,
|
||||
Opcode.INVOKE_INTERFACE_RANGE,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
)
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package app.revanced.patches.youtube.misc.video.quality.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.video.quality.annotations.RememberVideoQualityCompatibility
|
||||
import app.revanced.patches.youtube.misc.video.quality.fingerprints.VideoQualityReferenceFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.quality.fingerprints.VideoQualitySetterFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.quality.fingerprints.VideoUserQualityChangeFingerprint
|
||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.video.videoid.patch.VideoIdPatch
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import org.jf.dexlib2.iface.reference.FieldReference
|
||||
|
||||
@Patch
|
||||
@DependsOn([IntegrationsPatch::class, VideoIdPatch::class, SettingsPatch::class])
|
||||
@Name("remember-video-quality")
|
||||
@Description("Adds the ability to remember the video quality you chose in the video quality flyout.")
|
||||
@RememberVideoQualityCompatibility
|
||||
@Version("0.0.1")
|
||||
class RememberVideoQualityPatch : BytecodePatch(
|
||||
listOf(
|
||||
VideoQualitySetterFingerprint
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
SettingsPatch.PreferenceScreen.MISC.addPreferences(
|
||||
SwitchPreference(
|
||||
"revanced_remember_video_quality_selection",
|
||||
StringResource("revanced_remember_video_quality_selection_title", "Remember current video quality"),
|
||||
false,
|
||||
StringResource(
|
||||
"revanced_remember_video_quality_selection_summary_on",
|
||||
"The current video quality will not change"
|
||||
),
|
||||
StringResource(
|
||||
"revanced_remember_video_quality_selection_summary_off",
|
||||
"Video quality will be remembered until a new quality is chosen"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val setterMethod = VideoQualitySetterFingerprint.result!!
|
||||
|
||||
VideoUserQualityChangeFingerprint.resolve(context, setterMethod.classDef)
|
||||
val userQualityMethod = VideoUserQualityChangeFingerprint.result!!
|
||||
|
||||
VideoQualityReferenceFingerprint.resolve(context, setterMethod.classDef)
|
||||
val qualityFieldReference =
|
||||
VideoQualityReferenceFingerprint.result!!.method.let { method ->
|
||||
(method.implementation!!.instructions.elementAt(0) as ReferenceInstruction).reference as FieldReference
|
||||
}
|
||||
|
||||
VideoIdPatch.injectCall("Lapp/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch;->newVideoStarted(Ljava/lang/String;)V")
|
||||
|
||||
val qIndexMethodName =
|
||||
context.classes.single { it.type == qualityFieldReference.type }.methods.single { it.parameterTypes.first() == "I" }.name
|
||||
|
||||
setterMethod.mutableMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, ${setterMethod.classDef.type}->${qualityFieldReference.name}:${qualityFieldReference.type}
|
||||
const-string v1, "$qIndexMethodName"
|
||||
invoke-static {p1, p2, v0, v1}, Lapp/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch;->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I
|
||||
move-result p2
|
||||
""",
|
||||
)
|
||||
|
||||
userQualityMethod.mutableMethod.addInstruction(
|
||||
0,
|
||||
"invoke-static {p3}, Lapp/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch;->userChangedQuality(I)V"
|
||||
)
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.video.speed.custom.annotations
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.36.37", "17.41.37", "17.42.35", "17.43.36")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class CustomPlaybackSpeedCompatibility
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package app.revanced.patches.youtube.misc.video.speed.custom.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.annotations.CustomPlaybackSpeedCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("speed-array-generator-fingerprint")
|
||||
@FuzzyPatternScanMethod(2)
|
||||
@CustomPlaybackSpeedCompatibility
|
||||
@Version("0.0.1")
|
||||
object SpeedArrayGeneratorFingerprint : MethodFingerprint(
|
||||
"[L",
|
||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
opcodes = listOf(
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.GOTO,
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
),
|
||||
strings = listOf("0.0#")
|
||||
)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package app.revanced.patches.youtube.misc.video.speed.custom.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.annotations.CustomPlaybackSpeedCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("speed-limiter-fingerprint")
|
||||
@FuzzyPatternScanMethod(2)
|
||||
@CustomPlaybackSpeedCompatibility
|
||||
@Version("0.0.1")
|
||||
object SpeedLimiterFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
listOf("F"),
|
||||
listOf(
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.CONST_HIGH16,
|
||||
Opcode.GOTO,
|
||||
Opcode.CONST_HIGH16,
|
||||
Opcode.CONST_HIGH16,
|
||||
Opcode.INVOKE_STATIC,
|
||||
),
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.youtube.misc.video.speed.custom.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.annotations.CustomPlaybackSpeedCompatibility
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("video-speed-patch-fingerprint")
|
||||
@CustomPlaybackSpeedCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoSpeedPatchFingerprint : MethodFingerprint(
|
||||
opcodes = listOf(Opcode.FILL_ARRAY_DATA),
|
||||
customFingerprint = { methodDef ->
|
||||
methodDef.definingClass.endsWith("CustomVideoSpeedPatch;") && methodDef.name == "<clinit>"
|
||||
}
|
||||
)
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
package app.revanced.patches.youtube.misc.video.speed.custom.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.annotations.CustomPlaybackSpeedCompatibility
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.fingerprints.SpeedArrayGeneratorFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.fingerprints.SpeedLimiterFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.speed.custom.fingerprints.VideoSpeedPatchFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import org.jf.dexlib2.builder.instruction.BuilderArrayPayload
|
||||
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import org.jf.dexlib2.iface.reference.FieldReference
|
||||
import org.jf.dexlib2.iface.reference.MethodReference
|
||||
import java.util.stream.DoubleStream
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Patch
|
||||
@Name("custom-video-speed")
|
||||
@Description("Adds more video speed options.")
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
@CustomPlaybackSpeedCompatibility
|
||||
@Version("0.0.1")
|
||||
class CustomVideoSpeedPatch : BytecodePatch(
|
||||
listOf(
|
||||
SpeedArrayGeneratorFingerprint, SpeedLimiterFingerprint, VideoSpeedPatchFingerprint
|
||||
)
|
||||
) {
|
||||
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
val speedLimitMin = minVideoSpeed!!.toFloat()
|
||||
val speedLimitMax = maxVideoSpeed!!.toFloat().coerceAtLeast(speedLimitMin)
|
||||
val speedsGranularity = videoSpeedsGranularity!!.toFloat()
|
||||
|
||||
val arrayGenMethod = SpeedArrayGeneratorFingerprint.result?.mutableMethod!!
|
||||
val arrayGenMethodImpl = arrayGenMethod.implementation!!
|
||||
|
||||
val sizeCallIndex = arrayGenMethodImpl.instructions
|
||||
.indexOfFirst { ((it as? ReferenceInstruction)?.reference as? MethodReference)?.name == "size" }
|
||||
|
||||
if (sizeCallIndex == -1) return PatchResultError("Couldn't find call to size()")
|
||||
|
||||
val sizeCallResultRegister =
|
||||
(arrayGenMethodImpl.instructions.elementAt(sizeCallIndex + 1) as OneRegisterInstruction).registerA
|
||||
|
||||
arrayGenMethod.replaceInstruction(
|
||||
sizeCallIndex + 1,
|
||||
"const/4 v$sizeCallResultRegister, 0x0"
|
||||
)
|
||||
|
||||
val (arrayLengthConstIndex, arrayLengthConst) = arrayGenMethodImpl.instructions.withIndex()
|
||||
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 7 }
|
||||
|
||||
val arrayLengthConstDestination = (arrayLengthConst as OneRegisterInstruction).registerA
|
||||
|
||||
val videoSpeedsArrayType = "Lapp/revanced/integrations/patches/playback/speed/CustomVideoSpeedPatch;->videoSpeeds:[F"
|
||||
|
||||
arrayGenMethod.addInstructions(
|
||||
arrayLengthConstIndex + 1,
|
||||
"""
|
||||
sget-object v$arrayLengthConstDestination, $videoSpeedsArrayType
|
||||
array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination
|
||||
"""
|
||||
)
|
||||
|
||||
val (originalArrayFetchIndex, originalArrayFetch) = arrayGenMethodImpl.instructions.withIndex()
|
||||
.first {
|
||||
val reference = ((it.value as? ReferenceInstruction)?.reference as? FieldReference)
|
||||
reference?.definingClass?.contains("PlayerConfigModel") ?: false &&
|
||||
reference?.type == "[F"
|
||||
}
|
||||
|
||||
val originalArrayFetchDestination = (originalArrayFetch as OneRegisterInstruction).registerA
|
||||
|
||||
arrayGenMethod.replaceInstruction(
|
||||
originalArrayFetchIndex,
|
||||
"sget-object v$originalArrayFetchDestination, $videoSpeedsArrayType"
|
||||
)
|
||||
|
||||
val limiterMethod = SpeedLimiterFingerprint.result?.mutableMethod!!
|
||||
val limiterMethodImpl = limiterMethod.implementation!!
|
||||
|
||||
val (limiterMinConstIndex, limiterMinConst) = limiterMethodImpl.instructions.withIndex()
|
||||
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 0.25f.toRawBits() }
|
||||
val (limiterMaxConstIndex, limiterMaxConst) = limiterMethodImpl.instructions.withIndex()
|
||||
.first { (it.value as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits() }
|
||||
|
||||
val limiterMinConstDestination = (limiterMinConst as OneRegisterInstruction).registerA
|
||||
val limiterMaxConstDestination = (limiterMaxConst as OneRegisterInstruction).registerA
|
||||
|
||||
fun hexFloat(float: Float): String = "0x%08x".format(float.toRawBits())
|
||||
|
||||
limiterMethod.replaceInstruction(
|
||||
limiterMinConstIndex,
|
||||
"const/high16 v$limiterMinConstDestination, ${hexFloat(speedLimitMin)}"
|
||||
)
|
||||
limiterMethod.replaceInstruction(
|
||||
limiterMaxConstIndex,
|
||||
"const/high16 v$limiterMaxConstDestination, ${hexFloat(speedLimitMax)}"
|
||||
)
|
||||
|
||||
val constructorResult = VideoSpeedPatchFingerprint.result!!
|
||||
val constructor = constructorResult.mutableMethod
|
||||
val implementation = constructor.implementation!!
|
||||
|
||||
val stepsGranularity = 8F
|
||||
val step = speedLimitMax
|
||||
.minus(speedLimitMin) // calculate the range of the speeds
|
||||
.div(speedsGranularity)
|
||||
.times(stepsGranularity)
|
||||
.roundToInt()
|
||||
.div(stepsGranularity)// round to nearest multiple of stepsGranularity
|
||||
.coerceAtLeast(1 / stepsGranularity) // ensure steps are at least 1/8th of the step granularity
|
||||
|
||||
val videoSpeedsArray = buildList<Number> {
|
||||
DoubleStream
|
||||
.iterate(speedLimitMin.toDouble()) { it + step } // create a stream of speeds
|
||||
.let { speedStream ->
|
||||
for (speed in speedStream) {
|
||||
if (speed > speedLimitMax) break
|
||||
add(speed.toFloat().toRawBits())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjust the new array of speeds size
|
||||
constructor.replaceInstruction(
|
||||
0,
|
||||
"const/16 v0, ${videoSpeedsArray.size}"
|
||||
)
|
||||
|
||||
// create the payload with the new speeds
|
||||
val arrayPayloadIndex = implementation.instructions.size - 1
|
||||
implementation.replaceInstruction(
|
||||
arrayPayloadIndex,
|
||||
BuilderArrayPayload(
|
||||
4,
|
||||
videoSpeedsArray
|
||||
)
|
||||
)
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
|
||||
companion object : OptionsContainer() {
|
||||
private fun String?.validate(max: Int? = null) = this?.toFloatOrNull() != null &&
|
||||
toFloat().let { float ->
|
||||
float > 0 && max?.let { max -> float <= max } ?: true
|
||||
}
|
||||
|
||||
val videoSpeedsGranularity by option(
|
||||
PatchOption.StringOption(
|
||||
"granularity",
|
||||
"16",
|
||||
"Video speed granularity",
|
||||
"The granularity of the video speeds. The higher the value, the more speeds will be available.",
|
||||
true
|
||||
) {
|
||||
it.validate()
|
||||
}
|
||||
)
|
||||
|
||||
val minVideoSpeed by option(
|
||||
PatchOption.StringOption(
|
||||
"min",
|
||||
"0.25",
|
||||
"Minimum video speed",
|
||||
"The minimum video speed.",
|
||||
true
|
||||
) {
|
||||
it.validate()
|
||||
}
|
||||
)
|
||||
|
||||
val maxVideoSpeed by option(
|
||||
PatchOption.StringOption(
|
||||
"max",
|
||||
"5.0",
|
||||
"Maximum video speed",
|
||||
"The maximum video speed. Must be greater than the minimum video speed and smaller than 5.",
|
||||
true
|
||||
) {
|
||||
it.validate(5)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.video.videoid.annotation
|
||||
|
||||
import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Package
|
||||
|
||||
@Compatibility(
|
||||
[Package(
|
||||
"com.google.android.youtube", arrayOf("17.36.37", "17.41.37", "17.42.35", "17.43.36")
|
||||
)]
|
||||
)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
internal annotation class VideoIdCompatibility
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package app.revanced.patches.youtube.misc.video.videoid.fingerprint
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.video.videoid.annotation.VideoIdCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("video-id-fingerprint")
|
||||
@VideoIdCompatibility
|
||||
@Version("0.0.1")
|
||||
object VideoIdFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.DECLARED_SYNCHRONIZED or AccessFlags.FINAL or AccessFlags.PUBLIC,
|
||||
listOf("L"),
|
||||
listOf(Opcode.INVOKE_INTERFACE),
|
||||
customFingerprint = {
|
||||
it.definingClass.endsWith("PlaybackLifecycleMonitor;")
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
package app.revanced.patches.youtube.misc.video.videoid.patch
|
||||
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.instruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.DependsOn
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.video.videoid.annotation.VideoIdCompatibility
|
||||
import app.revanced.patches.youtube.misc.video.videoid.fingerprint.VideoIdFingerprint
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Name("video-id-hook")
|
||||
@Description("Hooks to detect when the video id changes")
|
||||
@VideoIdCompatibility
|
||||
@Version("0.0.1")
|
||||
@DependsOn([IntegrationsPatch::class])
|
||||
class VideoIdPatch : BytecodePatch(
|
||||
listOf(
|
||||
VideoIdFingerprint
|
||||
)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
with(VideoIdFingerprint.result!!) {
|
||||
insertMethod = mutableMethod
|
||||
insertIndex = scanResult.patternScanResult!!.endIndex + 2
|
||||
|
||||
videoIdRegister = (insertMethod.instruction(insertIndex - 1) as OneRegisterInstruction).registerA
|
||||
}
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var videoIdRegister = 0
|
||||
private var insertIndex = 0
|
||||
|
||||
private lateinit var insertMethod: MutableMethod
|
||||
|
||||
/**
|
||||
* Adds an invoke-static instruction, called with the new id when the video changes
|
||||
* @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;`
|
||||
*/
|
||||
fun injectCall(
|
||||
methodDescriptor: String
|
||||
) = insertMethod.addInstructions(
|
||||
insertIndex, // move-result-object offset
|
||||
"invoke-static {v$videoIdRegister}, $methodDescriptor"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user