From ede666b5cb64fcbaa1334ad8bef79e2634ced113 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 9 Dec 2024 05:11:00 +0400 Subject: [PATCH] feat(YouTube - Spoof video streams): Allow picking a default audio language track (#4050) --- .../shared/settings/BaseSettings.java | 10 +- .../shared/spoof/AudioStreamLanguage.java | 102 ++++++++++++++++ .../shared/spoof/SpoofVideoStreamsPatch.java | 1 - .../shared/spoof/requests/PlayerRoutes.java | 8 +- .../ReVancedPreferenceFragment.java | 56 ++++++++- .../resources/addresources/values/arrays.xml | 110 ++++++++++++++++++ .../resources/addresources/values/strings.xml | 55 +++++++++ 7 files changed, 328 insertions(+), 14 deletions(-) create mode 100644 extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java index a5b558acde..1253bf3657 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseSettings.java @@ -1,11 +1,12 @@ package app.revanced.extension.shared.settings; -import app.revanced.extension.shared.spoof.ClientType; -import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch; - import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static app.revanced.extension.shared.settings.Setting.parent; +import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.ForceiOSAVCAvailability; + +import app.revanced.extension.shared.spoof.AudioStreamLanguage; +import app.revanced.extension.shared.spoof.ClientType; /** * Settings shared across multiple apps. @@ -21,8 +22,9 @@ public class BaseSettings { public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false); public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message"); + public static final EnumSetting SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT); public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true, - "revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofVideoStreamsPatch.ForceiOSAVCAvailability()); + "revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new ForceiOSAVCAvailability()); public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS)); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java new file mode 100644 index 0000000000..0d9070e2fd --- /dev/null +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/AudioStreamLanguage.java @@ -0,0 +1,102 @@ +package app.revanced.extension.shared.spoof; + +import java.util.Locale; + +public enum AudioStreamLanguage { + DEFAULT, + + // Language codes found in locale_config.xml + // Region specific variants of Chinese/English/Spanish/French have been removed. + AF, + AM, + AR, + AS, + AZ, + BE, + BG, + BN, + BS, + CA, + CS, + DA, + DE, + EL, + EN, + ES, + ET, + EU, + FA, + FI, + FR, + GL, + GU, + HI, + HE, // App uses obsolete 'IW' and 'HE' is modern ISO code. + HR, + HU, + HY, + ID, + IS, + IT, + JA, + KA, + KK, + KM, + KN, + KO, + KY, + LO, + LT, + LV, + MK, + ML, + MN, + MR, + MS, + MY, + NE, + NL, + NB, + OR, + PA, + PL, + PT_BR, + PT_PT, + RO, + RU, + SI, + SK, + SL, + SQ, + SR, + SV, + SW, + TA, + TE, + TH, + TL, + TR, + UK, + UR, + UZ, + VI, + ZH, + ZU; + + private final String iso639_1; + + AudioStreamLanguage() { + iso639_1 = name().replace('_', '-'); + } + + public String getIso639_1() { + // Changing the app language does not force the app to completely restart, + // so the default needs to be the current language and not a static field. + if (this == DEFAULT) { + // Android VR requires uppercase language code. + return Locale.getDefault().toLanguageTag().toUpperCase(Locale.US); + } + + return iso639_1; + } +} diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java index 1381a9ba51..119b5306e5 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java @@ -13,7 +13,6 @@ import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.spoof.requests.StreamingDataRequest; -import app.revanced.extension.shared.settings.BaseSettings; @SuppressWarnings("unused") public class SpoofVideoStreamsPatch { diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java index fa5cc18558..6f6a6e7c52 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/PlayerRoutes.java @@ -7,9 +7,9 @@ import java.net.HttpURLConnection; import app.revanced.extension.shared.Logger; -import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.requests.Requester; import app.revanced.extension.shared.requests.Route; +import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.spoof.ClientType; final class PlayerRoutes { @@ -25,9 +25,6 @@ final class PlayerRoutes { */ private static final int CONNECTION_TIMEOUT_MILLISECONDS = 10 * 1000; // 10 Seconds. - private static final String LOCALE_LANGUAGE = Utils.getContext().getResources() - .getConfiguration().locale.getLanguage(); - private PlayerRoutes() { } @@ -38,8 +35,7 @@ static String createInnertubeBody(ClientType clientType) { JSONObject context = new JSONObject(); JSONObject client = new JSONObject(); - // Required to use correct default audio channel with iOS. - client.put("hl", LOCALE_LANGUAGE); + client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1()); client.put("clientName", clientType.name()); client.put("clientVersion", clientType.clientVersion); client.put("deviceModel", clientType.deviceModel); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java index 9e6cdeeeb7..ed4502aef4 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java @@ -10,6 +10,7 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceScreen; +import android.util.Pair; import android.util.TypedValue; import android.view.ViewGroup; import android.view.WindowInsets; @@ -18,6 +19,10 @@ import androidx.annotation.RequiresApi; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment; @@ -41,6 +46,46 @@ public static Drawable getBackButtonDrawable() { return Utils.getContext().getResources().getDrawable(backButtonResource); } + /** + * Sorts a preference list by menu entries, but preserves the first value as the first entry. + */ + private static void sortListPreferenceByValues(ListPreference listPreference) { + CharSequence[] entries = listPreference.getEntries(); + CharSequence[] entryValues = listPreference.getEntryValues(); + final int entrySize = entries.length; + + if (entrySize != entryValues.length) { + throw new IllegalStateException(); + } + + // Ensure the first entry remains the first after sorting. + CharSequence firstEntry = entries[0]; + CharSequence firstEntryValue = entryValues[0]; + + List> entryPairs = new ArrayList<>(entrySize); + for (int i = 1; i < entrySize; i++) { + entryPairs.add(new Pair<>(entries[i].toString(), entryValues[i].toString())); + } + + Collections.sort(entryPairs, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first)); + + CharSequence[] sortedEntries = new CharSequence[entrySize]; + CharSequence[] sortedEntryValues = new CharSequence[entrySize]; + + sortedEntries[0] = firstEntry; + sortedEntryValues[0] = firstEntryValue; + + int i = 1; + for (Pair pair : entryPairs) { + sortedEntries[i] = pair.first; + sortedEntryValues[i] = pair.second; + i++; + } + + listPreference.setEntries(sortedEntries); + listPreference.setEntryValues(sortedEntryValues); + } + @RequiresApi(api = Build.VERSION_CODES.O) @Override protected void initialize() { @@ -50,9 +95,14 @@ protected void initialize() { setPreferenceScreenToolbar(getPreferenceScreen()); // If the preference was included, then initialize it based on the available playback speed. - Preference defaultSpeedPreference = findPreference(Settings.PLAYBACK_SPEED_DEFAULT.key); - if (defaultSpeedPreference instanceof ListPreference) { - CustomPlaybackSpeedPatch.initializeListPreference((ListPreference) defaultSpeedPreference); + Preference preference = findPreference(Settings.PLAYBACK_SPEED_DEFAULT.key); + if (preference instanceof ListPreference playbackPreference) { + CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference); + } + + preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key); + if (preference instanceof ListPreference languagePreference) { + sortListPreferenceByValues(languagePreference); } } catch (Exception ex) { Logger.printException(() -> "initialize failure", ex); diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index de96c6e5b5..bc66bb4bea 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -11,6 +11,116 @@ ANDROID_VR IOS + + @string/revanced_spoof_video_streams_language_DEFAULT + @string/revanced_spoof_video_streams_language_AR + @string/revanced_spoof_video_streams_language_AZ + @string/revanced_spoof_video_streams_language_BG + @string/revanced_spoof_video_streams_language_BN + @string/revanced_spoof_video_streams_language_CA + @string/revanced_spoof_video_streams_language_CS + @string/revanced_spoof_video_streams_language_DA + @string/revanced_spoof_video_streams_language_DE + @string/revanced_spoof_video_streams_language_EL + @string/revanced_spoof_video_streams_language_EN + @string/revanced_spoof_video_streams_language_ES + @string/revanced_spoof_video_streams_language_ET + @string/revanced_spoof_video_streams_language_FA + @string/revanced_spoof_video_streams_language_FI + @string/revanced_spoof_video_streams_language_FR + @string/revanced_spoof_video_streams_language_GU + @string/revanced_spoof_video_streams_language_HI + @string/revanced_spoof_video_streams_language_HR + @string/revanced_spoof_video_streams_language_HU + @string/revanced_spoof_video_streams_language_ID + @string/revanced_spoof_video_streams_language_IT + @string/revanced_spoof_video_streams_language_JA + @string/revanced_spoof_video_streams_language_KK + @string/revanced_spoof_video_streams_language_KO + @string/revanced_spoof_video_streams_language_LT + @string/revanced_spoof_video_streams_language_LV + @string/revanced_spoof_video_streams_language_MK + @string/revanced_spoof_video_streams_language_MN + @string/revanced_spoof_video_streams_language_MR + @string/revanced_spoof_video_streams_language_MS + @string/revanced_spoof_video_streams_language_MY + @string/revanced_spoof_video_streams_language_NL + @string/revanced_spoof_video_streams_language_OR + @string/revanced_spoof_video_streams_language_PA + @string/revanced_spoof_video_streams_language_PL + @string/revanced_spoof_video_streams_language_PT_BR + @string/revanced_spoof_video_streams_language_PT_PT + @string/revanced_spoof_video_streams_language_RO + @string/revanced_spoof_video_streams_language_RU + @string/revanced_spoof_video_streams_language_SK + @string/revanced_spoof_video_streams_language_SL + @string/revanced_spoof_video_streams_language_SR + @string/revanced_spoof_video_streams_language_SV + @string/revanced_spoof_video_streams_language_SW + @string/revanced_spoof_video_streams_language_TA + @string/revanced_spoof_video_streams_language_TE + @string/revanced_spoof_video_streams_language_TH + @string/revanced_spoof_video_streams_language_TR + @string/revanced_spoof_video_streams_language_UK + @string/revanced_spoof_video_streams_language_UR + @string/revanced_spoof_video_streams_language_VI + @string/revanced_spoof_video_streams_language_ZH + + + DEFAULT + AR + AZ + BG + BN + CA + CS + DA + DE + EL + EN + ES + ET + FA + FI + FR + GU + HI + HR + HU + ID + IT + JA + KK + KO + LT + LV + MK + MN + MR + MS + MY + NL + OR + PA + PL + PT_BR + PT_PT + RO + RU + SK + SL + SR + SV + SW + TA + TE + TH + TR + UK + UR + VI + ZH + diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index ff24146950..82f37cd657 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -1232,6 +1232,61 @@ This is because Crowdin requires temporarily flattening this file and removing t • Private kids videos may not play\n• Livestreams start from the beginning\n• Videos may end 1 second early Android VR spoofing side effects • Kids videos may not play\n• Audio track menu is missing\n• Stable volume is not available + Video streams are spoofed + Preferred audio stream language + App language + Arabic + Azerbaijani + Bulgarian + Bengali + Catalan + Czech + Danish + German + Greek + English + Spanish + Estonian + Persian + Finnish + French + Gujarati + Hindi + Croatian + Hungarian + Indonesian + Italian + Japanese + Kazakh + Korean + Lithuanian + Latvian + Macedonian + Mongolian + Marathi + Malay + Burmese + Dutch + Odia + Punjabi + Polish + Portuguese (Brazil) + Portuguese (Portugal) + Romanian + Russian + Slovak + Slovene + Serbian + Swedish + Swahili + Tamil + Telugu + Thai + Turkish + Ukrainian + Urdu + Vietnamese + Chinese