Skip to content

Commit

Permalink
fix(YouTube - Default video quality): Match original ReVanced code
Browse files Browse the repository at this point in the history
  • Loading branch information
anddea committed Apr 20, 2024
1 parent ffe4d89 commit 1ee2743
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 151 deletions.
8 changes: 2 additions & 6 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -3596,16 +3596,12 @@ public final class app/revanced/patches/youtube/video/hdr/fingerprints/HdrCapabi
public static final field INSTANCE Lapp/revanced/patches/youtube/video/hdr/fingerprints/HdrCapabilitiesFingerprint;
}

public final class app/revanced/patches/youtube/video/quality/VideoQualityPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/quality/VideoQualityPatch;
public final class app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/quality/RememberVideoQualityPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/youtube/video/quality/fingerprints/VideoQualitySetterFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/quality/fingerprints/VideoQualitySetterFingerprint;
}

public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/video/speed/PlaybackSpeedPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ object OverrideQualityHookPatch : BytecodePatch(
}

private const val INTEGRATIONS_VIDEO_QUALITY_CLASS_DESCRIPTOR =
"$VIDEO_PATH/VideoQualityPatch;"
"$VIDEO_PATH/RememberVideoQualityPatch;"

private const val INTEGRATIONS_VIDEO_HELPER_CLASS_DESCRIPTOR =
"$INTEGRATIONS_PATH/utils/VideoHelpers;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object VideoQualityPatchFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("I"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lapp/revanced/integrations/youtube/patches/video/VideoQualityPatch;"
methodDef.definingClass == "Lapp/revanced/integrations/youtube/patches/video/RememberVideoQualityPatch;"
&& methodDef.name == "overrideQuality"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package app.revanced.patches.youtube.video.quality

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.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.utils.integrations.Constants.VIDEO_PATH
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch.contexts
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.patches.youtube.video.quality.fingerprints.NewVideoQualityChangedFingerprint
import app.revanced.patches.youtube.video.quality.fingerprints.SetQualityByIndexMethodClassFieldReferenceFingerprint
import app.revanced.patches.youtube.video.quality.fingerprints.VideoQualityItemOnClickParentFingerprint
import app.revanced.patches.youtube.video.quality.fingerprints.VideoQualitySetterFingerprint
import app.revanced.util.copyXmlNode
import app.revanced.util.exception
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

@Patch(
name = "Default video quality",
description = "Adds an option to remember the last video quality selected.",
dependencies = [
SettingsPatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.48.39",
"18.49.37",
"19.01.34",
"19.02.39",
"19.03.36",
"19.04.38",
"19.05.36",
"19.06.39",
"19.07.40",
"19.08.36",
"19.09.37"
]
)
]
)
@Suppress("unused")
object RememberVideoQualityPatch : BytecodePatch(
setOf(
VideoQualitySetterFingerprint,
VideoQualityItemOnClickParentFingerprint,
NewVideoQualityChangedFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$VIDEO_PATH/RememberVideoQualityPatch;"

override fun execute(context: BytecodeContext) {

/*
* The following code works by hooking the method which is called when the user selects a video quality
* to remember the last selected video quality.
*
* It also hooks the method which is called when the video quality to set is determined.
* Conveniently, at this point the video quality is overridden to the remembered playback speed.
*/
VideoIdPatch.onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "newVideoStarted")

// Inject a call to set the remembered quality once a video loads.
VideoQualitySetterFingerprint.result?.also {
if (!SetQualityByIndexMethodClassFieldReferenceFingerprint.resolve(context, it.classDef))
throw PatchException("Could not resolve fingerprint to find setQualityByIndex method")
}?.let {
// This instruction refers to the field with the type that contains the setQualityByIndex method.
val instructions = SetQualityByIndexMethodClassFieldReferenceFingerprint.result!!
.method.implementation!!.instructions

val getOnItemClickListenerClassReference =
(instructions.elementAt(0) as ReferenceInstruction).reference
val getSetQualityByIndexMethodClassFieldReference =
(instructions.elementAt(1) as ReferenceInstruction).reference

val setQualityByIndexMethodClassFieldReference =
getSetQualityByIndexMethodClassFieldReference as FieldReference

val setQualityByIndexMethodClass = context.classes
.find { classDef -> classDef.type == setQualityByIndexMethodClassFieldReference.type }!!

// Get the name of the setQualityByIndex method.
val setQualityByIndexMethod = setQualityByIndexMethodClass.methods
.find { method -> method.parameterTypes.first() == "I" }
?: throw PatchException("Could not find setQualityByIndex method")

it.mutableMethod.addInstructions(
0,
"""
# Get the object instance to invoke the setQualityByIndex method on.
iget-object v0, p0, $getOnItemClickListenerClassReference
iget-object v0, v0, $getSetQualityByIndexMethodClassFieldReference
# Get the method name.
const-string v1, "${setQualityByIndexMethod.name}"
# Set the quality.
# The first parameter is the array list of video qualities.
# The second parameter is the index of the selected quality.
# The register v0 stores the object instance to invoke the setQualityByIndex method on.
# The register v1 stores the name of the setQualityByIndex method.
invoke-static {p1, p2, v0, v1}, $INTEGRATIONS_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I
move-result p2
""",
)
} ?: throw VideoQualitySetterFingerprint.exception


// Inject a call to remember the selected quality.
VideoQualityItemOnClickParentFingerprint.result?.let {
val onItemClickMethod = it.mutableClass.methods.find { method -> method.name == "onItemClick" }

onItemClickMethod?.apply {
val listItemIndexParameter = 3

addInstruction(
0,
"invoke-static {p$listItemIndexParameter}, $INTEGRATIONS_CLASS_DESCRIPTOR->userChangedQuality(I)V"
)
} ?: throw PatchException("Failed to find onItemClick method")
} ?: throw VideoQualityItemOnClickParentFingerprint.exception


// Remember video quality if not using old layout menu.
NewVideoQualityChangedFingerprint.result?.apply {
mutableMethod.apply {
val index = scanResult.patternScanResult!!.startIndex
val qualityRegister = getInstruction<TwoRegisterInstruction>(index).registerA

addInstruction(
index + 1,
"invoke-static {v$qualityRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->userChangedQualityInNewFlyout(I)V"
)
}
} ?: throw NewVideoQualityChangedFingerprint.exception

/**
* Copy arrays
*/
contexts.copyXmlNode("youtube/quality/host", "values/arrays.xml", "resources")

/**
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE: VIDEO_SETTINGS",
"SETTINGS: VIDEO_EXPERIMENTAL_FLAGS",
"SETTINGS: DEFAULT_VIDEO_QUALITY"
)
)

SettingsPatch.updatePatchStatus("Default video quality")
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package app.revanced.patches.youtube.video.quality.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode

internal object NewVideoQualityChangedFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L"),
opcodes = listOf(
Opcode.IGET, // Video resolution (human readable).
Opcode.IGET_OBJECT,
Opcode.IGET_BOOLEAN,
Opcode.IGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_DIRECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.GOTO,
Opcode.CONST_4,
Opcode.IF_NE,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET,
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app.revanced.patches.youtube.video.quality.fingerprints

import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode

/**
* Resolves with the class found in [VideoQualitySetterFingerprint].
*/
internal object SetQualityByIndexMethodClassFieldReferenceFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("L"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IGET_OBJECT,
)
)
Loading

0 comments on commit 1ee2743

Please sign in to comment.