@@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 3.0
// @version 3.0.1
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@@ -13,7 +13,7 @@
// ==/UserScript==
'use strict' ;
const SCRIPT _VERSION = '3.0' ;
const SCRIPT _VERSION = '3.0.1 ' ;
const SCRIPT _HOME = 'https://github.com/redphx/better-xcloud' ;
const ENABLE _XCLOUD _LOGGER = false ;
@@ -158,19 +158,27 @@ const Translations = {
"activate" : {
"de-DE" : "Aktivieren" ,
"en-US" : "Activate" ,
"ja-JP " : "有効にする " ,
"es-ES " : "Activo " ,
"ja-JP" : "設定する" ,
"ko-KR" : "활성화" ,
"pl-PL" : "Aktywuj" ,
"pt-BR" : "Ativar" ,
"ru-RU" : "Активировать" ,
"tr-TR" : "Etkinleştir" ,
"uk-UA" : "Активувати" ,
"vi-VN" : "Kích hoạt" ,
} ,
"activated" : {
"de-DE" : "Aktiviert" ,
"en-US" : "Activated" ,
"ja-JP " : "有効化済み " ,
"es-ES " : "Activado " ,
"ja-JP" : "設定中" ,
"ko-KR" : "활성화 됨" ,
"pl-PL" : "Aktywowane" ,
"pt-BR" : "Ativado" ,
"ru-RU" : "Активирован" ,
"tr-TR" : "Etkin" ,
"uk-UA" : "Активований" ,
"vi-VN" : "Đã kích hoạt" ,
} ,
"active" : {
@@ -180,6 +188,8 @@ const Translations = {
"ja-JP" : "有効" ,
"ko-KR" : "활성화" ,
"pl-PL" : "Aktywny" ,
"pt-BR" : "Ativo" ,
"ru-RU" : "Активный" ,
"tr-TR" : "Etkin" ,
"uk-UA" : "Активний" ,
"vi-VN" : "Hoạt động" ,
@@ -204,8 +214,12 @@ const Translations = {
"apply" : {
"de-DE" : "Anwenden" ,
"en-US" : "Apply" ,
"es-ES" : "Aplicar" ,
"ja-JP" : "適用" ,
"pt-BR" : "Aplicar" ,
"ru-RU" : "Применить" ,
"tr-TR" : "Uygula" ,
"uk-UA" : "Застосувати" ,
"vi-VN" : "Áp dụng" ,
} ,
"audio" : {
@@ -438,6 +452,8 @@ const Translations = {
"ja-JP" : "キャンセル" ,
"ko-KR" : "취소" ,
"pl-PL" : "Anuluj" ,
"pt-BR" : "Cancelar" ,
"ru-RU" : "Отмена" ,
"tr-TR" : "Vazgeç" ,
"uk-UA" : "Скасувати" ,
"vi-VN" : "Hủy" ,
@@ -497,6 +513,8 @@ const Translations = {
"ja-JP" : "消去" ,
"ko-KR" : "비우기" ,
"pl-PL" : "Wyczyść" ,
"pt-BR" : "Limpar" ,
"ru-RU" : "Очистить" ,
"tr-TR" : "Temizle" ,
"uk-UA" : "Очистити" ,
"vi-VN" : "Xóa" ,
@@ -541,6 +559,8 @@ const Translations = {
"ja-JP" : "このプリセットを削除しますか?" ,
"ko-KR" : "이 프리셋을 삭제하시겠습니까?" ,
"pl-PL" : "Czy na pewno chcesz usunąć ten szablon?" ,
"pt-BR" : "Você quer excluir esta predefinição?" ,
"ru-RU" : "Вы точно хотите удалить этот шаблон?" ,
"tr-TR" : "Bu hazı r ayarı silmek istiyor musunuz?" ,
"uk-UA" : "Ви бажаєте видалити цей пресет?" ,
"vi-VN" : "Bạn có muốn xoá thiết lập sẵn này không?" ,
@@ -625,10 +645,14 @@ const Translations = {
"copy" : {
"de-DE" : "Kopieren" ,
"en-US" : "Copy" ,
"es-ES" : "Copiar" ,
"ja-JP" : "コピー" ,
"ko-KR" : "복사" ,
"pl-PL" : "Kopiuj" ,
"pt-BR" : "Copiar" ,
"ru-RU" : "Скопировать" ,
"tr-TR" : "Kopyala" ,
"uk-UA" : "Копіювати" ,
"vi-VN" : "Sao chép" ,
} ,
"custom" : {
@@ -650,8 +674,12 @@ const Translations = {
"deadzone-counterweight" : {
"de-DE" : "Deadzone Gegengewicht" ,
"en-US" : "Deadzone counterweight" ,
"es-ES" : "Contrapeso de la zona muerta" ,
"ja-JP" : "デッドゾーンのカウンターウエイト" ,
"pt-BR" : "Contador da Zona Morta" ,
"ru-RU" : "Противовес мертвой зоны" ,
"tr-TR" : "Ölü alan denge ağı rlı ğı " ,
"uk-UA" : "Противага Deadzone" ,
"vi-VN" : "Đối trọng vùng chết" ,
} ,
"default" : {
@@ -677,6 +705,8 @@ const Translations = {
"ja-JP" : "削除" ,
"ko-KR" : "삭제" ,
"pl-PL" : "Usuń" ,
"pt-BR" : "Deletar" ,
"ru-RU" : "Удалить" ,
"tr-TR" : "Sil" ,
"uk-UA" : "Видалити" ,
"vi-VN" : "Xóa" ,
@@ -797,6 +827,8 @@ const Translations = {
"ja-JP" : "無効" ,
"ko-KR" : "비활성화됨" ,
"pl-PL" : "Wyłączony" ,
"pt-BR" : "Desativado" ,
"ru-RU" : "Отключено" ,
"tr-TR" : "Kapalı " ,
"uk-UA" : "Вимкнено" ,
"vi-VN" : "Đã tắt" ,
@@ -809,6 +841,8 @@ const Translations = {
"ja-JP" : "編集" ,
"ko-KR" : "편집" ,
"pl-PL" : "Edytuj" ,
"pt-BR" : "Editar" ,
"ru-RU" : "Редактировать" ,
"tr-TR" : "Düzenle" ,
"uk-UA" : "Редагувати" ,
"vi-VN" : "Sửa" ,
@@ -914,6 +948,8 @@ const Translations = {
"ja-JP" : "有効" ,
"ko-KR" : "활성화됨" ,
"pl-PL" : "Włączony" ,
"pt-BR" : "Ativado" ,
"ru-RU" : "Включено" ,
"tr-TR" : "Açı k" ,
"uk-UA" : "Увімкнено" ,
"vi-VN" : "Đã bật" ,
@@ -926,6 +962,8 @@ const Translations = {
"ja-JP" : "エクスポート(書出し)" ,
"ko-KR" : "내보내기" ,
"pl-PL" : "Eksportuj" ,
"pt-BR" : "Exportar" ,
"ru-RU" : "Экспортировать" ,
"tr-TR" : "Dı şa aktar" ,
"uk-UA" : "Експорт" ,
"vi-VN" : "Xuất" ,
@@ -996,9 +1034,13 @@ const Translations = {
"horizontal-sensitivity" : {
"de-DE" : "Horizontale Empfindlichkeit" ,
"en-US" : "Horizontal sensitivity" ,
"es-ES" : "Sensibilidad horizontal" ,
"ja-JP" : "左右方向の感度" ,
"pl-PL" : "Czułość pozioma" ,
"pt-BR" : "Sensibilidade horizontal" ,
"ru-RU" : "Горизонтальная чувствительность" ,
"tr-TR" : "Yatay hassasiyet" ,
"uk-UA" : "Горизонтальна чутливість" ,
"vi-VN" : "Độ nhạy ngang" ,
} ,
"import" : {
@@ -1008,6 +1050,8 @@ const Translations = {
"ja-JP" : "インポート(読込み)" ,
"ko-KR" : "가져오기" ,
"pl-PL" : "Importuj" ,
"pt-BR" : "Importar" ,
"ru-RU" : "Импортировать" ,
"tr-TR" : "İçeri aktar" ,
"uk-UA" : "Імпорт" ,
"vi-VN" : "Nhập" ,
@@ -1063,10 +1107,14 @@ const Translations = {
"left-stick" : {
"de-DE" : "Linker Stick" ,
"en-US" : "Left stick" ,
"es-ES" : "Joystick izquierdo" ,
"ja-JP" : "左スティック" ,
"ko-KR" : "왼쪽 스틱" ,
"pl-PL" : "Lewy drążek analogowy" ,
"pt-BR" : "Direcional analógico esquerdo" ,
"ru-RU" : "Левый стик" ,
"tr-TR" : "Sol analog çubuk" ,
"uk-UA" : "Лівий стік" ,
"vi-VN" : "Analog trái" ,
} ,
"loading-screen" : {
@@ -1088,9 +1136,13 @@ const Translations = {
"map-mouse-to" : {
"de-DE" : "Maus binden an" ,
"en-US" : "Map mouse to" ,
"es-ES" : "Mapear ratón a" ,
"ja-JP" : "マウスの割り当て" ,
"pl-PL" : "Przypisz myszkę do" ,
"pt-BR" : "Mapear o mouse para" ,
"ru-RU" : "Наведите мышку на" ,
"tr-TR" : "Fareyi ata" ,
"uk-UA" : "Прив'язати мишу до" ,
"vi-VN" : "Gán chuột với" ,
} ,
"max-bitrate" : {
@@ -1173,15 +1225,23 @@ const Translations = {
"mkb-adjust-ingame-settings" : {
"de-DE" : "Vielleicht müssen auch Empfindlichkeit & Deadzone in den Spieleinstellungen angepasst werden" ,
"en-US" : "You may also need to adjust the in-game sensitivity & deadzone settings" ,
"es-ES" : "También puede que necesites ajustar la sensibilidad del juego y la configuración de la zona muerta" ,
"ja-JP" : "ゲーム内の設定で感度とデッドゾーンの調整が必要な場合があります" ,
"pt-BR" : "Você também pode precisar ajustar as configurações de sensibilidade e zona morta no jogo" ,
"ru-RU" : "Также может потребоваться изменить настройки чувствительности и мертвой зоны в игре" ,
"tr-TR" : "Bu seçenek etkinken bile oyun içi seçeneklerden hassasiyet ve ölü bölge ayarları nı düzeltmeniz gerekebilir" ,
"uk-UA" : "Можливо, вам також доведеться регулювати чутливість і deadzone у параметрах гри" ,
"vi-VN" : "Có thể bạn cần phải điều chỉnh các thông số độ nhạy và điểm chết trong game" ,
} ,
"mkb-click-to-activate" : {
"de-DE" : "Klicken zum Aktivieren" ,
"en-US" : "Click to activate" ,
"ja-JP " : "クリックして有効化 " ,
"es-ES " : "Haz clic para activar " ,
"ja-JP" : "マウスクリックで開始" ,
"pt-BR" : "Clique para ativar" ,
"ru-RU" : "Нажмите, чтобы активировать" ,
"tr-TR" : "Etkinleştirmek için tı klayı n" ,
"uk-UA" : "Натисніть, щоб активувати" ,
"vi-VN" : "Nhấn vào để kích hoạt" ,
} ,
"mouse-and-keyboard" : {
@@ -1221,6 +1281,8 @@ const Translations = {
"ja-JP" : "名前" ,
"ko-KR" : "이름" ,
"pl-PL" : "Nazwa" ,
"pt-BR" : "Nome" ,
"ru-RU" : "Имя" ,
"tr-TR" : "İsim" ,
"uk-UA" : "Назва" ,
"vi-VN" : "Tên" ,
@@ -1229,10 +1291,14 @@ const Translations = {
"new" : {
"de-DE" : "Neu" ,
"en-US" : "New" ,
"es-ES" : "Nuevo" ,
"ja-JP" : "新しい" ,
"ko-KR" : "새로 만들기" ,
"pl-PL" : "Nowy" ,
"pt-BR" : "Novo" ,
"ru-RU" : "Создать" ,
"tr-TR" : "Yeni" ,
"uk-UA" : "Новий" ,
"vi-VN" : "Tạo mới" ,
} ,
"no-consoles-found" : {
@@ -1351,6 +1417,8 @@ const Translations = {
"ja-JP" : "プレイ中" ,
"ko-KR" : "플레이 중" ,
"pl-PL" : "W grze" ,
"pt-BR" : "Jogando" ,
"ru-RU" : "Играет" ,
"tr-TR" : "Şu anda oyunda" ,
"uk-UA" : "Гра триває" ,
"vi-VN" : "Đang chơi" ,
@@ -1441,6 +1509,8 @@ const Translations = {
"ja-JP" : "プリセット" ,
"ko-KR" : "프리셋" ,
"pl-PL" : "Szablon" ,
"pt-BR" : "Predefinição" ,
"ru-RU" : "Шаблон" ,
"tr-TR" : "Hazı r ayar" ,
"uk-UA" : "Пресет" ,
"vi-VN" : "Thiết lập sẵn" ,
@@ -1453,6 +1523,8 @@ const Translations = {
"ja-JP" : "Escを押してキャンセル" ,
"ko-KR" : "ESC를 눌러 취소" ,
"pl-PL" : "Naciśnij Esc, aby anulować" ,
"pt-BR" : "Pressione Esc para cancelar" ,
"ru-RU" : "Нажмите Esc для отмены" ,
"tr-TR" : "İptal etmek için Esc'ye bası n" ,
"uk-UA" : "Натисніть Esc, щоб скасувати" ,
"vi-VN" : "Nhấn Esc để bỏ qua" ,
@@ -1465,6 +1537,8 @@ const Translations = {
"ja-JP" : e => ` ${ e . key } キーでマウスとキーボードの機能を切り替える ` ,
"ko-KR" : e => ` ${ e . key } 키를 눌러 마우스와 키보드 기능을 활성화 하십시오 ` ,
"pl-PL" : e => ` Naciśnij ${ e . key } , aby przełączyć funkcję myszy i klawiatury ` ,
"pt-BR" : e => ` Pressione ${ e . key } para ativar/desativar a função de Mouse e Teclado ` ,
"ru-RU" : e => ` Нажмите ${ e . key } для переключения функции мыши и клавиатуры ` ,
"tr-TR" : e => ` Klavye ve fare özelliğini açmak için ${ e . key } tuşuna bası n ` ,
"uk-UA" : e => ` Натисніть ${ e . key } , щоб увімкнути а б о вимкнути функцію миші та клавіатури ` ,
"vi-VN" : e => ` Nhấn ${ e . key } để bật/tắt tính năng Chuột và Bàn phím ` ,
@@ -1477,6 +1551,8 @@ const Translations = {
"ja-JP" : "キーを押すかマウスをクリックして割り当て..." ,
"ko-KR" : "정지하려면 아무키나 마우스를 클릭해주세요..." ,
"pl-PL" : "Naciśnij klawisz lub kliknij myszą, aby przypisać..." ,
"pt-BR" : "Pressione uma tecla ou clique do mouse para vincular..." ,
"ru-RU" : "Нажмите клавишу или щелкните мышкой, чтобы связать..." ,
"tr-TR" : "Klavyedeki bir tuşa basarak veya fareyle tı klayarak tuş ataması yapı n..." ,
"uk-UA" : "Натисніть клавішу а б о кнопку миші, щоб прив'язати..." ,
"vi-VN" : "Nhấn nút hoặc nhấn chuột để gán..." ,
@@ -1485,10 +1561,14 @@ const Translations = {
"prompt-preset-name" : {
"de-DE" : "Voreinstellung Name:" ,
"en-US" : "Preset's name:" ,
"es-ES" : "Nombre del preajuste:" ,
"ja-JP" : "プリセット名:" ,
"ko-KR" : "프리셋 이름:" ,
"pl-PL" : "Nazwa szablonu:" ,
"pt-BR" : "Nome da predefinição:" ,
"ru-RU" : "Имя шаблона:" ,
"tr-TR" : "Hazı r ayar adı :" ,
"uk-UA" : "Назва пресету:" ,
"vi-VN" : "Tên của mẫu sẵn:" ,
} ,
"ratio" : {
@@ -1557,27 +1637,39 @@ const Translations = {
"rename" : {
"de-DE" : "Umbenennen" ,
"en-US" : "Rename" ,
"es-ES" : "Renombrar" ,
"ja-JP" : "名前変更" ,
"ko-KR" : "이름 바꾸기" ,
"pl-PL" : "Zmień nazwę" ,
"pt-BR" : "Renomear" ,
"ru-RU" : "Переименовать" ,
"tr-TR" : "Ad değiştir" ,
"uk-UA" : "Перейменувати" ,
"vi-VN" : "Sửa tên" ,
} ,
"right-click-to-unbind" : {
"de-DE" : "Rechtsklick auf Taste: Zuordnung aufheben" ,
"en-US" : "Right-click on a key to unbind it" ,
"es-ES" : "Clic derecho en una tecla para desvincularla" ,
"ja-JP" : "右クリックで割り当て解除" ,
"ko-KR" : "할당 해제하려면 키를 오른쪽 클릭하세요" ,
"pt-BR" : "Clique com o botão direito em uma tecla para desvinculá-la" ,
"ru-RU" : "Щелкните правой кнопкой мыши по кнопке, чтобы отвязать её" ,
"tr-TR" : "Tuş ataması nı kaldı rmak için fareyle sağ tı k yapı n" ,
"uk-UA" : "Натисніть правою кнопкою миші, щоб відв'язати" ,
"vi-VN" : "Nhấn chuột phải lên một phím để gỡ nó" ,
} ,
"right-stick" : {
"de-DE" : "Rechter Stick" ,
"en-US" : "Right stick" ,
"es-ES" : "Joystick derecho" ,
"ja-JP" : "右スティック" ,
"ko-KR" : "오른쪽 스틱" ,
"pl-PL" : "Prawy drążek analogowy" ,
"pt-BR" : "Direcional analógico direito" ,
"ru-RU" : "Правый стик" ,
"tr-TR" : "Sağ analog çubuk" ,
"uk-UA" : "Правий стік" ,
"vi-VN" : "Analog phải" ,
} ,
"rocket-always-hide" : {
@@ -1683,6 +1775,8 @@ const Translations = {
"ja-JP" : "保存" ,
"ko-KR" : "저장" ,
"pl-PL" : "Zapisz" ,
"pt-BR" : "Salvar" ,
"ru-RU" : "Сохранить" ,
"tr-TR" : "Kaydet" ,
"uk-UA" : "Зберегти" ,
"vi-VN" : "Lưu" ,
@@ -2024,6 +2118,7 @@ const Translations = {
"de-DE" : "Stick Abklingzeit Minimum" ,
"en-US" : "Stick decay minimum" ,
"ja-JP" : "スティックの減衰の最小値" ,
"pt-BR" : "Mínimo decaimento do analógico" ,
"tr-TR" : "Çubuğun ortalanma süresi minimumu" ,
"vi-VN" : "Độ suy giảm tối thiểu của cần điều khiển" ,
} ,
@@ -2031,6 +2126,7 @@ const Translations = {
"de-DE" : "Stick Abklingzeit Geschwindigkeit" ,
"en-US" : "Stick decay strength" ,
"ja-JP" : "スティックの減衰の強さ" ,
"pt-BR" : "Força de decaimento do analógico" ,
"tr-TR" : "Çubuğun ortalanma gücü" ,
"vi-VN" : "Sức mạnh độ suy giảm của cần điều khiển" ,
} ,
@@ -2069,8 +2165,12 @@ const Translations = {
"support-better-xcloud" : {
"de-DE" : "\"Better xCloud\" unterstützen" ,
"en-US" : "Support Better xCloud" ,
"es-ES" : "Apoyar a Better xCloud" ,
"ja-JP" : "Better xCloudをサポート" ,
"pt-BR" : "Suporte ao Melhor xCloud" ,
"ru-RU" : "Поддержать Better xCloud" ,
"tr-TR" : "Better xCloud'a destek ver" ,
"uk-UA" : "Підтримати Better xCloud" ,
"vi-VN" : "Hỗ trợ Better xCloud" ,
} ,
"swap-buttons" : {
@@ -2389,9 +2489,13 @@ const Translations = {
"vertical-sensitivity" : {
"de-DE" : "Vertikale Empfindlichkeit" ,
"en-US" : "Vertical sensitivity" ,
"es-ES" : "Sensibilidad Vertical" ,
"ja-JP" : "上下方向の感度" ,
"pl-PL" : "Czułość pionowa" ,
"pt-BR" : "Sensibilidade vertical" ,
"ru-RU" : "Вертикальная чувствительность" ,
"tr-TR" : "Dikey hassasiyet" ,
"uk-UA" : "Вертикальна чутливість" ,
"vi-VN" : "Độ ngạy dọc" ,
} ,
"vibration-intensity" : {
@@ -5938,11 +6042,7 @@ class Preferences {
'default' : _ _ ( 'default' ) ,
} ;
if ( typeof RTCRtpTransceiver === 'undefined' || ! ( 'setCodecPreferences' in RTCRtpTransceiver . prototype ) ) {
return options ;
}
if ( ! ( 'getCapabilities' in RTCRtpReceiver ) ) {
if ( ! ( 'getCapabilities' in RTCRtpReceiver ) || typeof RTCRtpTransceiver === 'undefined' || ! ( 'setCodecPreferences' in RTCRtpTransceiver . prototype ) ) {
return options ;
}
@@ -5990,6 +6090,12 @@ class Preferences {
return options ;
} ) ( ) ,
'ready' : ( ) => {
const options = Preferences . SETTINGS [ Preferences . STREAM _CODEC _PROFILE ] . options ;
if ( Object . keys ( options ) . length <= 1 ) {
Preferences . SETTINGS [ Preferences . STREAM _CODEC _PROFILE ] . unsupported = _ _ ( 'browser-unsupported-feature' ) ;
}
} ,
} ,
[ Preferences . PREFER _IPV6 _SERVER ] : {
'default' : false ,
@@ -6015,6 +6121,7 @@ class Preferences {
'all' : _ _ ( 'tc-all-games' ) ,
'off' : _ _ ( 'off' ) ,
} ,
'unsupported' : ! HAS _TOUCH _SUPPORT ? _ _ ( 'device-unsupported-touch' ) : false ,
} ,
[ Preferences . STREAM _TOUCH _CONTROLLER _STYLE _STANDARD ] : {
'default' : 'default' ,
@@ -6023,6 +6130,7 @@ class Preferences {
'white' : _ _ ( 'tc-all-white' ) ,
'muted' : _ _ ( 'tc-muted-colors' ) ,
} ,
'unsupported' : ! HAS _TOUCH _SUPPORT ? _ _ ( 'device-unsupported-touch' ) : false ,
} ,
[ Preferences . STREAM _TOUCH _CONTROLLER _STYLE _CUSTOM ] : {
'default' : 'default' ,
@@ -6030,6 +6138,7 @@ class Preferences {
'default' : _ _ ( 'default' ) ,
'muted' : _ _ ( 'tc-muted-colors' ) ,
} ,
'unsupported' : ! HAS _TOUCH _SUPPORT ? _ _ ( 'device-unsupported-touch' ) : false ,
} ,
[ Preferences . STREAM _SIMPLIFY _MENU ] : {
'default' : false ,
@@ -6072,6 +6181,10 @@ class Preferences {
[ Preferences . MKB _ENABLED ] : {
'default' : false ,
'unsupported' : ( ( ) => {
const userAgent = ( window . navigator . orgUserAgent || window . navigator . userAgent || '' ) . toLowerCase ( ) ;
return userAgent . match ( /(android|iphone|ipad)/ ) ? _ _ ( 'browser-unsupported-feature' ) : false ;
} ) ( ) ,
} ,
[ Preferences . MKB _DEFAULT _PRESET _ID ] : {
@@ -6290,6 +6403,7 @@ class Preferences {
}
const setting = Preferences . SETTINGS [ settingId ] ;
setting && setting . migrate && setting . migrate . call ( this , savedPrefs , savedPrefs [ settingId ] ) ;
setting && setting . ready && setting . ready . call ( this ) ;
}
for ( let settingId in Preferences . SETTINGS ) {
@@ -6355,9 +6469,9 @@ class Preferences {
return ;
}
// Return " default" for STREAM_TOUCH_CONTROLLER pref when the browser doesn' t support touch
if ( ! HAS _TOUCH _SUPPORT && key === Preferences . STREAM _TOUCH _CONTROLLER ) {
return ' default' ;
// Return default value if the feature is no t supported
if ( Preferences . SETTINGS [ key ] . unsupported ) {
return Preferences . SETTINGS [ key ] . default;
}
let value = this . # prefs [ key ] ;
@@ -6570,7 +6684,7 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
const endIndex = funcStr . indexOf ( '},' , index ) ;
const newSettings = [
'EnableStreamGate: false',
// 'EnableStreamGate: false',
'PwaPrompt: false' ,
] ;
@@ -6607,15 +6721,6 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
return funcStr . replace ( text , 'get mouseAndKeyboardEnabled() {return this._titleSupportsMouseAndKeyboard;' ) ;
} ,
patchStreamHudSize : function ( funcStr ) {
if ( ! funcStr . includes ( '="StreamHUD-module__button' ) ) {
return false ;
}
funcStr = funcStr . replace ( '=3;' , '=5;' ) ;
return funcStr ;
} ,
disableGamepadDisconnectedScreen : function ( funcStr ) {
const index = funcStr . indexOf ( '"GamepadDisconnected_Title",' ) ;
if ( index === - 1 ) {
@@ -6651,9 +6756,24 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
return funcStr ;
} ,
// Disable StreamGate
disableStreamGate : function ( funcStr ) {
const index = funcStr . indexOf ( 'case"partially-ready":' ) ;
if ( index === - 1 ) {
return false ;
}
const bracketIndex = funcStr . indexOf ( '=>{' , index - 150 ) + 3 ;
funcStr = funcStr . substring ( 0 , bracketIndex ) + 'return 0;' + funcStr . substring ( bracketIndex ) ;
return funcStr ;
} ,
} ;
static # PATCH _ORDERS = [
[ 'disableStreamGate' ] ,
[
'disableAiTrack' ,
'disableTelemetry' ,
@@ -6683,12 +6803,11 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
[
'disableGamepadDisconnectedScreen' ,
'mkbMouseAndKeyboardEnabled' ,
'patchStreamHudSize' ,
] ,
] ;
static # patchFunctionBind ( ) {
Function . prototype . nativeBind = Function . prototype . bind ;
const nativeBind = Function . prototype . bind ;
Function . prototype . bind = function ( ) {
let valid = false ;
if ( arguments . length === 2 && arguments [ 0 ] === null ) {
@@ -6698,12 +6817,12 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
}
if ( ! valid ) {
return this . nativeBind . apply ( this , arguments ) ;
return nativeBind . apply ( this , arguments ) ;
}
if ( typeof arguments [ 1 ] === 'function' ) {
console . log ( '[Better xCloud] Restored Function.prototype.bind()' ) ;
Function . prototype . bind = Function . prototype . nativeBind ;
Function . prototype . bind = nativeBind ;
}
const orgFunc = this ;
@@ -6717,7 +6836,7 @@ if (window.BX_VIBRATION_INTENSITY && window.BX_VIBRATION_INTENSITY < 1) {
orgFunc ( a , item ) ;
}
return newFunc . nativeBind. apply ( newFunc , arguments ) ;
return nativeBind . apply ( newFunc , arguments ) ;
} ;
}
@@ -6933,6 +7052,15 @@ function addCss() {
src: url('https://redphx.github.io/better-xcloud/fonts/promptfont.otf');
}
/* Fix Stream menu buttons not hiding */
div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module__hiddenContainer]) {
opacity: 0;
pointer-events: none !important;
position: absolute;
top: -9999px;
left: -9999px;
}
.bx-button {
background-color: var(--bx-default-button-color);
user-select: none;
@@ -8164,7 +8292,7 @@ function getPreferredServerRegion() {
function updateIceCandidates ( candidates , options ) {
const pattern = new RegExp ( /a=candidate:(?<foundation>\d+) (?<component>\d+) UDP (?<priority>\d+) (?<ip>[^\s]+) (?<the_rest>.*)/ ) ;
const pattern = new RegExp ( /a=candidate:(?<foundation>\d+) (?<component>\d+) UDP (?<priority>\d+) (?<ip>[^\s]+) (?<port>\d+) (?<the_rest>.*)/ ) ;
const lst = [ ] ;
for ( let item of candidates ) {
@@ -8182,52 +8310,71 @@ function updateIceCandidates(candidates, options) {
const newCandidates = [ ] ;
let foundation = 1 ;
const newCandidate = candidate => {
return {
'candidate' : candidate ,
'messageType' : 'iceCandidate' ,
'sdpMLineIndex' : '0' ,
'sdpMid' : '0' ,
} ;
} ;
lst . forEach ( item => {
item . foundation = foundation ;
item . priority = ( foundation == 1 ) ? 10000 : 1 ;
newCandidates . push ( {
'candidate' : ` a=candidate: ${ item . foundation } 1 UDP ${ item . priority } ${ item . ip } ${ item . the _rest } ` ,
'messageType' : 'iceCandidate' ,
'sdpMLineIndex' : '0' ,
'sdpMid' : '0' ,
} ) ;
newCandidates . push ( newCandidate ( ` a=candidate: ${ item . foundation } 1 UDP ${ item . priority } ${ item . ip } ${ item . port } ${ item . the _rest } ` ) ) ;
++ 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' ,
} ) ;
if ( options . consoleAddrs ) {
for ( const ip in options . consoleAddrs ) {
const port = options . consoleAddrs [ ip ] ;
newCandidates . push ( newCandidate ( ` a=candidate: ${ newCandidates . length + 1 } 1 UDP 1 ${ ip } ${ port } typ host ` ) ) ;
}
}
newCandidates . push ( {
'candidate' : 'a=end-of-candidates' ,
'messageType' : 'iceCandidate' ,
'sdpMLineIndex' : '0' ,
'sdpMid' : '0' ,
} ) ;
newCandidates . push ( newCandidate ( 'a=end-of-candidates' ) ) ;
console . log ( newCandidates ) ;
return newCandidates ;
}
function clearDbLogs ( dbName , table ) {
const request = window . indexedDB . open ( dbName ) ;
request . onsuccess = e => {
const db = e . target . result ;
const objectStore = db . transaction ( table , 'readwrite' ) . objectStore ( table ) ;
const objectStoreRequest = objectStore . clear ( ) ;
objectStoreRequest . onsuccess = function ( event ) {
console . log ( ` [Better xCloud] Cleared ${ dbName } . ${ table } ` ) ;
} ;
}
}
function clearApplicationInsightsBuffers ( ) {
window . sessionStorage . removeItem ( 'AI_buffer' ) ;
window . sessionStorage . removeItem ( 'AI_sentBuffer' ) ;
}
function clearAllLogs ( ) {
clearApplicationInsightsBuffers ( ) ;
clearDbLogs ( 'StreamClientLogHandler' , 'logs' ) ;
clearDbLogs ( 'XCloudAppLogs' , 'logs' ) ;
}
function interceptHttpRequests ( ) {
var BLOCKED _URLS = [ ] ;
if ( PREFS . get ( Preferences . BLOCK _TRACKING ) ) {
// Clear Applications Insight buffers
clearApplicationInsightsBuffer s ( ) ;
clearAllLog s ( ) ;
BLOCKED _URLS = BLOCKED _URLS . concat ( [
'https://arc.msn.com' ,
@@ -8259,7 +8406,7 @@ function interceptHttpRequests() {
for ( let blocked of BLOCKED _URLS ) {
if ( this . _url . startsWith ( blocked ) ) {
if ( blocked === 'https://dc.services.visualstudio.com' ) {
setTimeout ( clearApplicationInsightsBuffer s , 1000 ) ;
setTimeout ( clearAllLog s , 1000 ) ;
}
return false ;
}
@@ -8278,8 +8425,8 @@ function interceptHttpRequests() {
const PREF _AUDIO _MIC _ON _PLAYING = PREFS . get ( Preferences . AUDIO _MIC _ON _PLAYING ) ;
const orgFetch = window . fetch ;
let consoleIp ;
le t consolePort ;
cons t consoleAddrs = { } ;
const patchIceCandidates = function ( ... arg ) {
// ICE server candidates
@@ -8297,7 +8444,7 @@ function interceptHttpRequests() {
const options = {
preferIpv6Server : PREF _PREFER _IPV6 _SERVER ,
consoleIp : consoleIp ,
consoleAddrs : consoleAddrs ,
} ;
const obj = JSON . parse ( text ) ;
@@ -8367,8 +8514,15 @@ function interceptHttpRequests() {
return promise . then ( response => {
return response . clone ( ) . json ( ) . then ( obj => {
console . log ( obj ) ;
consoleIp = obj . serverDetails . ipAddress ;
consolePort = obj . serverDetails . port ;
const serverDetails = obj . serverDetails ;
if ( serverDetails . ipV4Address ) {
consoleAddrs [ serverDetails . ipV4Address ] = serverDetails . ipV4Port ;
}
if ( serverDetails . ipV6Address ) {
consoleAddrs [ serverDetails . ipV6Address ] = serverDetails . ipV6Port ;
}
response . json = ( ) => Promise . resolve ( obj ) ;
response . text = ( ) => Promise . resolve ( JSON . stringify ( obj ) ) ;
@@ -8844,19 +8998,11 @@ function injectSettingsButton($parent) {
}
// Disable unsupported settings
if ( settingId === Preferences . STREAM _CODEC _PROFILE ) {
const options = Preferences . SETTINGS [ Preferences . STREAM _CODEC _PROFILE ] . options ;
if ( Object . keys ( options ) . length <= 1 ) {
$control . disabled = true ;
$control . title = _ _ ( 'browser-unsupported-feature' ) ;
}
} else if ( ! HAS _TOUCH _SUPPORT ) {
// Disable this setting for non-touchable devices
if ( [ Preferences . STREAM _TOUCH _CONTROLLER , Preferences . STREAM _TOUCH _CONTROLLER _STYLE _STANDARD , Preferences . STREAM _TOUCH _CONTROLLER _STYLE _CUSTOM ] . indexOf ( settingId ) > - 1 ) {
$control . disabled = true ;
$control . title = _ _ ( 'device-unsupported-touch' ) ;
}
if ( setting. unsupported ) {
$ control . disabled = true ;
$control . title = setting . unsupported ;
}
$control . disabled && ( $control . style . cursor = 'help' ) ;
const $label = CE ( 'label' , labelAttrs , settingLabel ) ;
@@ -9027,6 +9173,33 @@ function watchHeader() {
function cloneStreamHudButton ( $orgButton , label , svg _icon ) {
const $container = $orgButton . cloneNode ( true ) ;
let timeout ;
const onTransitionStart = e => {
if ( e . propertyName !== 'opacity' ) {
return ;
}
timeout && clearTimeout ( timeout ) ;
$container . style . pointerEvents = 'none' ;
} ;
const onTransitionEnd = e => {
if ( e . propertyName !== 'opacity' ) {
return ;
}
const left = document . getElementById ( 'StreamHud' ) . style . left ;
if ( left === '0px' ) {
timeout && clearTimeout ( timeout ) ;
timeout = setTimeout ( ( ) => {
$container . style . pointerEvents = 'auto' ;
} , 100 ) ;
}
} ;
$container . addEventListener ( 'transitionstart' , onTransitionStart ) ;
$container . addEventListener ( 'transitionend' , onTransitionEnd ) ;
const $button = $container . querySelector ( 'button' ) ;
$button . setAttribute ( 'title' , label ) ;
@@ -9082,7 +9255,7 @@ function injectStreamMenuButtons() {
$quickBar . classList . add ( 'bx-gone' ) ;
$parent . removeEventListener ( 'click' , hideQuickBarFunc ) ;
$parent. removeEventListener( 'touchstart', hideQuickBarFunc) ;
// $parent. removeEventListener( 'touchstart', hideQuickBarFunc);
}
let $btnStreamSettings ;
@@ -9165,12 +9338,19 @@ function injectStreamMenuButtons() {
return ;
}
const hideGripHandle = ( ) => {
$gripHandle . dispatchEvent ( new PointerEvent ( 'pointerdown' ) ) ;
$gripHandle . click ( ) ;
$gripHandle . dispatchEvent ( new PointerEvent ( 'pointerdown' ) ) ;
$gripHandle . click ( ) ;
}
// Create Stream Settings button
if ( ! $btnStreamSettings ) {
$btnStreamSettings = cloneStreamHudButton ( $orgButton , _ _ ( 'menu-stream-settings' ) , Icon . STREAM _SETTINGS ) ;
$btnStreamSettings . addEventListener ( 'click' , e => {
hideGripHandle ( ) ;
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
const msVideoProcessing = $STREAM _VIDEO . msVideoProcessing ;
$quickBar . setAttribute ( 'data-clarity-boost' , ( msVideoProcessing && msVideoProcessing !== 'default' ) ) ;
@@ -9179,12 +9359,10 @@ function injectStreamMenuButtons() {
$quickBar . classList . remove ( 'bx-gone' ) ;
$parent . addEventListener ( 'click' , hideQuickBarFunc ) ;
$parent. addEventListener( 'touchstart', hideQuickBarFunc) ;
// $parent. addEventListener( 'touchstart', hideQuickBarFunc);
const $touchSurface = document . getElementById ( 'MultiTouchSurface' ) ;
$touchSurface && $touchSurface . style . display != 'none' && $touchSurface . addEventListener ( 'touchstart' , hideQuickBarFunc ) ;
$gripHandle . click ( ) ;
} ) ;
}
@@ -9192,16 +9370,14 @@ function injectStreamMenuButtons() {
if ( ! $btnStreamStats ) {
$btnStreamStats = cloneStreamHudButton ( $orgButton , _ _ ( 'menu-stream-stats' ) , Icon . STREAM _STATS ) ;
$btnStreamStats . addEventListener ( 'click' , e => {
hideGripHandle ( ) ;
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
// Toggle Stream Stats
StreamStats . toggle ( ) ;
const btnStreamStatsOn = ( ! StreamStats . isHidden ( ) && ! StreamStats . isGlancing ( ) ) ;
$btnStreamStats . classList . toggle ( 'bx-stream-menu-button-on' , btnStreamStatsOn ) ;
$gripHandle . click ( ) ;
} ) ;
}