mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-28 18:31:44 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
0870065a81 | |||
e0f0617b12 | |||
38623cc086 | |||
bbce49791f | |||
d719f0c2b5 | |||
622057980d | |||
24d608bc3e | |||
f55344b4cb | |||
6139fb386b | |||
f7c46c5ef3 | |||
f5b495efa8 | |||
eb4803492e |
43
README.md
43
README.md
@ -1,9 +1,10 @@
|
||||
# Better xCloud
|
||||
Improve Xbox Cloud Gaming (xCloud) experience on [xbox.com/play](https://www.xbox.com/play).
|
||||
The main target of this script is mobile users, but it should work great on desktop too.
|
||||
Improve Xbox Cloud Gaming (xCloud) experience on [xbox.com/play](https://www.xbox.com/play). It also allows you to use Remote Play on the xCloud website.
|
||||
|
||||
Supported platforms:
|
||||
- Windows, macOS, Linux
|
||||
**Supported platforms:**
|
||||
- Windows
|
||||
- macOS
|
||||
- Linux, SteamOS (Steam Deck)
|
||||
- Android, Android TV
|
||||
- iOS, iPadOS
|
||||
|
||||
@ -35,8 +36,8 @@ If you like this project please give it a 🌟. Thank you 🙏.
|
||||
- [Stable version](https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js)
|
||||
<!-- - [Dev version](https://github.com/redphx/better-xcloud/raw/main/better-xcloud.user.js)-->
|
||||
I only distribute **Better xCloud** on GitHub, *DO NOT* download it on other websites or from unknown sources.
|
||||
4. Refresh [xCloud web page](https://www.xbox.com/play/).
|
||||
5. Click on the new "SERVER NAME" button next to your profile picture to adjust settings.
|
||||
4. Refresh the [xCloud web page](https://www.xbox.com/play/).
|
||||
5. Click on the new *\<SERVER NAME\>* button next to your profile picture to adjust settings.
|
||||
|
||||
To update manually, just install the script again (you won't lose your settings).
|
||||
|
||||
@ -49,12 +50,12 @@ To update manually, just install the script again (you won't lose your settings)
|
||||
- ➖ = unavailable
|
||||
- 🗒️ = see custom notes
|
||||
|
||||
| | Windows/Linux | macOS | Android/Android TV | iOS |
|
||||
|-----------------------------------------|:-----------------|:-----------------|:-------------------|:-----------------|
|
||||
| Chrome/Edge/Chromium variants | 👍 | 👍 | ❌ | ❌ |
|
||||
| Firefox | ✅ | ✅ | 🗒️<sup>(1)</sup> | ❌ |
|
||||
| Safari | ➖ | ✅<sup>(2)</sup> | ➖ | ✅<sup>(3)</sup> |
|
||||
| [Kiwi Browser](https://kiwibrowser.com) | ➖ | ➖ | 👍 | ➖ |
|
||||
| | Windows/Linux/SteamOS | macOS | Android/Android TV | iOS |
|
||||
|-----------------------------------------|:----------------------|:-----------------|:-------------------|:-----------------|
|
||||
| Chrome/Edge/Chromium... | 👍 | 👍 | ❌ | ❌ |
|
||||
| Firefox | ✅ | ✅ | 🗒️<sup>(1)</sup> | ❌ |
|
||||
| Safari | ➖ | ✅<sup>(2)</sup> | ➖ | ✅<sup>(3)</sup> |
|
||||
| [Kiwi Browser](https://kiwibrowser.com) | ➖ | ➖ | 👍 | ➖ |
|
||||
|
||||
Don't see your browser in the table? If it supports Tampermonkey/Userscript then the answer is likely **"YES"**.
|
||||
|
||||
@ -73,8 +74,7 @@ Don't see your browser in the table? If it supports Tampermonkey/Userscript then
|
||||
<br>
|
||||
<img width="600" alt="Stream HUD" src="https://github.com/redphx/better-xcloud/assets/96280/51bdb96c-79ab-402f-902a-a9e6229973b2">
|
||||
<br>
|
||||
<img width="600" alt="Stream settings" src="https://github.com/redphx/better-xcloud/assets/96280/f7df312c-e6bc-49a1-8239-24c280ed7fa6">
|
||||
|
||||
<img width="600" alt="Stream settings" src="https://github.com/redphx/better-xcloud/assets/96280/ed513cb3-6e6c-4e8e-9e06-c62e71e41c90">
|
||||
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@ Don't see your browser in the table? If it supports Tampermonkey/Userscript then
|
||||
- **🔥 Capture screenshot**
|
||||
> Exclusive to **Better xCloud**. Check the [**Capture screenshot** section](#capture-screenshot) for more info.
|
||||
- **🔥 Hold the "Quit game" button for one second to refresh the stream**
|
||||
> Sometimes you can fix the bad connection to the stream simply by refreshing the page.
|
||||
> Sometimes you can fix the bad connection to the stream or low FPS simply by refreshing the page.
|
||||
> Useful on mobile where the pull-to-refresh feature doesn't work while playing.
|
||||
- **🔥 Touch controller**
|
||||
> Enable touch controller support for all games.
|
||||
@ -217,13 +217,12 @@ Don't see your browser in the table? If it supports Tampermonkey/Userscript then
|
||||
<sup>(\*)</sup> By default (for compatibility reasons) xCloud only uses high quality codec profile when you use Tizen TV or Chrome/Edge/Chromium browser on Chrome/MacOS. Enable this setting will give you the best experience no matter what platform & browser you're on.
|
||||
|
||||
## Stream stats
|
||||
<img alt="Stream stats" src="https://github.com/redphx/better-xcloud/assets/96280/9fb51941-85a9-47c4-8d48-331456b9ce73">
|
||||
|
||||
<img width="418" alt="Stream stats settings" src="https://github.com/redphx/better-xcloud/assets/96280/6313a0c6-03bf-4325-b60d-18a23c681933">
|
||||
|
||||

|
||||
|
||||
<img width="500" alt="Stream stats" src="https://github.com/redphx/better-xcloud/assets/96280/142625ea-20ab-4392-a111-0c5bc08bae09">
|
||||
|
||||
- While playing > `...` > `Stream Stats`.
|
||||
- Double-click on the stats bar to show the Settings dialog.
|
||||
- While playing > `...` > `Stream Stats`.
|
||||
- Change settings by opening `Stream settings` while playing.
|
||||
- This bar is updated every second.
|
||||
- **Quick glance** feature: only show the stats bar when the System menu is expanded. The 👀 emoji at the beginning indicates that the stats bar is in the quick glance mode.
|
||||
- ⚠️ Using **Better xCloud** or showing the stats bar also affects the performance of the stream.
|
||||
@ -302,7 +301,7 @@ It's a reference to an Userscript called "better360" that I created many years a
|
||||
- **Korean**: [@rightones](https://github.com/rightones)
|
||||
- **Italian**: Greenylie, Rakan129, Carza-104, graziequalcuno, DioCannabinoide
|
||||
- **Japanese**: Tak_attack, udonshi
|
||||
- **Portuguese (Brazilian)**: [@ricardo404](https://github.com/ricardo404), [@Haisom](https://github.com/Haisom), italorafael22062009, PotatoPTT, guilhermecursi
|
||||
- **Portuguese (Brazilian)**: [@ricardo404](https://github.com/ricardo404), [@Haisom](https://github.com/Haisom), italorafael22062009, PotatoPTT, guilhermecursi, renatomaster01
|
||||
- **Polish**: [@aleksishere](https://github.com/aleksishere)
|
||||
- **Russian**: anpom6, soophik
|
||||
- **Spanish**: [@PabloSebas](https://github.com/PabloSebas), csvnchzn
|
||||
|
@ -1,5 +1,5 @@
|
||||
// ==UserScript==
|
||||
// @name Better xCloud
|
||||
// @namespace https://github.com/redphx
|
||||
// @version 2.1
|
||||
// @version 2.1.2
|
||||
// ==/UserScript==
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name Better xCloud
|
||||
// @namespace https://github.com/redphx
|
||||
// @version 2.1
|
||||
// @version 2.1.2
|
||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||
// @author redphx
|
||||
// @license MIT
|
||||
@ -13,11 +13,12 @@
|
||||
// ==/UserScript==
|
||||
'use strict';
|
||||
|
||||
const SCRIPT_VERSION = '2.1';
|
||||
const SCRIPT_VERSION = '2.1.2';
|
||||
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
||||
|
||||
const ENABLE_MKB = false;
|
||||
const ENABLE_XCLOUD_LOGGER = false;
|
||||
const ENABLE_PRELOAD_BX_UI = false;
|
||||
|
||||
console.log(`[Better xCloud] readyState: ${document.readyState}`);
|
||||
|
||||
@ -305,7 +306,7 @@ const Translations = {
|
||||
"ru-RU": "Яркость",
|
||||
"tr-TR": "Aydınlık",
|
||||
"uk-UA": "Яскравість",
|
||||
"vi-VN": "Độ sáng",
|
||||
"vi-VN": "Độ sáng",
|
||||
"zh-CN": "亮度",
|
||||
},
|
||||
"browser-unsupported-feature": {
|
||||
@ -474,22 +475,12 @@ const Translations = {
|
||||
"vi-VN": "Bộ điều khiển",
|
||||
"zh-CN": "手柄",
|
||||
},
|
||||
"controller-polling-rate": {
|
||||
"de-DE": "Controller-Abfragerate",
|
||||
"en-US": "Controller polling rate",
|
||||
"es-ES": "Tasa de sondeo del Joystick",
|
||||
"ja-JP": "コントローラーポーリングレート",
|
||||
"pl-PL": "Częstotliwości raportowania kontrolera",
|
||||
"pt-BR": "Taxa de consulta do controle",
|
||||
"ru-RU": "Частота опроса контроллера",
|
||||
"tr-TR": "Oyun kumandası işlem hızı",
|
||||
"uk-UA": "Частота опитувань контролера",
|
||||
"vi-VN": "Tần suất cập nhật của bộ điều khiển",
|
||||
},
|
||||
"controller-vibration": {
|
||||
"de-DE": "Vibration des Controllers",
|
||||
"en-US": "Controller vibration",
|
||||
"ja-JP": "コントローラーの振動",
|
||||
"pt-BR": "Vibração do controle",
|
||||
"ru-RU": "Вибрация контроллера",
|
||||
"tr-TR": "Oyun kumandası titreşimi",
|
||||
"vi-VN": "Rung bộ điều khiển",
|
||||
},
|
||||
@ -545,14 +536,18 @@ const Translations = {
|
||||
"de-DE": "Vibration des Geräts",
|
||||
"en-US": "Device vibration",
|
||||
"ja-JP": "デバイスの振動",
|
||||
"pt-BR": "Vibração do dispositivo",
|
||||
"ru-RU": "Вибрация устройства",
|
||||
"tr-TR": "Cihaz titreşimi",
|
||||
"uk-UA": "Вібрація пристрою",
|
||||
"vi-VN": "Rung thiết bị",
|
||||
},
|
||||
"device-vibration-not-using-gamepad": {
|
||||
"de-DE": "Aktiviert, wenn kein Gamepad verwendet wird",
|
||||
"de-DE": "An, wenn kein Gamepad verbunden",
|
||||
"en-US": "On when not using gamepad",
|
||||
"ja-JP": "ゲームパッド未使用時にオン",
|
||||
"pt-BR": "Ativar quando não estiver usando o dispositivo",
|
||||
"ru-RU": "Включить когда не используется геймпад",
|
||||
"tr-TR": "Oyun kumandası bağlanmadan titreşim",
|
||||
"uk-UA": "Увімкнена, коли не використовується геймпад",
|
||||
"vi-VN": "Bật khi không dùng tay cầm",
|
||||
@ -1623,22 +1618,6 @@ const Translations = {
|
||||
"vi-VN": "Stream",
|
||||
"zh-CN": "串流",
|
||||
},
|
||||
"stream-stats-settings": {
|
||||
"de-DE": "Stream Statistik Einstellungen",
|
||||
"en-US": "Stream stats settings",
|
||||
"es-ES": "Ajustes de estadísticas de stream",
|
||||
"fr-FR": "Paramètres des statistiques du stream",
|
||||
"it-IT": "Impostazioni statistiche dello streaming",
|
||||
"ja-JP": "ストリーミング統計の設定",
|
||||
"ko-KR": "스트리밍 통계 설정",
|
||||
"pl-PL": "Ustawienia statystyk strumienia",
|
||||
"pt-BR": "Ajustes de estatísticas",
|
||||
"ru-RU": "Настройки потоковой передачи",
|
||||
"tr-TR": "Yayın durumu ayarları",
|
||||
"uk-UA": "Налаштування статистики трансляції",
|
||||
"vi-VN": "Cấu hình thông số của stream",
|
||||
"zh-CN": "串流统计信息设置",
|
||||
},
|
||||
"stretch": {
|
||||
"de-DE": "Strecken",
|
||||
"en-US": "Stretch",
|
||||
@ -1660,6 +1639,7 @@ const Translations = {
|
||||
"en-US": "Swap buttons",
|
||||
"ja-JP": "ボタン入れ替え",
|
||||
"pt-BR": "Trocar botões",
|
||||
"ru-RU": "Поменять кнопки",
|
||||
"tr-TR": "Düğme düzenini ters çevir",
|
||||
"uk-UA": "Поміняти кнопки місцями",
|
||||
"vi-VN": "Hoán đổi nút",
|
||||
@ -1961,6 +1941,8 @@ const Translations = {
|
||||
"de-DE": "Vibrationsstärke",
|
||||
"en-US": "Vibration intensity",
|
||||
"ja-JP": "振動の強さ",
|
||||
"pt-BR": "Intensidade da vibração",
|
||||
"ru-RU": "Сила вибрации",
|
||||
"tr-TR": "Titreşim gücü",
|
||||
"vi-VN": "Cường độ rung",
|
||||
},
|
||||
@ -3224,7 +3206,7 @@ class StreamBadges {
|
||||
let totalIn = 0;
|
||||
let totalOut = 0;
|
||||
stats.forEach(stat => {
|
||||
if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
|
||||
if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') {
|
||||
totalIn += stat.bytesReceived;
|
||||
totalOut += stat.bytesSent;
|
||||
}
|
||||
@ -3365,8 +3347,6 @@ class StreamStats {
|
||||
static #$fl;
|
||||
static #$br;
|
||||
|
||||
static #$dialog;
|
||||
|
||||
static #lastStat;
|
||||
|
||||
static #quickGlanceObserver;
|
||||
@ -3391,8 +3371,10 @@ class StreamStats {
|
||||
StreamStats.#interval = null;
|
||||
StreamStats.#lastStat = null;
|
||||
|
||||
StreamStats.#$container.removeAttribute('data-display');
|
||||
StreamStats.#$container.classList.add('bx-gone');
|
||||
if (StreamStats.#$container) {
|
||||
StreamStats.#$container.removeAttribute('data-display');
|
||||
StreamStats.#$container.classList.add('bx-gone');
|
||||
}
|
||||
}
|
||||
|
||||
static toggle() {
|
||||
@ -3409,8 +3391,8 @@ class StreamStats {
|
||||
StreamStats.hideSettingsUi();
|
||||
}
|
||||
|
||||
static isHidden = () => StreamStats.#$container.classList.contains('bx-gone');
|
||||
static isGlancing = () => StreamStats.#$container.getAttribute('data-display') === 'glancing';
|
||||
static isHidden = () => StreamStats.#$container && StreamStats.#$container.classList.contains('bx-gone');
|
||||
static isGlancing = () => StreamStats.#$container && StreamStats.#$container.getAttribute('data-display') === 'glancing';
|
||||
|
||||
static quickGlanceSetup() {
|
||||
if (StreamStats.#quickGlanceObserver) {
|
||||
@ -3489,7 +3471,7 @@ class StreamStats {
|
||||
}
|
||||
|
||||
StreamStats.#lastStat = stat;
|
||||
} else if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
|
||||
} else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') {
|
||||
// Round Trip Time
|
||||
const roundTripTime = typeof stat.currentRoundTripTime !== 'undefined' ? stat.currentRoundTripTime * 1000 : '???';
|
||||
StreamStats.#$ping.textContent = roundTripTime;
|
||||
@ -3503,18 +3485,19 @@ class StreamStats {
|
||||
});
|
||||
}
|
||||
|
||||
static #refreshStyles() {
|
||||
static refreshStyles() {
|
||||
const PREF_ITEMS = PREFS.get(Preferences.STATS_ITEMS);
|
||||
const PREF_POSITION = PREFS.get(Preferences.STATS_POSITION);
|
||||
const PREF_TRANSPARENT = PREFS.get(Preferences.STATS_TRANSPARENT);
|
||||
const PREF_OPACITY = PREFS.get(Preferences.STATS_OPACITY);
|
||||
const PREF_TEXT_SIZE = PREFS.get(Preferences.STATS_TEXT_SIZE);
|
||||
|
||||
StreamStats.#$container.setAttribute('data-stats', '[' + PREF_ITEMS.join('][') + ']');
|
||||
StreamStats.#$container.setAttribute('data-position', PREF_POSITION);
|
||||
StreamStats.#$container.setAttribute('data-transparent', PREF_TRANSPARENT);
|
||||
StreamStats.#$container.style.opacity = PREF_OPACITY + '%';
|
||||
StreamStats.#$container.style.fontSize = PREF_TEXT_SIZE;
|
||||
const $container = StreamStats.#$container;
|
||||
$container.setAttribute('data-stats', '[' + PREF_ITEMS.join('][') + ']');
|
||||
$container.setAttribute('data-position', PREF_POSITION);
|
||||
$container.setAttribute('data-transparent', PREF_TRANSPARENT);
|
||||
$container.style.opacity = PREF_OPACITY + '%';
|
||||
$container.style.fontSize = PREF_TEXT_SIZE;
|
||||
}
|
||||
|
||||
static hideSettingsUi() {
|
||||
@ -3523,10 +3506,6 @@ class StreamStats {
|
||||
}
|
||||
}
|
||||
|
||||
static #toggleSettingsUi() {
|
||||
StreamStats.#$dialog.toggle();
|
||||
}
|
||||
|
||||
static render() {
|
||||
if (StreamStats.#$container) {
|
||||
return;
|
||||
@ -3549,79 +3528,9 @@ class StreamStats {
|
||||
}
|
||||
|
||||
StreamStats.#$container = CE('div', {'class': 'bx-stats-bar bx-gone'}, $barFragment);
|
||||
|
||||
let clickTimeout;
|
||||
StreamStats.#$container.addEventListener('mousedown', e => {
|
||||
clearTimeout(clickTimeout);
|
||||
if (clickTimeout) {
|
||||
// Double-clicked
|
||||
clickTimeout = null;
|
||||
StreamStats.#toggleSettingsUi();
|
||||
return;
|
||||
}
|
||||
|
||||
clickTimeout = setTimeout(() => {
|
||||
clickTimeout = null;
|
||||
}, 400);
|
||||
});
|
||||
|
||||
document.documentElement.appendChild(StreamStats.#$container);
|
||||
|
||||
const refreshFunc = e => {
|
||||
StreamStats.#refreshStyles()
|
||||
};
|
||||
|
||||
let $close;
|
||||
|
||||
|
||||
const STATS_UI = {
|
||||
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
|
||||
'label': __('show-stats-on-startup'),
|
||||
},
|
||||
[Preferences.STATS_QUICK_GLANCE]: {
|
||||
'label': __('enable-quick-glance-mode'),
|
||||
'onChange': e => {
|
||||
e.target.checked ? StreamStats.quickGlanceSetup() : StreamStats.quickGlanceStop();
|
||||
},
|
||||
},
|
||||
[Preferences.STATS_ITEMS]: {
|
||||
'label': __('stats'),
|
||||
'onChange': refreshFunc,
|
||||
},
|
||||
[Preferences.STATS_POSITION]: {
|
||||
'label': __('position'),
|
||||
'onChange': refreshFunc,
|
||||
},
|
||||
[Preferences.STATS_TEXT_SIZE]: {
|
||||
'label': __('text-size'),
|
||||
'onChange': refreshFunc,
|
||||
},
|
||||
[Preferences.STATS_OPACITY]: {
|
||||
'label': `${__('opacity')} (50-100%)`,
|
||||
'onChange': refreshFunc,
|
||||
},
|
||||
[Preferences.STATS_TRANSPARENT]: {
|
||||
'label': __('transparent-background'),
|
||||
'onChange': refreshFunc,
|
||||
},
|
||||
[Preferences.STATS_CONDITIONAL_FORMATTING]: {
|
||||
'label': __('conditional-formatting'),
|
||||
'onChange': refreshFunc,
|
||||
},
|
||||
};
|
||||
|
||||
const $fragment = document.createDocumentFragment();
|
||||
for (let settingKey in STATS_UI) {
|
||||
const setting = STATS_UI[settingKey];
|
||||
|
||||
$fragment.appendChild(CE('div', {},
|
||||
CE('label', {'for': `xcloud_setting_${settingKey}`}, setting.label),
|
||||
PREFS.toElement(settingKey, setting.onChange)
|
||||
));
|
||||
}
|
||||
|
||||
StreamStats.#$dialog = new Dialog(__('stream-stats-settings'), 'bx-stats-settings-dialog', $fragment, StreamStats.hideSettingsUi);
|
||||
StreamStats.#refreshStyles();
|
||||
StreamStats.refreshStyles();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4278,7 +4187,7 @@ class Preferences {
|
||||
|
||||
let $control;
|
||||
if ('options' in setting) {
|
||||
$control = CE('select', {'id': 'xcloud_setting_' + key});
|
||||
$control = CE('select', {'id': `bx_setting_${key}`});
|
||||
for (let value in setting.options) {
|
||||
const label = setting.options[value];
|
||||
|
||||
@ -4293,7 +4202,7 @@ class Preferences {
|
||||
onChange && onChange(e);
|
||||
});
|
||||
} else if ('multiple_options' in setting) {
|
||||
$control = CE('select', {'id': 'xcloud_setting_' + key, 'multiple': true});
|
||||
$control = CE('select', {'id': `bx_setting_${key}`, 'multiple': true});
|
||||
for (let value in setting.multiple_options) {
|
||||
const label = setting.multiple_options[value];
|
||||
|
||||
@ -4348,7 +4257,7 @@ class Preferences {
|
||||
});
|
||||
}
|
||||
|
||||
$control.id = `xcloud_setting_${key}`;
|
||||
$control.id = `bx_setting_${key}`;
|
||||
return $control;
|
||||
}
|
||||
|
||||
@ -4877,11 +4786,11 @@ function addCss() {
|
||||
--bx-monospaced-font: Consolas, "Courier New", Courier, monospace;
|
||||
|
||||
--bx-wait-time-box-z-index: 9999;
|
||||
--bx-stream-settings-z-index: 9999;
|
||||
--bx-stats-bar-z-index: 9001;
|
||||
--bx-stream-settings-z-index: 9000;
|
||||
--bx-screenshot-z-index: 8888;
|
||||
--bx-touch-controller-bar-z-index: 5555;
|
||||
--bx-dialog-z-index: 1010;
|
||||
--bx-stats-bar-z-index: 1000;
|
||||
--bx-dialog-overlay-z-index: 900;
|
||||
}
|
||||
|
||||
@ -5345,20 +5254,19 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
||||
-webkit-user-select: none;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 20px;
|
||||
bottom: 20px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: var(--bx-stream-settings-z-index);
|
||||
padding: 8px;
|
||||
width: 320px;
|
||||
padding: 16px;
|
||||
width: 420px;
|
||||
background: #1a1b1e;
|
||||
color: #fff;
|
||||
border-radius: 8px 0 0 8px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
font-family: var(--bx-title-font);
|
||||
text-align: center;
|
||||
box-shadow: 0px 0px 6px #000;
|
||||
opacity: 0.95;
|
||||
opacity: 0.98;
|
||||
overflow: overlay;
|
||||
}
|
||||
|
||||
@ -5385,27 +5293,34 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
||||
}
|
||||
|
||||
.bx-quick-settings-bar > div {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #40404080;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.bx-quick-settings-bar h2 {
|
||||
font-size: 32px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.bx-quick-settings-bar input[type="range"] {
|
||||
display: block;
|
||||
margin: 12px auto;
|
||||
width: 80%;
|
||||
margin: 12px auto 2px;
|
||||
width: 180px;
|
||||
color: #959595 !important;
|
||||
}
|
||||
|
||||
.bx-quick-settings-bar label {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.bx-quick-settings-bar button {
|
||||
@ -5423,9 +5338,12 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
||||
}
|
||||
|
||||
.bx-quick-settings-bar-note {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: lighter;
|
||||
font-style: italic;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.bx-toast {
|
||||
@ -5774,7 +5692,7 @@ function getPreferredServerRegion() {
|
||||
}
|
||||
|
||||
|
||||
function updateIceCandidates(candidates) {
|
||||
function updateIceCandidates(candidates, options) {
|
||||
const pattern = new RegExp(/a=candidate:(?<foundation>\d+) (?<component>\d+) UDP (?<priority>\d+) (?<ip>[^\s]+) (?<the_rest>.*)/);
|
||||
|
||||
const lst = [];
|
||||
@ -5787,13 +5705,15 @@ function updateIceCandidates(candidates) {
|
||||
lst.push(groups);
|
||||
}
|
||||
|
||||
lst.sort((a, b) => (a.ip.includes(':') || a.ip > b.ip) ? -1 : 1);
|
||||
if (options.preferIpv6Server) {
|
||||
lst.sort((a, b) => (!a.ip.includes(':') && b.ip.includes(':')) ? 1 : -1);
|
||||
}
|
||||
|
||||
const newCandidates = [];
|
||||
let foundation = 1;
|
||||
lst.forEach(item => {
|
||||
item.foundation = foundation;
|
||||
item.priority = (foundation == 1) ? 100 : 1;
|
||||
item.priority = (foundation == 1) ? 10000 : 1;
|
||||
|
||||
newCandidates.push({
|
||||
'candidate': `a=candidate:${item.foundation} 1 UDP ${item.priority} ${item.ip} ${item.the_rest}`,
|
||||
@ -5805,6 +5725,15 @@ function updateIceCandidates(candidates) {
|
||||
++foundation;
|
||||
});
|
||||
|
||||
if (options.consoleIp) {
|
||||
newCandidates.push({
|
||||
'candidate': `a=candidate:${newCandidates.length + 1} 1 UDP 1 ${options.consoleIp} 9002 typ host`,
|
||||
'messageType': 'iceCandidate',
|
||||
'sdpMLineIndex': '0',
|
||||
'sdpMid': '0',
|
||||
});
|
||||
}
|
||||
|
||||
newCandidates.push({
|
||||
'candidate': 'a=end-of-candidates',
|
||||
'messageType': 'iceCandidate',
|
||||
@ -5812,6 +5741,7 @@ function updateIceCandidates(candidates) {
|
||||
'sdpMid': '0',
|
||||
});
|
||||
|
||||
console.log(newCandidates);
|
||||
return newCandidates;
|
||||
}
|
||||
|
||||
@ -5837,11 +5767,6 @@ function interceptHttpRequests() {
|
||||
}
|
||||
|
||||
if (PREFS.get(Preferences.BLOCK_SOCIAL_FEATURES)) {
|
||||
// Disable WebSocket
|
||||
WebSocket = {
|
||||
CLOSING: 2,
|
||||
};
|
||||
|
||||
BLOCKED_URLS = BLOCKED_URLS.concat([
|
||||
'https://peoplehub.xboxlive.com/users/me',
|
||||
'https://accounts.xboxlive.com/family/memberXuid',
|
||||
@ -5883,13 +5808,15 @@ function interceptHttpRequests() {
|
||||
const PREF_OVERRIDE_CONFIGURATION = PREF_AUDIO_MIC_ON_PLAYING || PREF_STREAM_TOUCH_CONTROLLER === 'all';
|
||||
|
||||
const orgFetch = window.fetch;
|
||||
let consoleIp;
|
||||
let consolePort;
|
||||
|
||||
const patchIpv6 = function(...arg) {
|
||||
const patchIceCandidates = function(...arg) {
|
||||
// ICE server candidates
|
||||
const request = arg[0];
|
||||
const url = (typeof request === 'string') ? request : request.url;
|
||||
|
||||
if (PREF_PREFER_IPV6_SERVER && url && url.endsWith('/ice') && url.includes('/sessions/') && request.method === 'GET') {
|
||||
if (url && url.endsWith('/ice') && url.includes('/sessions/') && request.method === 'GET') {
|
||||
const promise = orgFetch(...arg);
|
||||
|
||||
return promise.then(response => {
|
||||
@ -5898,9 +5825,14 @@ function interceptHttpRequests() {
|
||||
return response;
|
||||
}
|
||||
|
||||
const options = {
|
||||
preferIpv6Server: PREF_PREFER_IPV6_SERVER,
|
||||
consoleIp: consoleIp,
|
||||
};
|
||||
|
||||
const obj = JSON.parse(text);
|
||||
let exchangeResponse = JSON.parse(obj.exchangeResponse);
|
||||
exchangeResponse = updateIceCandidates(exchangeResponse)
|
||||
exchangeResponse = updateIceCandidates(exchangeResponse, options)
|
||||
obj.exchangeResponse = JSON.stringify(exchangeResponse);
|
||||
|
||||
response.json = () => Promise.resolve(obj);
|
||||
@ -5916,19 +5848,21 @@ function interceptHttpRequests() {
|
||||
|
||||
window.fetch = async (...arg) => {
|
||||
let request = arg[0];
|
||||
const url = (typeof request === 'string') ? request : request.url;
|
||||
let url = (typeof request === 'string') ? request : request.url;
|
||||
|
||||
// Remote Play
|
||||
if (IS_REMOTE_PLAYING && url.includes('/home/play')) {
|
||||
if (url.endsWith('/play')) {
|
||||
// Setup UI
|
||||
setupBxUi();
|
||||
}
|
||||
|
||||
if (IS_REMOTE_PLAYING && url.includes('/sessions/home')) {
|
||||
const clone = request.clone();
|
||||
const cloneBody = await clone.json();
|
||||
cloneBody.settings.osName = 'windows';
|
||||
|
||||
// Clone headers
|
||||
const headers = {};
|
||||
for (const pair of clone.headers.entries()) {
|
||||
headers[pair[0]] = pair[1];
|
||||
}
|
||||
headers['authorization'] = `Bearer ${RemotePlay.XHOME_TOKEN}`;
|
||||
|
||||
const deviceInfo = RemotePlay.BASE_DEVICE_INFO;
|
||||
if (PREFS.get(Preferences.REMOTE_PLAY_RESOLUTION) === '720p') {
|
||||
@ -5936,16 +5870,41 @@ function interceptHttpRequests() {
|
||||
}
|
||||
|
||||
headers['x-ms-device-info'] = JSON.stringify(deviceInfo);
|
||||
headers['authorization'] = `Bearer ${RemotePlay.XHOME_TOKEN}`;
|
||||
|
||||
request = new Request('https://wus2.gssv-play-prodxhome.xboxlive.com/v5/sessions/home/play', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(cloneBody),
|
||||
const opts = {
|
||||
method: clone.method,
|
||||
headers: headers,
|
||||
});
|
||||
arg[0] = request;
|
||||
};
|
||||
|
||||
return orgFetch(...arg);
|
||||
if (clone.method === 'POST') {
|
||||
opts.body = await clone.text();
|
||||
}
|
||||
|
||||
const index = request.url.indexOf('.xboxlive.com');
|
||||
let newUrl = 'https://wus2.gssv-play-prodxhome' + request.url.substring(index);
|
||||
|
||||
request = new Request(newUrl, opts);
|
||||
|
||||
arg[0] = request;
|
||||
url = (typeof request === 'string') ? request : request.url;
|
||||
|
||||
// Get console IP
|
||||
if (url.includes('/configuration')) {
|
||||
const promise = orgFetch(...arg);
|
||||
|
||||
return promise.then(response => {
|
||||
return response.clone().json().then(obj => {
|
||||
console.log(obj);
|
||||
consoleIp = obj.serverDetails.ipAddress;
|
||||
consolePort = obj.serverDetails.port;
|
||||
|
||||
response.json = () => Promise.resolve(obj);
|
||||
return response;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return patchIceCandidates(...arg) || orgFetch(...arg);
|
||||
}
|
||||
|
||||
if (IS_REMOTE_PLAYING && url.includes('/login/user')) {
|
||||
@ -5992,38 +5951,10 @@ function interceptHttpRequests() {
|
||||
return orgFetch(...arg);
|
||||
}
|
||||
|
||||
if (url.includes('/sessions/home')) {
|
||||
const clone = request.clone();
|
||||
|
||||
const headers = {};
|
||||
for (const pair of clone.headers.entries()) {
|
||||
headers[pair[0]] = pair[1];
|
||||
}
|
||||
headers['authorization'] = `Bearer ${RemotePlay.XHOME_TOKEN}`;
|
||||
|
||||
const opts = {
|
||||
method: clone.method,
|
||||
headers: headers,
|
||||
};
|
||||
|
||||
if (clone.method === 'POST') {
|
||||
opts.body = await clone.text();
|
||||
}
|
||||
|
||||
const index = request.url.indexOf('.xboxlive.com');
|
||||
request = new Request('https://wus2.gssv-play-prodxhome' + request.url.substring(index), opts);
|
||||
|
||||
arg[0] = request;
|
||||
|
||||
return patchIpv6(...arg) || orgFetch(...arg);
|
||||
}
|
||||
|
||||
// ICE server candidates
|
||||
if (!IS_REMOTE_PLAYING) {
|
||||
const patchedIpv6 = patchIpv6(...arg);
|
||||
if (patchedIpv6) {
|
||||
return patchedIpv6;
|
||||
}
|
||||
const patchedIpv6 = patchIceCandidates(...arg);
|
||||
if (patchedIpv6) {
|
||||
return patchedIpv6;
|
||||
}
|
||||
|
||||
// Server list
|
||||
@ -6406,7 +6337,7 @@ function injectSettingsButton($parent) {
|
||||
} else if (settingId === Preferences.SERVER_REGION) {
|
||||
let selectedValue;
|
||||
|
||||
$control = CE('select', {id: 'xcloud_setting_' + settingId});
|
||||
$control = CE('select', {id: `bx_setting_${settingId}`});
|
||||
$control.addEventListener('change', e => {
|
||||
PREFS.set(settingId, e.target.value);
|
||||
});
|
||||
@ -6849,65 +6780,178 @@ function patchRtcCodecs() {
|
||||
}
|
||||
|
||||
|
||||
function setupVideoSettingsBar() {
|
||||
function setupQuickSettingsBar() {
|
||||
const CE = createElement;
|
||||
const isSafari = UserAgent.isSafari();
|
||||
const onVideoChange = e => {
|
||||
updateVideoPlayerCss();
|
||||
|
||||
const SETTINGS_UI = [
|
||||
{
|
||||
group: 'controller',
|
||||
label: __('controller'),
|
||||
items: {
|
||||
[Preferences.CONTROLLER_ENABLE_VIBRATION]: {
|
||||
label: __('controller-vibration'),
|
||||
unsupported: !VibrationManager.supportControllerVibration(),
|
||||
onChange: VibrationManager.updateGlobalVars,
|
||||
},
|
||||
|
||||
[Preferences.CONTROLLER_DEVICE_VIBRATION]: {
|
||||
label: __('device-vibration'),
|
||||
unsupported: !VibrationManager.supportDeviceVibration(),
|
||||
onChange: VibrationManager.updateGlobalVars,
|
||||
},
|
||||
|
||||
[Preferences.CONTROLLER_VIBRATION_INTENSITY]: (VibrationManager.supportControllerVibration() || VibrationManager.supportDeviceVibration()) && {
|
||||
label: __('vibration-intensity'),
|
||||
unsupported: !VibrationManager.supportDeviceVibration(),
|
||||
onChange: VibrationManager.updateGlobalVars,
|
||||
type: 'number-stepper',
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
group: 'audio',
|
||||
label: __('audio'),
|
||||
items: {
|
||||
[Preferences.AUDIO_VOLUME]: {
|
||||
label: __('volume'),
|
||||
onChange: (e, value) => {
|
||||
STREAM_AUDIO_GAIN_NODE && (STREAM_AUDIO_GAIN_NODE.gain.value = (value / 100).toFixed(2));
|
||||
},
|
||||
type: 'number-stepper',
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 100,
|
||||
disabled: !PREFS.get(Preferences.AUDIO_ENABLE_VOLUME_CONTROL),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
group: 'video',
|
||||
label: __('video'),
|
||||
note: CE('div', {'class': 'bx-quick-settings-bar-note bx-clarity-boost-warning'}, `⚠️ ${__('clarity-boost-warning')}`),
|
||||
items: {
|
||||
[Preferences.VIDEO_RATIO]: {
|
||||
label: __('ratio'),
|
||||
onChange: updateVideoPlayerCss,
|
||||
},
|
||||
|
||||
[Preferences.VIDEO_CLARITY]: {
|
||||
label: __('clarity'),
|
||||
onChange: updateVideoPlayerCss,
|
||||
type: 'number-stepper',
|
||||
unsupported: isSafari,
|
||||
params: {
|
||||
hideSlider: true,
|
||||
},
|
||||
},
|
||||
|
||||
[Preferences.VIDEO_SATURATION]: {
|
||||
label: __('saturation'),
|
||||
onChange: updateVideoPlayerCss,
|
||||
type: 'number-stepper',
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
|
||||
[Preferences.VIDEO_CONTRAST]: {
|
||||
label: __('contrast'),
|
||||
onChange: updateVideoPlayerCss,
|
||||
type: 'number-stepper',
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
|
||||
[Preferences.VIDEO_BRIGHTNESS]: {
|
||||
label: __('brightness'),
|
||||
onChange: updateVideoPlayerCss,
|
||||
type: 'number-stepper',
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
group: 'stats',
|
||||
label: __('menu-stream-stats'),
|
||||
items: {
|
||||
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
|
||||
label: __('show-stats-on-startup'),
|
||||
},
|
||||
[Preferences.STATS_QUICK_GLANCE]: {
|
||||
label: __('enable-quick-glance-mode'),
|
||||
onChange: e => {
|
||||
e.target.checked ? StreamStats.quickGlanceSetup() : StreamStats.quickGlanceStop();
|
||||
},
|
||||
},
|
||||
[Preferences.STATS_ITEMS]: {
|
||||
label: __('stats'),
|
||||
onChange: StreamStats.refreshStyles,
|
||||
},
|
||||
[Preferences.STATS_POSITION]: {
|
||||
label: __('position'),
|
||||
onChange: StreamStats.refreshStyles,
|
||||
},
|
||||
[Preferences.STATS_TEXT_SIZE]: {
|
||||
label: __('text-size'),
|
||||
onChange: StreamStats.refreshStyles,
|
||||
},
|
||||
[Preferences.STATS_OPACITY]: {
|
||||
label: `${__('opacity')} (50-100%)`,
|
||||
onChange: StreamStats.refreshStyles,
|
||||
},
|
||||
[Preferences.STATS_TRANSPARENT]: {
|
||||
label: __('transparent-background'),
|
||||
onChange: StreamStats.refreshStyles,
|
||||
},
|
||||
[Preferences.STATS_CONDITIONAL_FORMATTING]: {
|
||||
label: __('conditional-formatting'),
|
||||
onChange: StreamStats.refreshStyles,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const $wrapper = CE('div', {'class': 'bx-quick-settings-bar'});
|
||||
for (const settingGroup of SETTINGS_UI) {
|
||||
$wrapper.appendChild(CE('h2', {}, settingGroup.label));
|
||||
if (settingGroup.note) {
|
||||
if (typeof settingGroup.note === 'string') {
|
||||
settingGroup.note = document.createTextNode(settingGroup.note);
|
||||
}
|
||||
$wrapper.appendChild(settingGroup.note);
|
||||
}
|
||||
|
||||
for (const pref in settingGroup.items) {
|
||||
const setting = settingGroup.items[pref];
|
||||
if (!setting) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wrapper.appendChild(CE('div', {'data-type': settingGroup.group},
|
||||
CE('label', {for: `bx_setting_${pref}`},
|
||||
setting.label,
|
||||
setting.unsupported && CE('div', {'class': 'bx-quick-settings-bar-note'}, __('browser-unsupported-feature')),
|
||||
),
|
||||
!setting.unsupported && (setting.type === 'number-stepper' ? PREFS.toNumberStepper(pref, setting.onChange, setting.params) : PREFS.toElement(pref, setting.onChange)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let $stretchInp;
|
||||
const $wrapper = CE('div', {'class': 'bx-quick-settings-bar'},
|
||||
CE('h2', {}, __('controller')),
|
||||
CE('div', {},
|
||||
CE('label', {}, __('controller-vibration')),
|
||||
VibrationManager.supportControllerVibration() && PREFS.toElement(Preferences.CONTROLLER_ENABLE_VIBRATION, VibrationManager.updateGlobalVars),
|
||||
!VibrationManager.supportControllerVibration() && CE('div', {'class': 'bx-quick-settings-bar-note'}, __('browser-unsupported-feature')),
|
||||
),
|
||||
CE('div', {},
|
||||
CE('label', {}, __('device-vibration')),
|
||||
VibrationManager.supportDeviceVibration() && PREFS.toElement(Preferences.CONTROLLER_DEVICE_VIBRATION, VibrationManager.updateGlobalVars),
|
||||
!VibrationManager.supportDeviceVibration() && CE('div', {'class': 'bx-quick-settings-bar-note'}, __('browser-unsupported-feature')),
|
||||
),
|
||||
|
||||
(VibrationManager.supportControllerVibration() || VibrationManager.supportDeviceVibration()) &&
|
||||
CE('div', {},
|
||||
CE('label', {}, __('vibration-intensity')),
|
||||
PREFS.toNumberStepper(Preferences.CONTROLLER_VIBRATION_INTENSITY, VibrationManager.updateGlobalVars, {suffix: '%', ticks: 50}),
|
||||
),
|
||||
|
||||
CE('h2', {}, __('audio')),
|
||||
CE('div', {},
|
||||
CE('label', {}, __('volume')),
|
||||
PREFS.toNumberStepper(Preferences.AUDIO_VOLUME, (e, value) => {
|
||||
STREAM_AUDIO_GAIN_NODE && (STREAM_AUDIO_GAIN_NODE.gain.value = (value / 100).toFixed(2));
|
||||
}, {suffix: '%', ticks: 100, disabled: !PREFS.get(Preferences.AUDIO_ENABLE_VOLUME_CONTROL)}),
|
||||
),
|
||||
|
||||
CE('h2', {}, __('video')),
|
||||
CE('div', {'class': 'bx-quick-settings-bar-note bx-clarity-boost-warning'}, `⚠️ ${__('clarity-boost-warning')}`),
|
||||
CE('div', {'data-type': 'video'},
|
||||
CE('label', {}, __('ratio')),
|
||||
PREFS.toElement(Preferences.VIDEO_RATIO, onVideoChange),
|
||||
),
|
||||
CE('div', {'data-type': 'video'},
|
||||
CE('label', {}, __('clarity')),
|
||||
PREFS.toNumberStepper(Preferences.VIDEO_CLARITY, onVideoChange, {disabled: isSafari, hideSlider: true}), // disable this feature in Safari
|
||||
),
|
||||
CE('div', {'data-type': 'video'},
|
||||
CE('label', {}, __('saturation')),
|
||||
PREFS.toNumberStepper(Preferences.VIDEO_SATURATION, onVideoChange, {suffix: '%', ticks: 25}),
|
||||
),
|
||||
CE('div', {'data-type': 'video'},
|
||||
CE('label', {}, __('contrast')),
|
||||
PREFS.toNumberStepper(Preferences.VIDEO_CONTRAST, onVideoChange, {suffix: '%', ticks: 25}),
|
||||
),
|
||||
CE('div', {'data-type': 'video'},
|
||||
CE('label', {}, __('brightness')),
|
||||
PREFS.toNumberStepper(Preferences.VIDEO_BRIGHTNESS, onVideoChange, {suffix: '%', ticks: 25}),
|
||||
),
|
||||
);
|
||||
|
||||
document.documentElement.appendChild($wrapper);
|
||||
}
|
||||
|
||||
@ -7000,7 +7044,7 @@ function patchHistoryMethod(type) {
|
||||
|
||||
|
||||
function onHistoryChanged(e) {
|
||||
if (e.arguments[0] && e.arguments[0].origin === 'better-xcloud') {
|
||||
if (e.arguments && e.arguments[0] && e.arguments[0].origin === 'better-xcloud') {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7020,7 +7064,11 @@ function onHistoryChanged(e) {
|
||||
STREAM_AUDIO_GAIN_NODE = null;
|
||||
$STREAM_VIDEO = null;
|
||||
StreamStats.onStoppedPlaying();
|
||||
document.querySelector('.bx-screenshot-button').style = '';
|
||||
|
||||
const $screenshotBtn = document.querySelector('.bx-screenshot-button');
|
||||
if ($screenshotBtn) {
|
||||
$screenshotBtn.style = '';
|
||||
}
|
||||
|
||||
MouseCursorHider.stop();
|
||||
TouchController.reset();
|
||||
@ -7102,7 +7150,7 @@ function onStreamStarted($video) {
|
||||
} else if (stat.kind === 'audio') {
|
||||
audioCodecId = stat.codecId;
|
||||
}
|
||||
} else if (stat.type === 'candidate-pair' && stat.state === 'succeeded') {
|
||||
} else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') {
|
||||
candidateId = stat.remoteCandidateId;
|
||||
} else if (stat.type === 'remote-candidate') {
|
||||
allCandidates[stat.id] = stat.address;
|
||||
@ -7173,6 +7221,21 @@ function disablePwa() {
|
||||
}
|
||||
}
|
||||
|
||||
function setupBxUi() {
|
||||
updateVideoPlayerCss();
|
||||
|
||||
// Prevent initializing multiple times
|
||||
if (document.querySelector('.bx-quick-settings-bar')) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('resize', updateVideoPlayerCss);
|
||||
|
||||
setupQuickSettingsBar();
|
||||
setupScreenshotButton();
|
||||
StreamStats.render();
|
||||
}
|
||||
|
||||
|
||||
// Hide Settings UI when navigate to another page
|
||||
window.addEventListener('xcloud_popstate', onHistoryChanged);
|
||||
@ -7255,13 +7318,7 @@ patchVideoApi();
|
||||
|
||||
// Setup UI
|
||||
addCss();
|
||||
updateVideoPlayerCss();
|
||||
window.addEventListener('resize', updateVideoPlayerCss);
|
||||
Toast.setup();
|
||||
|
||||
setupVideoSettingsBar();
|
||||
setupScreenshotButton();
|
||||
StreamStats.render();
|
||||
ENABLE_PRELOAD_BX_UI && setupBxUi();
|
||||
|
||||
disablePwa();
|
||||
|
||||
|
Reference in New Issue
Block a user