From 9ba4d6da1569e67a504647862c4e675741526cb4 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Sun, 7 Apr 2024 17:33:11 +0900 Subject: [PATCH] refactor(YouTube Music/Video information): include playback speed and video quality --- .../flyoutpanel/replace/ReplaceReportPatch.kt | 8 +- .../PlaybackSpeedFlyoutPanelHookPatch.kt | 99 ++++++ ...PlaybackSpeedOnClickListenerFingerprint.kt | 2 +- .../OverrideQualityHookPatch.kt | 107 ------- .../VideoQualityPatchFingerprint.kt | 16 - .../overridespeed/OverrideSpeedHookPatch.kt | 143 --------- .../PlaybackSpeedPatchFingerprint.kt | 16 - .../information/VideoInformationPatch.kt | 301 ++++++++++++------ .../fingerprints/PlaybackSpeedFingerprint.kt | 2 +- .../PlaybackSpeedParentFingerprint.kt | 2 +- .../VideoQualityListFingerprint.kt | 2 +- .../VideoQualityTextFingerprint.kt | 2 +- .../music/video/quality/VideoQualityPatch.kt | 18 +- .../music/video/speed/PlaybackSpeedPatch.kt | 52 +-- 14 files changed, 353 insertions(+), 417 deletions(-) create mode 100644 src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/PlaybackSpeedFlyoutPanelHookPatch.kt rename src/main/kotlin/app/revanced/patches/music/utils/{overridespeed => flyoutpanel}/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt (90%) delete mode 100644 src/main/kotlin/app/revanced/patches/music/utils/overridequality/OverrideQualityHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityPatchFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/utils/overridespeed/OverrideSpeedHookPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedPatchFingerprint.kt rename src/main/kotlin/app/revanced/patches/music/{utils/overridespeed => video/information}/fingerprints/PlaybackSpeedFingerprint.kt (87%) rename src/main/kotlin/app/revanced/patches/music/{utils/overridespeed => video/information}/fingerprints/PlaybackSpeedParentFingerprint.kt (84%) rename src/main/kotlin/app/revanced/patches/music/{utils/overridequality => video/information}/fingerprints/VideoQualityListFingerprint.kt (86%) rename src/main/kotlin/app/revanced/patches/music/{utils/overridequality => video/information}/fingerprints/VideoQualityTextFingerprint.kt (89%) diff --git a/src/main/kotlin/app/revanced/patches/music/flyoutpanel/replace/ReplaceReportPatch.kt b/src/main/kotlin/app/revanced/patches/music/flyoutpanel/replace/ReplaceReportPatch.kt index a017fd1e4..bc17d260c 100644 --- a/src/main/kotlin/app/revanced/patches/music/flyoutpanel/replace/ReplaceReportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/flyoutpanel/replace/ReplaceReportPatch.kt @@ -5,12 +5,13 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patches.music.flyoutpanel.replace.fingerprints.TouchOutsideFingerprint import app.revanced.patches.music.flyoutpanel.shared.FlyoutPanelMenuItemPatch +import app.revanced.patches.music.utils.flyoutpanel.PlaybackSpeedFlyoutPanelHookPatch import app.revanced.patches.music.utils.integrations.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.music.utils.integrations.Constants.FLYOUT_CLASS_DESCRIPTOR -import app.revanced.patches.music.utils.overridespeed.OverrideSpeedHookPatch import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch import app.revanced.patches.music.utils.settings.CategoryType import app.revanced.patches.music.utils.settings.SettingsPatch +import app.revanced.patches.music.video.information.VideoInformationPatch import app.revanced.util.getTargetIndexWithMethodReferenceName import app.revanced.util.patch.BaseBytecodePatch import app.revanced.util.resultOrThrow @@ -22,10 +23,11 @@ object ReplaceReportPatch : BaseBytecodePatch( description = "Adds an option to replace \"Report\" with \"Playback speed\" in the flyout menu.", dependencies = setOf( FlyoutPanelMenuItemPatch::class, - OverrideSpeedHookPatch::class, + PlaybackSpeedFlyoutPanelHookPatch::class, ReplaceReportResourcePatch::class, SettingsPatch::class, - SharedResourceIdPatch::class + SharedResourceIdPatch::class, + VideoInformationPatch::class ), compatiblePackages = COMPATIBLE_PACKAGE, fingerprints = setOf(TouchOutsideFingerprint) diff --git a/src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/PlaybackSpeedFlyoutPanelHookPatch.kt b/src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/PlaybackSpeedFlyoutPanelHookPatch.kt new file mode 100644 index 000000000..8ce5956fa --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/PlaybackSpeedFlyoutPanelHookPatch.kt @@ -0,0 +1,99 @@ +package app.revanced.patches.music.utils.flyoutpanel + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.or +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable +import app.revanced.patches.music.utils.integrations.Constants.INTEGRATIONS_PATH +import app.revanced.patches.music.utils.flyoutpanel.fingerprints.PlaybackSpeedOnClickListenerFingerprint +import app.revanced.util.getTargetIndex +import app.revanced.util.getWideLiteralInstructionIndex +import app.revanced.util.indexOfFirstInstruction +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.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.ImmutableField +import com.android.tools.smali.dexlib2.util.MethodUtil + +@Patch(description = "Hooks YouTube Music to open the playback speed flyout panel in the integration.") +object PlaybackSpeedFlyoutPanelHookPatch : BytecodePatch( + setOf(PlaybackSpeedOnClickListenerFingerprint) +) { + private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR = + "$INTEGRATIONS_PATH/utils/VideoUtils;" + + override fun execute(context: BytecodeContext) { + + PlaybackSpeedOnClickListenerFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val startIndex = getWideLiteralInstructionIndex(147448) + val iGetObjectIndex = getTargetIndex(startIndex, Opcode.IGET_OBJECT) + val invokeInterfaceIndex = getTargetIndex(startIndex, Opcode.INVOKE_INTERFACE) + val invokeVirtualIndex = getTargetIndex(startIndex, Opcode.INVOKE_VIRTUAL) + + val iGetObjectReference = getInstruction(iGetObjectIndex).reference + val playbackRateBottomSheetClass = (iGetObjectReference as FieldReference).type + val invokeInterfaceReference = getInstruction(invokeInterfaceIndex).reference + val invokeVirtualReference = getInstruction(invokeVirtualIndex).reference + + it.mutableClass.methods.first { method -> + MethodUtil.isConstructor(method) + }.apply { + val iPutIndex = + indexOfFirstInstruction { + opcode == Opcode.IPUT_OBJECT && + (this as? ReferenceInstruction)?.reference == iGetObjectReference + } + val iPutRegister = getInstruction(iPutIndex).registerA + + addInstruction( + iPutIndex, + "sput-object v$iPutRegister, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->playbackRateBottomSheetClass:$playbackRateBottomSheetClass" + ) + } + + val videoUtilsMutableClass = context.findClass( + INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR + )!!.mutableClass + videoUtilsMutableClass.methods.single { method -> + method.name == "showPlaybackSpeedFlyoutPanel" + }.apply { + // add playback rate bottom sheet class + videoUtilsMutableClass.staticFields.add( + ImmutableField( + definingClass, + "playbackRateBottomSheetClass", + playbackRateBottomSheetClass, + AccessFlags.PUBLIC or AccessFlags.STATIC, + null, + annotations, + null + ).toMutable() + ) + + // call playback rate bottom sheet method + addInstructionsWithLabels( + 0, """ + sget-object v0, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->playbackRateBottomSheetClass:$playbackRateBottomSheetClass + if-eqz v0, :ignore + invoke-interface {v0}, $invokeInterfaceReference + move-result-object v0 + check-cast v0, ${(invokeVirtualReference as MethodReference).definingClass} + invoke-virtual {v0}, $invokeVirtualReference + :ignore + return-void + """ + ) + } + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt similarity index 90% rename from src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt index 3ac3f0cd7..950d02d96 100644 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/utils/flyoutpanel/fingerprints/PlaybackSpeedOnClickListenerFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.music.utils.overridespeed.fingerprints +package app.revanced.patches.music.utils.flyoutpanel.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/OverrideQualityHookPatch.kt b/src/main/kotlin/app/revanced/patches/music/utils/overridequality/OverrideQualityHookPatch.kt deleted file mode 100644 index fa6e45982..000000000 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/OverrideQualityHookPatch.kt +++ /dev/null @@ -1,107 +0,0 @@ -package app.revanced.patches.music.utils.overridequality - -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.or -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable -import app.revanced.patches.music.utils.integrations.Constants.INTEGRATIONS_PATH -import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH -import app.revanced.patches.music.utils.overridequality.fingerprints.VideoQualityListFingerprint -import app.revanced.patches.music.utils.overridequality.fingerprints.VideoQualityPatchFingerprint -import app.revanced.patches.music.utils.overridequality.fingerprints.VideoQualityTextFingerprint -import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch -import app.revanced.util.resultOrThrow -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction -import com.android.tools.smali.dexlib2.immutable.ImmutableField -import com.android.tools.smali.dexlib2.util.MethodUtil - -@Patch(dependencies = [SharedResourceIdPatch::class]) -object OverrideQualityHookPatch : BytecodePatch( - setOf( - VideoQualityListFingerprint, - VideoQualityPatchFingerprint, - VideoQualityTextFingerprint - ) -) { - private const val INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR = - "$VIDEO_PATH/VideoQualityPatch;" - - private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR = - "$INTEGRATIONS_PATH/utils/VideoUtils;" - - private lateinit var objectClass: String - private lateinit var objectMethod: String - - override fun execute(context: BytecodeContext) { - - VideoQualityListFingerprint.resultOrThrow().let { - val constructorMethod = - it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) } - val overrideMethod = - it.mutableClass.methods.find { method -> method.parameterTypes.first() == "I" } - - objectClass = it.method.definingClass - objectMethod = overrideMethod?.name - ?: throw PatchException("Failed to find hook method") - - constructorMethod.apply { - addInstruction( - 2, - "sput-object p0, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->qualityClass:$objectClass" - ) - } - - it.mutableMethod.apply { - val listIndex = it.scanResult.patternScanResult!!.startIndex - val listRegister = getInstruction(listIndex).registerD - - addInstruction( - listIndex, - "invoke-static {v$listRegister}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->setVideoQualityList([Ljava/lang/Object;)V" - ) - } - } - - VideoQualityPatchFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - it.mutableClass.staticFields.add( - ImmutableField( - definingClass, - "qualityClass", - objectClass, - AccessFlags.PUBLIC or AccessFlags.STATIC, - null, - annotations, - null - ).toMutable() - ) - - addInstructions( - 0, """ - sget-object v0, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->qualityClass:$objectClass - invoke-virtual {v0, p0}, $objectClass->$objectMethod(I)V - """ - ) - } - } - - VideoQualityTextFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val textIndex = it.scanResult.patternScanResult!!.endIndex - val textRegister = getInstruction(textIndex).registerA - - addInstruction( - textIndex + 1, - "sput-object v$textRegister, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->currentQuality:Ljava/lang/String;" - ) - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityPatchFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityPatchFingerprint.kt deleted file mode 100644 index 726265eaa..000000000 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityPatchFingerprint.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.music.utils.overridequality.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH -import com.android.tools.smali.dexlib2.AccessFlags - -internal object VideoQualityPatchFingerprint : MethodFingerprint( - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, - parameters = listOf("I"), - customFingerprint = { methodDef, _ -> - methodDef.definingClass == "$VIDEO_PATH/VideoQualityPatch;" - && methodDef.name == "overrideQuality" - } -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/OverrideSpeedHookPatch.kt b/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/OverrideSpeedHookPatch.kt deleted file mode 100644 index 352bf16f1..000000000 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/OverrideSpeedHookPatch.kt +++ /dev/null @@ -1,143 +0,0 @@ -package app.revanced.patches.music.utils.overridespeed - -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.or -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable -import app.revanced.patches.music.utils.integrations.Constants.INTEGRATIONS_PATH -import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH -import app.revanced.patches.music.utils.overridespeed.fingerprints.PlaybackSpeedFingerprint -import app.revanced.patches.music.utils.overridespeed.fingerprints.PlaybackSpeedOnClickListenerFingerprint -import app.revanced.patches.music.utils.overridespeed.fingerprints.PlaybackSpeedParentFingerprint -import app.revanced.patches.music.utils.overridespeed.fingerprints.PlaybackSpeedPatchFingerprint -import app.revanced.util.getTargetIndex -import app.revanced.util.getWalkerMethod -import app.revanced.util.getWideLiteralInstructionIndex -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.iface.instruction.OneRegisterInstruction -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.FieldReference -import com.android.tools.smali.dexlib2.iface.reference.MethodReference -import com.android.tools.smali.dexlib2.iface.reference.Reference -import com.android.tools.smali.dexlib2.immutable.ImmutableField -import com.android.tools.smali.dexlib2.util.MethodUtil - -object OverrideSpeedHookPatch : BytecodePatch( - setOf( - PlaybackSpeedOnClickListenerFingerprint, - PlaybackSpeedPatchFingerprint, - PlaybackSpeedParentFingerprint - ) -) { - private const val INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR = - "$VIDEO_PATH/PlaybackSpeedPatch;" - - private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR = - "$INTEGRATIONS_PATH/utils/VideoUtils;" - - private lateinit var iGetObjectReference: Reference - private lateinit var invokeInterfaceReference: Reference - private lateinit var invokeVirtualReference: Reference - private lateinit var objectClass: String - - override fun execute(context: BytecodeContext) { - - PlaybackSpeedOnClickListenerFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - val startIndex = getWideLiteralInstructionIndex(147448) - val iGetObjectIndex = getTargetIndex(startIndex, Opcode.IGET_OBJECT) - val invokeInterfaceIndex = getTargetIndex(startIndex, Opcode.INVOKE_INTERFACE) - val invokeVirtualIndex = getTargetIndex(startIndex, Opcode.INVOKE_VIRTUAL) - - iGetObjectReference = getInstruction(iGetObjectIndex).reference - objectClass = (iGetObjectReference as FieldReference).type - invokeInterfaceReference = getInstruction(invokeInterfaceIndex).reference - invokeVirtualReference = getInstruction(invokeVirtualIndex).reference - } - - it.mutableClass.methods.first { method -> - MethodUtil.isConstructor(method) - }.apply { - val iPutIndex = - implementation!!.instructions.indexOfFirst { instruction -> - instruction.opcode == Opcode.IPUT_OBJECT && - (instruction as? ReferenceInstruction)?.reference == iGetObjectReference - } - val iPutRegister = getInstruction(iPutIndex).registerA - - addInstruction( - iPutIndex, - "sput-object v$iPutRegister, $INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->objectClass:$objectClass" - ) - } - } - - PlaybackSpeedParentFingerprint.resultOrThrow().let { parentResult -> - PlaybackSpeedFingerprint.also { - it.resolve( - context, - parentResult.classDef - ) - }.resultOrThrow().let { - it.mutableMethod.apply { - val startIndex = it.scanResult.patternScanResult!!.startIndex - val endIndex = it.scanResult.patternScanResult!!.endIndex - - val speedRegister = - getInstruction(startIndex + 1).registerA - - val speedMethod = getWalkerMethod(context, endIndex) - - speedMethod.addInstruction( - speedMethod.implementation!!.instructions.size - 1, - "sput p1, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->currentSpeed:F" - ) - - addInstructions( - startIndex + 2, """ - invoke-static {}, $INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->getPlaybackSpeed()F - move-result v$speedRegister - """ - ) - } - - } - } - - PlaybackSpeedPatchFingerprint.resultOrThrow().let { - it.mutableMethod.apply { - it.mutableClass.staticFields.add( - ImmutableField( - definingClass, - "objectClass", - objectClass, - AccessFlags.PUBLIC or AccessFlags.STATIC, - null, - annotations, - null - ).toMutable() - ) - - addInstructionsWithLabels( - 0, """ - sget-object v0, $INTEGRATIONS_PLAYBACK_SPEED_CLASS_DESCRIPTOR->objectClass:$objectClass - if-eqz v0, :ignore - invoke-interface {v0}, $invokeInterfaceReference - move-result-object v0 - check-cast v0, ${(invokeVirtualReference as MethodReference).definingClass} - invoke-virtual {v0}, $invokeVirtualReference - :ignore - return-void - """ - ) - } - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedPatchFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedPatchFingerprint.kt deleted file mode 100644 index a398082d9..000000000 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedPatchFingerprint.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.music.utils.overridespeed.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH -import com.android.tools.smali.dexlib2.AccessFlags - -internal object PlaybackSpeedPatchFingerprint : MethodFingerprint( - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, - parameters = emptyList(), - customFingerprint = { methodDef, _ -> - methodDef.definingClass == "$VIDEO_PATH/PlaybackSpeedPatch;" - && methodDef.name == "showPlaybackSpeedMenu" - } -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/video/information/VideoInformationPatch.kt b/src/main/kotlin/app/revanced/patches/music/video/information/VideoInformationPatch.kt index 6dc62e1f7..488924dd7 100644 --- a/src/main/kotlin/app/revanced/patches/music/video/information/VideoInformationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/information/VideoInformationPatch.kt @@ -2,15 +2,23 @@ package app.revanced.patches.music.video.information import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprintResult import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable +import app.revanced.patcher.util.smali.toInstructions import app.revanced.patches.music.utils.fingerprints.SeekBarConstructorFingerprint import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH +import app.revanced.patches.music.video.information.fingerprints.VideoQualityListFingerprint +import app.revanced.patches.music.video.information.fingerprints.VideoQualityTextFingerprint +import app.revanced.patches.music.video.information.fingerprints.PlaybackSpeedFingerprint +import app.revanced.patches.music.video.information.fingerprints.PlaybackSpeedParentFingerprint import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch import app.revanced.patches.music.video.information.fingerprints.PlayerControllerSetTimeReferenceFingerprint import app.revanced.patches.music.video.information.fingerprints.VideoEndFingerprint @@ -21,11 +29,14 @@ import app.revanced.util.getTargetIndexWithMethodReferenceNameReversed import app.revanced.util.getWalkerMethod import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction 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.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter import com.android.tools.smali.dexlib2.util.MethodUtil @@ -39,20 +50,202 @@ import com.android.tools.smali.dexlib2.util.MethodUtil object VideoInformationPatch : BytecodePatch( setOf( PlayerControllerSetTimeReferenceFingerprint, + PlaybackSpeedParentFingerprint, SeekBarConstructorFingerprint, - VideoEndFingerprint + VideoEndFingerprint, + VideoQualityListFingerprint, + VideoQualityTextFingerprint ) ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = "$VIDEO_PATH/VideoInformation;" - private var playerInitInsertIndex = 4 - private var timeInitInsertIndex = 2 + private lateinit var playerConstructorMethod: MutableMethod + private var playerConstructorInsertIndex = 4 - private lateinit var playerInitMethod: MutableMethod - private lateinit var timeMethod: MutableMethod + private lateinit var videoTimeConstructorMethod: MutableMethod + private var videoTimeConstructorInsertIndex = 2 + // Used by other patches. lateinit var rectangleFieldName: String + internal lateinit var playbackSpeedResult: MethodFingerprintResult + + override fun execute(context: BytecodeContext) { + VideoEndFingerprint.resultOrThrow().let { + playerConstructorMethod = + it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) } + + // hook the player controller for use through integrations + onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize") + + it.mutableMethod.apply { + val seekSourceEnumType = parameterTypes[1].toString() + + it.mutableClass.methods.add( + ImmutableMethod( + definingClass, + "seekTo", + listOf(ImmutableMethodParameter("J", annotations, "time")), + "Z", + AccessFlags.PUBLIC or AccessFlags.FINAL, + annotations, + null, + ImmutableMethodImplementation( + 4, """ + sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType + invoke-virtual {p0, p1, p2, v0}, ${definingClass}->${name}(J$seekSourceEnumType)Z + move-result p1 + return p1 + """.toInstructions(), + null, + null + ) + ).toMutable() + ) + } + } + + /** + * Set the video time method + */ + PlayerControllerSetTimeReferenceFingerprint.resultOrThrow().let { + videoTimeConstructorMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex) + } + + /** + * Set current video time + */ + videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime") + + /** + * Set current video length + */ + VideoLengthFingerprint.resolve( + context, + SeekBarConstructorFingerprint.resultOrThrow().classDef + ) + VideoLengthFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val invalidateIndex = getTargetIndexWithMethodReferenceNameReversed("invalidate") + val rectangleIndex = getTargetIndexWithFieldReferenceTypeReversed(invalidateIndex + 1, "Landroid/graphics/Rect;") + rectangleFieldName = (getInstruction(rectangleIndex).reference as FieldReference).name + + val videoLengthRegisterIndex = it.scanResult.patternScanResult!!.startIndex + 1 + val videoLengthRegister = + getInstruction(videoLengthRegisterIndex).registerA + val dummyRegisterForLong = + videoLengthRegister + 1 // required for long values since they are wide + + addInstruction( + videoLengthRegisterIndex + 1, + "invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V" + ) + } + } + + /** + * Set current video id + */ + val videoIdMethodDescriptor = "$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V" + VideoIdPatch.hookVideoId(videoIdMethodDescriptor) + VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor) + + val videoInformationMutableClass = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!.mutableClass + + /** + * Hook current playback speed + */ + PlaybackSpeedFingerprint.resolve( + context, + PlaybackSpeedParentFingerprint.resultOrThrow().classDef + ) + PlaybackSpeedFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + playbackSpeedResult = it + val endIndex = it.scanResult.patternScanResult!!.endIndex + val speedMethod = getWalkerMethod(context, endIndex) + + // set current playback speed + speedMethod.addInstruction( + speedMethod.implementation!!.instructions.size - 1, + "invoke-static {p1}, $INTEGRATIONS_CLASS_DESCRIPTOR->setPlaybackSpeed(F)V" + ) + } + } + + /** + * Hook current video quality + */ + VideoQualityListFingerprint.resultOrThrow().let { + val constructorMethod = + it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) } + val overrideMethod = + it.mutableClass.methods.find { method -> method.parameterTypes.first() == "I" } + + val videoQualityClass = it.method.definingClass + val videoQualityMethodName = overrideMethod?.name + ?: throw PatchException("Failed to find hook method") + + // set video quality class + constructorMethod.apply { + addInstruction( + 2, + "sput-object p0, $INTEGRATIONS_CLASS_DESCRIPTOR->videoQualityClass:$videoQualityClass" + ) + } + + // set video quality array + it.mutableMethod.apply { + val listIndex = it.scanResult.patternScanResult!!.startIndex + val listRegister = getInstruction(listIndex).registerD + + addInstruction( + listIndex, + "invoke-static {v$listRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoQualityList([Ljava/lang/Object;)V" + ) + } + + videoInformationMutableClass.methods.single { method -> + method.name == "overrideVideoQuality" + }.apply { + videoInformationMutableClass.staticFields.add( + ImmutableField( + definingClass, + "videoQualityClass", + videoQualityClass, + AccessFlags.PUBLIC or AccessFlags.STATIC, + null, + annotations, + null + ).toMutable() + ) + + // call override video quality method + addInstructionsWithLabels( + 0, """ + sget-object v0, $INTEGRATIONS_CLASS_DESCRIPTOR->videoQualityClass:$videoQualityClass + if-eqz v0, :ignore + invoke-virtual {v0, p0}, $videoQualityClass->$videoQualityMethodName(I)V + :ignore + return-void + """ + ) + } + } + + // set current video quality + VideoQualityTextFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val textIndex = it.scanResult.patternScanResult!!.endIndex + val textRegister = getInstruction(textIndex).registerA + + addInstruction( + textIndex + 1, + "invoke-static {v$textRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoQuality(Ljava/lang/String;)V" + ) + } + } + } private fun MutableMethod.insert(insertIndex: Int, register: String, descriptor: String) = addInstruction(insertIndex, "invoke-static { $register }, $descriptor") @@ -70,8 +263,8 @@ object VideoInformationPatch : BytecodePatch( * @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( - playerInitInsertIndex++, + playerConstructorMethod.insert( + playerConstructorInsertIndex++, "v0", "$targetMethodClass->$targetMethodName(Ljava/lang/Object;)V" ) @@ -84,94 +277,8 @@ object VideoInformationPatch : BytecodePatch( * @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( - timeInitInsertIndex++, + videoTimeConstructorMethod.insertTimeHook( + videoTimeConstructorInsertIndex++, "$targetMethodClass->$targetMethodName(J)V" ) - - override fun execute(context: BytecodeContext) { - VideoEndFingerprint.resultOrThrow().let { - playerInitMethod = - it.mutableClass.methods.first { method -> MethodUtil.isConstructor(method) } - - // hook the player controller for use through integrations - onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "initialize") - - it.mutableMethod.apply { - val seekHelperMethod = ImmutableMethod( - definingClass, - "seekTo", - listOf(ImmutableMethodParameter("J", annotations, "time")), - "Z", - AccessFlags.PUBLIC or AccessFlags.FINAL, - annotations, null, - MutableMethodImplementation(4) - ).toMutable() - - val seekSourceEnumType = parameterTypes[1].toString() - - seekHelperMethod.addInstructions( - 0, """ - sget-object v0, $seekSourceEnumType->a:$seekSourceEnumType - invoke-virtual {p0, p1, p2, v0}, ${definingClass}->${name}(J$seekSourceEnumType)Z - move-result p1 - return p1 - """ - ) - it.mutableClass.methods.add(seekHelperMethod) - } - } - - - /** - * Set current video length - */ - SeekBarConstructorFingerprint.resultOrThrow().classDef.let { classDef -> - VideoLengthFingerprint.also { - it.resolve( - context, - classDef - ) - }.resultOrThrow().let { - it.mutableMethod.apply { - val invalidateIndex = getTargetIndexWithMethodReferenceNameReversed("invalidate") - val rectangleIndex = getTargetIndexWithFieldReferenceTypeReversed(invalidateIndex + 1, "Landroid/graphics/Rect;") - rectangleFieldName = (getInstruction(rectangleIndex).reference as FieldReference).name - - val videoLengthRegisterIndex = it.scanResult.patternScanResult!!.startIndex + 1 - val videoLengthRegister = - getInstruction(videoLengthRegisterIndex).registerA - val dummyRegisterForLong = - videoLengthRegister + 1 // required for long values since they are wide - - addInstruction( - videoLengthRegisterIndex + 1, - "invoke-static {v$videoLengthRegister, v$dummyRegisterForLong}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoLength(J)V" - ) - } - } - } - - - /** - * Set the video time method - */ - PlayerControllerSetTimeReferenceFingerprint.resultOrThrow().let { - timeMethod = it.getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex) - } - - - /** - * Set current video time - */ - videoTimeHook(INTEGRATIONS_CLASS_DESCRIPTOR, "setVideoTime") - - - /** - * Set current video id - */ - val videoIdMethodDescriptor = "$INTEGRATIONS_CLASS_DESCRIPTOR->setVideoId(Ljava/lang/String;)V" - VideoIdPatch.hookVideoId(videoIdMethodDescriptor) - VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor) - } } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/PlaybackSpeedFingerprint.kt similarity index 87% rename from src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/PlaybackSpeedFingerprint.kt index 89e5a6008..b35abee31 100644 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/PlaybackSpeedFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.music.utils.overridespeed.fingerprints +package app.revanced.patches.music.video.information.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/PlaybackSpeedParentFingerprint.kt similarity index 84% rename from src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedParentFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/PlaybackSpeedParentFingerprint.kt index 13a78f6ae..75ee0fff4 100644 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridespeed/fingerprints/PlaybackSpeedParentFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/PlaybackSpeedParentFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.music.utils.overridespeed.fingerprints +package app.revanced.patches.music.video.information.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityListFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/VideoQualityListFingerprint.kt similarity index 86% rename from src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityListFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/VideoQualityListFingerprint.kt index bdd783195..7bc5e35c2 100644 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityListFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/VideoQualityListFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.music.utils.overridequality.fingerprints +package app.revanced.patches.music.video.information.fingerprints import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch.QualityAuto import app.revanced.util.fingerprint.LiteralValueFingerprint diff --git a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityTextFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/VideoQualityTextFingerprint.kt similarity index 89% rename from src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityTextFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/VideoQualityTextFingerprint.kt index 51f9daa8e..d735a2374 100644 --- a/src/main/kotlin/app/revanced/patches/music/utils/overridequality/fingerprints/VideoQualityTextFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/information/fingerprints/VideoQualityTextFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.music.utils.overridequality.fingerprints +package app.revanced.patches.music.video.information.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/music/video/quality/VideoQualityPatch.kt b/src/main/kotlin/app/revanced/patches/music/video/quality/VideoQualityPatch.kt index 76beb4c5e..2941af1a1 100644 --- a/src/main/kotlin/app/revanced/patches/music/video/quality/VideoQualityPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/quality/VideoQualityPatch.kt @@ -6,9 +6,9 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patches.music.utils.integrations.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH -import app.revanced.patches.music.utils.overridequality.OverrideQualityHookPatch import app.revanced.patches.music.utils.settings.CategoryType import app.revanced.patches.music.utils.settings.SettingsPatch +import app.revanced.patches.music.video.information.VideoInformationPatch import app.revanced.patches.music.video.quality.fingerprints.UserQualityChangeFingerprint import app.revanced.patches.music.video.videoid.VideoIdPatch import app.revanced.util.patch.BaseBytecodePatch @@ -20,9 +20,9 @@ object VideoQualityPatch : BaseBytecodePatch( name = "Remember video quality", description = "Adds an option to remember the last video quality selected.", dependencies = setOf( - OverrideQualityHookPatch::class, SettingsPatch::class, - VideoIdPatch::class + VideoIdPatch::class, + VideoInformationPatch::class ), compatiblePackages = COMPATIBLE_PACKAGE, fingerprints = setOf(UserQualityChangeFingerprint) @@ -45,14 +45,10 @@ object VideoQualityPatch : BaseBytecodePatch( val onItemClickMethod = qualityChangedClass.methods.find { method -> method.name == "onItemClick" } - onItemClickMethod?.apply { - val listItemIndexParameter = 3 - - addInstruction( - 0, - "invoke-static {p$listItemIndexParameter}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->userChangedQuality(I)V" - ) - } ?: throw PatchException("Failed to find onItemClick method") + onItemClickMethod?.addInstruction( + 0, + "invoke-static {}, $INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR->userSelectedVideoQuality()V" + ) ?: throw PatchException("Failed to find onItemClick method") } } diff --git a/src/main/kotlin/app/revanced/patches/music/video/speed/PlaybackSpeedPatch.kt b/src/main/kotlin/app/revanced/patches/music/video/speed/PlaybackSpeedPatch.kt index 522cb3066..3180b5837 100644 --- a/src/main/kotlin/app/revanced/patches/music/video/speed/PlaybackSpeedPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/video/speed/PlaybackSpeedPatch.kt @@ -2,25 +2,27 @@ package app.revanced.patches.music.video.speed import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patches.music.utils.integrations.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.music.utils.integrations.Constants.VIDEO_PATH -import app.revanced.patches.music.utils.overridespeed.OverrideSpeedHookPatch import app.revanced.patches.music.utils.settings.CategoryType import app.revanced.patches.music.utils.settings.SettingsPatch +import app.revanced.patches.music.video.information.VideoInformationPatch import app.revanced.patches.music.video.speed.fingerprints.PlaybackSpeedBottomSheetFingerprint import app.revanced.patches.music.video.speed.fingerprints.PlaybackSpeedBottomSheetParentFingerprint import app.revanced.util.patch.BaseBytecodePatch import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Suppress("unused") object PlaybackSpeedPatch : BaseBytecodePatch( name = "Remember playback speed", description = "Adds an option to remember the last playback speed selected.", dependencies = setOf( - OverrideSpeedHookPatch::class, - SettingsPatch::class + SettingsPatch::class, + VideoInformationPatch::class ), compatiblePackages = COMPATIBLE_PACKAGE, fingerprints = setOf(PlaybackSpeedBottomSheetParentFingerprint) @@ -30,23 +32,35 @@ object PlaybackSpeedPatch : BaseBytecodePatch( override fun execute(context: BytecodeContext) { - PlaybackSpeedBottomSheetParentFingerprint.resultOrThrow().let { parentResult -> - PlaybackSpeedBottomSheetFingerprint.also { - it.resolve( - context, - parentResult.classDef - ) - }.resultOrThrow().let { - it.mutableMethod.apply { - val targetIndex = it.scanResult.patternScanResult!!.startIndex - val targetRegister = - getInstruction(targetIndex).registerD + PlaybackSpeedBottomSheetFingerprint.resolve( + context, + PlaybackSpeedBottomSheetParentFingerprint.resultOrThrow().classDef + ) + PlaybackSpeedBottomSheetFingerprint.resultOrThrow().let { + it.mutableMethod.apply { + val targetIndex = it.scanResult.patternScanResult!!.startIndex + val targetRegister = + getInstruction(targetIndex).registerD - addInstruction( - targetIndex, - "invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->userChangedSpeed(F)V" - ) - } + addInstruction( + targetIndex, + "invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->userSelectedPlaybackSpeed(F)V" + ) + } + } + + VideoInformationPatch.playbackSpeedResult.let { + it.mutableMethod.apply { + val startIndex = it.scanResult.patternScanResult!!.startIndex + val speedRegister = + getInstruction(startIndex + 1).registerA + + addInstructions( + startIndex + 2, """ + invoke-static {v$speedRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->getPlaybackSpeed(F)F + move-result v$speedRegister + """ + ) } }