Skip to content
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

feat(YouTube - Spoof video streams): Allow picking a default audio language track #4050

Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package app.revanced.extension.youtube.patches.spoof;

import android.content.res.Configuration;

import app.revanced.extension.shared.Utils;

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,
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
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 static final Configuration CONFIGURATION = Utils.getContext()
.getResources().getConfiguration();

private final String iso_639_1;

AudioStreamLanguage() {
iso_639_1 = this.name().replace('_', '-');
}

public String getIso_639_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) {
return CONFIGURATION.locale.getLanguage();
}

return iso_639_1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@

@SuppressWarnings("unused")
public class SpoofVideoStreamsPatch {
public static final class ForceiOSAVCAvailability implements Setting.Availability {
public static final class SpoofiOSAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return Settings.SPOOF_VIDEO_STREAMS.get() && Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
return Settings.SPOOF_VIDEO_STREAMS.get()
&& Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
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.youtube.patches.spoof.ClientType;
import app.revanced.extension.youtube.settings.Settings;

final class PlayerRoutes {
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
Expand All @@ -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() {
}

Expand All @@ -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", Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso_639_1());
client.put("clientName", clientType.name());
client.put("clientVersion", clientType.clientVersion);
client.put("deviceModel", clientType.deviceModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.*;
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
import static app.revanced.extension.youtube.patches.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.*;

Expand All @@ -19,8 +20,8 @@
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
import app.revanced.extension.youtube.patches.spoof.AudioStreamLanguage;
import app.revanced.extension.youtube.patches.spoof.ClientType;
import app.revanced.extension.youtube.patches.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;

public class Settings extends BaseSettings {
Expand Down Expand Up @@ -273,8 +274,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
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<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new SpoofiOSAvailability());
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 SpoofiOSAvailability());
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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.
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
*/
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();
}
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved

// Ensure the first entry remains the first after sorting.
CharSequence firstEntry = entries[0];
CharSequence firstEntryValue = entryValues[0];
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved

List<Pair<String, String>> 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<String, String> 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() {
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ val spoofVideoStreamsPatch = bytecodePatch(
"revanced_spoof_video_streams_client",
summaryKey = null,
),
ListPreference(
"revanced_spoof_video_streams_language",
summaryKey = null
),
SwitchPreference(
"revanced_spoof_video_streams_ios_force_avc",
tag = "app.revanced.extension.youtube.settings.preference.ForceAVCSpoofingPreference",
Expand Down
110 changes: 110 additions & 0 deletions patches/src/main/resources/addresources/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,116 @@
<item>ANDROID_VR</item>
<item>IOS</item>
</string-array>
<string-array name="revanced_spoof_video_streams_language_entries">
<item>@string/revanced_spoof_video_streams_language_DEFAULT</item>
<item>@string/revanced_spoof_video_streams_language_AR</item>
<item>@string/revanced_spoof_video_streams_language_AZ</item>
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
<item>@string/revanced_spoof_video_streams_language_BG</item>
<item>@string/revanced_spoof_video_streams_language_BN</item>
<item>@string/revanced_spoof_video_streams_language_CA</item>
<item>@string/revanced_spoof_video_streams_language_CS</item>
<item>@string/revanced_spoof_video_streams_language_DA</item>
<item>@string/revanced_spoof_video_streams_language_DE</item>
<item>@string/revanced_spoof_video_streams_language_EL</item>
<item>@string/revanced_spoof_video_streams_language_EN</item>
<item>@string/revanced_spoof_video_streams_language_ES</item>
<item>@string/revanced_spoof_video_streams_language_ET</item>
<item>@string/revanced_spoof_video_streams_language_FA</item>
<item>@string/revanced_spoof_video_streams_language_FI</item>
<item>@string/revanced_spoof_video_streams_language_FR</item>
<item>@string/revanced_spoof_video_streams_language_GU</item>
<item>@string/revanced_spoof_video_streams_language_HI</item>
<item>@string/revanced_spoof_video_streams_language_HR</item>
<item>@string/revanced_spoof_video_streams_language_HU</item>
<item>@string/revanced_spoof_video_streams_language_ID</item>
<item>@string/revanced_spoof_video_streams_language_IT</item>
<item>@string/revanced_spoof_video_streams_language_JA</item>
<item>@string/revanced_spoof_video_streams_language_KK</item>
<item>@string/revanced_spoof_video_streams_language_KO</item>
<item>@string/revanced_spoof_video_streams_language_LT</item>
<item>@string/revanced_spoof_video_streams_language_LV</item>
<item>@string/revanced_spoof_video_streams_language_MK</item>
<item>@string/revanced_spoof_video_streams_language_MN</item>
<item>@string/revanced_spoof_video_streams_language_MR</item>
<item>@string/revanced_spoof_video_streams_language_MS</item>
<item>@string/revanced_spoof_video_streams_language_MY</item>
<item>@string/revanced_spoof_video_streams_language_NL</item>
<item>@string/revanced_spoof_video_streams_language_OR</item>
<item>@string/revanced_spoof_video_streams_language_PA</item>
<item>@string/revanced_spoof_video_streams_language_PL</item>
<item>@string/revanced_spoof_video_streams_language_PT_BR</item>
<item>@string/revanced_spoof_video_streams_language_PT_PT</item>
<item>@string/revanced_spoof_video_streams_language_RO</item>
<item>@string/revanced_spoof_video_streams_language_RU</item>
<item>@string/revanced_spoof_video_streams_language_SK</item>
<item>@string/revanced_spoof_video_streams_language_SL</item>
<item>@string/revanced_spoof_video_streams_language_SR</item>
<item>@string/revanced_spoof_video_streams_language_SV</item>
<item>@string/revanced_spoof_video_streams_language_SW</item>
<item>@string/revanced_spoof_video_streams_language_TA</item>
<item>@string/revanced_spoof_video_streams_language_TE</item>
<item>@string/revanced_spoof_video_streams_language_TH</item>
<item>@string/revanced_spoof_video_streams_language_TR</item>
<item>@string/revanced_spoof_video_streams_language_UK</item>
<item>@string/revanced_spoof_video_streams_language_UR</item>
<item>@string/revanced_spoof_video_streams_language_VI</item>
<item>@string/revanced_spoof_video_streams_language_ZH</item>
</string-array>
<string-array name="revanced_spoof_video_streams_language_entry_values">
<item>DEFAULT</item>
<item>AR</item>
<item>AZ</item>
<item>BG</item>
<item>BN</item>
<item>CA</item>
<item>CS</item>
<item>DA</item>
<item>DE</item>
<item>EL</item>
<item>EN</item>
<item>ES</item>
<item>ET</item>
<item>FA</item>
<item>FI</item>
<item>FR</item>
<item>GU</item>
<item>HI</item>
<item>HR</item>
<item>HU</item>
<item>ID</item>
<item>IT</item>
<item>JA</item>
<item>KK</item>
<item>KO</item>
<item>LT</item>
<item>LV</item>
<item>MK</item>
<item>MN</item>
<item>MR</item>
<item>MS</item>
<item>MY</item>
<item>NL</item>
<item>OR</item>
<item>PA</item>
<item>PL</item>
<item>PT_BR</item>
<item>PT_PT</item>
<item>RO</item>
<item>RU</item>
<item>SK</item>
<item>SL</item>
<item>SR</item>
<item>SV</item>
<item>SW</item>
<item>TA</item>
<item>TE</item>
<item>TH</item>
<item>TR</item>
<item>UK</item>
<item>UR</item>
<item>VI</item>
<item>ZH</item>
</string-array>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string-array name="revanced_spoof_app_version_target_entries">
Expand Down
Loading
Loading