From f46722e54045e9dfae92164eca5aaf19ec10efba Mon Sep 17 00:00:00 2001
From: redphx <96280+redphx@users.noreply.github.com>
Date: Wed, 24 Jul 2024 20:48:45 +0700
Subject: [PATCH] Update better-xcloud.user.js
---
dist/better-xcloud.user.js | 4314 +++++++++++++++++++-----------------
1 file changed, 2319 insertions(+), 1995 deletions(-)
diff --git a/dist/better-xcloud.user.js b/dist/better-xcloud.user.js
index cf2828c..dcf5a2e 100644
--- a/dist/better-xcloud.user.js
+++ b/dist/better-xcloud.user.js
@@ -79,10 +79,10 @@ class UserAgent {
UserAgent.spoof();
}
static updateStorage(profile, custom) {
- const clonedConfig = deepClone(UserAgent.#config);
- if (clonedConfig.profile = profile, typeof custom !== "undefined")
- clonedConfig.custom = custom;
- window.localStorage.setItem(UserAgent.STORAGE_KEY, JSON.stringify(clonedConfig));
+ const config = UserAgent.#config;
+ if (config.profile = profile, profile === UserAgentProfile.CUSTOM && typeof custom !== "undefined")
+ config.custom = custom;
+ window.localStorage.setItem(UserAgent.STORAGE_KEY, JSON.stringify(config));
}
static getDefault() {
return window.navigator.orgUserAgent || window.navigator.userAgent;
@@ -163,7 +163,7 @@ var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.inclu
currentStream: {},
remotePlay: {},
pointerServerPort: 9269
-};
+}, STORAGE = {};
// src/utils/bx-event.ts
var BxEvent;
@@ -171,6 +171,7 @@ var BxEvent;
BxEvent2["JUMP_BACK_IN_READY"] = "bx-jump-back-in-ready";
BxEvent2["POPSTATE"] = "bx-popstate";
BxEvent2["TITLE_INFO_READY"] = "bx-title-info-ready";
+ BxEvent2["SETTINGS_CHANGED"] = "bx-settings-changed";
BxEvent2["STREAM_LOADING"] = "bx-stream-loading";
BxEvent2["STREAM_STARTING"] = "bx-stream-starting";
BxEvent2["STREAM_STARTED"] = "bx-stream-started";
@@ -190,7 +191,6 @@ var BxEvent;
BxEvent2["GAME_BAR_ACTION_ACTIVATED"] = "bx-game-bar-action-activated";
BxEvent2["MICROPHONE_STATE_CHANGED"] = "bx-microphone-state-changed";
BxEvent2["CAPTURE_SCREENSHOT"] = "bx-capture-screenshot";
- BxEvent2["GAINNODE_VOLUME_CHANGED"] = "bx-gainnode-volume-changed";
BxEvent2["POINTER_LOCK_REQUESTED"] = "bx-pointer-lock-requested";
BxEvent2["POINTER_LOCK_EXITED"] = "bx-pointer-lock-exited";
BxEvent2["NAVIGATION_FOCUS_CHANGED"] = "bx-nav-focus-changed";
@@ -232,6 +232,17 @@ var StreamVideoProcessing;
StreamVideoProcessing2["CAS"] = "cas";
})(StreamVideoProcessing || (StreamVideoProcessing = {}));
+// src/utils/navigation-utils.ts
+class NavigationUtils {
+ static setNearby($elm, nearby) {
+ $elm.nearby = $elm.nearby || {};
+ let key;
+ for (key in nearby)
+ $elm.nearby[key] = nearby[key];
+ }
+}
+var setNearby = NavigationUtils.setNearby;
+
// src/utils/html.ts
var createElement = function(elmName, props = {}, ..._) {
let $elm;
@@ -240,6 +251,8 @@ var createElement = function(elmName, props = {}, ..._) {
$elm = document.createElementNS(props.xmlns, elmName), delete props.xmlns;
else
$elm = document.createElement(elmName);
+ if (props._nearby)
+ setNearby($elm, props._nearby), delete props._nearby;
for (let key in props) {
if ($elm.hasOwnProperty(key))
continue;
@@ -289,7 +302,7 @@ var ButtonStyleIndices = Object.keys(ButtonStyle).splice(0, Object.keys(ButtonSt
const style = options.style || 0;
style && ButtonStyleIndices.forEach((index) => {
style & index && $btn.classList.add(ButtonStyle[index]);
- }), options.classes && $btn.classList.add(...options.classes), options.icon && $btn.appendChild(createSvgIcon(options.icon)), options.label && $btn.appendChild(CE("span", {}, options.label)), options.title && $btn.setAttribute("title", options.title), options.disabled && ($btn.disabled = !0), options.onClick && $btn.addEventListener("click", options.onClick), typeof options.tabIndex === "number" && ($btn.tabIndex = options.tabIndex);
+ }), options.classes && $btn.classList.add(...options.classes), options.icon && $btn.appendChild(createSvgIcon(options.icon)), options.label && $btn.appendChild(CE("span", {}, options.label)), options.title && $btn.setAttribute("title", options.title), options.disabled && ($btn.disabled = !0), options.onClick && $btn.addEventListener("click", options.onClick), $btn.tabIndex = typeof options.tabIndex === "number" ? options.tabIndex : 0;
for (let key in options.attributes)
if (!$btn.hasOwnProperty(key))
$btn.setAttribute(key, options.attributes[key]);
@@ -297,6 +310,86 @@ var ButtonStyleIndices = Object.keys(ButtonStyle).splice(0, Object.keys(ButtonSt
}, CTN = document.createTextNode.bind(document);
window.BX_CE = createElement;
+// src/enums/pref-keys.ts
+var StorageKey;
+(function(StorageKey2) {
+ StorageKey2["GLOBAL"] = "better_xcloud";
+})(StorageKey || (StorageKey = {}));
+var PrefKey;
+(function(PrefKey2) {
+ PrefKey2["LAST_UPDATE_CHECK"] = "version_last_check";
+ PrefKey2["LATEST_VERSION"] = "version_latest";
+ PrefKey2["CURRENT_VERSION"] = "version_current";
+ PrefKey2["BETTER_XCLOUD_LOCALE"] = "bx_locale";
+ PrefKey2["SERVER_REGION"] = "server_region";
+ PrefKey2["SERVER_BYPASS_RESTRICTION"] = "server_bypass_restriction";
+ PrefKey2["PREFER_IPV6_SERVER"] = "prefer_ipv6_server";
+ PrefKey2["STREAM_TARGET_RESOLUTION"] = "stream_target_resolution";
+ PrefKey2["STREAM_PREFERRED_LOCALE"] = "stream_preferred_locale";
+ PrefKey2["STREAM_CODEC_PROFILE"] = "stream_codec_profile";
+ PrefKey2["USER_AGENT_PROFILE"] = "user_agent_profile";
+ PrefKey2["STREAM_SIMPLIFY_MENU"] = "stream_simplify_menu";
+ PrefKey2["STREAM_COMBINE_SOURCES"] = "stream_combine_sources";
+ PrefKey2["STREAM_TOUCH_CONTROLLER"] = "stream_touch_controller";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_AUTO_OFF"] = "stream_touch_controller_auto_off";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY"] = "stream_touch_controller_default_opacity";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_STYLE_STANDARD"] = "stream_touch_controller_style_standard";
+ PrefKey2["STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM"] = "stream_touch_controller_style_custom";
+ PrefKey2["STREAM_DISABLE_FEEDBACK_DIALOG"] = "stream_disable_feedback_dialog";
+ PrefKey2["BITRATE_VIDEO_MAX"] = "bitrate_video_max";
+ PrefKey2["GAME_BAR_POSITION"] = "game_bar_position";
+ PrefKey2["LOCAL_CO_OP_ENABLED"] = "local_co_op_enabled";
+ PrefKey2["CONTROLLER_ENABLE_SHORTCUTS"] = "controller_enable_shortcuts";
+ PrefKey2["CONTROLLER_ENABLE_VIBRATION"] = "controller_enable_vibration";
+ PrefKey2["CONTROLLER_DEVICE_VIBRATION"] = "controller_device_vibration";
+ PrefKey2["CONTROLLER_VIBRATION_INTENSITY"] = "controller_vibration_intensity";
+ PrefKey2["CONTROLLER_SHOW_CONNECTION_STATUS"] = "controller_show_connection_status";
+ PrefKey2["NATIVE_MKB_ENABLED"] = "native_mkb_enabled";
+ PrefKey2["NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY"] = "native_mkb_scroll_x_sensitivity";
+ PrefKey2["NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY"] = "native_mkb_scroll_y_sensitivity";
+ PrefKey2["MKB_ENABLED"] = "mkb_enabled";
+ PrefKey2["MKB_HIDE_IDLE_CURSOR"] = "mkb_hide_idle_cursor";
+ PrefKey2["MKB_ABSOLUTE_MOUSE"] = "mkb_absolute_mouse";
+ PrefKey2["MKB_DEFAULT_PRESET_ID"] = "mkb_default_preset_id";
+ PrefKey2["SCREENSHOT_APPLY_FILTERS"] = "screenshot_apply_filters";
+ PrefKey2["BLOCK_TRACKING"] = "block_tracking";
+ PrefKey2["BLOCK_SOCIAL_FEATURES"] = "block_social_features";
+ PrefKey2["SKIP_SPLASH_VIDEO"] = "skip_splash_video";
+ PrefKey2["HIDE_DOTS_ICON"] = "hide_dots_icon";
+ PrefKey2["REDUCE_ANIMATIONS"] = "reduce_animations";
+ PrefKey2["UI_LOADING_SCREEN_GAME_ART"] = "ui_loading_screen_game_art";
+ PrefKey2["UI_LOADING_SCREEN_WAIT_TIME"] = "ui_loading_screen_wait_time";
+ PrefKey2["UI_LOADING_SCREEN_ROCKET"] = "ui_loading_screen_rocket";
+ PrefKey2["UI_CONTROLLER_FRIENDLY"] = "ui_controller_friendly";
+ PrefKey2["UI_LAYOUT"] = "ui_layout";
+ PrefKey2["UI_SCROLLBAR_HIDE"] = "ui_scrollbar_hide";
+ PrefKey2["UI_HIDE_SECTIONS"] = "ui_hide_sections";
+ PrefKey2["UI_HOME_CONTEXT_MENU_DISABLED"] = "ui_home_context_menu_disabled";
+ PrefKey2["UI_GAME_CARD_SHOW_WAIT_TIME"] = "ui_game_card_show_wait_time";
+ PrefKey2["VIDEO_PLAYER_TYPE"] = "video_player_type";
+ PrefKey2["VIDEO_PROCESSING"] = "video_processing";
+ PrefKey2["VIDEO_POWER_PREFERENCE"] = "video_power_preference";
+ PrefKey2["VIDEO_SHARPNESS"] = "video_sharpness";
+ PrefKey2["VIDEO_RATIO"] = "video_ratio";
+ PrefKey2["VIDEO_BRIGHTNESS"] = "video_brightness";
+ PrefKey2["VIDEO_CONTRAST"] = "video_contrast";
+ PrefKey2["VIDEO_SATURATION"] = "video_saturation";
+ PrefKey2["AUDIO_MIC_ON_PLAYING"] = "audio_mic_on_playing";
+ PrefKey2["AUDIO_ENABLE_VOLUME_CONTROL"] = "audio_enable_volume_control";
+ PrefKey2["AUDIO_VOLUME"] = "audio_volume";
+ PrefKey2["STATS_ITEMS"] = "stats_items";
+ PrefKey2["STATS_SHOW_WHEN_PLAYING"] = "stats_show_when_playing";
+ PrefKey2["STATS_QUICK_GLANCE"] = "stats_quick_glance";
+ PrefKey2["STATS_POSITION"] = "stats_position";
+ PrefKey2["STATS_TEXT_SIZE"] = "stats_text_size";
+ PrefKey2["STATS_TRANSPARENT"] = "stats_transparent";
+ PrefKey2["STATS_OPACITY"] = "stats_opacity";
+ PrefKey2["STATS_CONDITIONAL_FORMATTING"] = "stats_conditional_formatting";
+ PrefKey2["REMOTE_PLAY_ENABLED"] = "xhome_enabled";
+ PrefKey2["REMOTE_PLAY_RESOLUTION"] = "xhome_resolution";
+ PrefKey2["GAME_FORTNITE_FORCE_CONSOLE"] = "game_fortnite_force_console";
+})(PrefKey || (PrefKey = {}));
+
// src/utils/bx-logger.ts
var TextColor;
(function(TextColor2) {
@@ -365,10 +458,12 @@ var SUPPORTED_LANGUAGES = {
"badge-playtime": "Playtime",
"badge-server": "Server",
"badge-video": "Video",
+ "better-xcloud": "Better xCloud",
"bitrate-audio-maximum": "Maximum audio bitrate",
"bitrate-video-maximum": "Maximum video bitrate",
"bottom-left": "Bottom-left",
"bottom-right": "Bottom-right",
+ brazil: "Brazil",
brightness: "Brightness",
"browser-unsupported-feature": "Your browser doesn't support this feature",
"bypass-region-restriction": "Bypass region restriction",
@@ -429,7 +524,6 @@ var SUPPORTED_LANGUAGES = {
"fortnite-force-console-version": "Fortnite: force console version",
"game-bar": "Game Bar",
"getting-consoles-list": "Getting the list of consoles...",
- "gpu-configuration": "GPU configuration",
help: "Help",
hide: "Hide",
"hide-idle-cursor": "Hide mouse cursor on idle",
@@ -444,6 +538,7 @@ var SUPPORTED_LANGUAGES = {
import: "Import",
increase: "Increase",
"install-android": "Install Better xCloud app for Android",
+ japan: "Japan",
"keyboard-shortcuts": "Keyboard shortcuts",
language: "Language",
large: "Large",
@@ -473,6 +568,7 @@ var SUPPORTED_LANGUAGES = {
opacity: "Opacity",
other: "Other",
playing: "Playing",
+ poland: "Poland",
position: "Position",
"powered-off": "Powered off",
"powered-on": "Powered on",
@@ -482,9 +578,9 @@ var SUPPORTED_LANGUAGES = {
"press-esc-to-cancel": "Press Esc to cancel",
"press-key-to-toggle-mkb": [
(e) => `Press ${e.key} to toggle this feature`,
- ,
+ (e) => `Premeu ${e.key} per alternar aquesta funció`,
(e) => `${e.key}: Funktion an-/ausschalten`,
- ,
+ (e) => `Tekan ${e.key} untuk mengaktifkan fitur ini`,
(e) => `Pulsa ${e.key} para alternar esta función`,
(e) => `Appuyez sur ${e.key} pour activer cette fonctionnalité`,
(e) => `Premi ${e.key} per attivare questa funzionalità`,
@@ -493,7 +589,7 @@ var SUPPORTED_LANGUAGES = {
(e) => `Naciśnij ${e.key} aby przełączyć tę funkcję`,
(e) => `Pressione ${e.key} para alternar este recurso`,
(e) => `Нажмите ${e.key} для переключения этой функции`,
- ,
+ (e) => `กด ${e.key} เพื่อสลับคุณสมบัตินี้`,
(e) => `Etkinleştirmek için ${e.key} tuşuna basın`,
(e) => `Натисніть ${e.key} щоб перемкнути цю функцію`,
(e) => `Nhấn ${e.key} để bật/tắt tính năng này`,
@@ -508,6 +604,7 @@ var SUPPORTED_LANGUAGES = {
"remote-play": "Remote Play",
rename: "Rename",
renderer: "Renderer",
+ "renderer-configuration": "Renderer configuration",
"right-click-to-unbind": "Right-click on a key to unbind it",
"right-stick": "Right stick",
"rocket-always-hide": "Always hide",
@@ -590,7 +687,7 @@ var SUPPORTED_LANGUAGES = {
(e) => `Układ sterowania dotykowego stworzony przez ${e.name}`,
(e) => `Disposição de controle por toque feito por ${e.name}`,
(e) => `Сенсорная раскладка по ${e.name}`,
- ,
+ (e) => `รูปแบบการควบคุมแบบสัมผัสโดย ${e.name}`,
(e) => `${e.name} kişisinin dokunmatik kontrolcü tuş şeması`,
(e) => `Розташування сенсорного керування від ${e.name}`,
(e) => `Bố cục điều khiển cảm ứng tạo bởi ${e.name}`,
@@ -601,6 +698,7 @@ var SUPPORTED_LANGUAGES = {
"transparent-background": "Transparent background",
ui: "UI",
"unexpected-behavior": "May cause unexpected behavior",
+ "united-states": "United States",
unknown: "Unknown",
unlimited: "Unlimited",
unmuted: "Unmuted",
@@ -634,11 +732,15 @@ class Translations {
static #supportedLocales = Object.keys(SUPPORTED_LANGUAGES);
static #foreignTranslations = {};
static async init() {
- Translations.#enUsIndex = Translations.#supportedLocales.indexOf(Translations.#EN_US), Translations.refreshCurrentLocale(), await Translations.#loadTranslations();
+ Translations.#enUsIndex = Translations.#supportedLocales.indexOf(Translations.#EN_US), Translations.refreshLocale(), await Translations.#loadTranslations();
}
- static refreshCurrentLocale() {
+ static refreshLocale(newLocale) {
+ let locale;
+ if (newLocale)
+ localStorage.setItem(Translations.#KEY_LOCALE, newLocale), locale = newLocale;
+ else
+ locale = localStorage.getItem(Translations.#KEY_LOCALE);
const supportedLocales = Translations.#supportedLocales;
- let locale = localStorage.getItem(Translations.#KEY_LOCALE);
if (!locale) {
if (locale = window.navigator.language || Translations.#EN_US, supportedLocales.indexOf(locale) === -1)
locale = Translations.#EN_US;
@@ -693,176 +795,34 @@ class Translations {
window.localStorage.setItem(Translations.#KEY_TRANSLATIONS, JSON.stringify(translations)), Translations.#foreignTranslations = translations;
});
}
+ static switchLocale(locale) {
+ localStorage.setItem(Translations.#KEY_LOCALE, locale);
+ }
}
var t = Translations.get;
Translations.init();
-// src/utils/settings.ts
-var SettingElementType;
-(function(SettingElementType2) {
- SettingElementType2["OPTIONS"] = "options";
- SettingElementType2["MULTIPLE_OPTIONS"] = "multiple-options";
- SettingElementType2["NUMBER"] = "number";
- SettingElementType2["NUMBER_STEPPER"] = "number-stepper";
- SettingElementType2["CHECKBOX"] = "checkbox";
-})(SettingElementType || (SettingElementType = {}));
+// src/enums/bypass-servers.ts
+var BypassServers = {
+ br: t("brazil"),
+ jp: t("japan"),
+ pl: t("poland"),
+ us: t("united-states")
+}, BypassServerIps = {
+ br: "169.150.198.66",
+ jp: "138.199.21.239",
+ pl: "45.134.212.66",
+ us: "143.244.47.65"
+};
-class SettingElement {
- static #renderOptions(key, setting, currentValue, onChange) {
- const $control = CE("select", {
- tabindex: 0
- });
- let $parent;
- if (setting.optionsGroup)
- $parent = CE("optgroup", { label: setting.optionsGroup }), $control.appendChild($parent);
- else
- $parent = $control;
- for (let value in setting.options) {
- const label = setting.options[value], $option = CE("option", { value }, label);
- $parent.appendChild($option);
- }
- return $control.value = currentValue, onChange && $control.addEventListener("input", (e) => {
- const target = e.target, value = setting.type && setting.type === "number" ? parseInt(target.value) : target.value;
- onChange(e, value);
- }), $control.setValue = (value) => {
- $control.value = value;
- }, $control;
- }
- static #renderMultipleOptions(key, setting, currentValue, onChange, params = {}) {
- const $control = CE("select", {
- multiple: !0,
- tabindex: 0
- });
- if (params && params.size)
- $control.setAttribute("size", params.size.toString());
- for (let value in setting.multipleOptions) {
- const label = setting.multipleOptions[value], $option = CE("option", { value }, label);
- $option.selected = currentValue.indexOf(value) > -1, $option.addEventListener("mousedown", function(e) {
- e.preventDefault();
- const target = e.target;
- target.selected = !target.selected;
- const $parent = target.parentElement;
- $parent.focus(), $parent.dispatchEvent(new Event("input"));
- }), $control.appendChild($option);
- }
- return $control.addEventListener("mousedown", function(e) {
- const self = this, orgScrollTop = self.scrollTop;
- window.setTimeout(() => self.scrollTop = orgScrollTop, 0);
- }), $control.addEventListener("mousemove", (e) => e.preventDefault()), onChange && $control.addEventListener("input", (e) => {
- const target = e.target, values = Array.from(target.selectedOptions).map((i) => i.value);
- onChange(e, values);
- }), $control;
- }
- static #renderNumber(key, setting, currentValue, onChange) {
- const $control = CE("input", { tabindex: 0, type: "number", min: setting.min, max: setting.max });
- return $control.value = currentValue, onChange && $control.addEventListener("change", (e) => {
- const target = e.target, value = Math.max(setting.min, Math.min(setting.max, parseInt(target.value)));
- target.value = value.toString(), onChange(e, value);
- }), $control;
- }
- static #renderCheckbox(key, setting, currentValue, onChange) {
- const $control = CE("input", { type: "checkbox", tabindex: 0 });
- return $control.checked = currentValue, onChange && $control.addEventListener("change", (e) => {
- onChange(e, e.target.checked);
- }), $control;
- }
- static #renderNumberStepper(key, setting, value, onChange, options = {}) {
- options = options || {}, options.suffix = options.suffix || "", options.disabled = !!options.disabled, options.hideSlider = !!options.hideSlider;
- let $text, $btnDec, $btnInc, $range, controlValue = value;
- const { min: MIN, max: MAX } = setting, STEPS = Math.max(setting.steps || 1, 1), renderTextValue = (value2) => {
- value2 = parseInt(value2);
- let textContent = null;
- if (options.customTextValue)
- textContent = options.customTextValue(value2);
- if (textContent === null)
- textContent = value2.toString() + options.suffix;
- return textContent;
- }, updateButtonsVisibility = () => {
- $btnDec.classList.toggle("bx-inactive", controlValue === MIN), $btnInc.classList.toggle("bx-inactive", controlValue === MAX);
- }, $wrapper = CE("div", { class: "bx-number-stepper", id: `bx_setting_${key}` }, $btnDec = CE("button", {
- "data-type": "dec",
- type: "button",
- class: options.hideSlider ? "bx-focusable" : "",
- tabindex: options.hideSlider ? 0 : -1
- }, "-"), $text = CE("span", {}, renderTextValue(value)), $btnInc = CE("button", {
- "data-type": "inc",
- type: "button",
- class: options.hideSlider ? "bx-focusable" : "",
- tabindex: options.hideSlider ? 0 : -1
- }, "+"));
- if (!options.disabled && !options.hideSlider) {
- if ($range = CE("input", {
- id: `bx_setting_${key}`,
- type: "range",
- min: MIN,
- max: MAX,
- value,
- step: STEPS,
- tabindex: 0
- }), $range.addEventListener("input", (e) => {
- if (value = parseInt(e.target.value), controlValue === value)
- return;
- controlValue = value, updateButtonsVisibility(), $text.textContent = renderTextValue(value), !e.ignoreOnChange && onChange && onChange(e, value);
- }), $wrapper.appendChild($range), options.ticks || options.exactTicks) {
- const markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId });
- if ($range.setAttribute("list", markersId), options.exactTicks) {
- let start = Math.max(Math.floor(MIN / options.exactTicks), 1) * options.exactTicks;
- if (start === MIN)
- start += options.exactTicks;
- for (let i = start;i < MAX; i += options.exactTicks)
- $markers.appendChild(CE("option", { value: i }));
- } else
- for (let i = MIN + options.ticks;i < MAX; i += options.ticks)
- $markers.appendChild(CE("option", { value: i }));
- $wrapper.appendChild($markers);
- }
- }
- if (options.disabled)
- return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), $wrapper;
- updateButtonsVisibility();
- let interval, isHolding = !1;
- const onClick = (e) => {
- if (isHolding) {
- e.preventDefault(), isHolding = !1;
- return;
- }
- const $btn = e.target;
- let value2 = parseInt(controlValue);
- if ($btn.dataset.type === "dec")
- value2 = Math.max(MIN, value2 - STEPS);
- else
- value2 = Math.min(MAX, value2 + STEPS);
- controlValue = value2, updateButtonsVisibility(), $text.textContent = renderTextValue(value2), $range && ($range.value = value2.toString()), isHolding = !1, onChange && onChange(e, value2);
- }, onMouseDown = (e) => {
- e.preventDefault(), isHolding = !0;
- const args = arguments;
- interval && clearInterval(interval), interval = window.setInterval(() => {
- const event = new Event("click");
- event.arguments = args, e.target?.dispatchEvent(event);
- }, 200);
- }, onMouseUp = (e) => {
- e.preventDefault(), interval && clearInterval(interval), isHolding = !1;
- }, onContextMenu = (e) => e.preventDefault();
- return $wrapper.setValue = (value2) => {
- controlValue = parseInt(value2), $text.textContent = renderTextValue(value2), $range && ($range.value = value2);
- }, $btnDec.addEventListener("click", onClick), $btnDec.addEventListener("pointerdown", onMouseDown), $btnDec.addEventListener("pointerup", onMouseUp), $btnDec.addEventListener("contextmenu", onContextMenu), $btnInc.addEventListener("click", onClick), $btnInc.addEventListener("pointerdown", onMouseDown), $btnInc.addEventListener("pointerup", onMouseUp), $btnInc.addEventListener("contextmenu", onContextMenu), $wrapper;
- }
- static #METHOD_MAP = {
- [SettingElementType.OPTIONS]: SettingElement.#renderOptions,
- [SettingElementType.MULTIPLE_OPTIONS]: SettingElement.#renderMultipleOptions,
- [SettingElementType.NUMBER]: SettingElement.#renderNumber,
- [SettingElementType.NUMBER_STEPPER]: SettingElement.#renderNumberStepper,
- [SettingElementType.CHECKBOX]: SettingElement.#renderCheckbox
- };
- static render(type, key, setting, currentValue, onChange, options) {
- const method = SettingElement.#METHOD_MAP[type], $control = method(...Array.from(arguments).slice(1));
- if (type !== SettingElementType.NUMBER_STEPPER)
- $control.id = `bx_setting_${key}`;
- if (type === SettingElementType.OPTIONS || type === SettingElementType.MULTIPLE_OPTIONS)
- $control.name = $control.id;
- return $control;
- }
-}
+// src/enums/ui-sections.ts
+var UiSection;
+(function(UiSection2) {
+ UiSection2["NEWS"] = "news";
+ UiSection2["FRIENDS"] = "friends";
+ UiSection2["MOST_POPULAR"] = "most-popular";
+ UiSection2["ALL_GAMES"] = "all-games";
+})(UiSection || (UiSection = {}));
// src/modules/stream/stream-stats.ts
var StreamStat;
@@ -1020,106 +980,306 @@ class StreamStats {
}
}
-// src/enums/ui-sections.ts
-var UiSection;
-(function(UiSection2) {
- UiSection2["NEWS"] = "news";
- UiSection2["FRIENDS"] = "friends";
- UiSection2["MOST_POPULAR"] = "most-popular";
- UiSection2["ALL_GAMES"] = "all-games";
-})(UiSection || (UiSection = {}));
+// src/utils/setting-element.ts
+var SettingElementType;
+(function(SettingElementType2) {
+ SettingElementType2["OPTIONS"] = "options";
+ SettingElementType2["MULTIPLE_OPTIONS"] = "multiple-options";
+ SettingElementType2["NUMBER"] = "number";
+ SettingElementType2["NUMBER_STEPPER"] = "number-stepper";
+ SettingElementType2["CHECKBOX"] = "checkbox";
+})(SettingElementType || (SettingElementType = {}));
-// src/enums/bypass-servers.ts
-var BypassServers = {
- br: "Brazil",
- jp: "Japan",
- pl: "Poland",
- us: "United States"
-}, BypassServerIps = {
- br: "169.150.198.66",
- jp: "138.199.21.239",
- pl: "45.134.212.66",
- us: "143.244.47.65"
+class SettingElement {
+ static #renderOptions(key, setting, currentValue, onChange) {
+ const $control = CE("select", {
+ tabindex: 0
+ });
+ let $parent;
+ if (setting.optionsGroup)
+ $parent = CE("optgroup", { label: setting.optionsGroup }), $control.appendChild($parent);
+ else
+ $parent = $control;
+ for (let value in setting.options) {
+ const label = setting.options[value], $option = CE("option", { value }, label);
+ $parent.appendChild($option);
+ }
+ return $control.value = currentValue, onChange && $control.addEventListener("input", (e) => {
+ const target = e.target, value = setting.type && setting.type === "number" ? parseInt(target.value) : target.value;
+ !e.ignoreOnChange && onChange(e, value);
+ }), $control.setValue = (value) => {
+ $control.value = value;
+ }, $control;
+ }
+ static #renderMultipleOptions(key, setting, currentValue, onChange, params = {}) {
+ const $control = CE("select", {
+ multiple: !0,
+ tabindex: 0
+ });
+ if (params && params.size)
+ $control.setAttribute("size", params.size.toString());
+ for (let value in setting.multipleOptions) {
+ const label = setting.multipleOptions[value], $option = CE("option", { value }, label);
+ $option.selected = currentValue.indexOf(value) > -1, $option.addEventListener("mousedown", function(e) {
+ e.preventDefault();
+ const target = e.target;
+ target.selected = !target.selected;
+ const $parent = target.parentElement;
+ $parent.focus(), $parent.dispatchEvent(new Event("input"));
+ }), $control.appendChild($option);
+ }
+ return $control.addEventListener("mousedown", function(e) {
+ const self = this, orgScrollTop = self.scrollTop;
+ window.setTimeout(() => self.scrollTop = orgScrollTop, 0);
+ }), $control.addEventListener("mousemove", (e) => e.preventDefault()), onChange && $control.addEventListener("input", (e) => {
+ const target = e.target, values = Array.from(target.selectedOptions).map((i) => i.value);
+ !e.ignoreOnChange && onChange(e, values);
+ }), $control;
+ }
+ static #renderNumber(key, setting, currentValue, onChange) {
+ const $control = CE("input", { tabindex: 0, type: "number", min: setting.min, max: setting.max });
+ return $control.value = currentValue, onChange && $control.addEventListener("change", (e) => {
+ const target = e.target, value = Math.max(setting.min, Math.min(setting.max, parseInt(target.value)));
+ target.value = value.toString(), !e.ignoreOnChange && onChange(e, value);
+ }), $control;
+ }
+ static #renderCheckbox(key, setting, currentValue, onChange) {
+ const $control = CE("input", { type: "checkbox", tabindex: 0 });
+ return $control.checked = currentValue, onChange && $control.addEventListener("change", (e) => {
+ !e.ignoreOnChange && onChange(e, e.target.checked);
+ }), $control;
+ }
+ static #renderNumberStepper(key, setting, value, onChange, options = {}) {
+ options = options || {}, options.suffix = options.suffix || "", options.disabled = !!options.disabled, options.hideSlider = !!options.hideSlider;
+ let $text, $btnDec, $btnInc, $range = null, controlValue = value;
+ const { min: MIN, max: MAX } = setting, STEPS = Math.max(setting.steps || 1, 1), renderTextValue = (value2) => {
+ value2 = parseInt(value2);
+ let textContent = null;
+ if (options.customTextValue)
+ textContent = options.customTextValue(value2);
+ if (textContent === null)
+ textContent = value2.toString() + options.suffix;
+ return textContent;
+ }, updateButtonsVisibility = () => {
+ $btnDec.classList.toggle("bx-inactive", controlValue === MIN), $btnInc.classList.toggle("bx-inactive", controlValue === MAX);
+ }, $wrapper = CE("div", { class: "bx-number-stepper", id: `bx_setting_${key}` }, $btnDec = CE("button", {
+ "data-type": "dec",
+ type: "button",
+ class: options.hideSlider ? "bx-focusable" : "",
+ tabindex: options.hideSlider ? 0 : -1
+ }, "-"), $text = CE("span", {}, renderTextValue(value)), $btnInc = CE("button", {
+ "data-type": "inc",
+ type: "button",
+ class: options.hideSlider ? "bx-focusable" : "",
+ tabindex: options.hideSlider ? 0 : -1
+ }, "+"));
+ if (options.disabled)
+ $wrapper.disabled = !0;
+ if (!options.disabled && !options.hideSlider) {
+ if ($range = CE("input", {
+ id: `bx_setting_${key}`,
+ type: "range",
+ min: MIN,
+ max: MAX,
+ value,
+ step: STEPS,
+ tabindex: 0
+ }), $range.addEventListener("input", (e) => {
+ if (value = parseInt(e.target.value), controlValue === value)
+ return;
+ controlValue = value, updateButtonsVisibility(), $text.textContent = renderTextValue(value), !e.ignoreOnChange && onChange && onChange(e, value);
+ }), $wrapper.appendChild($range), options.ticks || options.exactTicks) {
+ const markersId = `markers-${key}`, $markers = CE("datalist", { id: markersId });
+ if ($range.setAttribute("list", markersId), options.exactTicks) {
+ let start = Math.max(Math.floor(MIN / options.exactTicks), 1) * options.exactTicks;
+ if (start === MIN)
+ start += options.exactTicks;
+ for (let i = start;i < MAX; i += options.exactTicks)
+ $markers.appendChild(CE("option", { value: i }));
+ } else
+ for (let i = MIN + options.ticks;i < MAX; i += options.ticks)
+ $markers.appendChild(CE("option", { value: i }));
+ $wrapper.appendChild($markers);
+ }
+ }
+ if (options.disabled)
+ return $btnInc.disabled = !0, $btnInc.classList.add("bx-inactive"), $btnDec.disabled = !0, $btnDec.classList.add("bx-inactive"), $wrapper;
+ updateButtonsVisibility();
+ let interval, isHolding = !1;
+ const onClick = (e) => {
+ if (isHolding) {
+ e.preventDefault(), isHolding = !1;
+ return;
+ }
+ const $btn = e.target;
+ let value2 = parseInt(controlValue);
+ if ($btn.dataset.type === "dec")
+ value2 = Math.max(MIN, value2 - STEPS);
+ else
+ value2 = Math.min(MAX, value2 + STEPS);
+ controlValue = value2, updateButtonsVisibility(), $text.textContent = renderTextValue(value2), $range && ($range.value = value2.toString()), isHolding = !1, !e.ignoreOnChange && onChange && onChange(e, value2);
+ }, onMouseDown = (e) => {
+ e.preventDefault(), isHolding = !0;
+ const args = arguments;
+ interval && clearInterval(interval), interval = window.setInterval(() => {
+ const event = new Event("click");
+ event.arguments = args, e.target?.dispatchEvent(event);
+ }, 200);
+ }, onMouseUp = (e) => {
+ e.preventDefault(), interval && clearInterval(interval), isHolding = !1;
+ }, onContextMenu = (e) => e.preventDefault();
+ return $wrapper.setValue = (value2) => {
+ controlValue = parseInt(value2), $text.textContent = renderTextValue(value2), $range && ($range.value = value2);
+ }, $btnDec.addEventListener("click", onClick), $btnDec.addEventListener("pointerdown", onMouseDown), $btnDec.addEventListener("pointerup", onMouseUp), $btnDec.addEventListener("contextmenu", onContextMenu), $btnInc.addEventListener("click", onClick), $btnInc.addEventListener("pointerdown", onMouseDown), $btnInc.addEventListener("pointerup", onMouseUp), $btnInc.addEventListener("contextmenu", onContextMenu), setNearby($wrapper, {
+ focus: $range || $btnInc
+ }), $wrapper;
+ }
+ static #METHOD_MAP = {
+ [SettingElementType.OPTIONS]: SettingElement.#renderOptions,
+ [SettingElementType.MULTIPLE_OPTIONS]: SettingElement.#renderMultipleOptions,
+ [SettingElementType.NUMBER]: SettingElement.#renderNumber,
+ [SettingElementType.NUMBER_STEPPER]: SettingElement.#renderNumberStepper,
+ [SettingElementType.CHECKBOX]: SettingElement.#renderCheckbox
+ };
+ static render(type, key, setting, currentValue, onChange, options) {
+ const method = SettingElement.#METHOD_MAP[type], $control = method(...Array.from(arguments).slice(1));
+ if (type !== SettingElementType.NUMBER_STEPPER)
+ $control.id = `bx_setting_${key}`;
+ if (type === SettingElementType.OPTIONS || type === SettingElementType.MULTIPLE_OPTIONS)
+ $control.name = $control.id;
+ return $control;
+ }
+ static fromPref(key, storage, onChange, overrideParams = {}) {
+ const definition = storage.getDefinition(key);
+ let currentValue = storage.getSetting(key), type;
+ if ("type" in definition)
+ type = definition.type;
+ else if ("options" in definition)
+ type = SettingElementType.OPTIONS;
+ else if ("multipleOptions" in definition)
+ type = SettingElementType.MULTIPLE_OPTIONS;
+ else if (typeof definition.default === "number")
+ type = SettingElementType.NUMBER;
+ else
+ type = SettingElementType.CHECKBOX;
+ const params = Object.assign(overrideParams, definition.params || {});
+ if (params.disabled)
+ currentValue = definition.default;
+ return SettingElement.render(type, key, definition, currentValue, (e, value) => {
+ storage.setSetting(key, value), onChange && onChange(e, value);
+ }, params);
+ }
+}
+
+// src/utils/settings-storages/base-settings-storage.ts
+class BaseSettingsStore {
+ storage;
+ storageKey;
+ _settings;
+ definitions;
+ constructor(storageKey, definitions) {
+ this.storage = window.localStorage, this.storageKey = storageKey, this.definitions = definitions, this._settings = null;
+ }
+ get settings() {
+ if (this._settings)
+ return this._settings;
+ const settings = JSON.parse(this.storage.getItem(this.storageKey) || "{}");
+ return this._settings = settings, settings;
+ }
+ getDefinition(key) {
+ if (!this.definitions[key]) {
+ const error = "Request invalid definition: " + key;
+ throw alert(error), Error(error);
+ }
+ return this.definitions[key];
+ }
+ getSetting(key) {
+ if (typeof key === "undefined") {
+ debugger;
+ return;
+ }
+ if (this.definitions[key].unsupported)
+ return this.definitions[key].default;
+ if (!(key in this.settings))
+ this.settings[key] = this.validateValue(key, null);
+ return this.settings[key];
+ }
+ setSetting(key, value, emitEvent = !1) {
+ return value = this.validateValue(key, value), this.settings[key] = value, this.saveSettings(), emitEvent && BxEvent.dispatch(window, BxEvent.SETTINGS_CHANGED, {
+ storageKey: this.storageKey,
+ settingKey: key,
+ settingValue: value
+ }), value;
+ }
+ saveSettings() {
+ this.storage.setItem(this.storageKey, JSON.stringify(this.settings));
+ }
+ validateValue(key, value) {
+ const def = this.definitions[key];
+ if (!def)
+ return value;
+ if (typeof value === "undefined" || value === null)
+ value = def.default;
+ if ("min" in def)
+ value = Math.max(def.min, value);
+ if ("max" in def)
+ value = Math.min(def.max, value);
+ if ("options" in def && !(value in def.options))
+ value = def.default;
+ else if ("multipleOptions" in def) {
+ if (value.length) {
+ const validOptions = Object.keys(def.multipleOptions);
+ value.forEach((item2, idx) => {
+ validOptions.indexOf(item2) === -1 && value.splice(idx, 1);
+ });
+ }
+ if (!value.length)
+ value = def.default;
+ }
+ return value;
+ }
+}
+
+// src/utils/settings-storages/global-settings-storage.ts
+var getSupportedCodecProfiles = function() {
+ const options = {
+ default: t("default")
+ };
+ if (!("getCapabilities" in RTCRtpReceiver))
+ return options;
+ let hasLowCodec = !1, hasNormalCodec = !1, hasHighCodec = !1;
+ const codecs = RTCRtpReceiver.getCapabilities("video").codecs;
+ for (let codec of codecs) {
+ if (codec.mimeType.toLowerCase() !== "video/h264" || !codec.sdpFmtpLine)
+ continue;
+ const fmtp = codec.sdpFmtpLine.toLowerCase();
+ if (fmtp.includes("profile-level-id=4d"))
+ hasHighCodec = !0;
+ else if (fmtp.includes("profile-level-id=42e"))
+ hasNormalCodec = !0;
+ else if (fmtp.includes("profile-level-id=420"))
+ hasLowCodec = !0;
+ }
+ if (hasHighCodec)
+ if (!hasLowCodec && !hasNormalCodec)
+ options.default = `${t("visual-quality-high")} (${t("default")})`;
+ else
+ options.high = t("visual-quality-high");
+ if (hasNormalCodec)
+ if (!hasLowCodec && !hasHighCodec)
+ options.default = `${t("visual-quality-normal")} (${t("default")})`;
+ else
+ options.normal = t("visual-quality-normal");
+ if (hasLowCodec)
+ if (!hasNormalCodec && !hasHighCodec)
+ options.default = `${t("visual-quality-low")} (${t("default")})`;
+ else
+ options.low = t("visual-quality-low");
+ return options;
};
-// src/utils/preferences.ts
-var PrefKey;
-(function(PrefKey2) {
- PrefKey2["LAST_UPDATE_CHECK"] = "version_last_check";
- PrefKey2["LATEST_VERSION"] = "version_latest";
- PrefKey2["CURRENT_VERSION"] = "version_current";
- PrefKey2["BETTER_XCLOUD_LOCALE"] = "bx_locale";
- PrefKey2["SERVER_REGION"] = "server_region";
- PrefKey2["SERVER_BYPASS_RESTRICTION"] = "server_bypass_restriction";
- PrefKey2["PREFER_IPV6_SERVER"] = "prefer_ipv6_server";
- PrefKey2["STREAM_TARGET_RESOLUTION"] = "stream_target_resolution";
- PrefKey2["STREAM_PREFERRED_LOCALE"] = "stream_preferred_locale";
- PrefKey2["STREAM_CODEC_PROFILE"] = "stream_codec_profile";
- PrefKey2["USER_AGENT_PROFILE"] = "user_agent_profile";
- PrefKey2["STREAM_SIMPLIFY_MENU"] = "stream_simplify_menu";
- PrefKey2["STREAM_COMBINE_SOURCES"] = "stream_combine_sources";
- PrefKey2["STREAM_TOUCH_CONTROLLER"] = "stream_touch_controller";
- PrefKey2["STREAM_TOUCH_CONTROLLER_AUTO_OFF"] = "stream_touch_controller_auto_off";
- PrefKey2["STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY"] = "stream_touch_controller_default_opacity";
- PrefKey2["STREAM_TOUCH_CONTROLLER_STYLE_STANDARD"] = "stream_touch_controller_style_standard";
- PrefKey2["STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM"] = "stream_touch_controller_style_custom";
- PrefKey2["STREAM_DISABLE_FEEDBACK_DIALOG"] = "stream_disable_feedback_dialog";
- PrefKey2["BITRATE_VIDEO_MAX"] = "bitrate_video_max";
- PrefKey2["GAME_BAR_POSITION"] = "game_bar_position";
- PrefKey2["LOCAL_CO_OP_ENABLED"] = "local_co_op_enabled";
- PrefKey2["CONTROLLER_ENABLE_SHORTCUTS"] = "controller_enable_shortcuts";
- PrefKey2["CONTROLLER_ENABLE_VIBRATION"] = "controller_enable_vibration";
- PrefKey2["CONTROLLER_DEVICE_VIBRATION"] = "controller_device_vibration";
- PrefKey2["CONTROLLER_VIBRATION_INTENSITY"] = "controller_vibration_intensity";
- PrefKey2["CONTROLLER_SHOW_CONNECTION_STATUS"] = "controller_show_connection_status";
- PrefKey2["NATIVE_MKB_ENABLED"] = "native_mkb_enabled";
- PrefKey2["NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY"] = "native_mkb_scroll_x_sensitivity";
- PrefKey2["NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY"] = "native_mkb_scroll_y_sensitivity";
- PrefKey2["MKB_ENABLED"] = "mkb_enabled";
- PrefKey2["MKB_HIDE_IDLE_CURSOR"] = "mkb_hide_idle_cursor";
- PrefKey2["MKB_ABSOLUTE_MOUSE"] = "mkb_absolute_mouse";
- PrefKey2["MKB_DEFAULT_PRESET_ID"] = "mkb_default_preset_id";
- PrefKey2["SCREENSHOT_APPLY_FILTERS"] = "screenshot_apply_filters";
- PrefKey2["BLOCK_TRACKING"] = "block_tracking";
- PrefKey2["BLOCK_SOCIAL_FEATURES"] = "block_social_features";
- PrefKey2["SKIP_SPLASH_VIDEO"] = "skip_splash_video";
- PrefKey2["HIDE_DOTS_ICON"] = "hide_dots_icon";
- PrefKey2["REDUCE_ANIMATIONS"] = "reduce_animations";
- PrefKey2["UI_LOADING_SCREEN_GAME_ART"] = "ui_loading_screen_game_art";
- PrefKey2["UI_LOADING_SCREEN_WAIT_TIME"] = "ui_loading_screen_wait_time";
- PrefKey2["UI_LOADING_SCREEN_ROCKET"] = "ui_loading_screen_rocket";
- PrefKey2["UI_CONTROLLER_FRIENDLY"] = "ui_controller_friendly";
- PrefKey2["UI_LAYOUT"] = "ui_layout";
- PrefKey2["UI_SCROLLBAR_HIDE"] = "ui_scrollbar_hide";
- PrefKey2["UI_HIDE_SECTIONS"] = "ui_hide_sections";
- PrefKey2["UI_HOME_CONTEXT_MENU_DISABLED"] = "ui_home_context_menu_disabled";
- PrefKey2["UI_GAME_CARD_SHOW_WAIT_TIME"] = "ui_game_card_show_wait_time";
- PrefKey2["VIDEO_PLAYER_TYPE"] = "video_player_type";
- PrefKey2["VIDEO_PROCESSING"] = "video_processing";
- PrefKey2["VIDEO_POWER_PREFERENCE"] = "video_power_preference";
- PrefKey2["VIDEO_SHARPNESS"] = "video_sharpness";
- PrefKey2["VIDEO_RATIO"] = "video_ratio";
- PrefKey2["VIDEO_BRIGHTNESS"] = "video_brightness";
- PrefKey2["VIDEO_CONTRAST"] = "video_contrast";
- PrefKey2["VIDEO_SATURATION"] = "video_saturation";
- PrefKey2["AUDIO_MIC_ON_PLAYING"] = "audio_mic_on_playing";
- PrefKey2["AUDIO_ENABLE_VOLUME_CONTROL"] = "audio_enable_volume_control";
- PrefKey2["AUDIO_VOLUME"] = "audio_volume";
- PrefKey2["STATS_ITEMS"] = "stats_items";
- PrefKey2["STATS_SHOW_WHEN_PLAYING"] = "stats_show_when_playing";
- PrefKey2["STATS_QUICK_GLANCE"] = "stats_quick_glance";
- PrefKey2["STATS_POSITION"] = "stats_position";
- PrefKey2["STATS_TEXT_SIZE"] = "stats_text_size";
- PrefKey2["STATS_TRANSPARENT"] = "stats_transparent";
- PrefKey2["STATS_OPACITY"] = "stats_opacity";
- PrefKey2["STATS_CONDITIONAL_FORMATTING"] = "stats_conditional_formatting";
- PrefKey2["REMOTE_PLAY_ENABLED"] = "xhome_enabled";
- PrefKey2["REMOTE_PLAY_RESOLUTION"] = "xhome_resolution";
- PrefKey2["GAME_FORTNITE_FORCE_CONSOLE"] = "game_fortnite_force_console";
-})(PrefKey || (PrefKey = {}));
-
-class Preferences {
- static SETTINGS = {
+class GlobalSettingsStorage extends BaseSettingsStore {
+ static DEFINITIONS = {
[PrefKey.LAST_UPDATE_CHECK]: {
default: 0
},
@@ -1193,42 +1353,7 @@ class Preferences {
[PrefKey.STREAM_CODEC_PROFILE]: {
label: t("visual-quality"),
default: "default",
- options: (() => {
- const options = {
- default: t("default")
- };
- if (!("getCapabilities" in RTCRtpReceiver))
- return options;
- let hasLowCodec = !1, hasNormalCodec = !1, hasHighCodec = !1;
- const codecs = RTCRtpReceiver.getCapabilities("video").codecs;
- for (let codec of codecs) {
- if (codec.mimeType.toLowerCase() !== "video/h264" || !codec.sdpFmtpLine)
- continue;
- const fmtp = codec.sdpFmtpLine.toLowerCase();
- if (!hasHighCodec && fmtp.includes("profile-level-id=4d"))
- hasHighCodec = !0;
- else if (!hasNormalCodec && fmtp.includes("profile-level-id=42e"))
- hasNormalCodec = !0;
- else if (!hasLowCodec && fmtp.includes("profile-level-id=420"))
- hasLowCodec = !0;
- }
- if (hasHighCodec)
- if (!hasLowCodec && !hasNormalCodec)
- options.default = `${t("visual-quality-high")} (${t("default")})`;
- else
- options.high = t("visual-quality-high");
- if (hasNormalCodec)
- if (!hasLowCodec && !hasHighCodec)
- options.default = `${t("visual-quality-normal")} (${t("default")})`;
- else
- options.normal = t("visual-quality-normal");
- if (hasLowCodec)
- if (!hasNormalCodec && !hasHighCodec)
- options.default = `${t("visual-quality-low")} (${t("default")})`;
- else
- options.low = t("visual-quality-low");
- return options;
- })(),
+ options: getSupportedCodecProfiles(),
ready: (setting) => {
const options = setting.options;
if (Object.keys(options).length <= 1)
@@ -1337,14 +1462,6 @@ class Preferences {
else
return (value / 1024000).toFixed(1) + " Mb/s";
}
- },
- migrate: function(savedPrefs, value) {
- try {
- if (value = parseInt(value), value !== 0 && value < 100)
- value *= 1024000;
- this.set(PrefKey.BITRATE_VIDEO_MAX, value, !0), savedPrefs[PrefKey.BITRATE_VIDEO_MAX] = value;
- } catch (e) {
- }
}
},
[PrefKey.GAME_BAR_POSITION]: {
@@ -1493,7 +1610,7 @@ class Preferences {
},
[PrefKey.UI_CONTROLLER_FRIENDLY]: {
label: t("controller-friendly-ui"),
- default: !STATES.browser.capabilities.touch || BX_FLAGS.DeviceInfo?.deviceType === "android-tv"
+ default: !0
},
[PrefKey.UI_LAYOUT]: {
label: t("layout"),
@@ -1567,7 +1684,7 @@ class Preferences {
}
},
[PrefKey.VIDEO_POWER_PREFERENCE]: {
- label: t("gpu-configuration"),
+ label: t("renderer-configuration"),
default: "default",
options: {
default: t("default"),
@@ -1648,7 +1765,7 @@ class Preferences {
default: 100,
min: 0,
max: 600,
- steps: 20,
+ steps: 10,
params: {
suffix: "%",
ticks: 100
@@ -1732,102 +1849,12 @@ class Preferences {
note: t("fortnite-allow-stw-mode")
}
};
- #storage = localStorage;
- #key = "better_xcloud";
- #prefs = {};
constructor() {
- let savedPrefsStr = this.#storage.getItem(this.#key);
- if (savedPrefsStr == null)
- savedPrefsStr = "{}";
- const savedPrefs = JSON.parse(savedPrefsStr);
- for (let settingId in Preferences.SETTINGS) {
- const setting = Preferences.SETTINGS[settingId];
- if (setting.migrate && settingId in savedPrefs)
- setting.migrate.call(this, savedPrefs, savedPrefs[settingId]);
- setting.ready && setting.ready.call(this, setting);
- }
- for (let settingId in Preferences.SETTINGS) {
- const setting = Preferences.SETTINGS[settingId];
- if (!setting) {
- alert(`Undefined setting key: ${settingId}`), console.log("Undefined setting key");
- continue;
- }
- if (setting.migrate)
- continue;
- if (settingId in savedPrefs)
- this.#prefs[settingId] = this.#validateValue(settingId, savedPrefs[settingId]);
- else
- this.#prefs[settingId] = setting.default;
- }
- }
- #validateValue(key, value) {
- const config = Preferences.SETTINGS[key];
- if (!config)
- return value;
- if (typeof value === "undefined" || value === null)
- value = config.default;
- if ("min" in config)
- value = Math.max(config.min, value);
- if ("max" in config)
- value = Math.min(config.max, value);
- if ("options" in config && !(value in config.options))
- value = config.default;
- else if ("multipleOptions" in config) {
- if (value.length) {
- const validOptions = Object.keys(config.multipleOptions);
- value.forEach((item2, idx) => {
- validOptions.indexOf(item2) === -1 && value.splice(idx, 1);
- });
- }
- if (!value.length)
- value = config.default;
- }
- return value;
- }
- get(key) {
- if (typeof key === "undefined") {
- debugger;
- return;
- }
- if (Preferences.SETTINGS[key].unsupported)
- return Preferences.SETTINGS[key].default;
- if (!(key in this.#prefs))
- this.#prefs[key] = this.#validateValue(key, null);
- return this.#prefs[key];
- }
- set(key, value, skipSave) {
- return value = this.#validateValue(key, value), this.#prefs[key] = value, !skipSave && this.#updateStorage(), value;
- }
- #updateStorage() {
- this.#storage.setItem(this.#key, JSON.stringify(this.#prefs));
- }
- toElement(key, onChange, overrideParams = {}) {
- const setting = Preferences.SETTINGS[key];
- let currentValue = this.get(key), type;
- if ("type" in setting)
- type = setting.type;
- else if ("options" in setting)
- type = SettingElementType.OPTIONS;
- else if ("multipleOptions" in setting)
- type = SettingElementType.MULTIPLE_OPTIONS;
- else if (typeof setting.default === "number")
- type = SettingElementType.NUMBER;
- else
- type = SettingElementType.CHECKBOX;
- const params = Object.assign(overrideParams, setting.params || {});
- if (params.disabled)
- currentValue = Preferences.SETTINGS[key].default;
- return SettingElement.render(type, key, setting, currentValue, (e, value) => {
- this.set(key, value), onChange && onChange(e, value);
- }, params);
- }
- toNumberStepper(key, onChange, options = {}) {
- return SettingElement.render(SettingElementType.NUMBER_STEPPER, key, Preferences.SETTINGS[key], this.get(key), (e, value) => {
- this.set(key, value), onChange && onChange(e, value);
- }, options);
+ super(StorageKey.GLOBAL, GlobalSettingsStorage.DEFINITIONS);
}
}
-var prefs = new Preferences, getPref = prefs.get.bind(prefs), setPref = prefs.set.bind(prefs), toPrefElement = prefs.toElement.bind(prefs);
+var globalSettings = new GlobalSettingsStorage, getPrefDefinition = globalSettings.getDefinition.bind(globalSettings), getPref = globalSettings.getSetting.bind(globalSettings), setPref = globalSettings.setSetting.bind(globalSettings);
+STORAGE.Global = globalSettings;
// src/utils/screenshot.ts
class Screenshot {
@@ -2559,6 +2586,335 @@ class NativeMkbHandler extends MkbHandler {
}
}
+// src/modules/stream/stream-settings-utils.ts
+function onChangeVideoPlayerType() {
+ const playerType = getPref(PrefKey.VIDEO_PLAYER_TYPE), $videoProcessing = document.getElementById("bx_setting_video_processing"), $videoSharpness = document.getElementById("bx_setting_video_sharpness"), $videoPowerPreference = document.getElementById("bx_setting_video_power_preference");
+ if (!$videoProcessing)
+ return;
+ let isDisabled = !1;
+ const $optCas = $videoProcessing.querySelector(`option[value=${StreamVideoProcessing.CAS}]`);
+ if (playerType === StreamPlayerType.WEBGL2)
+ $optCas && ($optCas.disabled = !1);
+ else if ($videoProcessing.value = StreamVideoProcessing.USM, setPref(PrefKey.VIDEO_PROCESSING, StreamVideoProcessing.USM), $optCas && ($optCas.disabled = !0), UserAgent.isSafari())
+ isDisabled = !0;
+ $videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== StreamPlayerType.WEBGL2), updateVideoPlayer();
+}
+function updateVideoPlayer() {
+ const streamPlayer = STATES.currentStream.streamPlayer;
+ if (!streamPlayer)
+ return;
+ const options = {
+ processing: getPref(PrefKey.VIDEO_PROCESSING),
+ sharpness: getPref(PrefKey.VIDEO_SHARPNESS),
+ saturation: getPref(PrefKey.VIDEO_SATURATION),
+ contrast: getPref(PrefKey.VIDEO_CONTRAST),
+ brightness: getPref(PrefKey.VIDEO_BRIGHTNESS)
+ };
+ streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE)), streamPlayer.updateOptions(options), streamPlayer.refreshPlayer();
+}
+window.addEventListener("resize", updateVideoPlayer);
+
+// src/modules/ui/dialog/navigation-dialog.ts
+var NavigationDirection;
+(function(NavigationDirection2) {
+ NavigationDirection2[NavigationDirection2["UP"] = 1] = "UP";
+ NavigationDirection2[NavigationDirection2["RIGHT"] = 2] = "RIGHT";
+ NavigationDirection2[NavigationDirection2["DOWN"] = 3] = "DOWN";
+ NavigationDirection2[NavigationDirection2["LEFT"] = 4] = "LEFT";
+})(NavigationDirection || (NavigationDirection = {}));
+
+class NavigationDialog {
+ dialogManager;
+ constructor() {
+ this.dialogManager = NavigationDialogManager.getInstance();
+ }
+ show() {
+ if (NavigationDialogManager.getInstance().show(this), !this.getFocusedElement())
+ this.focusIfNeeded();
+ }
+ hide() {
+ NavigationDialogManager.getInstance().hide();
+ }
+ getFocusedElement() {
+ const $activeElement = document.activeElement;
+ if (!$activeElement)
+ return null;
+ if (this.$container.contains($activeElement))
+ return $activeElement;
+ return null;
+ }
+ onBeforeMount() {
+ }
+ onMounted() {
+ }
+ onBeforeUnmount() {
+ }
+ onUnmounted() {
+ }
+ handleKeyPress(key) {
+ return !1;
+ }
+ handleGamepad(button) {
+ return !0;
+ }
+}
+
+class NavigationDialogManager {
+ static instance;
+ static getInstance() {
+ if (!NavigationDialogManager.instance)
+ NavigationDialogManager.instance = new NavigationDialogManager;
+ return NavigationDialogManager.instance;
+ }
+ static GAMEPAD_POLLING_INTERVAL = 50;
+ static GAMEPAD_KEYS = [
+ GamepadKey.UP,
+ GamepadKey.DOWN,
+ GamepadKey.LEFT,
+ GamepadKey.RIGHT,
+ GamepadKey.A,
+ GamepadKey.B,
+ GamepadKey.LB,
+ GamepadKey.RB,
+ GamepadKey.LT,
+ GamepadKey.RT
+ ];
+ static GAMEPAD_DIRECTION_MAP = {
+ [GamepadKey.UP]: NavigationDirection.UP,
+ [GamepadKey.DOWN]: NavigationDirection.DOWN,
+ [GamepadKey.LEFT]: NavigationDirection.LEFT,
+ [GamepadKey.RIGHT]: NavigationDirection.RIGHT,
+ [GamepadKey.LS_UP]: NavigationDirection.UP,
+ [GamepadKey.LS_DOWN]: NavigationDirection.DOWN,
+ [GamepadKey.LS_LEFT]: NavigationDirection.LEFT,
+ [GamepadKey.LS_RIGHT]: NavigationDirection.RIGHT
+ };
+ static SIBLING_PROPERTY_MAP = {
+ horizontal: {
+ [NavigationDirection.LEFT]: "previousElementSibling",
+ [NavigationDirection.RIGHT]: "nextElementSibling"
+ },
+ vertical: {
+ [NavigationDirection.UP]: "previousElementSibling",
+ [NavigationDirection.DOWN]: "nextElementSibling"
+ }
+ };
+ gamepadPollingIntervalId = null;
+ gamepadLastButtons = [];
+ $overlay;
+ $container;
+ dialog = null;
+ constructor() {
+ this.$overlay = CE("div", { class: "bx-navigation-dialog-overlay bx-gone" }), this.$overlay.addEventListener("click", (e) => {
+ e.preventDefault(), e.stopPropagation(), this.hide();
+ }), document.documentElement.appendChild(this.$overlay), this.$container = CE("div", { class: "bx-navigation-dialog bx-gone" }), document.documentElement.appendChild(this.$container);
+ }
+ handleEvent(event) {
+ switch (event.type) {
+ case "keydown":
+ const $target = event.target, keyboardEvent = event, keyCode = keyboardEvent.code || keyboardEvent.key;
+ let handled = this.dialog?.handleKeyPress(keyCode);
+ if (handled) {
+ event.preventDefault(), event.stopPropagation();
+ return;
+ }
+ if (keyCode === "ArrowUp" || keyCode === "ArrowDown")
+ handled = !0, this.focusDirection(keyCode === "ArrowUp" ? NavigationDirection.UP : NavigationDirection.DOWN);
+ else if (keyCode === "ArrowLeft" || keyCode === "ArrowRight") {
+ if (!($target instanceof HTMLInputElement && ($target.type === "text" || $target.type === "range")))
+ handled = !0, this.focusDirection(keyCode === "ArrowLeft" ? NavigationDirection.LEFT : NavigationDirection.RIGHT);
+ } else if (keyCode === "Enter" || keyCode === "Space") {
+ if (!($target instanceof HTMLInputElement))
+ handled = !0, $target.dispatchEvent(new Event("click"));
+ } else if (keyCode === "Escape")
+ handled = !0, this.hide();
+ if (handled)
+ event.preventDefault(), event.stopPropagation();
+ break;
+ }
+ }
+ isShowing() {
+ return this.$container && !this.$container.classList.contains("bx-gone");
+ }
+ pollGamepad() {
+ const gamepads = window.navigator.getGamepads();
+ let direction = null;
+ for (let gamepad of gamepads) {
+ if (!gamepad || !gamepad.connected)
+ continue;
+ if (gamepad.id === EmulatedMkbHandler.VIRTUAL_GAMEPAD_ID)
+ continue;
+ const { axes, buttons } = gamepad;
+ let lastButton = this.gamepadLastButtons[gamepad.index], pressedButton = null, holdingButton = null;
+ for (let key of NavigationDialogManager.GAMEPAD_KEYS)
+ if (typeof lastButton === "number") {
+ if (lastButton === key && !buttons[key].pressed) {
+ pressedButton = key;
+ break;
+ }
+ } else if (buttons[key].pressed) {
+ holdingButton = key;
+ break;
+ }
+ if (holdingButton === null && pressedButton === null && axes && axes.length >= 2) {
+ if (typeof lastButton === "number") {
+ const releasedHorizontal = Math.abs(axes[0]) < 0.1 && (lastButton === GamepadKey.LS_LEFT || lastButton === GamepadKey.LS_RIGHT), releasedVertical = Math.abs(axes[1]) < 0.1 && (lastButton === GamepadKey.LS_UP || lastButton === GamepadKey.LS_DOWN);
+ if (releasedHorizontal || releasedVertical)
+ pressedButton = lastButton;
+ } else if (axes[0] < -0.5)
+ holdingButton = GamepadKey.LS_LEFT;
+ else if (axes[0] > 0.5)
+ holdingButton = GamepadKey.LS_RIGHT;
+ else if (axes[1] < -0.5)
+ holdingButton = GamepadKey.LS_UP;
+ else if (axes[1] > 0.5)
+ holdingButton = GamepadKey.LS_DOWN;
+ }
+ if (holdingButton !== null)
+ this.gamepadLastButtons[gamepad.index] = holdingButton;
+ if (pressedButton === null)
+ continue;
+ if (this.gamepadLastButtons[gamepad.index] = null, pressedButton === GamepadKey.A) {
+ document.activeElement && document.activeElement.dispatchEvent(new MouseEvent("click"));
+ return;
+ } else if (pressedButton === GamepadKey.B) {
+ this.hide();
+ return;
+ }
+ let handled = this.dialog?.handleGamepad(pressedButton);
+ if (handled)
+ return;
+ if (direction = NavigationDialogManager.GAMEPAD_DIRECTION_MAP[pressedButton], !direction)
+ return;
+ if (document.activeElement instanceof HTMLInputElement && document.activeElement.type === "range") {
+ const $range = document.activeElement;
+ if (direction === NavigationDirection.LEFT || direction === NavigationDirection.RIGHT)
+ $range.value = (parseInt($range.value) + parseInt($range.step) * (direction === NavigationDirection.LEFT ? -1 : 1)).toString(), $range.dispatchEvent(new InputEvent("input")), handled = !0;
+ }
+ if (!handled)
+ this.focusDirection(direction);
+ }
+ }
+ show(dialog) {
+ if (window.addEventListener(BxEvent.XCLOUD_GUIDE_MENU_SHOWN, (e) => this.hide()), window.BX_EXPOSED.disableGamepadPolling = !0, document.body.classList.add("bx-no-scroll"), this.$overlay.classList.remove("bx-gone"), STATES.isPlaying)
+ this.$overlay.classList.add("bx-invisible");
+ this.unmountCurrentDialog(), this.dialog = dialog, dialog.onBeforeMount(), this.$container.appendChild(dialog.getContent()), dialog.onMounted(), this.$container.classList.remove("bx-gone"), this.$container.addEventListener("keydown", this), this.startGamepadPolling();
+ }
+ hide() {
+ document.body.classList.remove("bx-no-scroll"), this.$overlay.classList.add("bx-gone"), this.$overlay.classList.remove("bx-invisible"), this.$container.classList.add("bx-gone"), this.$container.removeEventListener("keydown", this), this.stopGamepadPolling(), this.unmountCurrentDialog(), window.BX_EXPOSED.disableGamepadPolling = !1;
+ }
+ focus($elm) {
+ if (!$elm)
+ return !1;
+ if ($elm.nearby && $elm.nearby.focus)
+ if ($elm.nearby.focus instanceof HTMLElement)
+ return this.focus($elm.nearby.focus);
+ else
+ return $elm.nearby.focus();
+ return $elm.focus(), $elm === document.activeElement;
+ }
+ getOrientation($elm) {
+ const nearby = $elm.nearby || {};
+ if (nearby.selfOrientation)
+ return nearby.selfOrientation;
+ let orientation, $current = $elm.parentElement;
+ while ($current !== this.$container) {
+ const tmp = $current.nearby?.orientation;
+ if ($current.nearby && tmp) {
+ orientation = tmp;
+ break;
+ }
+ $current = $current.parentElement;
+ }
+ return orientation = orientation || "vertical", setNearby($elm, {
+ selfOrientation: orientation
+ }), orientation;
+ }
+ findNextTarget($focusing, direction, checkParent = !1, checked = []) {
+ if (!$focusing || $focusing === this.$container)
+ return null;
+ if (checked.includes($focusing))
+ return null;
+ checked.push($focusing);
+ let $target = $focusing;
+ const $parent = $target.parentElement, nearby = $target.nearby || {}, orientation = this.getOrientation($target);
+ let siblingProperty = NavigationDialogManager.SIBLING_PROPERTY_MAP[orientation][direction];
+ if (siblingProperty) {
+ let $sibling = $target;
+ while ($sibling[siblingProperty]) {
+ $sibling = $sibling[siblingProperty];
+ const $focusable = this.findFocusableElement($sibling, direction);
+ if ($focusable)
+ return $focusable;
+ }
+ }
+ if (nearby.loop) {
+ if (nearby.loop(direction))
+ return null;
+ }
+ if (checkParent)
+ return this.findNextTarget($parent, direction, checkParent, checked);
+ return null;
+ }
+ findFocusableElement($elm, direction) {
+ if (!$elm)
+ return null;
+ if (!!$elm.disabled)
+ return null;
+ const rect = $elm.getBoundingClientRect();
+ if (!(!!rect.width && !!rect.height))
+ return null;
+ if ($elm.tabIndex > -1)
+ return $elm;
+ const focus = $elm.nearby?.focus;
+ if (focus) {
+ if (focus instanceof HTMLElement)
+ return this.findFocusableElement(focus, direction);
+ else if (typeof focus === "function") {
+ if (focus())
+ return document.activeElement;
+ }
+ }
+ const children = Array.from($elm.children), orientation = $elm.nearby?.orientation;
+ if (orientation === "horizontal" || orientation === "vertical" && direction === NavigationDirection.UP)
+ children.reverse();
+ for (let $child of children) {
+ if (!$child || !($child instanceof HTMLElement))
+ return null;
+ const $target = this.findFocusableElement($child, direction);
+ if ($target)
+ return $target;
+ }
+ return null;
+ }
+ startGamepadPolling() {
+ this.stopGamepadPolling(), this.gamepadPollingIntervalId = window.setInterval(this.pollGamepad.bind(this), NavigationDialogManager.GAMEPAD_POLLING_INTERVAL);
+ }
+ stopGamepadPolling() {
+ this.gamepadLastButtons = [], this.gamepadPollingIntervalId && window.clearInterval(this.gamepadPollingIntervalId), this.gamepadPollingIntervalId = null;
+ }
+ focusDirection(direction) {
+ const dialog = this.dialog;
+ if (!dialog)
+ return;
+ const $focusing = dialog.getFocusedElement();
+ if (!$focusing || !this.findFocusableElement($focusing, direction))
+ return dialog.focusIfNeeded(), null;
+ const $target = this.findNextTarget($focusing, direction, !0);
+ this.focus($target);
+ }
+ unmountCurrentDialog() {
+ const dialog = this.dialog;
+ dialog && dialog.onBeforeUnmount(), this.$container.firstChild?.remove(), dialog && dialog.onUnmounted(), this.dialog = null;
+ }
+}
+
+// src/assets/svg/better-xcloud.svg
+var better_xcloud_default = "\n";
+
+// src/assets/svg/close.svg
+var close_default = "\n";
+
// src/assets/svg/command.svg
var command_default = "\n";
@@ -2648,8 +3004,10 @@ var upload_default = "