Skip to content

Commit bfbffbd

Browse files
feat(YouTube): Add Disable video codecs patch (#5981)
1 parent ee47556 commit bfbffbd

File tree

15 files changed

+181
-94
lines changed

15 files changed

+181
-94
lines changed

extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/AbstractPreferenceFragment.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
5353
* Set by subclasses if Strings cannot be added as a resource.
5454
*/
5555
@Nullable
56-
protected static String restartDialogTitle, restartDialogMessage, restartDialogButtonText, confirmDialogTitle;
56+
protected static CharSequence restartDialogTitle, restartDialogMessage, restartDialogButtonText, confirmDialogTitle;
5757

5858
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
5959
try {
@@ -125,10 +125,13 @@ private void showSettingUserDialogConfirmation(Preference pref, Setting<?> setti
125125

126126
showingUserDialogMessage = true;
127127

128+
CharSequence message = BulletPointPreference.formatIntoBulletPoints(
129+
Objects.requireNonNull(setting.userDialogMessage).toString());
130+
128131
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
129132
context,
130133
confirmDialogTitle, // Title.
131-
Objects.requireNonNull(setting.userDialogMessage).toString(), // No message.
134+
message,
132135
null, // No EditText.
133136
null, // OK button text.
134137
() -> {

extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/BulletPointPreference.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@
1515
@SuppressWarnings({"unused", "deprecation"})
1616
public class BulletPointPreference extends Preference {
1717

18-
public static SpannedString formatIntoBulletPoints(CharSequence source) {
18+
/**
19+
* Replaces bullet points with styled spans.
20+
*/
21+
public static CharSequence formatIntoBulletPoints(CharSequence source) {
22+
final char bulletPoint = '•';
23+
if (TextUtils.indexOf(source, bulletPoint) < 0) {
24+
return source; // Nothing to do.
25+
}
26+
1927
SpannableStringBuilder builder = new SpannableStringBuilder(source);
2028

2129
int lineStart = 0;
@@ -26,7 +34,7 @@ public static SpannedString formatIntoBulletPoints(CharSequence source) {
2634
if (lineEnd < 0) lineEnd = length;
2735

2836
// Apply BulletSpan only if the line starts with the '•' character.
29-
if (lineEnd > lineStart && builder.charAt(lineStart) == '•') {
37+
if (lineEnd > lineStart && builder.charAt(lineStart) == bulletPoint) {
3038
int deleteEnd = lineStart + 1; // remove the bullet itself
3139

3240
// If there's a single space right after the bullet, remove that too.

extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,8 @@ static String createInnertubeBody(ClientType clientType, String videoId) {
4242
JSONObject context = new JSONObject();
4343

4444
AppLanguage language = SpoofVideoStreamsPatch.getLanguageOverride();
45-
if (language == null || clientType == ANDROID_VR_1_43_32) {
45+
if (language == null) {
4646
// Force original audio has not overrode the language.
47-
// Or if YT has fallen over to the last unauthenticated client (VR 1.43), then
48-
// always use the app language because forcing an audio stream of specific languages
49-
// can sometimes fail so it's better to try and load something rather than nothing.
5047
language = BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get();
5148
}
5249
//noinspection ExtractMethodRecommender

extensions/shared/library/src/main/java/app/revanced/extension/shared/ui/CustomDialog.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ public class CustomDialog {
5858
* @param dismissDialogOnNeutralClick If the dialog should be dismissed when the Neutral button is clicked.
5959
* @return The Dialog and its main LinearLayout container.
6060
*/
61-
public static Pair<Dialog, LinearLayout> create(Context context, String title, CharSequence message,
62-
@Nullable EditText editText, String okButtonText,
61+
public static Pair<Dialog, LinearLayout> create(Context context, CharSequence title, CharSequence message,
62+
@Nullable EditText editText, CharSequence okButtonText,
6363
Runnable onOkClick, Runnable onCancelClick,
64-
@Nullable String neutralButtonText,
64+
@Nullable CharSequence neutralButtonText,
6565
@Nullable Runnable onNeutralClick,
6666
boolean dismissDialogOnNeutralClick) {
6767
Logger.printDebug(() -> "Creating custom dialog with title: " + title);
@@ -85,9 +85,9 @@ public static Pair<Dialog, LinearLayout> create(Context context, String title, C
8585
* @param onNeutralClick Action to perform when the Neutral button is clicked, or null if no Neutral button is needed.
8686
* @param dismissDialogOnNeutralClick If the dialog should be dismissed when the Neutral button is clicked.
8787
*/
88-
private CustomDialog(Context context, String title, CharSequence message, @Nullable EditText editText,
89-
String okButtonText, Runnable onOkClick, Runnable onCancelClick,
90-
@Nullable String neutralButtonText, @Nullable Runnable onNeutralClick,
88+
private CustomDialog(Context context, CharSequence title, CharSequence message, @Nullable EditText editText,
89+
CharSequence okButtonText, Runnable onOkClick, Runnable onCancelClick,
90+
@Nullable CharSequence neutralButtonText, @Nullable Runnable onNeutralClick,
9191
boolean dismissDialogOnNeutralClick) {
9292
this.context = context;
9393
this.dialog = new Dialog(context);
@@ -139,7 +139,7 @@ private LinearLayout createMainLayout() {
139139
*
140140
* @param title The title text to display.
141141
*/
142-
private void addTitle(String title) {
142+
private void addTitle(CharSequence title) {
143143
if (TextUtils.isEmpty(title)) return;
144144

145145
TextView titleView = new TextView(context);
@@ -232,8 +232,8 @@ private void addContent(CharSequence message, @Nullable EditText editText) {
232232
* @param onNeutralClick Action for the Neutral button click, or null if no Neutral button.
233233
* @param dismissDialogOnNeutralClick If the dialog should dismiss on Neutral button click.
234234
*/
235-
private void addButtons(String okButtonText, Runnable onOkClick, Runnable onCancelClick,
236-
@Nullable String neutralButtonText, @Nullable Runnable onNeutralClick,
235+
private void addButtons(CharSequence okButtonText, Runnable onOkClick, Runnable onCancelClick,
236+
@Nullable CharSequence neutralButtonText, @Nullable Runnable onNeutralClick,
237237
boolean dismissDialogOnNeutralClick) {
238238
// Button container.
239239
LinearLayout buttonContainer = new LinearLayout(context);
@@ -280,7 +280,7 @@ private void addButtons(String okButtonText, Runnable onOkClick, Runnable onCanc
280280
* @param dismissDialog If the dialog should dismiss when the button is clicked.
281281
* @return The created Button.
282282
*/
283-
private Button createButton(String text, Runnable onClick, boolean isOkButton, boolean dismissDialog) {
283+
private Button createButton(CharSequence text, Runnable onClick, boolean isOkButton, boolean dismissDialog) {
284284
Button button = new Button(context, null, 0);
285285
button.setText(text);
286286
button.setTextSize(14);

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/DisableHdrPatch.java renamed to extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/DisableVideoCodecsPatch.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import app.revanced.extension.youtube.settings.Settings;
66

77
@SuppressWarnings("unused")
8-
public class DisableHdrPatch {
8+
public class DisableVideoCodecsPatch {
99

1010
/**
1111
* Injection point.
@@ -15,5 +15,12 @@ public static int[] disableHdrVideo(Display.HdrCapabilities capabilities) {
1515
? new int[0]
1616
: capabilities.getSupportedHdrTypes();
1717
}
18+
19+
/**
20+
* Injection point.
21+
*/
22+
public static boolean allowVP9() {
23+
return !Settings.FORCE_AVC_CODEC.get();
24+
}
1825
}
1926

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ForceOriginalAudioPatch.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ public class ForceOriginalAudioPatch {
1515
*/
1616
public static void setPreferredLanguage() {
1717
if (Settings.FORCE_ORIGINAL_AUDIO.get()
18-
&& SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams()) {
18+
&& SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams()
19+
&& !Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get().useAuth) {
1920
// If client spoofing does not use authentication and lacks multi-audio streams,
2021
// then can use any language code for the request and if that requested language is
2122
// not available YT uses the original audio language. Authenticated requests ignore

extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,22 @@ public class SpoofVideoStreamsPatch {
1818
* Injection point.
1919
*/
2020
public static void setClientOrderToUse() {
21-
List<ClientType> availableClients = List.of(
21+
final boolean forceAVC = Settings.FORCE_AVC_CODEC.get();
22+
23+
// VR 1.61 uses VP9/AV1, and cannot force AVC.
24+
ClientType client = Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
25+
if (forceAVC && client == ANDROID_VR_1_61_48) {
26+
client = ANDROID_VR_1_43_32; // Use VR 1.43 instead.
27+
}
28+
29+
List<ClientType> availableClients = forceAVC
30+
? List.of(
31+
ANDROID_VR_1_43_32,
32+
VISIONOS,
33+
ANDROID_CREATOR,
34+
ANDROID_VR_1_61_48,
35+
IPADOS)
36+
: List.of(
2237
ANDROID_VR_1_61_48,
2338
VISIONOS,
2439
ANDROID_CREATOR,
@@ -27,6 +42,6 @@ public static void setClientOrderToUse() {
2742
);
2843

2944
app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
30-
availableClients, Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get());
45+
availableClients, client);
3146
}
3247
}

extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848

4949
public class Settings extends BaseSettings {
5050
// Video
51+
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
52+
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
53+
public static final BooleanSetting FORCE_AVC_CODEC = new BooleanSetting("revanced_force_avc_codec", FALSE, true, "revanced_force_avc_codec_user_dialog_message");
5154
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
5255
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
5356
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
@@ -56,8 +59,6 @@ public class Settings extends BaseSettings {
5659
public static final BooleanSetting REMEMBER_SHORTS_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_shorts_quality_last_selected", FALSE);
5760
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED_TOAST = new BooleanSetting("revanced_remember_video_quality_last_selected_toast", TRUE, false,
5861
parentsAny(REMEMBER_VIDEO_QUALITY_LAST_SELECTED, REMEMBER_SHORTS_QUALITY_LAST_SELECTED));
59-
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
60-
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
6162

6263
// Speed
6364
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);

patches/api/patches.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,10 @@ public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPa
17271727
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
17281728
}
17291729

1730+
public final class app/revanced/patches/youtube/video/codecs/DisableVideoCodecsPatchKt {
1731+
public static final fun getDisableVideoCodecsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
1732+
}
1733+
17301734
public final class app/revanced/patches/youtube/video/hdr/DisableHdrPatchKt {
17311735
public static final fun getDisableHdrPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
17321736
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package app.revanced.patches.youtube.video.codecs
2+
3+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
4+
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
5+
import app.revanced.patcher.patch.bytecodePatch
6+
import app.revanced.patches.all.misc.resources.addResources
7+
import app.revanced.patches.all.misc.resources.addResourcesPatch
8+
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
9+
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
10+
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
11+
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
12+
import app.revanced.patches.youtube.misc.settings.settingsPatch
13+
import app.revanced.util.getReference
14+
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
15+
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
16+
17+
private const val EXTENSION_CLASS_DESCRIPTOR =
18+
"Lapp/revanced/extension/youtube/patches/DisableVideoCodecsPatch;"
19+
20+
@Suppress("unused")
21+
val disableVideoCodecsPatch = bytecodePatch(
22+
name = "disable video codecs",
23+
description = "Adds options to disable HDR and VP9 codecs.",
24+
) {
25+
dependsOn(
26+
sharedExtensionPatch,
27+
settingsPatch,
28+
addResourcesPatch,
29+
/**
30+
* Override all calls of `getSupportedHdrTypes`.
31+
*/
32+
transformInstructionsPatch(
33+
filterMap = filterMap@{ classDef, _, instruction, instructionIndex ->
34+
if (classDef.type.startsWith("Lapp/revanced/")) {
35+
return@filterMap null
36+
}
37+
38+
val reference = instruction.getReference<MethodReference>()
39+
if (reference?.definingClass =="Landroid/view/Display\$HdrCapabilities;"
40+
&& reference.name == "getSupportedHdrTypes") {
41+
return@filterMap instruction to instructionIndex
42+
}
43+
return@filterMap null
44+
},
45+
transform = { method, entry ->
46+
val (instruction, index) = entry
47+
val register = (instruction as FiveRegisterInstruction).registerC
48+
49+
method.replaceInstruction(
50+
index,
51+
"invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->" +
52+
"disableHdrVideo(Landroid/view/Display\$HdrCapabilities;)[I",
53+
)
54+
}
55+
)
56+
)
57+
58+
compatibleWith(
59+
"com.google.android.youtube"(
60+
"19.34.42",
61+
"20.07.39",
62+
"20.13.41",
63+
"20.14.43",
64+
)
65+
)
66+
67+
execute {
68+
addResources("youtube", "video.codecs.disableVideoCodecsPatch")
69+
70+
PreferenceScreen.VIDEO.addPreferences(
71+
SwitchPreference("revanced_disable_hdr_video"),
72+
SwitchPreference("revanced_force_avc_codec")
73+
)
74+
75+
vp9CapabilityFingerprint.method.addInstructionsWithLabels(
76+
0,
77+
"""
78+
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->allowVP9()Z
79+
move-result v0
80+
if-nez v0, :default
81+
return v0
82+
:default
83+
nop
84+
"""
85+
)
86+
}
87+
}

0 commit comments

Comments
 (0)