Skip to content

feat(YouTube): Add player button to change video quality #5435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final BooleanSetting VIDEO_QUALITY_DIALOG_BUTTON = new BooleanSetting("revanced_video_quality_dialog_button", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,25 @@ public void hide() {
if (view != null) view.setVisibility(View.GONE);
isVisible = false;
}

/**
* Sets the icon of the button.
* @param resourceName The name of the drawable resource.
*/
public void setIcon(String resourceName) {
try {
View button = buttonRef.get();
if (button instanceof ImageView) {
int resourceId = Utils.getResourceIdentifier(resourceName, "drawable");
if (resourceId != 0) {
((ImageView) button).setImageResource(resourceId);
Logger.printDebug(() -> "Set button icon to: " + resourceName);
} else {
Logger.printDebug(() -> "Resource not found: " + resourceName);
}
}
} catch (Exception ex) {
Logger.printException(() -> "setIcon failure", ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package app.revanced.extension.youtube.videoplayer;

import android.view.View;

import androidx.annotation.Nullable;

import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch;
import app.revanced.extension.youtube.settings.Settings;

import java.util.List;

import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.showToastShort;

@SuppressWarnings("unused")
public class VideoQualityDialogButton {
@Nullable
private static PlayerControlButton instance;

/**
* Updates the button icon based on the current video quality.
*/
public static void updateButtonIcon() {
if (instance == null) return;

try {
// Get the current quality.
int lastAppliedQualityIndex = RememberVideoQualityPatch.getLastAppliedQualityIndex();
List<Integer> videoQualities = RememberVideoQualityPatch.getVideoQualities();

if (videoQualities == null || lastAppliedQualityIndex < 0 || lastAppliedQualityIndex >= videoQualities.size()) {
// Default to a generic icon or "Auto".
instance.setIcon("revanced_video_quality_dialog_button");
return;
}

int quality = videoQualities.get(lastAppliedQualityIndex);
String iconResource = switch (quality) {
case 144, 240, 360, 480 -> "revanced_video_quality_dialog_button_lhd";
case 720 -> "revanced_video_quality_dialog_button_hd";
case 1080 -> "revanced_video_quality_dialog_button_fhd";
case 1440 -> "revanced_video_quality_dialog_button_2k";
case 2160 -> "revanced_video_quality_dialog_button_4k";
default -> "revanced_video_quality_dialog_button";
};

instance.setIcon(iconResource);
Logger.printDebug(() -> "Updated button icon to: " + iconResource);
} catch (Exception ex) {
Logger.printException(() -> "Failed to update button icon", ex);
}
}

/**
* Injection point.
*/
public static void initializeButton(View controlsView) {
try {
instance = new PlayerControlButton(
controlsView,
"revanced_video_quality_dialog_button",
"revanced_video_quality_dialog_button_placeholder",
Settings.VIDEO_QUALITY_DIALOG_BUTTON::get,
view -> {
try {
RememberVideoQualityPatch.showVideoQualityDialog(view.getContext());
updateButtonIcon(); // Update icon after dialog interaction
} catch (Exception ex) {
Logger.printException(() -> "Video quality button onClick failure", ex);
}
},
view -> {
try {
// Reset to automatic quality.
final int autoQuality = -2; // Auto.
RememberVideoQualityPatch.userChangedQualityInNewFlyout(autoQuality);
updateButtonIcon(); // Update icon after reset.
showToastShort(str("revanced_video_quality_reset_toast"));
return true;
} catch (Exception ex) {
Logger.printException(() -> "Video quality button reset failure", ex);
return false;
}
}
);
updateButtonIcon(); // Set initial icon.
} catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex);
}
}

/**
* Injection point.
*/
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) {
instance.setVisibilityImmediate(visible);
if (visible) updateButtonIcon();
}
}

/**
* Injection point.
*/
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) {
instance.setVisibility(visible, animated);
if (visible) updateButtonIcon();
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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
import app.revanced.patches.youtube.video.quality.button.videoQualityButtonPatch

/**
* Video quality settings. Used to organize all speed related settings together.
Expand All @@ -19,6 +20,7 @@ val videoQualityPatch = bytecodePatch(
dependsOn(
rememberVideoQualityPatch,
advancedVideoQualityMenuPatch,
videoQualityButtonPatch,
)

compatibleWith(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package app.revanced.patches.youtube.video.quality.button

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.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
import app.revanced.patches.youtube.video.quality.rememberVideoQualityPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources

private val videoQualityButtonResourcePatch = resourcePatch {
dependsOn(playerControlsResourcePatch)

execute {
copyResources(
"qualitybutton",
ResourceGroup(
"drawable",
"revanced_video_quality_dialog_button.xml",
"revanced_video_quality_dialog_button_4k.xml",
"revanced_video_quality_dialog_button_fhd.xml",
"revanced_video_quality_dialog_button_hd.xml",
"revanced_video_quality_dialog_button_lhd.xml",
"revanced_video_quality_dialog_button_qhd.xml",
),
)

addBottomControl("qualitybutton")
}
}

private const val QUALITY_BUTTON_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/videoplayer/VideoQualityDialogButton;"

val videoQualityButtonPatch = bytecodePatch(
description = "Adds the option to display video quality dialog button in the video player.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
rememberVideoQualityPatch,
videoQualityButtonResourcePatch,
playerControlsPatch,
)

execute {
addResources("youtube", "video.quality.button.videoQualityButtonPatch")

PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_video_quality_dialog_button"),
)

initializeBottomControl(QUALITY_BUTTON_CLASS_DESCRIPTOR)
injectVisibilityCheckCall(QUALITY_BUTTON_CLASS_DESCRIPTOR)
}
}
8 changes: 8 additions & 0 deletions patches/src/main/resources/addresources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,14 @@ Enabling this can unlock higher video qualities"</string>
<string name="revanced_playback_speed_dialog_button_summary_on">Button is shown. Tap and hold to reset playback speed to default</string>
<string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
<string name="revanced_video_quality_dialog_button_title">Show video quality button</string>
<string name="revanced_video_quality_dialog_button_summary_on">Button is shown. Tap and hold to reset video quality to Auto</string>
<string name="revanced_video_quality_dialog_button_summary_off">Button is not shown</string>
<string name="revanced_video_quality_no_qualities_available">No quality available</string>
<string name="revanced_video_quality_selected_toast">Changed video quality to: %s</string>
<string name="revanced_video_quality_reset_toast">Reset video quality to Auto</string>
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
<string name="revanced_custom_speed_menu_title">Custom playback speed menu</string>
<string name="revanced_custom_speed_menu_summary_on">Custom speed menu is shown</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11.5,20.5 L11.5,15.5 L12.5,15.5 L12.5,17.5 L20.5,17.5 L20.5,18.5 L12.5,18.5 L12.5,20.5 Z M3.5,18.5 L3.5,17.5 L8.5,17.5 L8.5,18.5 Z M7.5,14.5 L7.5,12.5 L3.5,12.5 L3.5,11.5 L7.5,11.5 L7.5,9.5 L8.5,9.5 L8.5,14.5 Z M11.5,12.5 L11.5,11.5 L20.5,11.5 L20.5,12.5 Z M15.5,8.5 L15.5,3.5 L16.5,3.5 L16.5,5.5 L20.5,5.5 L20.5,6.5 L16.5,6.5 L16.5,8.5 Z M3.5,6.5 L3.5,5.5 L12.5,5.5 L12.5,6.5 Z M3.5,6.5" />
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3.61719,5 C3.15625,5,2.76953,5.15234,2.46094,5.46094 C2.15234,5.76953,2,6.15625,2,6.61719 L2,17.3828 C2,17.8438,2.15234,18.2305,2.46094,18.5391 C2.76953,18.8477,3.15625,19,3.61719,19 L20.3828,19 C20.8438,19,21.2305,18.8477,21.5391,18.5391 C21.8477,18.2305,22,17.8438,22,17.3828 L22,6.61719 C22,6.15625,21.8477,5.76953,21.5391,5.46094 C21.2305,5.15234,20.8438,5,20.3828,5 L3.61719,5 Z M3.61719,6 L20.3828,6 C20.5625,6,20.7109,6.05859,20.8281,6.17188 C20.9414,6.28906,21,6.4375,21,6.61719 L21,17.3828 C21,17.5625,20.9414,17.7109,20.8281,17.8281 C20.7109,17.9414,20.5625,18,20.3828,18 L3.61719,18 C3.4375,18,3.28906,17.9414,3.17188,17.8281 C3.05859,17.7109,3,17.5625,3,17.3828 L3,6.61719 C3,6.4375,3.05859,6.28906,3.17188,6.17188 C3.28906,6.05859,3.4375,6,3.61719,6 Z M6.61719,9.30859 L6.61719,13.1914 L9.38672,13.1914 L9.38672,14.6914 L10.2715,14.6914 L10.2715,13.1914 L11.502,13.1914 L11.502,12.3086 L10.2715,12.3086 L10.2715,9.30859 L9.38672,9.30859 L9.38672,12.3086 L7.50195,12.3086 L7.50195,9.30859 L6.61719,9.30859 Z M13.0176,9.30859 L13.0176,14.6914 L13.9023,14.6914 L13.9023,12.3262 L16.1914,14.6914 L17.4199,14.6914 L14.7676,11.9609 L17.4199,9.30859 L16.2285,9.30859 L13.9023,11.6348 L13.9023,9.30859 L13.0176,9.30859 Z" />
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M9.80859,14.6914 L10.6914,14.6914 L10.6914,12.6914 L12.8086,12.6914 L12.8086,14.6914 L13.6914,14.6914 L13.6914,9.30859 L12.8086,9.30859 L12.8086,11.8086 L10.6914,11.8086 L10.6914,9.30859 L9.80859,9.30859 Z M15.0781,14.6914 L17.8828,14.6914 C18.1797,14.6914,18.4336,14.5859,18.6445,14.375 C18.8555,14.1641,18.9609,13.9102,18.9609,13.6172 L18.9609,10.3828 C18.9609,10.0898,18.8555,9.83594,18.6445,9.625 C18.4336,9.41406,18.1797,9.30859,17.8828,9.30859 L15.0781,9.30859 Z M15.9609,13.8086 L15.9609,10.1914 L17.7695,10.1914 C17.8477,10.1914,17.918,10.2227,17.9805,10.2891 C18.043,10.3516,18.0781,10.4219,18.0781,10.5 L18.0781,13.5 C18.0781,13.5781,18.043,13.6484,17.9805,13.7109 C17.918,13.7773,17.8477,13.8086,17.7695,13.8086 Z M5.03906,14.6914 L5.92188,14.6914 L5.92188,12.6172 L7.92188,12.6172 L7.92188,11.7305 L5.92188,11.7305 L5.92188,10.1914 L8.42188,10.1914 L8.42188,9.30859 L5.03906,9.30859 Z M3.61719,19 C3.15625,19,2.76953,18.8477,2.46094,18.5391 C2.15234,18.2305,2,17.8438,2,17.3828 L2,6.61719 C2,6.15625,2.15234,5.76953,2.46094,5.46094 C2.76953,5.15234,3.15625,5,3.61719,5 L20.3828,5 C20.8438,5,21.2305,5.15234,21.5391,5.46094 C21.8477,5.76953,22,6.15625,22,6.61719 L22,17.3828 C22,17.8438,21.8477,18.2305,21.5391,18.5391 C21.2305,18.8477,20.8438,19,20.3828,19 Z M3.61719,18 L20.3828,18 C20.5625,18,20.7109,17.9414,20.8281,17.8281 C20.9414,17.7109,21,17.5625,21,17.3828 L21,6.61719 C21,6.4375,20.9414,6.28906,20.8281,6.17188 C20.7109,6.05859,20.5625,6,20.3828,6 L3.61719,6 C3.4375,6,3.28906,6.05859,3.17188,6.17188 C3.05859,6.28906,3,6.4375,3,6.61719 L3,17.3828 C3,17.5625,3.05859,17.7109,3.17188,17.8281 C3.28906,17.9414,3.4375,18,3.61719,18 Z M3,18 L3,6 Z M3,18" />
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3.61719,5 C3.15625,5,2.76953,5.15234,2.46094,5.46094 C2.15234,5.76953,2,6.15625,2,6.61719 L2,17.3828 C2,17.8438,2.15234,18.2305,2.46094,18.5391 C2.76953,18.8477,3.15625,19,3.61719,19 L20.3828,19 C20.8438,19,21.2305,18.8477,21.5391,18.5391 C21.8477,18.2305,22,17.8438,22,17.3828 L22,6.61719 C22,6.15625,21.8477,5.76953,21.5391,5.46094 C21.2305,5.15234,20.8438,5,20.3828,5 L3.61719,5 Z M3.61719,6 L20.3828,6 C20.5625,6,20.7109,6.05859,20.8281,6.17188 C20.9414,6.28906,21,6.4375,21,6.61719 L21,17.3828 C21,17.5625,20.9414,17.7109,20.8281,17.8281 C20.7109,17.9414,20.5625,18,20.3828,18 L3.61719,18 C3.4375,18,3.28906,17.9414,3.17188,17.8281 C3.05859,17.7109,3,17.5625,3,17.3828 L3,6.61719 C3,6.4375,3.05859,6.28906,3.17188,6.17188 C3.28906,6.05859,3.4375,6,3.61719,6 Z M6.63867,9.30859 L6.63867,14.6914 L7.52148,14.6914 L7.52148,12.6914 L10.1387,12.6914 L10.1387,14.6914 L11.0215,14.6914 L11.0215,9.30859 L10.1387,9.30859 L10.1387,11.8086 L7.52148,11.8086 L7.52148,9.30859 L6.63867,9.30859 Z M13.0215,9.30859 L13.0215,14.6914 L16.3301,14.6914 C16.627,14.6914,16.877,14.5859,17.0879,14.375 C17.2988,14.1641,17.4043,13.9102,17.4043,13.6172 L17.4043,10.3828 C17.4043,10.0898,17.2988,9.83594,17.0879,9.625 C16.877,9.41406,16.627,9.30859,16.3301,9.30859 L13.0215,9.30859 Z M13.9043,10.1914 L16.2129,10.1914 C16.291,10.1914,16.3613,10.2227,16.4238,10.2891 C16.4902,10.3516,16.5215,10.4219,16.5215,10.5 L16.5215,13.5 C16.5215,13.5781,16.4902,13.6484,16.4238,13.7109 C16.3613,13.7773,16.291,13.8086,16.2129,13.8086 L13.9043,13.8086 L13.9043,10.1914 Z" />
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3.61719,5 C3.15625,5,2.76953,5.15234,2.46094,5.46094 C2.15234,5.76953,2,6.15625,2,6.61719 L2,17.3828 C2,17.8438,2.15234,18.2305,2.46094,18.5391 C2.76953,18.8477,3.15625,19,3.61719,19 L20.3828,19 C20.8438,19,21.2305,18.8477,21.5391,18.5391 C21.8477,18.2305,22,17.8438,22,17.3828 L22,6.61719 C22,6.15625,21.8477,5.76953,21.5391,5.46094 C21.2305,5.15234,20.8438,5,20.3828,5 L3.61719,5 Z M3.61719,6 L20.3828,6 C20.5625,6,20.7109,6.05859,20.8281,6.17188 C20.9414,6.28906,21,6.4375,21,6.61719 L21,17.3828 C21,17.5625,20.9414,17.7109,20.8281,17.8281 C20.7109,17.9414,20.5625,18,20.3828,18 L3.61719,18 C3.4375,18,3.28906,17.9414,3.17188,17.8281 C3.05859,17.7109,3,17.5625,3,17.3828 L3,6.61719 C3,6.4375,3.05859,6.28906,3.17188,6.17188 C3.28906,6.05859,3.4375,6,3.61719,6 Z M5.03906,9.30859 L5.03906,14.6914 L8.42188,14.6914 L8.42188,13.8086 L5.92188,13.8086 L5.92188,9.30859 L5.03906,9.30859 Z M9.80859,9.30859 L9.80859,14.6914 L10.6914,14.6914 L10.6914,12.6914 L12.8086,12.6914 L12.8086,14.6914 L13.6914,14.6914 L13.6914,9.30859 L12.8086,9.30859 L12.8086,11.8086 L10.6914,11.8086 L10.6914,9.30859 L9.80859,9.30859 Z M15.0781,9.30859 L15.0781,14.6914 L17.8828,14.6914 C18.1797,14.6914,18.4336,14.5859,18.6445,14.375 C18.8555,14.1641,18.9609,13.9102,18.9609,13.6172 L18.9609,10.3828 C18.9609,10.0898,18.8555,9.83594,18.6445,9.625 C18.4336,9.41406,18.1797,9.30859,17.8828,9.30859 L15.0781,9.30859 Z M15.9609,10.1914 L17.7695,10.1914 C17.8477,10.1914,17.918,10.2227,17.9805,10.2891 C18.043,10.3516,18.0781,10.4219,18.0781,10.5 L18.0781,13.5 C18.0781,13.5781,18.043,13.6484,17.9805,13.7109 C17.918,13.7773,17.8477,13.8086,17.7695,13.8086 L15.9609,13.8086 L15.9609,10.1914 Z" />
</vector>
Loading
Loading