fix(YouTube): Combine Restore old video quality menu and Remember video quality into Video quality patch (#4552)

This commit is contained in:
LisoUseInAIKyrios 2025-03-06 14:56:32 +02:00 committed by GitHub
parent 49bf811252
commit ee67b763d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 324 additions and 301 deletions

View File

@ -2,20 +2,20 @@ package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.patches.playback.quality.RestoreOldVideoQualityMenuPatch;
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
import app.revanced.extension.youtube.settings.Settings;
/**
* Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}.
* Abuse LithoFilter for {@link AdvancedVideoQualityMenuPatch}.
*/
public final class VideoQualityMenuFilterPatch extends Filter {
public final class AdvancedVideoQualityMenuFilter extends Filter {
// Must be volatile or synchronized, as litho filtering runs off main thread
// and this field is then access from the main thread.
public static volatile boolean isVideoQualityMenuVisible;
public VideoQualityMenuFilterPatch() {
public AdvancedVideoQualityMenuFilter() {
addPathCallbacks(new StringFilterGroup(
Settings.RESTORE_OLD_VIDEO_QUALITY_MENU,
Settings.ADVANCED_VIDEO_QUALITY_MENU,
"quick_quality_sheet_content.eml-js"
));
}

View File

@ -8,30 +8,30 @@ import android.widget.ListView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.components.VideoQualityMenuFilterPatch;
import app.revanced.extension.youtube.patches.components.AdvancedVideoQualityMenuFilter;
import app.revanced.extension.youtube.settings.Settings;
/**
* This patch contains the logic to show the old video quality menu.
* This patch contains the logic to always open the advanced video quality menu.
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
* and a ListView in the old one.
*/
@SuppressWarnings("unused")
public final class RestoreOldVideoQualityMenuPatch {
public final class AdvancedVideoQualityMenuPatch {
/**
* Injection point.
*/
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return;
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try {
// Check if the current view is the quality menu.
if (!VideoQualityMenuFilterPatch.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) {
if (!AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) {
return;
}
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false;
AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible = false;
ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3);
if (!(quickQualityViewParent instanceof ViewGroup)) {
@ -39,16 +39,15 @@ public final class RestoreOldVideoQualityMenuPatch {
}
View firstChild = recyclerView.getChildAt(0);
if (!(firstChild instanceof ViewGroup)) {
if (!(firstChild instanceof ViewGroup firstChildGroup)) {
return;
}
ViewGroup advancedQualityParentView = (ViewGroup) firstChild;
if (advancedQualityParentView.getChildCount() < 4) {
if (firstChildGroup.getChildCount() < 4) {
return;
}
View advancedQualityView = advancedQualityParentView.getChildAt(3);
View advancedQualityView = firstChildGroup.getChildAt(3);
if (advancedQualityView == null) {
return;
}
@ -71,7 +70,7 @@ public final class RestoreOldVideoQualityMenuPatch {
* Used to force the creation of the advanced menu item for the Shorts quality flyout.
*/
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
return Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get() || original;
return Settings.ADVANCED_VIDEO_QUALITY_MENU.get() || original;
}
/**
@ -79,8 +78,8 @@ public final class RestoreOldVideoQualityMenuPatch {
*
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
*/
public static void showOldVideoQualityMenu(final ListView listView) {
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return;
public static void showAdvancedVideoQualityMenu(ListView listView) {
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override

View File

@ -47,14 +47,14 @@ import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings {
// Video
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_shorts_quality_default_wifi", -2, true);
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_shorts_quality_default_mobile", -2, true);
public static final BooleanSetting REMEMBER_SHORTS_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_shorts_quality_last_selected", FALSE);
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
// Speed
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
@ -391,6 +391,7 @@ public class Settings extends BaseSettings {
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
static {
// region Migration
@ -411,6 +412,8 @@ public class Settings extends BaseSettings {
migrateOldSettingToNew(DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN, HIDE_END_SCREEN_SUGGESTED_VIDEO);
migrateOldSettingToNew(DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU, ADVANCED_VIDEO_QUALITY_MENU);
// Migrate renamed enum.
//noinspection deprecation
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {

View File

@ -1463,6 +1463,10 @@ public final class app/revanced/patches/youtube/video/quality/RememberVideoQuali
public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchKt {
public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@ -5,10 +5,10 @@ import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
internal const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
internal const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused")
val spoofWifiPatch = bytecodePatch(

View File

@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR =
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.

View File

@ -11,7 +11,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal const val EXTENSION_CLASS_DESCRIPTOR =
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;"
val settingsPatch = bytecodePatch(

View File

@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR =
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;"
@Suppress("unused")

View File

@ -11,7 +11,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
internal const val EXTENSION_CLASS_DESCRIPTOR =
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;"
val hideGetPremiumPatch = bytecodePatch(

View File

@ -50,7 +50,7 @@ private val downloadsResourcePatch = resourcePatch {
}
}
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"

View File

@ -23,7 +23,7 @@ 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.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR =
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;"
val navigationButtonsPatch = bytecodePatch(

View File

@ -14,7 +14,7 @@ import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR =
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;"
val disableFullscreenAmbientModePatch = bytecodePatch(

View File

@ -8,7 +8,10 @@ import app.revanced.patcher.patch.stringOption
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
@ -71,6 +74,9 @@ val themePatch = bytecodePatch(
)
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
lithoColorHookPatch,
seekbarColorPatch,
versionCheckPatch,
@ -78,23 +84,31 @@ val themePatch = bytecodePatch(
dependsOn(
settingsPatch,
resourceMappingPatch,
addResourcesPatch,
)
execute {
addResources("youtube", "layout.theme.themeResourcePatch")
PreferenceScreen.SEEKBAR.addPreferences(
val preferences = mutableSetOf<BasePreference>(
SwitchPreference("revanced_seekbar_custom_color"),
TextPreference("revanced_seekbar_custom_color_primary", inputType = InputType.TEXT_CAP_CHARACTERS),
)
if (is_19_25_or_greater) {
PreferenceScreen.SEEKBAR.addPreferences(
TextPreference("revanced_seekbar_custom_color_accent", inputType = InputType.TEXT_CAP_CHARACTERS),
preferences += TextPreference(
"revanced_seekbar_custom_color_accent",
inputType = InputType.TEXT_CAP_CHARACTERS
)
}
PreferenceScreen.SEEKBAR.addPreferences(
PreferenceCategory(
// Title is hidden, but is used for sorting the group.
titleKey = "revanced_seekbar_custom_color_title",
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = preferences
)
)
// Edit theme colors via resources.
document("res/values/colors.xml").use { document ->
@ -125,7 +139,6 @@ val themePatch = bytecodePatch(
colorValue: String,
) {
document(resourceFile).use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
resourcesNode.appendChild(
@ -133,7 +146,7 @@ val themePatch = bytecodePatch(
setAttribute("name", colorName)
setAttribute("category", "color")
textContent = colorValue
},
}
)
}
}
@ -152,11 +165,10 @@ val themePatch = bytecodePatch(
// Edit splash screen files and change the background color,
// if the background colors are set.
if (darkThemeBackgroundColor != null && lightThemeBackgroundColor != null) {
val splashScreenResourceFiles =
listOf(
"res/drawable/quantum_launchscreen_youtube.xml",
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml",
)
val splashScreenResourceFiles = listOf(
"res/drawable/quantum_launchscreen_youtube.xml",
"res/drawable-sw600dp/quantum_launchscreen_youtube.xml",
)
splashScreenResourceFiles.forEach editSplashScreen@{ resourceFile ->
document(resourceFile).use { document ->
@ -200,10 +212,7 @@ val themePatch = bytecodePatch(
}
}
}
},
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
}
)
compatibleWith(

View File

@ -27,7 +27,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
lateinit var addLithoFilter: (String) -> Unit
private set
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/LithoFilterPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/LithoFilterPatch;"
val lithoFilterPatch = bytecodePatch(
description = "Hooks the method which parses the bytes into a ComponentContext to filter components.",

View File

@ -15,7 +15,7 @@ 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
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerTypeHookPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PlayerTypeHookPatch;"
internal var reelWatchPlayerId = -1L
private set

View File

@ -301,11 +301,9 @@ object PreferenceScreen : BasePreferenceScreen() {
summaryKey = null,
)
// Don't sort, because title sorting scatters the custom color preferences.
val SEEKBAR = Screen(
key = "revanced_settings_screen_07_seekbar",
summaryKey = null,
sorting = Sorting.UNSORTED,
)
val SWIPE_CONTROLS = Screen(
key = "revanced_settings_screen_08_swipe_controls",

View File

@ -0,0 +1,118 @@
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.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal var videoQualityBottomSheetListFragmentTitle = -1L
private set
internal var videoQualityQuickMenuAdvancedMenuDescription = -1L
private set
private val advancedVideoQualityMenuResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
execute {
// Used for the old type of the video quality menu.
videoQualityBottomSheetListFragmentTitle = resourceMappings[
"layout",
"video_quality_bottom_sheet_list_fragment_title",
]
videoQualityQuickMenuAdvancedMenuDescription = resourceMappings[
"string",
"video_quality_quick_menu_advanced_menu_description",
]
}
}
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/AdvancedVideoQualityMenuPatch;"
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/AdvancedVideoQualityMenuFilter;"
internal val advancedVideoQualityMenuPatch = bytecodePatch {
dependsOn(
advancedVideoQualityMenuResourcePatch,
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
lithoFilterPatch,
recyclerViewTreeHookPatch,
)
execute {
addResources("youtube", "video.quality.advancedVideoQualityMenuPatch")
settingsMenuVideoQualityGroup.add(
SwitchPreference("revanced_advanced_video_quality_menu")
)
// region Patch for the old type of the video quality menu.
// Used for regular videos when spoofing to old app version,
// and for the Shorts quality flyout on newer app versions.
videoQualityMenuViewInflateFingerprint.let {
it.method.apply {
val checkCastIndex = it.patternMatch!!.endIndex
val listViewRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(
checkCastIndex + 1,
"invoke-static { v$listViewRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"showAdvancedVideoQualityMenu(Landroid/widget/ListView;)V",
)
}
}
// Force YT to add the 'advanced' quality menu for Shorts.
videoQualityMenuOptionsFingerprint.let {
val patternMatch = it.patternMatch!!
val startIndex = patternMatch.startIndex
val insertIndex = patternMatch.endIndex
if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex")
it.method.apply {
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// A condition controls whether to show the three or four items quality menu.
// Force the four items quality menu to make the "Advanced" item visible, necessary for the patch.
addInstructions(
insertIndex,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z
move-result v$register
"""
)
}
}
// endregion
// region Patch for the new type of the video quality menu.
addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR)
// Required to check if the video quality menu is currently shown in order to click on the "Advanced" item.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
}
}

View File

@ -1,6 +1,7 @@
package app.revanced.patches.youtube.video.quality
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@ -35,3 +36,41 @@ internal val videoQualitySetterFingerprint = fingerprint {
)
strings("menu_item_video_quality")
}
internal val videoQualityMenuOptionsFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC)
returns("[L")
parameters("Landroid/content/Context", "L", "L")
opcodes(
Opcode.CONST_4, // First instruction of method.
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN, // Use the quality menu, that contains the advanced menu.
Opcode.IF_NEZ,
)
literal { videoQualityQuickMenuAdvancedMenuDescription }
}
internal val videoQualityMenuViewInflateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters("L", "L", "L")
opcodes(
Opcode.INVOKE_SUPER,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_16,
Opcode.INVOKE_VIRTUAL,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
)
literal { videoQualityBottomSheetListFragmentTitle }
}

View File

@ -8,12 +8,9 @@ import app.revanced.patcher.patch.bytecodePatch
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
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
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.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
import app.revanced.patches.youtube.video.information.onCreateHook
@ -25,10 +22,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;"
val rememberVideoQualityPatch = bytecodePatch(
name = "Remember video quality",
description = "Adds an option to remember the last video quality selected.",
) {
val rememberVideoQualityPatch = bytecodePatch {
dependsOn(
sharedExtensionPatch,
videoInformationPatch,
@ -37,59 +31,38 @@ val rememberVideoQualityPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "video.quality.rememberVideoQualityPatch")
PreferenceScreen.VIDEO.addPreferences(
// Keep the preferences organized together.
PreferenceCategory(
key = "revanced_01_video_key", // Dummy key to force the quality preferences first.
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = setOf(
ListPreference(
key = "revanced_video_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
ListPreference(
key = "revanced_video_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
SwitchPreference("revanced_remember_video_quality_last_selected"),
settingsMenuVideoQualityGroup.addAll(listOf(
ListPreference(
key = "revanced_video_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
ListPreference(
key = "revanced_video_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
SwitchPreference("revanced_remember_video_quality_last_selected"),
ListPreference(
key = "revanced_shorts_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
ListPreference(
key = "revanced_shorts_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
SwitchPreference("revanced_remember_shorts_quality_last_selected")
)
)
)
ListPreference(
key = "revanced_shorts_quality_default_mobile",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
ListPreference(
key = "revanced_shorts_quality_default_wifi",
summaryKey = null,
entriesKey = "revanced_video_quality_default_entries",
entryValuesKey = "revanced_video_quality_default_entry_values",
),
SwitchPreference("revanced_remember_shorts_quality_last_selected")
))
/*
* The following code works by hooking the method which is called when the user selects a video quality

View File

@ -0,0 +1,48 @@
package app.revanced.patches.youtube.video.quality
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
/**
* Video quality settings. Used to organize all speed related settings together.
*/
internal val settingsMenuVideoQualityGroup = mutableSetOf<BasePreference>()
@Suppress("unused")
val videoQualityPatch = bytecodePatch(
name = "Video quality",
description = "Adds options to use the advanced video quality menu and set default video qualities."
) {
dependsOn(
rememberVideoQualityPatch,
advancedVideoQualityMenuPatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
)
)
execute {
PreferenceScreen.VIDEO.addPreferences(
// Keep the preferences organized together.
PreferenceCategory(
key = "revanced_01_video_key", // Dummy key to force the quality preferences first.
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = settingsMenuVideoQualityGroup
)
)
}
}

View File

@ -17,13 +17,13 @@ internal val settingsMenuVideoSpeedGroup = mutableSetOf<BasePreference>()
@Suppress("unused")
val playbackSpeedPatch = bytecodePatch(
name = "Playback speed",
description = "Adds options to customize available playback speeds, remember the last playback speed selected " +
description = "Adds options to customize available playback speeds, set default a playback speed, " +
"and show a speed dialog button in the video player.",
) {
dependsOn(
playbackSpeedButtonPatch,
customPlaybackSpeedPatch,
rememberPlaybackSpeedPatch,
playbackSpeedButtonPatch,
)
compatibleWith(
@ -38,10 +38,10 @@ val playbackSpeedPatch = bytecodePatch(
)
)
finalize {
execute {
PreferenceScreen.VIDEO.addPreferences(
PreferenceCategory(
key = "revanced_zz_key", // Dummy key to force the speed settings last.
key = "revanced_zz_video_key", // Dummy key to force the speed settings last.
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",

View File

@ -5,6 +5,7 @@ import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playercontrols.*
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
@ -35,11 +36,12 @@ val playbackSpeedButtonPatch = bytecodePatch(
description = "Adds the option to display playback speed dialog button in the video player.",
) {
dependsOn(
playbackSpeedButtonResourcePatch,
customPlaybackSpeedPatch,
playerControlsPatch,
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
customPlaybackSpeedPatch,
playbackSpeedButtonResourcePatch,
playerControlsPatch,
)
execute {

View File

@ -60,12 +60,12 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
) {
dependsOn(
sharedExtensionPatch,
lithoFilterPatch,
settingsPatch,
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch,
addResourcesPatch,
versionCheckPatch
lithoFilterPatch,
versionCheckPatch,
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch
)
execute {

View File

@ -22,9 +22,9 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch {
dependsOn(
sharedExtensionPatch,
settingsPatch,
videoInformationPatch,
customPlaybackSpeedPatch,
addResourcesPatch,
videoInformationPatch,
customPlaybackSpeedPatch
)
execute {
@ -44,6 +44,7 @@ internal val rememberPlaybackSpeedPatch = bytecodePatch {
)
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted")
userSelectedPlaybackSpeedHook(
EXTENSION_CLASS_DESCRIPTOR,
"userSelectedPlaybackSpeed",

View File

@ -1,43 +0,0 @@
package app.revanced.patches.youtube.video.videoqualitymenu
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val videoQualityMenuOptionsFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC)
returns("[L")
parameters("Landroid/content/Context", "L", "L")
opcodes(
Opcode.CONST_4, // First instruction of method.
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.IGET_BOOLEAN, // Use the quality menu, that contains the advanced menu.
Opcode.IF_NEZ,
)
literal { videoQualityQuickMenuAdvancedMenuDescription }
}
internal val videoQualityMenuViewInflateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters("L", "L", "L")
opcodes(
Opcode.INVOKE_SUPER,
Opcode.CONST,
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_16,
Opcode.INVOKE_VIRTUAL,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
)
literal { videoQualityBottomSheetListFragmentTitle }
}

View File

@ -1,135 +1,10 @@
package app.revanced.patches.youtube.video.videoqualitymenu
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.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal var videoQualityBottomSheetListFragmentTitle = -1L
private set
internal var videoQualityQuickMenuAdvancedMenuDescription = -1L
private set
private val restoreOldVideoQualityMenuResourcePatch = resourcePatch {
dependsOn(
settingsPatch,
resourceMappingPatch,
addResourcesPatch,
)
execute {
addResources("youtube", "video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_restore_old_video_quality_menu"),
)
// Used for the old type of the video quality menu.
videoQualityBottomSheetListFragmentTitle = resourceMappings[
"layout",
"video_quality_bottom_sheet_list_fragment_title",
]
videoQualityQuickMenuAdvancedMenuDescription = resourceMappings[
"string",
"video_quality_quick_menu_advanced_menu_description",
]
}
}
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/VideoQualityMenuFilterPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/RestoreOldVideoQualityMenuPatch;"
import app.revanced.patches.youtube.video.quality.videoQualityPatch
@Suppress("unused")
val restoreOldVideoQualityMenuPatch = bytecodePatch(
name = "Restore old video quality menu",
description = "Adds an option to restore the old video quality menu with specific video resolution options.",
) {
dependsOn(
sharedExtensionPatch,
restoreOldVideoQualityMenuResourcePatch,
lithoFilterPatch,
recyclerViewTreeHookPatch,
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
// region Patch for the old type of the video quality menu.
// Used for regular videos when spoofing to old app version,
// and for the Shorts quality flyout on newer app versions.
videoQualityMenuViewInflateFingerprint.method.apply {
val checkCastIndex = videoQualityMenuViewInflateFingerprint.patternMatch!!.endIndex
val listViewRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(
checkCastIndex + 1,
"invoke-static { v$listViewRegister }, " +
"$EXTENSION_CLASS_DESCRIPTOR->" +
"showOldVideoQualityMenu(Landroid/widget/ListView;)V",
)
}
// Force YT to add the 'advanced' quality menu for Shorts.
val patternMatch = videoQualityMenuOptionsFingerprint.patternMatch!!
val startIndex = patternMatch.startIndex
if (startIndex != 0) throw PatchException("Unexpected opcode start index: $startIndex")
val insertIndex = patternMatch.endIndex
videoQualityMenuOptionsFingerprint.method.apply {
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
// A condition controls whether to show the three or four items quality menu.
// Force the four items quality menu to make the "Advanced" item visible, necessary for the patch.
addInstructions(
insertIndex,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->forceAdvancedVideoQualityMenuCreation(Z)Z
move-result v$register
""",
)
}
// endregion
// region Patch for the new type of the video quality menu.
addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR)
// Required to check if the video quality menu is currently shown in order to click on the "Advanced" item.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
}
}
@Deprecated("Use 'Video Quality' instead.")
val restoreOldVideoQualityMenuPatch = bytecodePatch {
dependsOn(videoQualityPatch)
}

View File

@ -1257,8 +1257,6 @@ Swipe to expand or close"</string>
<string name="revanced_gradient_loading_screen_title">Enable gradient loading screen</string>
<string name="revanced_gradient_loading_screen_summary_on">Loading screen will have a gradient background</string>
<string name="revanced_gradient_loading_screen_summary_off">Loading screen will have a solid background</string>
</patch>
<patch id="layout.theme.themeResourcePatch">
<string name="revanced_seekbar_custom_color_title">Enable custom seekbar color</string>
<string name="revanced_seekbar_custom_color_summary_on">Custom seekbar color is shown</string>
<string name="revanced_seekbar_custom_color_summary_off">Original seekbar color is shown</string>
@ -1373,7 +1371,6 @@ Enabling this can unlock higher video qualities"</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_screen_title">Video quality</string>
<string name="revanced_video_quality_default_entry_1">Auto</string>
<string name="revanced_remember_video_quality_last_selected_title">Remember video quality changes</string>
<string name="revanced_remember_video_quality_last_selected_summary_on">Quality changes apply to all videos</string>
@ -1381,8 +1378,8 @@ Enabling this can unlock higher video qualities"</string>
<string name="revanced_video_quality_default_wifi_title">Default video quality on Wi-Fi network</string>
<string name="revanced_video_quality_default_mobile_title">Default video quality on mobile network</string>
<string name="revanced_remember_shorts_quality_last_selected_title">Remember Shorts quality changes</string>
<string name="revanced_remember_shorts_quality_last_selected_summary_on">Quality changes apply to all Shorts videos</string>
<string name="revanced_remember_shorts_quality_last_selected_summary_off">Quality changes only apply to the current Shorts video</string>
<string name="revanced_remember_shorts_quality_last_selected_summary_on">Quality changes apply to all Shorts</string>
<string name="revanced_remember_shorts_quality_last_selected_summary_off">Quality changes only apply to the current Short</string>
<string name="revanced_shorts_quality_default_wifi_title">Default Shorts quality on Wi-Fi network</string>
<string name="revanced_shorts_quality_default_mobile_title">Default Shorts quality on mobile network</string>
<string name="revanced_remember_video_quality_mobile">mobile</string>
@ -1419,10 +1416,10 @@ Enabling this can unlock higher video qualities"</string>
<string name="revanced_disable_hdr_video_summary_on">HDR video is disabled</string>
<string name="revanced_disable_hdr_video_summary_off">HDR video is enabled</string>
</patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch">
<string name="revanced_restore_old_video_quality_menu_title">Restore old video quality menu</string>
<string name="revanced_restore_old_video_quality_menu_summary_on">Old video quality menu is shown</string>
<string name="revanced_restore_old_video_quality_menu_summary_off">Old video quality menu is not shown</string>
<patch id="video.quality.advancedVideoQualityMenuPatch">
<string name="revanced_advanced_video_quality_menu_title">Show advanced video quality menu</string>
<string name="revanced_advanced_video_quality_menu_summary_on">Advanced video quality menu is shown</string>
<string name="revanced_advanced_video_quality_menu_summary_off">Advanced video quality menu is not shown</string>
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
<string name="revanced_slide_to_seek_title">Enable slide to seek</string>