diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java index 8700b66d5..020012d82 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java @@ -106,6 +106,34 @@ public class GeneralPatch { // endregion + // region [Disable layout updates] patch + + private static final String[] REQUEST_HEADER_KEYS = { + "X-Youtube-Cold-Config-Data", + "X-Youtube-Cold-Hash-Data", + "X-Youtube-Hot-Config-Data", + "X-Youtube-Hot-Hash-Data" + }; + + private static final boolean DISABLE_LAYOUT_UPDATES = + Settings.DISABLE_LAYOUT_UPDATES.get(); + + /** + * @param key Keys to be added to the header of CronetBuilder. + * @param value Values to be added to the header of CronetBuilder. + * @return Empty value if setting is enabled. + */ + public static String disableLayoutUpdates(String key, String value) { + if (DISABLE_LAYOUT_UPDATES && StringUtils.equalsAny(key, REQUEST_HEADER_KEYS)) { + Logger.printDebug(() -> "Blocking: " + key); + return ""; + } + + return value; + } + + // endregion + // region [Disable splash animation] patch public static boolean disableSplashAnimation(boolean original) { diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java index 4042cdf78..b9eda5de2 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -158,6 +158,7 @@ public class Settings extends BaseSettings { public static final EnumSetting CHANGE_FORM_FACTOR = new EnumSetting<>("revanced_change_form_factor", FormFactor.DEFAULT, true, "revanced_change_form_factor_user_dialog_message"); public static final BooleanSetting CHANGE_LIVE_RING_CLICK_ACTION = new BooleanSetting("revanced_change_live_ring_click_action", FALSE, true); + public static final BooleanSetting DISABLE_LAYOUT_UPDATES = new BooleanSetting("revanced_disable_layout_updates", false, true, "revanced_disable_layout_updates_user_dialog_message"); public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", false, true, "revanced_spoof_app_version_user_dialog_message"); public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", PatchStatus.SpoofAppVersionDefaultString(), true, parent(SPOOF_APP_VERSION)); diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/updates/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/updates/Fingerprints.kt new file mode 100644 index 000000000..beffc631c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/updates/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.general.updates + +import app.revanced.util.fingerprint.legacyFingerprint +import app.revanced.util.or +import com.android.tools.smali.dexlib2.AccessFlags + +internal val cronetHeaderFingerprint = legacyFingerprint( + name = "cronetHeaderFingerprint", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Ljava/lang/String;", "Ljava/lang/String;"), + strings = listOf("Accept-Encoding"), + // In YouTube 19.16.39 or earlier, there are two methods with almost the same structure. + // Check the fields of the class to identify them correctly. + customFingerprint = { _, classDef -> + classDef.fields.find { it.type == "J" } != null + } +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/updates/LayoutUpdatesPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/updates/LayoutUpdatesPatch.kt new file mode 100644 index 000000000..1543fc746 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/updates/LayoutUpdatesPatch.kt @@ -0,0 +1,50 @@ +package app.revanced.patches.youtube.general.updates + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE +import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR +import app.revanced.patches.youtube.utils.patch.PatchList.DISABLE_LAYOUT_UPDATES +import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference +import app.revanced.patches.youtube.utils.settings.settingsPatch +import app.revanced.util.fingerprint.matchOrThrow + +@Suppress("unused") +val layoutUpdatesPatch = bytecodePatch( + DISABLE_LAYOUT_UPDATES.title, + DISABLE_LAYOUT_UPDATES.summary, +) { + compatibleWith(COMPATIBLE_PACKAGE) + + dependsOn(settingsPatch) + + execute { + + cronetHeaderFingerprint.matchOrThrow().let { + it.method.apply { + val index = it.stringMatches!!.first().index + + addInstructions( + index, """ + invoke-static {p1, p2}, $GENERAL_CLASS_DESCRIPTOR->disableLayoutUpdates(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object p2 + """ + ) + } + } + + // region add settings + + addPreference( + arrayOf( + "PREFERENCE_SCREEN: GENERAL", + "PREFERENCE_CATEGORY: GENERAL_EXPERIMENTAL_FLAGS", + "SETTINGS: DISABLE_LAYOUT_UPDATES" + ), + DISABLE_LAYOUT_UPDATES + ) + + // endregion + + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/patch/PatchList.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/patch/PatchList.kt index c8dca6a6f..508b49496 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/patch/PatchList.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/patch/PatchList.kt @@ -81,6 +81,10 @@ internal enum class PatchList( "Disable haptic feedback", "Adds options to disable haptic feedback when swiping in the video player." ), + DISABLE_LAYOUT_UPDATES( + "Disable layout updates", + "Adds an option to disable layout updates by server." + ), DISABLE_RESUMING_MINIPLAYER_ON_STARTUP( "Disable resuming Miniplayer on startup", "Adds an option to disable the Miniplayer 'Continue watching' from resuming on app startup." diff --git a/patches/src/main/resources/youtube/settings/host/values/strings.xml b/patches/src/main/resources/youtube/settings/host/values/strings.xml index 2f3c79735..3b18b2f2b 100644 --- a/patches/src/main/resources/youtube/settings/host/values/strings.xml +++ b/patches/src/main/resources/youtube/settings/host/values/strings.xml @@ -518,6 +518,20 @@ Automotive layout • Shorts open in the regular player. • Feed is organized by topics and channels. • Video description cannot be opened when 'Spoof streaming data' is turned off." + Disable layout updates + Layout will not be updated by the server. + Layout will be updated by the server. + "App layout reverts to the layout it was using when it was first installed. + +Some server-side layouts may not revert. + +Changes include: +• Components in the player flyout menu (or related settings) may not work. +• Rolling numbers are not animated. +• The Library tab is used. +• The Music section of video description may not work. +• Account switch button may not appear on the Library tab. Use the 'Enable wide search bar in You tab' setting." + Spoof app version Version spoofed Version not spoofed diff --git a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml index 3915effdd..8240aa8de 100644 --- a/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/patches/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -275,6 +275,9 @@ + +