Compare commits

...

21 Commits

Author SHA1 Message Date
343e243c77 Version 1.11 (#91)
* Update README.md

* Bump version to 1.11

* Bump version to 1.11
2023-08-12 19:41:14 +07:00
0722e02ab0 Minor fix 2023-08-12 17:56:23 +07:00
c520fde438 Show GamePass app's version (#85) 2023-08-12 17:35:40 +07:00
2f4396d948 Update Settings UI 2023-08-12 17:01:44 +07:00
88d46581bf Add touch's controller button styles: default/white/muted 2023-08-12 16:24:37 +07:00
0cd6106957 Increase TouchController.#dispatchMessage timeout time from 10ms to 100ms 2023-08-12 14:55:54 +07:00
43b932f5d6 Add "Enable microphone on game launch" setting (#89)
* Add "Enable microphone on game launch" setting

* Stream menu: clone the second last button instead of the first one (usually the mic button)

* Close the HUD when clicking clicking on the Video Settings button

* Use a different CSS for the Stream Stats button when it's ON
2023-08-12 14:52:27 +07:00
8315cee7ec Update README.md 2023-08-12 14:31:21 +07:00
9d6db3ed37 Fix for Safari (#88)
* Reload the page until the script is loaded first

* Trying to fix the issue with error page

* Show a message when reloading the page

* Minor refactoring in overrides.inputConfiguration

* Automatically reload the page when running into the "We are sorry..." error message
2023-08-12 10:43:51 +07:00
e85fc9aa47 Update README.md 2023-08-11 08:18:41 +07:00
2d680d63a1 Update README.md 2023-08-10 11:03:22 +07:00
aefd9e9320 Add warnings for Safari 2023-08-08 21:33:40 +07:00
ecaead1522 Update README.md 2023-08-08 08:47:01 +07:00
f71152595f Update issue templates 2023-08-08 08:14:58 +07:00
c633c81c90 Bump version to 1.10.2 2023-08-08 07:37:39 +07:00
c76cf83c58 Bump version to 1.10.2 2023-08-08 07:37:23 +07:00
5a5c65e7a2 Fix the fullscreen bug on Android (#49) 2023-08-08 07:35:34 +07:00
da7ff64471 Update feature_request.md 2023-08-07 08:54:28 +07:00
cd9f53a052 Update issue templates 2023-08-07 08:53:16 +07:00
c782526c32 Update README.md 2023-08-07 07:49:02 +07:00
e8ad1f9350 Update README.md 2023-08-07 07:19:22 +07:00
5 changed files with 302 additions and 123 deletions

View File

@ -7,28 +7,15 @@ assignees: ''
---
**Platform**
- Device: [e.g. Phone, Laptop, Desktop, TV]
- OS: [e.g. Windows, Android, iOS]
- Browser: [e.g. Chrome, Kiwi]
- Browser Version: [e.g. 100]
- Better xCloud Version: [e.g. 1.10]
**Describe the bug**
A clear and concise description of what the bug is.
...
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Platform (please complete the following information):**
- Device: [e.g. Phone/Laptop/Desktop/TV]
- OS: [e.g. Android]
- Browser: [e.g. Chrome, Kiwi]
- Browser Version: [e.g. 100]
- Better xCloud Version: [e.g. 1.4]
**Additional context**
Add any other context about the problem here.
**Screenshots/Videos**
If applicable, add screenshots/videos to help explain your problem.

View File

@ -7,14 +7,10 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
**I'm using:**
- Device:
- OS:
- Browser:
**I want to suggest this feature:**
...

View File

@ -1,9 +1,9 @@
# Better xCloud
Improve [Xbox Cloud Gaming (xCloud)](https://www.xbox.com/play/) experience on web browser.
The main target of this script is mobile users, but it should work great on desktop too.
The main target of this script is mobile users, but it should work great on desktop too.
This script makes me spend more time with xCloud, and I hope the same thing happens to you.
Give this project a 🌟 if you like it. Thank you 🙏.
If you like this project please give it a 🌟. Thank you 🙏.
[![Latest version](https://img.shields.io/github/v/release/redphx/better-xcloud?label=latest)](https://github.com/redphx/better-xcloud/releases)
[![Total stars](https://img.shields.io/github/stars/redphx/better-xcloud?color=%23cca400)](https://github.com/redphx/better-xcloud/stargazers)
@ -11,10 +11,22 @@ Give this project a 🌟 if you like it. Thank you 🙏.
[![Total downloads](https://img.shields.io/github/downloads/redphx/better-xcloud/total?color=%23e15f2c)](https://github.com/redphx/better-xcloud/releases)
-->
## Table of Contents
- [**Features**](#features)
- [**How to install**](#how-to-install)
- [**Compatibility**](#compatibility)
- [**Stream stats**](#stream-stats)
- [**Capture screenshot**](#capture-screenshot)
- [**FAQ**](#faq)
- [**Donation**](#donation)
- [**Acknowledgements**](#acknowledgements)
- [**Disclaimers**](#disclaimers)
## Features
<img width="400" alt="Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/6e74fdaf-dc84-416a-a2bc-5e117a3717e3">
<img width="400" alt="Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/65ee4193-c31d-46fb-b580-196614246ee6">
<br>
<img width="600" alt="Stream HUD" src="https://github.com/redphx/better-xcloud/assets/96280/e30f6514-13ca-41c6-bff2-979573cff956">
<br>
@ -42,7 +54,7 @@ Give this project a 🌟 if you like it. Thank you 🙏.
- **Prefer IPv6 server**
> Might reduce latency.
### Stream quality
### Stream
- **Set target resolution**
> By default you only get 1080p stream when playing on desktop.
> This feature can give you 1080p stream even on mobile, without having to change User-Agent.
@ -54,18 +66,26 @@ Give this project a 🌟 if you like it. Thank you 🙏.
> Comparison video with the setting ON & OFF: https://youtu.be/-9PuBJJSgR4
- **Disable bandwidth checking**
> xCloud won't warn about slow connection speed.
- **Enable microphone on game launch**
> Automatically enable the mic when starting to play a game.
- **Hide mouse cursor on idle**
> Hide the mouse cursor after 3 seconds of not moving.
### Controller
- **🔥 Touch controller**
> Only for mobile (Android/iOS/iPadOS).
### 🔥 Touch controller
- **Availability**
> Only for devices with touch support (Android/iOS/iPadOS/...).
> - **Default**: nothing change.
> - **Off**: stop the touch controller from showing when touching the screen. Useful when you play on a device with a built-in controller like Logitech G Cloud, Steam Deck, etc.
> - **All games**: enable touch controller support for all games. Games with custom layout won't be affected.
> Double-tap anywhere at the bottom of the screen to show/hide the controller. Useful when you're viewing cutscenes.
> ![touch-controller](https://github.com/redphx/better-xcloud/assets/96280/6ad6fc76-74aa-46f8-8fb0-806474f494ad)
- **Button styles**
> - Default
> - Muted
> - All white (only for standard/default controller)
> <img width="400" alt="Button styles" src="https://github.com/redphx/better-xcloud/assets/96280/2bfef2b3-6712-4924-b067-c2312f8c8062">
- **Hide mouse cursor on idle**
> Hide the mouse cursor after 3 seconds of not moving.
### UI
- **Simplify Stream's menu**
@ -93,7 +113,7 @@ Give this project a 🌟 if you like it. Thank you 🙏.
- **Display stream's statuses**
> Region/Server/Codecs/Resolution...
> Current playtime of the session.
> Current battery level.
> Current battery level. Not working on [some browsers](https://caniuse.com/battery-status).
> Estimated total data sent/received.
### Advanced features
@ -105,8 +125,8 @@ Give this project a 🌟 if you like it. Thank you 🙏.
<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.
## How to use
1. Install [Tampermonkey extension](https://www.tampermonkey.net/) on suppported browsers. For Safari, use [Userscripts app](https://apps.apple.com/us/app/userscripts/id1463298887).
## How to install
1. Install [Tampermonkey extension](https://www.tampermonkey.net/) on suppported browsers. For Safari, use [Userscripts app](https://apps.apple.com/us/app/userscripts/id1463298887) (not working properly, see [#81](https://github.com/redphx/better-xcloud/issues/81)).
2. Install **Better xCloud**:
- [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)
@ -117,7 +137,9 @@ Give this project a 🌟 if you like it. Thank you 🙏.
To update manually, just install the script again (you won't lose your settings).
## Tutorial videos
⚠️⚠️⚠️ If you're using Kiwi Browser on Android, make sure to follow the steps correctly and install the script with Tampermonkey (not installing it as an extension), or else it won't work.
### Tutorial videos
If you still have trouble installing **Better xCloud**, you can follow one of these tutorial videos:
- 🇧🇷 [Tudo isso agora tem no xCloud!! (ChipTec)](https://youtu.be/zS8Zy0mYIbU?t=40)
- 🇫🇷 [#Tuto Xbox Cloud Gaming : Ecran ultra large et adieu les bandes noires sur smartphone (Cloud Gaming France)](https://www.youtube.com/watch?v=5U05KoTdDHs)
@ -140,11 +162,11 @@ If you still have trouble installing **Better xCloud**, you can follow one of th
Don't see your browser in the table? If it supports Tampermonkey/Userscript then the answer is likely **"YES"**.
<sup>1</sup> Follow [this guide](https://support.mozilla.org/en-US/kb/find-and-install-add-ons-firefox-android) to install Tampermonkey on Firefox Android. Its Gamepad API doesn't work properly so it might not recognize your controller.
<sup>2, 3</sup> Requires [Userscripts app](https://apps.apple.com/us/app/userscripts/id1463298887) (free & open source).
<sup>2, 3</sup> Requires [Userscripts app](https://apps.apple.com/us/app/userscripts/id1463298887) (free & open-source). Check [this page](https://github.com/redphx/better-xcloud/wiki/Using-with-Safari) before using.
<sup>4</sup> NOT RECOMMENDED at the moment since its Userscript implementation is not working properly (see https://github.com/redphx/better-xcloud/issues/5 for full details).
---
- **Kiwi Browser** is the best choice on Android. All features work, it means you can get 1080p stream + high quality codec profile (the best possible quality).
- **Kiwi Browser** is the best choice on Android. All features work, it means you can get 1080p stream + high-quality codec profile (the best possible quality).
- **Better xCloud** also works on Android TV, but you'll have to sideload the browser APK and need a Bluetooth mouse if you want to interact with the Settings.
## Stream stats
@ -205,7 +227,10 @@ I think it's very unlikely that you'll get banned for using this. Most of the fe
It's because not many browsers on Android support installing extensions (and not all extensions can be installed).
3. **Why doesn't the xCloud website implement *this* or *that* feature from Better xCloud?**
For being an unofficial tool, **Better xCloud** has the luxury to implement anything on the xCloud website. On the xCloud's side, they have a lot more users and devices to support, so it's more difficult for them to implement a new feature. Also it's not easy to explain some of the features of **Better xCloud** to normal xCloud users.
Think of this project as an unofficial beta version of xCloud.
- **Better xCloud** doesn't have to worry about the compatibility much: if it doesn't work on this browser, it can just suggest you switch to another one. xCloud can't do the same.
- On the xCloud's side, they have a lot more users and devices to support, so it's more difficult for them to implement a new feature.
- Also, it's not easy to explain some of the features of **Better xCloud** to normal xCloud users.
4. **Can I use this with the Xbox Android app?**
No, you can't. You'll have to modify the app.
@ -214,7 +239,13 @@ No, you can't. You'll have to modify the app.
No. The "Clarity Boost" feature uses an exclusive API (`Video.msVideoProcessing`) that's only available on Edge browser for desktop at the moment.
6. **Will it be able to request a lower FPS or increase the maximum bitrate (15Mbps) of the stream?**
Sorry, no. The server decides all these settings.
Sorry, no. The server decides all these settings.
7. **What's the meaning behind the name "Better xCloud"?**
It's a reference to an Userscript called "better360" that I created many years ago. I regret not choosing the name "xCloud Enhancement Suite", or XES for short.
## Donation
I'm doing this for fun, so you don't have to donate anything. You're already supporting me by using this script. Save that money toward your Xbox Game Pass Ultimate subscription 😄.
## User-Agent
Moved to [wiki](https://github.com/redphx/better-xcloud/wiki/UserAgent).

View File

@ -1,5 +1,5 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 1.10.1
// @version 1.11
// ==/UserScript==

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 1.10.1
// @version 1.11
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -13,7 +13,79 @@
// ==/UserScript==
'use strict';
const SCRIPT_VERSION = '1.10.1';
console.log(`[Better xCloud] readyState: ${document.readyState}`);
// Quickly create a tree of elements without having to use innerHTML
function createElement(elmName, props = {}) {
const $elm = document.createElement(elmName);
for (let key in props) {
if (!props.hasOwnProperty(key) || $elm.hasOwnProperty(key)) {
continue;
}
$elm.setAttribute(key, props[key]);
}
for (let i = 2, size = arguments.length; i < size; i++) {
const arg = arguments[i];
const argType = typeof arg;
if (argType === 'string' || argType === 'number') {
$elm.textContent = arg;
} else if (arg) {
$elm.appendChild(arg);
}
}
return $elm;
}
const ENABLE_SAFARI_WORKAROUND = true;
if (ENABLE_SAFARI_WORKAROUND && document.readyState !== 'loading') {
// Stop loading
window.stop();
// Show the reloading overlay
const $elm = createElement('div', {'class': 'better-xcloud-reload-overlay'}, 'Failed to run Better xCloud. Retrying, please wait...');
const css = `
.better-xcloud-reload-overlay {
position: fixed;
top: 0;
background: #000000cc;
z-index: 9999;
width: 100%;
line-height: 100vh;
color: #fff;
text-align: center;
font-weight: 400;
font-family: "Segoe UI", SegoeUI, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1.3rem;
}
`;
document.documentElement.appendChild(createElement('style', {}, css));
document.documentElement.appendChild($elm);
// Reload the page
window.location.reload(true);
// Stop processing the script
throw new Error('[Better xCloud] Executing workaround for Safari');
}
// Automatically reload the page when running into the "We are sorry..." error message
window.addEventListener('load', e => {
setTimeout(() => {
if (document.body.classList.contains('legacyBackground')) {
// Has error message -> reload page
window.location.reload(true);
}
}, 2000);
});
const SCRIPT_VERSION = '1.11';
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
const SERVER_REGIONS = {};
@ -22,6 +94,7 @@ var $STREAM_VIDEO;
var $SCREENSHOT_CANVAS;
var GAME_TITLE_ID;
const HAS_TOUCH_SUPPORT = ('ontouchstart' in window || navigator.maxTouchPoints > 0);
const TOUCH_SUPPORTED_GAME_IDS = new Set();
// Credit: https://phosphoricons.com
@ -99,6 +172,9 @@ class TouchController {
}
static setup() {
const $style = document.createElement('style');
document.documentElement.appendChild($style);
const $bar = createElement('div', {'id': 'better-xcloud-touch-controller-bar'});
document.documentElement.appendChild($bar);
@ -120,8 +196,32 @@ class TouchController {
TouchController.#$bar = $bar;
const PREF_STYLE_STANDARD = PREFS.get(Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD);
const PREF_STYLE_CUSTOM = PREFS.get(Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM);
RTCPeerConnection.prototype.orgCreateDataChannel = RTCPeerConnection.prototype.createDataChannel;
RTCPeerConnection.prototype.createDataChannel = function() {
// Apply touch controller's style
const $babylonCanvas = document.getElementById('babylon-canvas');
let filter = '';
if (TouchController.#enable) {
if (PREF_STYLE_STANDARD === 'white') {
filter = 'grayscale(1) brightness(2)';
} else if (PREF_STYLE_STANDARD === 'muted') {
filter = 'sepia(0.5)';
}
} else if (PREF_STYLE_CUSTOM === 'muted') {
filter = 'sepia(0.5)';
}
if (filter) {
$style.textContent = `
#babylon-canvas {
filter: ${filter} !important;
}
`;
}
const dataChannel = this.orgCreateDataChannel.apply(this, arguments);
if (!TouchController.#enable) {
return dataChannel;
@ -131,7 +231,7 @@ class TouchController {
// Fix sometimes the touch controller doesn't show at the beginning
dataChannel.addEventListener('open', e => {
TouchController.#show();
setTimeout(TouchController.#show, 1000);
});
dataChannel.addEventListener('message', msg => {
@ -405,7 +505,7 @@ class StreamStats {
static #quickGlanceObserver;
static start(glancing=false) {
if (!StreamStats.isHidden() || (glancing && StreamStats.#isGlancing())) {
if (!StreamStats.isHidden() || (glancing && StreamStats.isGlancing())) {
return;
}
@ -416,7 +516,7 @@ class StreamStats {
}
static stop(glancing=false) {
if (glancing && !StreamStats.#isGlancing()) {
if (glancing && !StreamStats.isGlancing()) {
return;
}
@ -429,7 +529,7 @@ class StreamStats {
}
static toggle() {
if (StreamStats.#isGlancing()) {
if (StreamStats.isGlancing()) {
StreamStats.#$container.setAttribute('data-display', 'fixed');
} else {
StreamStats.isHidden() ? StreamStats.start() : StreamStats.stop();
@ -443,7 +543,7 @@ class StreamStats {
}
static isHidden = () => StreamStats.#$container.classList.contains('better-xcloud-gone');
static #isGlancing = () => StreamStats.#$container.getAttribute('data-display') === 'glancing';
static isGlancing = () => StreamStats.#$container.getAttribute('data-display') === 'glancing';
static quickGlanceSetup() {
if (StreamStats.#quickGlanceObserver) {
@ -551,7 +651,7 @@ class StreamStats {
static hideSettingsUi() {
StreamStats.#$settings.style.display = 'none';
if (StreamStats.#isGlancing() && !PREFS.get(Preferences.STATS_QUICK_GLANCE)) {
if (StreamStats.isGlancing() && !PREFS.get(Preferences.STATS_QUICK_GLANCE)) {
StreamStats.stop();
}
}
@ -701,15 +801,18 @@ class PreloadedState {
static override() {
Object.defineProperty(window, '__PRELOADED_STATE__', {
configurable: true,
get: () => this._state,
set: (state) => {
get: () => {
// Override User-Agent
const userAgent = UserAgent.spoof();
if (userAgent) {
state.appContext.requestInfo.userAgent = userAgent;
state.appContext.requestInfo.origin = 'https://www.xbox.com';
this._state.appContext.requestInfo.userAgent = userAgent;
}
return this._state;
},
set: (state) => {
this._state = state;
// Get a list of touch-supported games
if (PREFS.get(Preferences.STREAM_TOUCH_CONTROLLER) === 'all') {
let titles = {};
@ -725,7 +828,6 @@ class PreloadedState {
}
}
}
this._state = state;
}
});
}
@ -745,9 +847,12 @@ class Preferences {
static get USER_AGENT_PROFILE() { return 'user_agent_profile'; }
static get USER_AGENT_CUSTOM() { return 'user_agent_custom'; }
static get STREAM_HIDE_IDLE_CURSOR() { return 'stream_hide_idle_cursor';}
static get STREAM_TOUCH_CONTROLLER() { return 'stream_touch_controller'; }
static get STREAM_SIMPLIFY_MENU() { return 'stream_simplify_menu'; }
static get STREAM_TOUCH_CONTROLLER() { return 'stream_touch_controller'; }
static get STREAM_TOUCH_CONTROLLER_STYLE_STANDARD() { return 'stream_touch_controller_style_standard'; }
static get STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM() { return 'stream_touch_controller_style_custom'; }
static get SCREENSHOT_BUTTON_POSITION() { return 'screenshot_button_position'; }
static get BLOCK_TRACKING() { return 'block_tracking'; }
static get BLOCK_SOCIAL_FEATURES() { return 'block_social_features'; }
@ -761,6 +866,8 @@ class Preferences {
static get VIDEO_CONTRAST() { return 'video_contrast'; }
static get VIDEO_SATURATION() { return 'video_saturation'; }
static get AUDIO_MIC_ON_PLAYING() { return 'audio_mic_on_playing'; }
static get STATS_SHOW_WHEN_PLAYING() { return 'stats_show_when_playing'; }
static get STATS_QUICK_GLANCE() { return 'stats_quick_glance'; }
static get STATS_POSITION() { return 'stats_position'; }
@ -855,6 +962,21 @@ class Preferences {
'off': 'Off',
},
},
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
'default': 'default',
'options': {
'default': 'Default colors',
'white': 'All white',
'muted': 'Muted colors',
},
},
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: {
'default': 'default',
'options': {
'default': 'Default colors',
'muted': 'Muted colors',
},
},
[Preferences.STREAM_SIMPLIFY_MENU]: {
'default': false,
},
@ -901,6 +1023,9 @@ class Preferences {
'min': 0,
'max': 150,
},
[Preferences.AUDIO_MIC_ON_PLAYING]: {
'default': false,
},
[Preferences.STATS_SHOW_WHEN_PLAYING]: {
'default': false,
},
@ -969,6 +1094,11 @@ 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';
}
const value = this._prefs[key];
if (typeof value !== 'undefined' && value !== null && value !== '') {
@ -1223,6 +1353,13 @@ function addCss() {
background-color: #00753c;
}
.better-xcloud-settings-app-version {
margin-top: 10px;
text-align: center;
color: #484848;
font-size: 12px;
}
.better-xcloud-settings-custom-user-agent {
display: block;
width: 100%;
@ -1527,6 +1664,12 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
font-family: Consolas, "Courier New", Courier, monospace;
}
.better-xcloud-stream-menu-button-on {
fill: #000 !important;
background-color: #fff !important;
color: #000 !important;
}
#better-xcloud-touch-controller-bar {
display: none;
opacity: 0;
@ -1777,9 +1920,12 @@ function interceptHttpRequests() {
const PREF_PREFER_IPV6_SERVER = PREFS.get(Preferences.PREFER_IPV6_SERVER);
const PREF_STREAM_TARGET_RESOLUTION = PREFS.get(Preferences.STREAM_TARGET_RESOLUTION);
const PREF_STREAM_PREFERRED_LOCALE = PREFS.get(Preferences.STREAM_PREFERRED_LOCALE);
const PREF_STREAM_TOUCH_CONTROLLER = PREFS.get(Preferences.STREAM_TOUCH_CONTROLLER);
const PREF_USE_DESKTOP_CODEC = PREFS.get(Preferences.USE_DESKTOP_CODEC);
const PREF_STREAM_TOUCH_CONTROLLER = PREFS.get(Preferences.STREAM_TOUCH_CONTROLLER);
const PREF_AUDIO_MIC_ON_PLAYING = PREFS.get(Preferences.AUDIO_MIC_ON_PLAYING);
const PREF_OVERRIDE_CONFIGURATION = PREF_AUDIO_MIC_ON_PLAYING || PREF_STREAM_TOUCH_CONTROLLER === 'all';
const orgFetch = window.fetch;
window.fetch = async (...arg) => {
const request = arg[0];
@ -1859,21 +2005,27 @@ function interceptHttpRequests() {
return orgFetch(...arg);
}
if (PREF_STREAM_TOUCH_CONTROLLER === 'all' && url.endsWith('/configuration') && url.includes('/sessions/cloud/') && request.method === 'GET') {
TouchController.disable();
// Get game ID from window.location
const match = window.location.pathname.match(/\/launch\/[^\/]+\/([\w\d]+)/);
// Check touch support
if (match && !TOUCH_SUPPORTED_GAME_IDS.has(match[1])) {
TouchController.enable();
}
if (PREF_OVERRIDE_CONFIGURATION && url.endsWith('/configuration') && url.includes('/sessions/cloud/') && request.method === 'GET') {
const promise = orgFetch(...arg);
if (!TouchController.isEnabled()) {
return promise;
// Touch controller for all games
if (PREF_STREAM_TOUCH_CONTROLLER === 'all') {
TouchController.disable();
// Get game ID from window.location
const match = window.location.pathname.match(/\/launch\/[^\/]+\/([\w\d]+)/);
// Check touch support
if (match && !TOUCH_SUPPORTED_GAME_IDS.has(match[1])) {
TouchController.enable();
}
// If both settings are invalid -> return promise
if (!PREF_AUDIO_MIC_ON_PLAYING && !TouchController.isEnabled()) {
return promise;
}
}
// Intercept result to make xCloud show the touch controller
// Intercept configurations
return promise.then(response => {
return response.clone().text().then(text => {
if (!text.length) {
@ -1882,10 +2034,20 @@ function interceptHttpRequests() {
const obj = JSON.parse(text);
let overrides = JSON.parse(obj.clientStreamingConfigOverrides || '{}') || {};
overrides.inputConfiguration = {
enableTouchInput: true,
maxTouchPoints: 10,
};
// Enable touch controller
if (TouchController.isEnabled()) {
overrides.inputConfiguration = overrides.inputConfiguration || {};
overrides.inputConfiguration.enableTouchInput = true;
overrides.inputConfiguration.maxTouchPoints = 10;
}
// Enable mic
if (PREF_AUDIO_MIC_ON_PLAYING) {
overrides.audioConfiguration = overrides.audioConfiguration || {};
overrides.audioConfiguration.enableMicrophone = true;
}
obj.clientStreamingConfigOverrides = JSON.stringify(overrides);
response.json = () => Promise.resolve(obj);
@ -1950,33 +2112,6 @@ function interceptHttpRequests() {
}
// Quickly create a tree of elements without having to use innerHTML
function createElement(elmName, props = {}) {
const $elm = document.createElement(elmName);
for (let key in props) {
if (!props.hasOwnProperty(key) || $elm.hasOwnProperty(key)) {
continue;
}
let value = props[key];
$elm.setAttribute(key, value);
}
for (let i = 2, size = arguments.length; i < size; i++) {
const arg = arguments[i];
const argType = typeof arg;
if (argType == 'string' || argType == 'number') {
$elm.innerText = arg;
} else if (arg) {
$elm.appendChild(arg);
}
}
return $elm;
}
function injectSettingsButton($parent) {
if (!$parent) {
return;
@ -2037,15 +2172,18 @@ function injectSettingsButton($parent) {
[Preferences.STREAM_PREFERRED_LOCALE]: 'Preferred game\'s language',
[Preferences.PREFER_IPV6_SERVER]: 'Prefer IPv6 server',
},
'Stream quality': {
'Stream': {
[Preferences.STREAM_TARGET_RESOLUTION]: 'Target resolution',
[Preferences.USE_DESKTOP_CODEC]: 'Force high-quality codec',
[Preferences.DISABLE_BANDWIDTH_CHECKING]: 'Disable bandwidth checking',
},
'Controller': {
[Preferences.STREAM_TOUCH_CONTROLLER]: 'Touch controller',
[Preferences.AUDIO_MIC_ON_PLAYING]: 'Enable microphone on game launch',
[Preferences.STREAM_HIDE_IDLE_CURSOR]: 'Hide mouse cursor on idle',
},
'Touch controller': {
[Preferences.STREAM_TOUCH_CONTROLLER]: 'Availability',
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: 'Standard layout\'s button style',
[Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: 'Custom layout\'s button style',
},
'UI': {
[Preferences.STREAM_SIMPLIFY_MENU]: 'Simplify Stream\'s menu',
[Preferences.SKIP_SPLASH_VIDEO]: 'Skip Xbox splash video',
@ -2133,9 +2271,9 @@ function injectSettingsButton($parent) {
$control.disabled = true;
$control.checked = false;
$control.title = 'Your browser doesn\'t support this feature';
} else if (settingId === Preferences.STREAM_TOUCH_CONTROLLER) {
} else if (!HAS_TOUCH_SUPPORT) {
// Disable this setting for non-touchable devices
if (!('ontouchstart'in window) && navigator.maxTouchPoints === 0) {
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 = 'Your device doesn\'t have touch support';
}
@ -2166,6 +2304,13 @@ function injectSettingsButton($parent) {
});
$wrapper.appendChild($reloadBtn);
// Show Game Pass app version
try {
const appVersion = document.querySelector('meta[name=gamepass-app-version]').content;
const appDate = new Date(document.querySelector('meta[name=gamepass-app-date]').content).toISOString().substring(0, 10);
$wrapper.appendChild(CE('div', {'class': 'better-xcloud-settings-app-version'}, `GamePass app ${appVersion} (${appDate})`));
} catch (e) {}
// Add Settings UI to the web page
const $pageContent = document.getElementById('PageContent');
$pageContent.parentNode.insertBefore($container, $pageContent);
@ -2258,7 +2403,7 @@ function cloneStreamMenuButton($orgButton, label, svg_icon) {
}
function injectVideoSettingsButton() {
function injectStreamMenuButtons() {
const $screen = document.querySelector('#PageContent section[class*=PureScreens]');
if (!$screen) {
return;
@ -2300,7 +2445,8 @@ function injectVideoSettingsButton() {
return;
}
const $orgButton = node.querySelector('div > div > button');
// Get the second last button
const $orgButton = node.querySelector('div > div > button:nth-last-child(2)');
if (!$orgButton) {
return;
}
@ -2311,6 +2457,9 @@ function injectVideoSettingsButton() {
e.preventDefault();
e.stopPropagation();
// Close HUD
$btnCloseHud.click();
// Show Quick settings bar
$quickBar.style.display = 'flex';
@ -2342,6 +2491,9 @@ function injectVideoSettingsButton() {
StreamStats.toggle();
});
const btnStreamStatsOn = (!StreamStats.isHidden() && !StreamStats.isGlancing());
$btnStreamStats.classList.toggle('better-xcloud-stream-menu-button-on', btnStreamStatsOn);
// Insert after Video Settings button
$orgButton.parentElement.insertBefore($btnStreamStats, $btnVideoSettings);
@ -2413,7 +2565,7 @@ function patchVideoApi() {
}
this.addEventListener('playing', showFunc);
injectVideoSettingsButton();
injectStreamMenuButtons();
return this.orgPlay.apply(this);
};
@ -2784,6 +2936,22 @@ function onStreamStarted($video) {
}
function disablePwa() {
const userAgent = (window.navigator.orgUserAgent || window.navigator.userAgent || '').toLowerCase();
if (!userAgent) {
return;
}
// Check if it's Safari on mobile
if (userAgent.includes('mobile') && userAgent.includes('safari') && !userAgent.includes('chrom')) {
// Disable the PWA prompt
Object.defineProperty(window.navigator, 'standalone', {
value: true,
});
}
}
// Hide Settings UI when navigate to another page
window.addEventListener('xcloud_popstate', onHistoryChanged);
window.addEventListener('popstate', onHistoryChanged);
@ -2831,7 +2999,4 @@ setupVideoSettingsBar();
setupScreenshotButton();
StreamStats.render();
// Disable PWA prompt in Safari on iOS/iPadOS
Object.defineProperty(window.navigator, 'standalone', {
value: true,
});
disablePwa();