@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 2.0
// @version 2.0.1
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -13,7 +13,7 @@
// ==/UserScript==
'use strict' ;
const SCRIPT _VERSION = '2.0' ;
const SCRIPT _VERSION = '2.0.1 ' ;
const SCRIPT _HOME = 'https://github.com/redphx/better-xcloud' ;
const ENABLE _MKB = false ;
@ -441,8 +441,10 @@ const Translations = {
"zh-CN" : "手柄" ,
} ,
"controller-polling-rate" : {
"de-DE" : "Controller-Abfragerate" ,
"en-US" : "Controller polling rate" ,
"ja-JP" : "コントローラーポーリングレート" ,
"pl-PL" : "Częstotliwości raportowania kontrolera" ,
"pt-BR" : "Taxa de consulta do controle" ,
"tr-TR" : "Oyun kumandası işlem hı zı " ,
"vi-VN" : "Tần suất cập nhật của bộ điều khiển" ,
@ -592,8 +594,10 @@ const Translations = {
"zh-CN" : "游戏启动时打开麦克风" ,
} ,
"enable-mkb" : {
"de-DE" : "Maus- und Tastaturunterstützung aktivieren" ,
"en-US" : "Enable Mouse & Keyboard support" ,
"ja-JP" : "マウス&キーボードのサポートを有効化" ,
"pl-PL" : "Włącz obsługę myszy i klawiatury" ,
"tr-TR" : "Klavye ve fare desteğini aktive et" ,
"vi-VN" : "Kích hoạt hỗ trợ Chuột & Bàn phím" ,
} ,
@ -612,6 +616,14 @@ const Translations = {
"vi-VN" : "Bật chế độ \"Xem nhanh\"" ,
"zh-CN" : "仅在打开设置时显示统计信息" ,
} ,
"enable-remote-play-feature" : {
"de-DE" : "\"Remote Play\" Funktion aktivieren" ,
"en-US" : "Enable the \"Remote Play\" feature" ,
"ja-JP" : "リモートプレイ機能を有効化" ,
"pl-PL" : "Włącz funkcję \"Gra zdalna\"" ,
"tr-TR" : "\"Uzaktan Oynama\" özelliğini aktive et" ,
"vi-VN" : "Bật tính năng \"Chơi từ xa\"" ,
} ,
"enable-volume-control" : {
"de-DE" : "Lautstärkeregelung aktivieren" ,
"en-US" : "Enable volume control feature" ,
@ -627,8 +639,10 @@ const Translations = {
"zh-CN" : "启用音量控制" ,
} ,
"fast" : {
"de-DE" : "Schnell" ,
"en-US" : "Fast" ,
"ja-JP" : "高速" ,
"pl-PL" : "Szybko" ,
"tr-TR" : "Hı zlı " ,
"vi-VN" : "Nhanh" ,
} ,
@ -703,8 +717,10 @@ const Translations = {
"zh-CN" : "大" ,
} ,
"layout" : {
"de-DE" : "Layout" ,
"en-US" : "Layout" ,
"ja-JP" : "レイアウト" ,
"pl-PL" : "Układ" ,
"pt-BR" : "Layout" ,
"tr-TR" : "Arayüz Görünümü" ,
"vi-VN" : "Bố cục" ,
@ -725,14 +741,18 @@ const Translations = {
"zh-CN" : "载入画面" ,
} ,
"max-bitrate" : {
"de-DE" : "Max. Bitrate" ,
"en-US" : "Max bitrate" ,
"ja-JP" : "最大ビットレート" ,
"pl-PL" : "Maksymalny bitrate" ,
"tr-TR" : "Maksimum bithı zı " ,
"vi-VN" : "Bitrate tối đa" ,
} ,
"may-not-work-properly" : {
"de-DE" : "Funktioniert evtl. nicht fehlerfrei!" ,
"en-US" : "May not work properly!" ,
"ja-JP" : "正常に動作しない場合があります!" ,
"pl-PL" : "Może nie działać poprawnie!" ,
"tr-TR" : "Düzgün çalı şmayabilir!" ,
"vi-VN" : "Có thể không hoạt động!" ,
} ,
@ -778,8 +798,10 @@ const Translations = {
"zh-CN" : "麦克风" ,
} ,
"mouse-and-keyboard" : {
"de-DE" : "Maus & Tastatur" ,
"en-US" : "Mouse & Keyboard" ,
"ja-JP" : "マウス&キーボード" ,
"pl-PL" : "Mysz i klawiatura" ,
"tr-TR" : "Klavye ve Fare" ,
"vi-VN" : "Chuột và Bàn phím" ,
} ,
@ -846,9 +868,11 @@ const Translations = {
"vi-VN" : "Bật" ,
"zh-CN" : "开启" ,
} ,
"only-support-some-games" : {
"en-US " : "Only support some games " ,
"only-supports -some-games" : {
"de-DE " : "Unterstützt nur einige Spiele " ,
"en-US" : "Only supports some games" ,
"ja-JP" : "一部のゲームのみサポート" ,
"pl-PL" : "Wspiera tylko niektóre gry" ,
"tr-TR" : "Yalnı zca belli oyunlar destekleniyor" ,
"vi-VN" : "Chỉ hỗ trợ một vài game" ,
} ,
@ -901,7 +925,7 @@ const Translations = {
"de-DE" : "Ausgeschaltet" ,
"en-US" : "Powered off" ,
"it-IT" : "Spento" ,
"ja-JP" : "電源 オフ" ,
"ja-JP" : "本体 オフ" ,
"pl-PL" : "Zasilanie wyłączone" ,
"pt-BR" : "Desligado" ,
"tr-TR" : "Kapalı " ,
@ -912,7 +936,7 @@ const Translations = {
"de-DE" : "Eingeschaltet" ,
"en-US" : "Powered on" ,
"it-IT" : "Acceso" ,
"ja-JP" : "電源 オン" ,
"ja-JP" : "本体 オン" ,
"pl-PL" : "Zasilanie włączone" ,
"pt-BR" : "Ligado" ,
"tr-TR" : "Açı k" ,
@ -1231,8 +1255,10 @@ const Translations = {
"zh-CN" : "跳过 Xbox 启动动画" ,
} ,
"slow" : {
"de-DE" : "Langsam" ,
"en-US" : "Slow" ,
"ja-JP" : "低速" ,
"pl-PL" : "Wolno" ,
"tr-TR" : "Yavaş" ,
"vi-VN" : "Chậm" ,
} ,
@ -1252,8 +1278,10 @@ const Translations = {
"zh-CN" : "小" ,
} ,
"smart-tv" : {
"de-DE" : "Smart TV" ,
"en-US" : "Smart TV" ,
"ja-JP" : "スマートTV" ,
"pl-PL" : "Smart TV" ,
"pt-BR" : "Smart TV" ,
"tr-TR" : "Akı llı TV" ,
"vi-VN" : "TV thông minh" ,
@ -1652,8 +1680,10 @@ const Translations = {
"zh-CN" : "未知" ,
} ,
"unlimited" : {
"de-DE" : "Unbegrenzt" ,
"en-US" : "Unlimited" ,
"ja-JP" : "無制限" ,
"pl-PL" : "Bez ograniczeń" ,
"tr-TR" : "Limitsiz" ,
"vi-VN" : "Không giới hạn" ,
} ,
@ -1669,8 +1699,10 @@ const Translations = {
"zh-CN" : "已取消静音" ,
} ,
"use-mouse-absolute-position" : {
"de-DE" : "Absolute Position der Maus verwenden" ,
"en-US" : "Use mouse's absolute position" ,
"ja-JP" : "マウスの絶対座標を使用" ,
"pl-PL" : "Użyj pozycji bezwzględnej myszy" ,
"tr-TR" : "Farenin mutlak pozisyonunu baz al" ,
"vi-VN" : "Sử dụng vị trí tuyệt đối của chuột" ,
} ,
@ -2074,18 +2106,11 @@ class RemotePlay {
RemotePlay . # $content . parentElement . replaceChild ( $fragment , RemotePlay . # $content ) ;
}
static # onLoad ( ) {
const CE = createElement ;
const $ui = CE ( 'div' , { 'class' : 'bx-container' } ,
CE ( 'h2' , { } , _ _ ( 'remote-play' ) ) ,
CE ( 'div' , { 'id' : 'bxUi' } , _ _ ( 'getting-consoles-list' ) ) ,
) ;
const $landingPageHeader = document . querySelector ( 'h2[class*=LandingPage-module__header]' ) ;
$landingPageHeader . parentElement . insertBefore ( $ui , $landingPageHeader ) ;
}
static detect ( ) {
if ( ! PREFS . get ( Preferences . REMOTE _PLAY _ENABLED ) ) {
return ;
}
IS _REMOTE _PLAYING = window . location . pathname . includes ( '/launch/' ) && window . location . hash . startsWith ( '#remote-play' ) ;
if ( IS _REMOTE _PLAYING ) {
window . BX _REMOTE _PLAY _CONFIG = REMOTE _PLAY _CONFIG ;
@ -3276,7 +3301,7 @@ class PreloadedState {
return this . _state ;
} ,
set : ( state ) => {
set : state => {
this . _state = state ;
APP _CONTEXT = structuredClone ( state . appContext ) ;
@ -3322,7 +3347,6 @@ class Preferences {
static get STREAM _DISABLE _FEEDBACK _DIALOG ( ) { return 'stream_disable_feedback_dialog' ; }
static get CONTROLLER _ENABLE _SHORTCUTS ( ) { return 'controller_enable_shortcuts' ; }
static get CONTROLLER _POLLING _RATE ( ) { return 'controller_polling_rate' ; }
static get MKB _ENABLED ( ) { return 'mkb_enabled' ; }
static get MKB _ABSOLUTE _MOUSE ( ) { return 'mkb_absolute_mouse' ; }
@ -3360,6 +3384,7 @@ class Preferences {
static get STATS _OPACITY ( ) { return 'stats_opacity' ; }
static get STATS _CONDITIONAL _FORMATTING ( ) { return 'stats_conditional_formatting' ; }
static get REMOTE _PLAY _ENABLED ( ) { return 'xhome_enabled' ; }
static get REMOTE _PLAY _RESOLUTION ( ) { return 'xhome_resolution' ; }
// Deprecated
@ -3553,15 +3578,6 @@ class Preferences {
[ Preferences . CONTROLLER _ENABLE _SHORTCUTS ] : {
'default' : false ,
} ,
[ Preferences . CONTROLLER _POLLING _RATE ] : {
'type' : 'number' ,
'default' : 50 ,
'options' : {
16 : '62.5 Hz (16ms)' ,
32 : '31.25 Hz (32ms)' ,
50 : ` 20 Hz (50ms - ${ _ _ ( 'default' ) } ) ` ,
} ,
} ,
[ Preferences . MKB _ENABLED ] : {
'default' : false ,
@ -3707,6 +3723,10 @@ class Preferences {
'default' : false ,
} ,
[ Preferences . REMOTE _PLAY _ENABLED ] : {
'default' : false ,
} ,
[ Preferences . REMOTE _PLAY _RESOLUTION ] : {
'default' : 1080 ,
'options' : {
@ -4033,36 +4053,24 @@ const PREFS = new Preferences();
class Patcher {
static # PATCHES = {
// Modify controller polling rate
controllerPollingRate : ( PREFS . get ( Preferences . CONTROLLER _POLLING _RATE ) !== 50 ) && function ( funcStr ) {
const text = '.startGamepadPolling=()' ;
const index = funcStr . indexOf ( text ) ;
if ( index === - 1 ) {
return false ;
}
const patchedStr = funcStr . substring ( index , index + 100 ) . replace ( /setInterval\((\w+),(\d+)\)/ , ` setInterval( $ 1, ${ PREFS . get ( Preferences . CONTROLLER _POLLING _RATE ) } ) ` ) ;
return funcStr . substring ( 0 , index ) + patchedStr + funcStr . substring ( index + 100 ) ;
} ,
// Enable Remote Play feature
c onnectMode: function ( funcStr ) {
remotePlayC onnectMode: PREFS . get ( Preferences . REMOTE _PLAY _ENABLED ) && function ( funcStr ) {
const text = 'connectMode:"cloud-connect"' ;
if ( ! funcStr . includes ( text ) ) {
return false ;
}
return funcStr . replace ( text , ` connectMode:window.BX_REMOTE_PLAY_CONFIG?"xhome-connect":"cloud-connect",remotePlayServerId:window.BX_REMOTE_PLAY_CONFIG&&window.BX_REMOTE_PLAY_CONFIG.serverId||'' ` ) ;
return funcStr . replace ( text , ` connectMode:window.BX_REMOTE_PLAY_CONFIG?"xhome-connect":"cloud-connect",remotePlayServerId:( window.BX_REMOTE_PLAY_CONFIG&&window.BX_REMOTE_PLAY_CONFIG.serverId) ||'' ` ) ;
} ,
// Replace "/direct-connect" with "/play"
d irectConnectUrl: function ( funcStr ) {
remotePlayD irectConnectUrl: PREFS . get ( Preferences . REMOTE _PLAY _ENABLED ) && function ( funcStr ) {
const index = funcStr . indexOf ( '/direct-connect' ) ;
if ( index === - 1 ) {
return false ;
}
return funcStr . replace ( funcStr . substring ( index - 9 , index + 15 ) , '/play' ) ;
return funcStr . replace ( funcStr . substring ( index - 9 , index + 15 ) , 'https://www.xbox.com /play' ) ;
} ,
// Set disableTelemetry() to true
@ -4085,6 +4093,31 @@ class Patcher {
return funcStr . replace ( text , 'this.trackEvent=e=>{},this.uwuwu=' ) ;
} ,
// Disable ApplicationInsights.track() function
disableAiTrack : PREFS . get ( Preferences . BLOCK _TRACKING ) && function ( funcStr ) {
const text = '.track=function(' ;
const index = funcStr . indexOf ( text ) ;
if ( index === - 1 ) {
return false ;
}
if ( funcStr . substring ( 0 , index + 200 ) . includes ( '"AppInsightsCore' ) ) {
return false ;
}
return funcStr . substring ( 0 , index ) + '.track=function(e){},!!function(' + funcStr . substring ( index + text . length ) ;
} ,
// Block WebRTC stats collector
blockWebRtcStatsCollector : PREFS . get ( Preferences . BLOCK _TRACKING ) && function ( funcStr ) {
const text = 'this.intervalMs=0,' ;
if ( ! funcStr . includes ( text ) ) {
return false ;
}
return funcStr . replace ( text , 'false,' + text ) ;
} ,
// Set TV layout
tvLayout : PREFS . get ( Preferences . UI _LAYOUT ) === 'tv' && function ( funcStr ) {
const text = '?"tv":"default"' ;
@ -4107,7 +4140,7 @@ class Patcher {
}
return funcStr ;
}
} ,
} ;
static # patchFunctionBind ( ) {
@ -5620,6 +5653,7 @@ function injectSettingsButton($parent) {
const CE = createElement ;
const PREF _PREFERRED _REGION = getPreferredServerRegion ( ) ;
const PREF _LATEST _VERSION = PREFS . get ( Preferences . LATEST _VERSION ) ;
const PREF _REMOTE _PLAY _ENABLED = PREFS . get ( Preferences . REMOTE _PLAY _ENABLED ) ;
// Setup Settings button
const $button = CE ( 'button' , { 'class' : 'bx-settings-button' } , PREF _PREFERRED _REGION ) ;
@ -5643,7 +5677,7 @@ function injectSettingsButton($parent) {
} ) ;
let $updateAvailable ;
let $remotePlayLink ;
let $remotePlayBtn ;
const $wrapper = CE ( 'div' , { 'class' : 'bx-settings-wrapper' } ,
CE ( 'div' , { 'class' : 'bx-settings-title-wrapper' } ,
CE ( 'a' , {
@ -5651,7 +5685,7 @@ function injectSettingsButton($parent) {
'href' : SCRIPT _HOME ,
'target' : '_blank' ,
} , 'Better xCloud ' + SCRIPT _VERSION ) ,
$remotePlayLink = CE ( 'button' , { 'class' : 'bx-primary-button bx-no-margin' } , _ _ ( 'remote-play' ) ) ,
$remotePlayBtn = CE ( 'button' , { 'class' : 'bx-primary-button bx-no-margin' } , _ _ ( 'remote-play' ) ) ,
)
) ;
$updateAvailable = CE ( 'a' , {
@ -5660,12 +5694,17 @@ function injectSettingsButton($parent) {
'target' : '_blank' ,
} ) ;
$remotePlayLink . addEventListener ( 'click' , e => {
R emotePlay. showDialog ( ) ;
if ( PREF _REMOTE _PLAY _ENABLED ) {
$r emotePlayBtn . addEventListener ( 'click' , e => {
RemotePlay . showDialog ( ) ;
// Hide Settings
$container . classList . add ( 'bx-gone' ) ;
} ) ;
} else {
$remotePlayBtn . classList . add ( 'bx-gone' ) ;
}
// Hide Settings
$container . classList . add ( 'bx-gone' ) ;
} ) ;
$wrapper . appendChild ( $updateAvailable ) ;
// Show new version indicator
@ -5678,6 +5717,7 @@ function injectSettingsButton($parent) {
const SETTINGS _UI = {
'Better xCloud' : {
[ Preferences . BETTER _XCLOUD _LOCALE ] : _ _ ( 'language' ) ,
[ Preferences . REMOTE _PLAY _ENABLED ] : _ _ ( 'enable-remote-play-feature' ) ,
} ,
[ _ _ ( 'server' ) ] : {
[ Preferences . SERVER _REGION ] : _ _ ( 'region' ) ,
@ -5694,7 +5734,6 @@ function injectSettingsButton($parent) {
[ Preferences . STREAM _DISABLE _FEEDBACK _DIALOG ] : _ _ ( 'disable-post-stream-feedback-dialog' ) ,
} ,
[ _ _ ( 'controller' ) ] : {
[ Preferences . CONTROLLER _POLLING _RATE ] : _ _ ( 'controller-polling-rate' ) ,
[ Preferences . CONTROLLER _ENABLE _SHORTCUTS ] : _ _ ( 'enable-controller-shortcuts' ) ,
} ,
[ _ _ ( 'touch-controller' ) ] : {
@ -5705,7 +5744,7 @@ function injectSettingsButton($parent) {
[ _ _ ( 'mouse-and-keyboard' ) ] : {
'_note' : '⚠️ ' + _ _ ( 'may-not-work-properly' ) ,
[ Preferences . MKB _ENABLED ] : [ _ _ ( 'enable-mkb' ) , _ _ ( 'only-support-some-games' ) ] ,
[ Preferences . MKB _ENABLED ] : [ _ _ ( 'enable-mkb' ) , _ _ ( 'only-supports -some-games' ) ] ,
[ Preferences . MKB _ABSOLUTE _MOUSE ] : _ _ ( 'use-mouse-absolute-position' ) ,
} ,
@ -6623,4 +6662,5 @@ if (PREFS.get(Preferences.CONTROLLER_ENABLE_SHORTCUTS)) {
}
Patcher . initialize ( ) ;
RemotePlay . detect ( ) ;