mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-07-06 06:11:43 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
35a783c53e | |||
afe3809061 | |||
5eeb15f201 | |||
71b4109385 | |||
8286429cc3 | |||
ae24005f08 | |||
2626408cbe | |||
804f751646 | |||
13a20f30e5 | |||
46265f2ccd | |||
93d77c3783 |
@ -1,5 +1,5 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 3.5.0
|
// @version 3.5.1
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 3.5.0
|
// @version 3.5.1
|
||||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||||
// @author redphx
|
// @author redphx
|
||||||
// @license MIT
|
// @license MIT
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
/* ADDITIONAL CODE */
|
/* ADDITIONAL CODE */
|
||||||
|
|
||||||
const SCRIPT_VERSION = '3.4.0';
|
const SCRIPT_VERSION = '3.5.1';
|
||||||
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
||||||
|
|
||||||
// Setup flags
|
// Setup flags
|
||||||
@ -3601,6 +3601,7 @@ class RemotePlay {
|
|||||||
static XCLOUD_TOKEN;
|
static XCLOUD_TOKEN;
|
||||||
static XHOME_TOKEN;
|
static XHOME_TOKEN;
|
||||||
static #CONSOLES = null;
|
static #CONSOLES = null;
|
||||||
|
static #REGIONS;
|
||||||
|
|
||||||
static #STATE_LABELS = {
|
static #STATE_LABELS = {
|
||||||
'On': t('powered-on'),
|
'On': t('powered-on'),
|
||||||
@ -3775,6 +3776,7 @@ class RemotePlay {
|
|||||||
},
|
},
|
||||||
}).then(resp => resp.json())
|
}).then(resp => resp.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
|
RemotePlay.#REGIONS = json.offeringSettings.regions;
|
||||||
RemotePlay.XHOME_TOKEN = json.gsToken;
|
RemotePlay.XHOME_TOKEN = json.gsToken;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
@ -3786,13 +3788,6 @@ class RemotePlay {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let servers;
|
|
||||||
if (!REMOTE_PLAY_SERVER) {
|
|
||||||
servers = ['wus2', 'eus', 'uks']; // Possible values: wus2 (WestUS2), eus (EastUS), uks (UkSouth)
|
|
||||||
} else {
|
|
||||||
servers = REMOTE_PLAY_SERVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
@ -3801,16 +3796,16 @@ class RemotePlay {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Test servers one by one
|
// Test servers one by one
|
||||||
for (const server of servers) {
|
for (const region of RemotePlay.#REGIONS) {
|
||||||
try {
|
try {
|
||||||
const url = `https://${server}.gssv-play-prodxhome.xboxlive.com/v6/servers/home?mr=50`;
|
const url = `${region.baseUri}/v6/servers/home?mr=50`;
|
||||||
const resp = await fetch(url, options);
|
const resp = await fetch(url, options);
|
||||||
|
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
RemotePlay.#CONSOLES = json.results;
|
RemotePlay.#CONSOLES = json.results;
|
||||||
|
|
||||||
// Store working server
|
// Store working server
|
||||||
REMOTE_PLAY_SERVER = server;
|
REMOTE_PLAY_SERVER = region.baseUri;
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
@ -7967,6 +7962,16 @@ class Patcher {
|
|||||||
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)||''`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Fix the Guide/Nexus button not working in Remote Play
|
||||||
|
remotePlayGuideWorkaround: function(funcStr) {
|
||||||
|
const text = 'nexusButtonHandler:this.featureGates.EnableClientGuideInStream';
|
||||||
|
if (!funcStr.includes(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return funcStr.replace(text, `nexusButtonHandler: !window.BX_REMOTE_PLAY_CONFIG && this.featureGates.EnableClientGuideInStream`);
|
||||||
|
},
|
||||||
|
|
||||||
// Disable trackEvent() function
|
// Disable trackEvent() function
|
||||||
disableTrackEvent: function(funcStr) {
|
disableTrackEvent: function(funcStr) {
|
||||||
const text = 'this.trackEvent=';
|
const text = 'this.trackEvent=';
|
||||||
@ -8239,6 +8244,22 @@ if (gamepadFound) {
|
|||||||
funcStr = funcStr.replace(text, 'this.useCombinedAudioVideoStream=true');
|
funcStr = funcStr.replace(text, 'this.useCombinedAudioVideoStream=true');
|
||||||
return funcStr;
|
return funcStr;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
patchStreamHud: function(funcStr) {
|
||||||
|
const text = 'let{onCollapse';
|
||||||
|
if (!funcStr.includes(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the "..." button
|
||||||
|
funcStr = funcStr.replace(text, 'e.guideUI = null;' + text);
|
||||||
|
|
||||||
|
// Remove the TAK Edit button when the touch controller is disabled
|
||||||
|
if (getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'off') {
|
||||||
|
funcStr = funcStr.replace(text, 'e.canShowTakHUD = false;' + text);
|
||||||
|
}
|
||||||
|
return funcStr;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static #PATCH_ORDERS = [
|
static #PATCH_ORDERS = [
|
||||||
@ -8279,6 +8300,9 @@ if (gamepadFound) {
|
|||||||
// Only when playing
|
// Only when playing
|
||||||
static #PLAYING_PATCH_ORDERS = [
|
static #PLAYING_PATCH_ORDERS = [
|
||||||
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'],
|
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayConnectMode'],
|
||||||
|
getPref(Preferences.REMOTE_PLAY_ENABLED) && ['remotePlayGuideWorkaround'],
|
||||||
|
|
||||||
|
['patchStreamHud'],
|
||||||
|
|
||||||
['playVibration'],
|
['playVibration'],
|
||||||
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
|
HAS_TOUCH_SUPPORT && getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all' && ['exposeTouchLayoutManager'],
|
||||||
@ -8558,6 +8582,11 @@ div[class^=HUDButton-module__hiddenContainer] ~ div:not([class^=HUDButton-module
|
|||||||
left: -9999px;
|
left: -9999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove the "Cloud Gaming" text in header */
|
||||||
|
header a[href="/play"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
a.bx-button {
|
a.bx-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@ -8648,31 +8677,27 @@ a.bx-button.bx-full-width {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bx-remote-play-button {
|
.bx-header-remote-play-button {
|
||||||
height: auto;
|
height: auto;
|
||||||
margin-right: 8px !important;
|
margin-right: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bx-remote-play-button svg {
|
.bx-header-remote-play-button svg {
|
||||||
width: 28px;
|
width: 24px;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bx-remote-play-button[disabled] {
|
.bx-header-settings-button {
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bx-settings-button {
|
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-transform: none;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bx-settings-button[data-update-available]::after {
|
.bx-header-settings-button[data-update-available]::after {
|
||||||
content: ' 🌟';
|
content: ' 🌟';
|
||||||
}
|
}
|
||||||
|
|
||||||
.bx-button.bx-focusable, .bx-settings-button {
|
.bx-button.bx-focusable, .bx-header-settings-button {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9903,16 +9928,16 @@ body::-webkit-scrollbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getPreferredServerRegion() {
|
function getPreferredServerRegion(shortName = false) {
|
||||||
let preferredRegion = getPref(Preferences.SERVER_REGION);
|
let preferredRegion = getPref(Preferences.SERVER_REGION);
|
||||||
if (preferredRegion in SERVER_REGIONS) {
|
if (preferredRegion in SERVER_REGIONS) {
|
||||||
return preferredRegion;
|
return shortName ? SERVER_REGIONS[preferredRegion].shortName : preferredRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let regionName in SERVER_REGIONS) {
|
for (let regionName in SERVER_REGIONS) {
|
||||||
const region = SERVER_REGIONS[regionName];
|
const region = SERVER_REGIONS[regionName];
|
||||||
if (region.isDefault) {
|
if (region.isDefault) {
|
||||||
return regionName;
|
return shortName ? region.shortName : regionName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10141,7 +10166,7 @@ function interceptHttpRequests() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const index = request.url.indexOf('.xboxlive.com');
|
const index = request.url.indexOf('.xboxlive.com');
|
||||||
let newUrl = `https://${REMOTE_PLAY_SERVER}.gssv-play-prodxhome` + request.url.substring(index);
|
let newUrl = REMOTE_PLAY_SERVER + request.url.substring(index + 13);
|
||||||
|
|
||||||
request = new Request(newUrl, opts);
|
request = new Request(newUrl, opts);
|
||||||
|
|
||||||
@ -10266,7 +10291,38 @@ function interceptHttpRequests() {
|
|||||||
|
|
||||||
// Get server list
|
// Get server list
|
||||||
if (!Object.keys(SERVER_REGIONS).length) {
|
if (!Object.keys(SERVER_REGIONS).length) {
|
||||||
|
const serverEmojis = {
|
||||||
|
AustraliaEast: '🇦🇺',
|
||||||
|
AustraliaSouthEast: '🇦🇺',
|
||||||
|
BrazilSouth: '🇧🇷',
|
||||||
|
EastUS: '🇺🇸',
|
||||||
|
EastUS2: '🇺🇸',
|
||||||
|
JapanEast: '🇯🇵',
|
||||||
|
KoreaCentral: '🇰🇷',
|
||||||
|
MexicoCentral: '🇲🇽',
|
||||||
|
NorthCentralUs: '🇺🇸',
|
||||||
|
SouthCentralUS: '🇺🇸',
|
||||||
|
UKSouth: '🇬🇧',
|
||||||
|
WestEurope: '🇪🇺',
|
||||||
|
WestUS: '🇺🇸',
|
||||||
|
WestUS2: '🇺🇸',
|
||||||
|
};
|
||||||
|
|
||||||
|
const regex = /\/\/(\w+)\./;
|
||||||
|
|
||||||
for (let region of obj.offeringSettings.regions) {
|
for (let region of obj.offeringSettings.regions) {
|
||||||
|
let shortName = region.name;
|
||||||
|
|
||||||
|
let match = regex.exec(region.baseUri);
|
||||||
|
if (match) {
|
||||||
|
shortName = match[1];
|
||||||
|
if (serverEmojis[region.name]) {
|
||||||
|
shortName = serverEmojis[region.name] + ' ' + shortName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
region.shortName = shortName.toUpperCase();
|
||||||
|
|
||||||
SERVER_REGIONS[region.name] = Object.assign({}, region);
|
SERVER_REGIONS[region.name] = Object.assign({}, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10405,7 +10461,7 @@ function interceptHttpRequests() {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && (url.includes('/titles') || url.includes('/mru'))) {
|
if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && (url.includes('/v2/titles') || url.includes('/mru'))) {
|
||||||
const response = await NATIVE_FETCH(...arg);
|
const response = await NATIVE_FETCH(...arg);
|
||||||
const json = await response.clone().json()
|
const json = await response.clone().json()
|
||||||
for (let game of json.results) {
|
for (let game of json.results) {
|
||||||
@ -10650,7 +10706,7 @@ function setupSettingsUi() {
|
|||||||
const region = SERVER_REGIONS[regionName];
|
const region = SERVER_REGIONS[regionName];
|
||||||
let value = regionName;
|
let value = regionName;
|
||||||
|
|
||||||
let label = regionName;
|
let label = `${region.shortName} - ${regionName}`;
|
||||||
if (region.isDefault) {
|
if (region.isDefault) {
|
||||||
label += ` (${t('default')})`;
|
label += ` (${t('default')})`;
|
||||||
value = 'default';
|
value = 'default';
|
||||||
@ -10742,7 +10798,7 @@ function injectSettingsButton($parent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PREF_PREFERRED_REGION = getPreferredServerRegion();
|
const PREF_PREFERRED_REGION = getPreferredServerRegion(true);
|
||||||
const PREF_LATEST_VERSION = getPref(Preferences.LATEST_VERSION);
|
const PREF_LATEST_VERSION = getPref(Preferences.LATEST_VERSION);
|
||||||
|
|
||||||
const $headerFragment = document.createDocumentFragment();
|
const $headerFragment = document.createDocumentFragment();
|
||||||
@ -10750,7 +10806,7 @@ function injectSettingsButton($parent) {
|
|||||||
// Remote Play button
|
// Remote Play button
|
||||||
if (getPref(Preferences.REMOTE_PLAY_ENABLED)) {
|
if (getPref(Preferences.REMOTE_PLAY_ENABLED)) {
|
||||||
const $remotePlayBtn = createButton({
|
const $remotePlayBtn = createButton({
|
||||||
classes: ['bx-remote-play-button'],
|
classes: ['bx-header-remote-play-button'],
|
||||||
icon: Icon.REMOTE_PLAY,
|
icon: Icon.REMOTE_PLAY,
|
||||||
title: t('remote-play'),
|
title: t('remote-play'),
|
||||||
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE,
|
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE,
|
||||||
@ -10764,7 +10820,7 @@ function injectSettingsButton($parent) {
|
|||||||
|
|
||||||
// Setup Settings button
|
// Setup Settings button
|
||||||
const $settingsBtn = createButton({
|
const $settingsBtn = createButton({
|
||||||
classes: ['bx-settings-button'],
|
classes: ['bx-header-settings-button'],
|
||||||
label: PREF_PREFERRED_REGION,
|
label: PREF_PREFERRED_REGION,
|
||||||
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT,
|
style: ButtonStyle.GHOST | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_HEIGHT,
|
||||||
onClick: e => {
|
onClick: e => {
|
||||||
@ -10889,7 +10945,7 @@ div[data-testid="media-container"] {
|
|||||||
|
|
||||||
|
|
||||||
function checkHeader() {
|
function checkHeader() {
|
||||||
const $button = document.querySelector('.bx-settings-button');
|
const $button = document.querySelector('.bx-header-settings-button');
|
||||||
|
|
||||||
if (!$button) {
|
if (!$button) {
|
||||||
const $rightHeader = document.querySelector('#PageContent div[class*=EdgewaterHeader-module__rightSectionSpacing]');
|
const $rightHeader = document.querySelector('#PageContent div[class*=EdgewaterHeader-module__rightSectionSpacing]');
|
||||||
|
Reference in New Issue
Block a user