mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-29 02:41:44 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
5479eeb66f | |||
a9eb70eaae | |||
0e2066e461 | |||
2ee2469a10 | |||
9056b5926f | |||
730799821b | |||
fed7d489f4 | |||
42b600714e | |||
5dd02f04de | |||
c6c7fc7de5 | |||
169a0a0d4f | |||
90bb75fe56 |
34
README.md
34
README.md
@ -1,23 +1,24 @@
|
||||
# Better xCloud
|
||||
Improve [Xbox Cloud Gaming (xCloud)](https://www.xbox.com/play/) experience.
|
||||
Improve [Xbox Cloud Gaming (xCloud)](https://www.xbox.com/play/) experience on web browser.
|
||||
The main target of this script is Android users, but it should work great on desktop too.
|
||||
|
||||
## Features:
|
||||
## Features
|
||||
|
||||
<img width="474" alt="screenshot" src="https://github.com/redphx/better-xcloud/assets/96280/a0e85915-4e3f-4c1b-8885-eda1c712eeb6">
|
||||
<img width="474" alt="image" src="https://github.com/redphx/better-xcloud/assets/96280/2793d404-3185-4c91-a500-dde362c661dd">
|
||||
|
||||
- Switch region of streaming server.
|
||||
- Prefer IPv6 streaming server (might improve latency).
|
||||
- Force HD stream by disabling bandwidth checking -> xCloud always tries to use the best possible quality.
|
||||
- Skip Xbox splash video.
|
||||
- Make the top-left dots icon invisible while playing. You can still click on it, but it doesn't block the screen anymore.
|
||||
- Stretch video to full sctreen. Useful when you don't have a 16:9 screen.
|
||||
- Adjust video filters (brightness/contrast/saturation).
|
||||
- Hide footer and other UI elements.
|
||||
- Reduce UI animations (the smooth scrolling cannot be disabled).
|
||||
- Disable social features (friends, chat...).
|
||||
- Disable xCloud analytics. The analytics contains statistics of your streaming session, so I'd recommend to enable analytics to help Xbox improve xCloud's experence in the future.
|
||||
- Disable xCloud analytics. The analytics contains statistics of your streaming session, so I'd recommend to allow analytics to help Xbox improve xCloud's experence in the future.
|
||||
|
||||
## How to use:
|
||||
## How to use
|
||||
1. Install [Tampermonkey extension](https://www.tampermonkey.net/) on suppported browsers. It's also available for Firefox on Android.
|
||||
2. Install **Better xCloud**:
|
||||
- [Directly on Github](https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js)
|
||||
@ -41,10 +42,20 @@ Other options (only do one of these):
|
||||
- Add ` 36102dd3-6953-45f6-8b48-031fb95e0e0d` to become a Logitech G Cloud device.
|
||||
- Add ` 0ed22b6f-b61d-41eb-810a-a1ed586a550b` to become a Razer Edge device.
|
||||
|
||||
## Tested on:
|
||||
- Chrome on macOS.
|
||||
- Firefox for Android with Tampermonkey add-on.
|
||||
- *(NOT RECOMMENDED at the moment since its Userscript implementation is not working properly)* [Hermit Browser](https://hermit.chimbori.com) on Android. It supports custom User-Agent and has built-in Userscript support (premium features, only $7.99) so you don't have to install anything else. I built **Better xCloud** just so I could use it with Hermit.
|
||||
## Compatibility
|
||||
✅ = confirmed to be working
|
||||
❓ = not yet tested
|
||||
❌ = not supported (mostly because of lacking Userscript/extension support)
|
||||
⚠️ = see custom notes
|
||||
| | Desktop | Android | iOS |
|
||||
|----------------------------------------|----------|------------------|-----|
|
||||
| Chrome | ✅ | ❌ | ❌ |
|
||||
| Firefox | ✅ | ✅ | ❌ |
|
||||
| Edge | ❓ | ❌ | ❌ |
|
||||
| Safari | ❓ | ❌ | ❓ |
|
||||
| [Hermit](https://hermit.chimbori.com) | ❌ | ⚠️<sup>(1)</sup> | ❌ |
|
||||
|
||||
<sup>1</sup> NOT RECOMMENDED at the moment since its Userscript implementation is not working properly. Non-network related features (skip splash video, video settings...) still work. It's still my favorite app to play xCloud on because it's lightweight, supports both custom User-Agent and Userscript (premium features, only $7.99) without having to install anything else. I built **Better xCloud** just so I could use it with Hermit.
|
||||
|
||||
## FAQ
|
||||
1. **Why is it an Userscript and not extension?**
|
||||
@ -54,7 +65,10 @@ It's because not many browsers on Android support installing extensions (and not
|
||||
That means Tampermonkey is not working properly. Please make sure you're using the latest version or switch to a well-known browser.
|
||||
|
||||
3. **Can I use this with the Xbox Android app?**
|
||||
No you can't. You'll have to modidy the app.
|
||||
No you can't. You'll have to modify the app.
|
||||
|
||||
4. **Will you able to enable "Clarity Boost" feature on non-Edge browsers?**
|
||||
No. "Clarity Boost" feature uses an exclusive API (`Video.msVideoProcessing`) that's only available on Edge browser for desktop at the moment.
|
||||
|
||||
## Acknowledgements
|
||||
**Better xCloud** is inspired by these projects:
|
||||
|
@ -1,19 +1,19 @@
|
||||
// ==UserScript==
|
||||
// @name Better xCloud
|
||||
// @namespace https://github.com/redphx
|
||||
// @version 1.0
|
||||
// @version 1.1.2
|
||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||
// @author redphx
|
||||
// @license MIT
|
||||
// @match https://www.xbox.com/*/play*
|
||||
// @run-at document-start
|
||||
// @grant none
|
||||
// @updateURL https://github.com/redphx/better-xcloud/raw/main/better-xcloud.user.js
|
||||
// @downloadURL https://github.com/redphx/better-xcloud/raw/main/better-xcloud.user.js
|
||||
// @updateURL https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js
|
||||
// @downloadURL https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js
|
||||
// ==/UserScript==
|
||||
'use strict';
|
||||
|
||||
const SCRIPT_VERSION = '1.0';
|
||||
const SCRIPT_VERSION = '1.1.2';
|
||||
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
||||
|
||||
const SERVER_REGIONS = {};
|
||||
@ -30,6 +30,7 @@ class Preferences {
|
||||
static get HIDE_DOTS_ICON() { return 'hide_dots_icon'; }
|
||||
static get REDUCE_ANIMATIONS() { return 'reduce_animations'; }
|
||||
|
||||
static get VIDEO_FILL_FULL_SCREEN() { return 'video_fill_full_screen'; }
|
||||
static get VIDEO_BRIGHTNESS() { return 'video_brightness'; }
|
||||
static get VIDEO_CONTRAST() { return 'video_contrast'; }
|
||||
static get VIDEO_SATURATION() { return 'video_saturation'; }
|
||||
@ -83,6 +84,12 @@ class Preferences {
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
'id': Preferences.VIDEO_FILL_FULL_SCREEN,
|
||||
'label': 'Stretch video to full screen',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
'id': Preferences.VIDEO_SATURATION,
|
||||
'label': 'Video saturation (%)',
|
||||
@ -243,14 +250,24 @@ function addCss() {
|
||||
background-color: #06743f;
|
||||
}
|
||||
|
||||
.better_xcloud_settings_color_bars {
|
||||
.better_xcloud_settings_preview_screen {
|
||||
display: none;
|
||||
width: 100%;
|
||||
aspect-ratio: 16/6;
|
||||
margin-top: 10px;
|
||||
aspect-ratio: 20/9;
|
||||
background: #1e1e1e;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
max-height: 180px;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
.better_xcloud_settings_color_bars div {
|
||||
.better_xcloud_settings_preview_video {
|
||||
display: flex;
|
||||
aspect-ratio: 16/9;
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.better_xcloud_settings_preview_video div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@ -267,6 +284,9 @@ div[class*=NotFocusedDialog] {
|
||||
height: 0px !important;
|
||||
}
|
||||
|
||||
#game-stream video {
|
||||
visibility: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
// Reduce animations
|
||||
@ -290,6 +310,14 @@ div[class*=Grip-module__container] {
|
||||
button[class*=GripHandle-module__container][aria-expanded=true] div[class*=Grip-module__container] {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
button[class*=GripHandle-module__container][aria-expanded=false] {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
div[class*=StreamHUD-module__buttonsContainer] {
|
||||
padding: 0px !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -508,6 +536,31 @@ function createElement(elmName, props = {}) {
|
||||
}
|
||||
|
||||
|
||||
function generateVideoPreviewBox() {
|
||||
const $screen = createElement('div', {'class': 'better_xcloud_settings_preview_screen'});
|
||||
const $video = createElement('div', {'class': 'better_xcloud_settings_preview_video'});
|
||||
|
||||
const COLOR_BARS = [
|
||||
'white',
|
||||
'yellow',
|
||||
'cyan',
|
||||
'green',
|
||||
'magenta',
|
||||
'red',
|
||||
'blue',
|
||||
'black',
|
||||
];
|
||||
|
||||
COLOR_BARS.forEach(color => {
|
||||
$video.appendChild(createElement('div', {
|
||||
style: `background-color: ${color}`,
|
||||
}));
|
||||
});
|
||||
|
||||
$screen.appendChild($video);
|
||||
return $screen;
|
||||
}
|
||||
|
||||
function injectSettingsButton($parent) {
|
||||
if (!$parent) {
|
||||
return;
|
||||
@ -587,13 +640,7 @@ function injectSettingsButton($parent) {
|
||||
}
|
||||
|
||||
PREFS.set(e.target.getAttribute('data-key'), parseInt(e.target.value));
|
||||
|
||||
const filters = getVideoPlayerFilterStyle();
|
||||
const $elm = document.querySelector('.better_xcloud_settings_color_bars');
|
||||
$elm.style.display = 'flex';
|
||||
$elm.style.filter = filters;
|
||||
|
||||
updateVideoPlayerCss();
|
||||
updateVideoPlayerPreview();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -602,8 +649,13 @@ function injectSettingsButton($parent) {
|
||||
type: 'checkbox',
|
||||
'data-key': setting.id,
|
||||
});
|
||||
|
||||
$control.addEventListener('change', e => {
|
||||
PREFS.set(e.target.getAttribute('data-key'), e.target.checked);
|
||||
|
||||
if (setting.id == Preferences.VIDEO_FILL_FULL_SCREEN) {
|
||||
updateVideoPlayerPreview();
|
||||
}
|
||||
});
|
||||
|
||||
setting.value = PREFS.get(setting.id);
|
||||
@ -618,25 +670,8 @@ function injectSettingsButton($parent) {
|
||||
$wrapper.appendChild($elm);
|
||||
}
|
||||
|
||||
const COLOR_BARS = [
|
||||
'white',
|
||||
'yellow',
|
||||
'cyan',
|
||||
'green',
|
||||
'magenta',
|
||||
'red',
|
||||
'blue',
|
||||
'black',
|
||||
];
|
||||
|
||||
const $colorBars = CE('div', {'class': 'better_xcloud_settings_color_bars'});
|
||||
COLOR_BARS.forEach(color => {
|
||||
$colorBars.appendChild(CE('div', {
|
||||
style: `background-color: ${color}`,
|
||||
}));
|
||||
});
|
||||
|
||||
$wrapper.appendChild($colorBars);
|
||||
const $videoPreview = generateVideoPreviewBox();
|
||||
$wrapper.appendChild($videoPreview);
|
||||
|
||||
const $reloadBtn = CE('button', {'class': 'setting_button'}, 'Reload page to reflect changes');
|
||||
$reloadBtn.addEventListener('click', e => window.location.reload());
|
||||
@ -678,21 +713,45 @@ function updateVideoPlayerCss() {
|
||||
let filters = getVideoPlayerFilterStyle();
|
||||
let css = '';
|
||||
if (filters) {
|
||||
css = `#game-stream video {filter: ${filters}}`;
|
||||
css += `filter: ${filters} !important;`;
|
||||
}
|
||||
|
||||
if (PREFS.get(Preferences.VIDEO_FILL_FULL_SCREEN)) {
|
||||
css += 'object-fit: fill !important;';
|
||||
}
|
||||
|
||||
if (css) {
|
||||
css = `#game-stream video {${css}}`;
|
||||
}
|
||||
|
||||
$elm.textContent = css;
|
||||
}
|
||||
|
||||
|
||||
function updateVideoPlayerPreview() {
|
||||
const $screen = document.querySelector('.better_xcloud_settings_preview_screen');
|
||||
$screen.style.display = 'block';
|
||||
|
||||
const filters = getVideoPlayerFilterStyle();
|
||||
const $video = document.querySelector('.better_xcloud_settings_preview_video');
|
||||
$video.style.filter = filters;
|
||||
|
||||
if (PREFS.get(Preferences.VIDEO_FILL_FULL_SCREEN)) {
|
||||
$video.style.height = 'auto';
|
||||
} else {
|
||||
$video.style.height = '100%';
|
||||
}
|
||||
|
||||
updateVideoPlayerCss();
|
||||
}
|
||||
|
||||
|
||||
function checkHeader() {
|
||||
const $button = document.querySelector('#PageContent header .better_xcloud_settings_button');
|
||||
|
||||
if (!$button) {
|
||||
const $rightHeader = document.querySelector('#PageContent header div[class*=EdgewaterHeader-module__rightSectionSpacing]');
|
||||
injectSettingsButton($rightHeader);
|
||||
|
||||
updateVideoPlayerCss();
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,17 +785,25 @@ function patchVideoApi() {
|
||||
|
||||
HTMLMediaElement.prototype.orgPlay = HTMLMediaElement.prototype.play;
|
||||
HTMLMediaElement.prototype.play = function() {
|
||||
if (!this.className.startsWith('XboxSplashVideo')) {
|
||||
return this.orgPlay.apply(this);
|
||||
if (this.className.startsWith('XboxSplashVideo')) {
|
||||
this.volume = 0;
|
||||
this.style.display = 'none';
|
||||
this.dispatchEvent(new Event('ended'));
|
||||
|
||||
return {
|
||||
catch: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
this.volume = 0;
|
||||
this.style.display = 'none';
|
||||
this.dispatchEvent(new Event('ended'));
|
||||
// Show video player when it's ready
|
||||
let showFunc;
|
||||
showFunc = function() {
|
||||
this.style.visibility = 'visible';
|
||||
this.removeEventListener('playing', showFunc);
|
||||
}
|
||||
this.addEventListener('playing', showFunc);
|
||||
|
||||
return {
|
||||
catch: () => {},
|
||||
};
|
||||
return this.orgPlay.apply(this);
|
||||
};
|
||||
}
|
||||
|
||||
@ -787,6 +854,8 @@ interceptHttpRequests();
|
||||
|
||||
patchVideoApi();
|
||||
|
||||
updateVideoPlayerCss();
|
||||
|
||||
// Workaround for Hermit browser
|
||||
var onLoadTriggered = false;
|
||||
window.onload = () => {
|
||||
|
Reference in New Issue
Block a user