mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-19 00:53:57 +00:00
fix(YouTube - Video quality): Use 1080p enhanced bitrate for Premium users (#5565)
This commit is contained in:
committed by
GitHub
parent
9ccf13b680
commit
1adbd563b2
@@ -5,9 +5,9 @@ import static app.revanced.extension.shared.Utils.NetworkType;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import com.google.android.libraries.youtube.innertube.model.media.VideoQuality;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
@@ -20,6 +20,14 @@ import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RememberVideoQualityPatch {
|
||||
|
||||
/**
|
||||
* Interface to use obfuscated methods.
|
||||
*/
|
||||
public interface VideoQualityMenuInterface {
|
||||
void patch_setMenuIndexFromQuality(VideoQuality quality);
|
||||
}
|
||||
|
||||
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
|
||||
private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
|
||||
private static final IntegerSetting videoQualityMobile = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
|
||||
@@ -30,7 +38,8 @@ public class RememberVideoQualityPatch {
|
||||
|
||||
/**
|
||||
* If the user selected a new quality from the flyout menu,
|
||||
* and {@link Settings#REMEMBER_VIDEO_QUALITY_LAST_SELECTED} is enabled.
|
||||
* and {@link Settings#REMEMBER_VIDEO_QUALITY_LAST_SELECTED}
|
||||
* or {@link Settings#REMEMBER_SHORTS_QUALITY_LAST_SELECTED} is enabled.
|
||||
*/
|
||||
private static boolean userChangedDefaultQuality;
|
||||
|
||||
@@ -40,10 +49,10 @@ public class RememberVideoQualityPatch {
|
||||
private static int userSelectedQualityIndex;
|
||||
|
||||
/**
|
||||
* The available qualities of the current video in human readable form: [1080, 720, 480]
|
||||
* The available qualities of the current video.
|
||||
*/
|
||||
@Nullable
|
||||
private static List<Integer> videoQualities;
|
||||
private static List<VideoQuality> videoQualities;
|
||||
|
||||
private static boolean shouldRememberVideoQuality() {
|
||||
BooleanSetting preference = ShortsPlayerState.isOpen() ?
|
||||
@@ -52,23 +61,27 @@ public class RememberVideoQualityPatch {
|
||||
return preference.get();
|
||||
}
|
||||
|
||||
private static void changeDefaultQuality(int defaultQuality) {
|
||||
private static void changeDefaultQuality(int qualityResolution) {
|
||||
final boolean shortPlayerOpen = ShortsPlayerState.isOpen();
|
||||
String networkTypeMessage;
|
||||
boolean useShortsPreference = ShortsPlayerState.isOpen();
|
||||
IntegerSetting qualitySetting;
|
||||
if (Utils.getNetworkType() == NetworkType.MOBILE) {
|
||||
if (useShortsPreference) shortsQualityMobile.save(defaultQuality);
|
||||
else videoQualityMobile.save(defaultQuality);
|
||||
networkTypeMessage = str("revanced_remember_video_quality_mobile");
|
||||
qualitySetting = shortPlayerOpen ? shortsQualityMobile : videoQualityMobile;
|
||||
} else {
|
||||
if (useShortsPreference) shortsQualityWifi.save(defaultQuality);
|
||||
else videoQualityWifi.save(defaultQuality);
|
||||
networkTypeMessage = str("revanced_remember_video_quality_wifi");
|
||||
qualitySetting = shortPlayerOpen ? shortsQualityWifi : videoQualityWifi;
|
||||
}
|
||||
qualitySetting.save(qualityResolution);
|
||||
|
||||
if (Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED_TOAST.get())
|
||||
Utils.showToastShort(str(
|
||||
useShortsPreference ? "revanced_remember_video_quality_toast_shorts" : "revanced_remember_video_quality_toast",
|
||||
networkTypeMessage, (defaultQuality + "p")
|
||||
));
|
||||
shortPlayerOpen
|
||||
? "revanced_remember_video_quality_toast_shorts"
|
||||
: "revanced_remember_video_quality_toast",
|
||||
networkTypeMessage,
|
||||
(qualityResolution + "p"))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +90,11 @@ public class RememberVideoQualityPatch {
|
||||
* @param qualities Video qualities available, ordered from largest to smallest, with index 0 being the 'automatic' value of -2
|
||||
* @param originalQualityIndex quality index to use, as chosen by YouTube
|
||||
*/
|
||||
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
|
||||
public static int setVideoQuality(VideoQuality[] qualities, VideoQualityMenuInterface menu, int originalQualityIndex) {
|
||||
try {
|
||||
boolean useShortsPreference = ShortsPlayerState.isOpen();
|
||||
Utils.verifyOnMainThread();
|
||||
|
||||
final boolean useShortsPreference = ShortsPlayerState.isOpen();
|
||||
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
|
||||
? (useShortsPreference ? shortsQualityMobile : videoQualityMobile).get()
|
||||
: (useShortsPreference ? shortsQualityWifi : videoQualityWifi).get();
|
||||
@@ -89,16 +104,8 @@ public class RememberVideoQualityPatch {
|
||||
}
|
||||
|
||||
if (videoQualities == null || videoQualities.size() != qualities.length) {
|
||||
videoQualities = new ArrayList<>(qualities.length);
|
||||
for (Object streamQuality : qualities) {
|
||||
for (Field field : streamQuality.getClass().getFields()) {
|
||||
if (field.getType().isAssignableFrom(Integer.TYPE)
|
||||
&& field.getName().length() <= 2) {
|
||||
videoQualities.add(field.getInt(streamQuality));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
videoQualities = Arrays.asList(qualities);
|
||||
|
||||
// After changing videos the qualities can initially be for the prior video.
|
||||
// So if the qualities have changed an update is needed.
|
||||
qualityNeedsUpdating = true;
|
||||
@@ -107,9 +114,9 @@ public class RememberVideoQualityPatch {
|
||||
|
||||
if (userChangedDefaultQuality) {
|
||||
userChangedDefaultQuality = false;
|
||||
final int quality = videoQualities.get(userSelectedQualityIndex);
|
||||
VideoQuality quality = videoQualities.get(userSelectedQualityIndex);
|
||||
Logger.printDebug(() -> "User changed default quality to: " + quality);
|
||||
changeDefaultQuality(quality);
|
||||
changeDefaultQuality(quality.patch_getResolution());
|
||||
return userSelectedQualityIndex;
|
||||
}
|
||||
|
||||
@@ -119,65 +126,86 @@ public class RememberVideoQualityPatch {
|
||||
qualityNeedsUpdating = false;
|
||||
|
||||
// Find the highest quality that is equal to or less than the preferred.
|
||||
int qualityToUse = videoQualities.get(0); // first element is automatic mode
|
||||
VideoQuality qualityToUse = videoQualities.get(0); // First element is automatic mode.
|
||||
int qualityIndexToUse = 0;
|
||||
int i = 0;
|
||||
for (Integer quality : videoQualities) {
|
||||
if (quality <= preferredQuality && qualityToUse < quality) {
|
||||
for (VideoQuality quality : videoQualities) {
|
||||
final int qualityResolution = quality.patch_getResolution();
|
||||
if (qualityResolution > qualityToUse.patch_getResolution() && qualityResolution <= preferredQuality) {
|
||||
qualityToUse = quality;
|
||||
qualityIndexToUse = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// If the desired quality index is equal to the original index,
|
||||
// then the video is already set to the desired default quality.
|
||||
final int qualityToUseFinal = qualityToUse;
|
||||
String qualityToUseName = qualityToUse.patch_getQualityName();
|
||||
if (qualityIndexToUse == originalQualityIndex) {
|
||||
// On first load of a new video, if the UI video quality flyout menu
|
||||
// is not updated then it will still show 'Auto' (ie: Auto (480p)),
|
||||
// even though it's already set to the desired resolution.
|
||||
//
|
||||
// To prevent confusion, set the video index anyways (even if it matches the existing index)
|
||||
// as that will force the UI picker to not display "Auto".
|
||||
Logger.printDebug(() -> "Video is already preferred quality: " + qualityToUseFinal);
|
||||
Logger.printDebug(() -> "Video is already preferred quality: " + qualityToUseName);
|
||||
} else {
|
||||
Logger.printDebug(() -> "Changing video quality from: "
|
||||
+ videoQualities.get(originalQualityIndex) + " to: " + qualityToUseFinal);
|
||||
+ videoQualities.get(originalQualityIndex).patch_getQualityName()
|
||||
+ " to: " + qualityToUseName);
|
||||
}
|
||||
|
||||
Method m = qInterface.getClass().getMethod(qIndexMethod, Integer.TYPE);
|
||||
m.invoke(qInterface, qualityToUse);
|
||||
// On first load of a new video, if the video is already the desired quality
|
||||
// then the quality flyout will show 'Auto' (ie: Auto (720p)).
|
||||
//
|
||||
// To prevent user confusion, set the video index even if the
|
||||
// quality is already correct so the UI picker will not display "Auto".
|
||||
menu.patch_setMenuIndexFromQuality(qualities[qualityIndexToUse]);
|
||||
|
||||
return qualityIndexToUse;
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Failed to set quality", ex);
|
||||
Logger.printException(() -> "setVideoQuality failure", ex);
|
||||
return originalQualityIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Old quality menu.
|
||||
* Injection point. Fixes bad data used by YouTube.
|
||||
*/
|
||||
public static void userChangedQuality(int selectedQualityIndex) {
|
||||
public static int fixVideoQualityResolution(String name, int quality) {
|
||||
final int correctQuality = 480;
|
||||
if (name.equals("480p") && quality != correctQuality) {
|
||||
Logger.printDebug(() -> "Fixing bad data of " + name + " from: " + quality
|
||||
+ " to: " + correctQuality);
|
||||
return correctQuality;
|
||||
}
|
||||
|
||||
return quality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* @param qualityIndex Element index of {@link #videoQualities}.
|
||||
*/
|
||||
public static void userChangedQuality(int qualityIndex) {
|
||||
if (shouldRememberVideoQuality()) {
|
||||
userSelectedQualityIndex = selectedQualityIndex;
|
||||
userSelectedQualityIndex = qualityIndex;
|
||||
userChangedDefaultQuality = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. New quality menu.
|
||||
* Injection point.
|
||||
* @param videoResolution Human readable resolution: 480, 720, 1080.
|
||||
*/
|
||||
public static void userChangedQualityInNewFlyout(int selectedQuality) {
|
||||
public static void userChangedQualityInFlyout(int videoResolution) {
|
||||
Utils.verifyOnMainThread();
|
||||
if (!shouldRememberVideoQuality()) return;
|
||||
|
||||
changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080).
|
||||
changeDefaultQuality(videoResolution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) {
|
||||
Utils.verifyOnMainThread();
|
||||
|
||||
Logger.printDebug(() -> "newVideoStarted");
|
||||
qualityNeedsUpdating = true;
|
||||
videoQualities = null;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.google.android.libraries.youtube.innertube.model.media;
|
||||
|
||||
public class VideoQuality {
|
||||
public final String patch_getQualityName() {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
|
||||
public final int patch_getResolution() {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ internal val subtitleButtonControllerFingerprint = fingerprint {
|
||||
)
|
||||
}
|
||||
|
||||
internal val newVideoQualityChangedFingerprint = fingerprint {
|
||||
internal val videoQualityChangedFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("L")
|
||||
parameters("L")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package app.revanced.patches.youtube.video.information
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
|
||||
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
@@ -110,7 +110,7 @@ internal val seekRelativeFingerprint = fingerprint {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves with the class found in [newVideoQualityChangedFingerprint].
|
||||
* Resolves with the class found in [videoQualityChangedFingerprint].
|
||||
*/
|
||||
internal val playbackSpeedMenuSpeedChangedFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
|
||||
@@ -9,7 +9,7 @@ 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.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
|
||||
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
|
||||
import app.revanced.patches.youtube.video.playerresponse.Hook
|
||||
import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook
|
||||
import app.revanced.patches.youtube.video.playerresponse.playerResponseMethodHookPatch
|
||||
@@ -263,7 +263,7 @@ val videoInformationPatch = bytecodePatch(
|
||||
|
||||
// Handle new playback speed menu.
|
||||
playbackSpeedMenuSpeedChangedFingerprint.match(
|
||||
newVideoQualityChangedFingerprint.originalClassDef,
|
||||
videoQualityChangedFingerprint.originalClassDef,
|
||||
).method.apply {
|
||||
val index = indexOfFirstInstructionOrThrow(Opcode.IGET)
|
||||
|
||||
|
||||
@@ -5,6 +5,21 @@ import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;"
|
||||
|
||||
internal val videoQualityFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters(
|
||||
"I", // Resolution.
|
||||
"Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc
|
||||
"Z",
|
||||
"L"
|
||||
)
|
||||
custom { _, classDef ->
|
||||
classDef.type == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches with the class found in [videoQualitySetterFingerprint].
|
||||
*/
|
||||
@@ -23,6 +38,22 @@ internal val videoQualityItemOnClickParentFingerprint = fingerprint {
|
||||
strings("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT")
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to class found in [videoQualityItemOnClickFingerprint].
|
||||
*/
|
||||
internal val videoQualityItemOnClickFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters(
|
||||
"Landroid/widget/AdapterView;",
|
||||
"Landroid/view/View;",
|
||||
"I",
|
||||
"J"
|
||||
)
|
||||
custom { method, _ ->
|
||||
method.name == "onItemClick"
|
||||
}
|
||||
}
|
||||
|
||||
internal val videoQualitySetterFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
@@ -37,7 +68,6 @@ internal val videoQualitySetterFingerprint = fingerprint {
|
||||
strings("menu_item_video_quality")
|
||||
}
|
||||
|
||||
|
||||
internal val videoQualityMenuOptionsFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.STATIC)
|
||||
returns("[L")
|
||||
|
||||
@@ -3,8 +3,8 @@ package app.revanced.patches.youtube.video.quality
|
||||
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.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||
@@ -12,15 +12,21 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
|
||||
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
|
||||
import app.revanced.patches.youtube.video.information.onCreateHook
|
||||
import app.revanced.patches.youtube.video.information.videoInformationPatch
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
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.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;"
|
||||
private const val EXTENSION_VIDEO_QUALITY_MENU_INTERFACE =
|
||||
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch\$VideoQualityMenuInterface;"
|
||||
|
||||
val rememberVideoQualityPatch = bytecodePatch {
|
||||
dependsOn(
|
||||
@@ -70,72 +76,151 @@ val rememberVideoQualityPatch = bytecodePatch {
|
||||
*/
|
||||
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted")
|
||||
|
||||
videoQualityFingerprint.let {
|
||||
// Fix bad data used by YouTube.
|
||||
it.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p2, p1 }, $EXTENSION_CLASS_DESCRIPTOR->fixVideoQualityResolution(Ljava/lang/String;I)I
|
||||
move-result p1
|
||||
"""
|
||||
)
|
||||
|
||||
// Add methods to access obfuscated quality fields.
|
||||
it.classDef.apply {
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_getQualityName",
|
||||
listOf(),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
// Only one string field.
|
||||
val qualityNameField = fields.single { field ->
|
||||
field.type == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget-object v0, p0, $qualityNameField
|
||||
return-object v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_getResolution",
|
||||
listOf(),
|
||||
"I",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
val resolutionField = fields.single { field ->
|
||||
field.type == "I"
|
||||
}
|
||||
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
iget v0, p0, $resolutionField
|
||||
return v0
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Inject a call to set the remembered quality once a video loads.
|
||||
setQualityByIndexMethodClassFieldReferenceFingerprint.match(
|
||||
videoQualitySetterFingerprint.originalClassDef,
|
||||
videoQualitySetterFingerprint.originalClassDef
|
||||
).let { match ->
|
||||
// This instruction refers to the field with the type that contains the setQualityByIndex method.
|
||||
val instructions = match.method.implementation!!.instructions
|
||||
|
||||
val getOnItemClickListenerClassReference =
|
||||
val onItemClickListenerClassReference =
|
||||
(instructions.elementAt(0) as ReferenceInstruction).reference
|
||||
val getSetQualityByIndexMethodClassFieldReference =
|
||||
(instructions.elementAt(1) as ReferenceInstruction).reference
|
||||
val setQualityFieldReference =
|
||||
((instructions.elementAt(1) as ReferenceInstruction).reference) as FieldReference
|
||||
|
||||
val setQualityByIndexMethodClassFieldReference =
|
||||
getSetQualityByIndexMethodClassFieldReference as FieldReference
|
||||
proxy(
|
||||
classes.find { classDef ->
|
||||
classDef.type == setQualityFieldReference.type
|
||||
}!!
|
||||
).mutableClass.apply {
|
||||
// Add interface and helper methods to allow extension code to call obfuscated methods.
|
||||
interfaces.add(EXTENSION_VIDEO_QUALITY_MENU_INTERFACE)
|
||||
|
||||
val setQualityByIndexMethodClass = classes
|
||||
.find { classDef -> classDef.type == setQualityByIndexMethodClassFieldReference.type }!!
|
||||
// Get the name of the setQualityByIndex method.
|
||||
val setQualityMenuIndexMethod = methods.single {
|
||||
method -> method.parameterTypes.firstOrNull() == YOUTUBE_VIDEO_QUALITY_CLASS_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")
|
||||
methods.add(
|
||||
ImmutableMethod(
|
||||
type,
|
||||
"patch_setMenuIndexFromQuality",
|
||||
listOf(
|
||||
ImmutableMethodParameter(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE, null, null)
|
||||
),
|
||||
"V",
|
||||
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(2),
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-virtual { p0, p1 }, $setQualityMenuIndexMethod
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
videoQualitySetterFingerprint.method.addInstructions(
|
||||
0,
|
||||
"""
|
||||
# Get the object instance to invoke the setQualityByIndex method on.
|
||||
iget-object v0, p0, $getOnItemClickListenerClassReference
|
||||
iget-object v0, v0, $getSetQualityByIndexMethodClassFieldReference
|
||||
iget-object v0, p0, $onItemClickListenerClassReference
|
||||
iget-object v0, v0, $setQualityFieldReference
|
||||
|
||||
# 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 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I
|
||||
invoke-static { p1, v0, p2 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([$YOUTUBE_VIDEO_QUALITY_CLASS_TYPE${EXTENSION_VIDEO_QUALITY_MENU_INTERFACE}I)I
|
||||
move-result p2
|
||||
""",
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
// Inject a call to remember the selected quality.
|
||||
videoQualityItemOnClickParentFingerprint.classDef.methods.find { it.name == "onItemClick" }
|
||||
?.apply {
|
||||
val listItemIndexParameter = 3
|
||||
videoQualityItemOnClickFingerprint.match(
|
||||
videoQualityItemOnClickParentFingerprint.classDef
|
||||
).method.addInstruction(
|
||||
0,
|
||||
"invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V"
|
||||
)
|
||||
|
||||
// Inject a call to remember the user selected quality.
|
||||
videoQualityChangedFingerprint.let {
|
||||
it.method.apply {
|
||||
val index = it.patternMatch!!.startIndex
|
||||
val register = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
|
||||
addInstruction(
|
||||
0,
|
||||
"invoke-static { p$listItemIndexParameter }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V",
|
||||
index + 1,
|
||||
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->userChangedQualityInFlyout(I)V",
|
||||
)
|
||||
} ?: throw PatchException("Failed to find onItemClick method")
|
||||
|
||||
// Remember video quality if not using old layout menu.
|
||||
newVideoQualityChangedFingerprint.method.apply {
|
||||
val index = newVideoQualityChangedFingerprint.patternMatch!!.startIndex
|
||||
val qualityRegister = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
|
||||
addInstruction(
|
||||
index + 1,
|
||||
"invoke-static { v$qualityRegister }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->userChangedQualityInNewFlyout(I)V",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user