Skip to content

Commit

Permalink
fix(youtube/remember-playback-speed): allow to not remember playback …
Browse files Browse the repository at this point in the history
…speed (#1762)
  • Loading branch information
LisoUseInAIKyrios authored Mar 19, 2023
1 parent 4d10756 commit 49ec3e8
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ import app.revanced.patcher.annotation.Package
)]
)
@Target(AnnotationTarget.CLASS)
internal annotation class RememberPlaybackRateCompatibility
internal annotation class RememberPlaybackSpeedCompatibility
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package app.revanced.patches.youtube.misc.video.speed.remember.fingerprint

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object ChangePlaybackRateFragmentStateFingerprint : MethodFingerprint(
object ChangePlaybackSpeedFragmentStateFingerprint : MethodFingerprint(
"V",
strings = listOf("PLAYBACK_RATE_MENU_BOTTOM_SHEET_FRAGMENT")
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package app.revanced.patches.youtube.misc.video.speed.remember.fingerprint

import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

object InitializePlaybackRateValuesFingerprint : MethodFingerprint(
object InitializePlaybackSpeedValuesFingerprint : MethodFingerprint(
parameters = listOf("[L", "I")
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package app.revanced.patches.youtube.misc.video.speed.remember.fingerprint
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode

object OnPlaybackRateItemClickFingerprint : MethodFingerprint(
object OnPlaybackSpeedItemClickFingerprint : MethodFingerprint(
customFingerprint = { it.name == "onItemClick" },
opcodes = listOf(
Opcode.IGET_OBJECT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,120 +18,123 @@ import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.shared.settings.preference.impl.SwitchPreference
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.video.speed.remember.annotation.RememberPlaybackRateCompatibility
import app.revanced.patches.youtube.misc.video.speed.remember.fingerprint.ChangePlaybackRateFragmentStateFingerprint
import app.revanced.patches.youtube.misc.video.speed.remember.fingerprint.InitializePlaybackRateValuesFingerprint
import app.revanced.patches.youtube.misc.video.speed.remember.fingerprint.OnPlaybackRateItemClickFingerprint
import app.revanced.patches.youtube.misc.video.speed.remember.annotation.RememberPlaybackSpeedCompatibility
import app.revanced.patches.youtube.misc.video.speed.remember.fingerprint.ChangePlaybackSpeedFragmentStateFingerprint
import app.revanced.patches.youtube.misc.video.speed.remember.fingerprint.InitializePlaybackSpeedValuesFingerprint
import app.revanced.patches.youtube.misc.video.speed.remember.fingerprint.OnPlaybackSpeedItemClickFingerprint
import app.revanced.patches.youtube.misc.video.videoid.patch.VideoIdPatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction

@Patch
@Name("remember-playback-rate")
@Description("Adds the ability to remember the playback rate you chose in the video playback rate flyout.")
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
@RememberPlaybackRateCompatibility
@Name("remember-playback-speed")
@Description("Adds the ability to remember the playback speed you chose in the video playback speed flyout.")
@DependsOn([IntegrationsPatch::class, SettingsPatch::class, VideoIdPatch::class])
@RememberPlaybackSpeedCompatibility
@Version("0.0.1")
class RememberPlaybackRatePatch : BytecodePatch(
listOf(ChangePlaybackRateFragmentStateFingerprint)
class RememberPlaybackSpeedPatch : BytecodePatch(
listOf(ChangePlaybackSpeedFragmentStateFingerprint)
) {
private companion object {
const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/playback/speed/RememberPlaybackRatePatch;"

fun MethodFingerprint.getReference(offsetFromPatternScanResultStartIndex: Int = 0) = this.result!!.let {
val referenceInstruction = it.mutableMethod
.instruction(it.scanResult.patternScanResult!!.startIndex + offsetFromPatternScanResultStartIndex) as ReferenceInstruction
referenceInstruction.reference.toString()
}

fun BytecodeContext.resolveFingerprints() {
ChangePlaybackRateFragmentStateFingerprint.result?.also {
fun MethodFingerprint.resolve() = resolve(this@resolveFingerprints, it.classDef)

OnPlaybackRateItemClickFingerprint.resolve()
InitializePlaybackRateValuesFingerprint.resolve()

} ?: throw ChangePlaybackRateFragmentStateFingerprint.toErrorResult()
}
}

override fun execute(context: BytecodeContext): PatchResult {
SettingsPatch.PreferenceScreen.MISC.addPreferences(
SwitchPreference(
"revanced_remember_playback_rate_last_selected",
StringResource("revanced_remember_playback_rate_last_selected_title", "Remember playback rate changes"),
"revanced_remember_playback_speed_last_selected",
StringResource(
"revanced_remember_playback_speed_last_selected_title",
"Remember playback speed changes"
),
true,
StringResource(
"revanced_remember_playback_rate_last_selected_summary_on",
"Playback rate changes apply to all videos"
"revanced_remember_playback_speed_last_selected_summary_on",
"Playback speed changes apply to all videos"
),
StringResource(
"revanced_remember_playback_rate_last_selected_summary_off",
"Playback rate changes only apply to the current video"
"revanced_remember_playback_speed_last_selected_summary_off",
"Playback speed changes only apply to the current video"
)
)
)

context.resolveFingerprints()

// Set the remembered playback rate.
InitializePlaybackRateValuesFingerprint.result!!.apply {
// Infer everything necessary for setPlaybackRate()
VideoIdPatch.injectCall("${INTEGRATIONS_CLASS_DESCRIPTOR}->newVideoLoaded(Ljava/lang/String;)V")

// Set the remembered playback speed.
InitializePlaybackSpeedValuesFingerprint.result!!.apply {
// Infer everything necessary for setPlaybackSate()

val playbackHandlerWrapperFieldReference =
(object : MethodFingerprint(opcodes = listOf(Opcode.IF_EQZ)) {}).apply {
OnPlaybackRateItemClickFingerprint.result!!.apply {
OnPlaybackSpeedItemClickFingerprint.result!!.apply {
resolve(
context,
method,
classDef
)
}
}.getReference(-1)
val playbackHandlerWrapperImplementorClassReference = OnPlaybackRateItemClickFingerprint
val playbackHandlerWrapperImplementorClassReference = OnPlaybackSpeedItemClickFingerprint
.getReference(-1)
val playbackHandlerFieldReference = OnPlaybackRateItemClickFingerprint
val playbackHandlerFieldReference = OnPlaybackSpeedItemClickFingerprint
.getReference()
val setPlaybackRateMethodReference = OnPlaybackRateItemClickFingerprint
val setPlaybackSpeedMethodReference = OnPlaybackSpeedItemClickFingerprint
.getReference(1)

mutableMethod.addInstructions(
0,
"""
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRememberedPlaybackRate()F
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getCurrentPlaybackSpeed()F
move-result v0
# check if the playback rate is below 0 (when a playback rate was never remembered)
const/4 v1, 0x0
# check if the playback speed is not 1.0x
const/high16 v1, 0x3f800000 # 1.0f
cmpg-float v1, v0, v1
if-lez v1, :do_not_override
if-eqz v1, :do_not_override
# invoke setPlaybackRate
# invoke setPlaybackSpeed
iget-object v1, p0, $playbackHandlerWrapperFieldReference
check-cast v1, $playbackHandlerWrapperImplementorClassReference
iget-object v2, v1, $playbackHandlerFieldReference
invoke-virtual {v2, v0}, $setPlaybackRateMethodReference
invoke-virtual {v2, v0}, $setPlaybackSpeedMethodReference
""".trimIndent(),
listOf(ExternalLabel("do_not_override", mutableMethod.instruction(0)))
)
}

// Remember the selected playback rate.
OnPlaybackRateItemClickFingerprint.result!!.apply {
val setPlaybackRateIndex = scanResult.patternScanResult!!.endIndex
val selectedPlaybackRateRegister =
(mutableMethod.instruction(setPlaybackRateIndex) as FiveRegisterInstruction).registerD
// Remember the selected playback speed.
OnPlaybackSpeedItemClickFingerprint.result!!.apply {
val setPlaybackSpeedIndex = scanResult.patternScanResult!!.endIndex
val selectedPlaybackSpeedRegister =
(mutableMethod.instruction(setPlaybackSpeedIndex) as FiveRegisterInstruction).registerD

mutableMethod.addInstruction(
setPlaybackRateIndex,
"invoke-static { v$selectedPlaybackRateRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->rememberPlaybackRate(F)V"
setPlaybackSpeedIndex,
"invoke-static { v$selectedPlaybackSpeedRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->setPlaybackSpeed(F)V"
)
}


return PatchResultSuccess()
}

private companion object {
const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/playback/speed/RememberPlaybackSpeedPatch;"

fun MethodFingerprint.getReference(offsetFromPatternScanResultStartIndex: Int = 0) = this.result!!.let {
val referenceInstruction = it.mutableMethod
.instruction(it.scanResult.patternScanResult!!.startIndex + offsetFromPatternScanResultStartIndex) as ReferenceInstruction
referenceInstruction.reference.toString()
}

fun BytecodeContext.resolveFingerprints() {
ChangePlaybackSpeedFragmentStateFingerprint.result?.also {
fun MethodFingerprint.resolve() = resolve(this@resolveFingerprints, it.classDef)

OnPlaybackSpeedItemClickFingerprint.resolve()
InitializePlaybackSpeedValuesFingerprint.resolve()

} ?: throw ChangePlaybackSpeedFragmentStateFingerprint.toErrorResult()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,20 @@ class VideoIdPatch : BytecodePatch(
private lateinit var insertMethod: MutableMethod

/**
* Adds an invoke-static instruction, called with the new id when the video changes
* Adds an invoke-static instruction, called with the new id when the video changes.
* Be aware, this can be called multiple times for the same video id.
*
* @param methodDescriptor which method to call. Params have to be `Ljava/lang/String;`
*/
fun injectCall(
methodDescriptor: String
) {
insertMethod.addInstructions(
// TODO: The order has been proven to not be required, so remove the logic for keeping order.
// Keep injection calls in the order they're added:
// Increment index. So if additional injection calls are added, those calls run after this injection call.
insertIndex++,
"invoke-static {v$videoIdRegister}, $methodDescriptor"
)
}
) = insertMethod.addInstructions(
// Keep injection calls in the order they're added.
// Order has been proven to be important for the same reason that order of patch execution is important
// such as for the VideoInformation patch.
insertIndex++,
"invoke-static {v$videoIdRegister}, $methodDescriptor"
)
}
}

0 comments on commit 49ec3e8

Please sign in to comment.