mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-08-04 20:36:41 +02:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
033ac31333 | ||
![]() |
1f754d4a1d | ||
![]() |
0d39ccf8bf | ||
![]() |
966d7f2f6c | ||
![]() |
fdbf618253 | ||
![]() |
aaa7612293 | ||
![]() |
d578718958 | ||
![]() |
46647dbffd |
@@ -1,5 +1,5 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 3.1.1
|
// @version 3.1.3
|
||||||
// ==/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.1.1
|
// @version 3.1.3
|
||||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||||
// @author redphx
|
// @author redphx
|
||||||
// @license MIT
|
// @license MIT
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const SCRIPT_VERSION = '3.1.1';
|
const SCRIPT_VERSION = '3.1.3';
|
||||||
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
||||||
|
|
||||||
const ENABLE_XCLOUD_LOGGER = false;
|
const ENABLE_XCLOUD_LOGGER = false;
|
||||||
@@ -44,11 +44,39 @@ const BxEvent = {
|
|||||||
JUMP_BACK_IN_READY: 'bx-jump-back-in-ready',
|
JUMP_BACK_IN_READY: 'bx-jump-back-in-ready',
|
||||||
POPSTATE: 'bx-popstate',
|
POPSTATE: 'bx-popstate',
|
||||||
|
|
||||||
|
STREAM_LOADING: 'bx-stream-loading',
|
||||||
STREAM_STARTING: 'bx-stream-starting',
|
STREAM_STARTING: 'bx-stream-starting',
|
||||||
STREAM_STARTED: 'bx-stream-started',
|
STREAM_STARTED: 'bx-stream-started',
|
||||||
|
STREAM_PLAYING: 'bx-stream-playing',
|
||||||
STREAM_STOPPED: 'bx-stream-stopped',
|
STREAM_STOPPED: 'bx-stream-stopped',
|
||||||
|
STREAM_ERROR_PAGE: 'bx-stream-error-page',
|
||||||
|
|
||||||
|
STREAM_MENU_SHOWN: 'bx-stream-menu-shown',
|
||||||
|
STREAM_MENU_HIDDEN: 'bx-stream-menu-hidden',
|
||||||
|
|
||||||
|
STREAM_WEBRTC_CONNECTED: 'bx-stream-webrtc-connected',
|
||||||
|
STREAM_WEBRTC_DISCONNECTED: 'bx-stream-webrtc-disconnected',
|
||||||
|
|
||||||
CUSTOM_TOUCH_LAYOUTS_LOADED: 'bx-custom-touch-layouts-loaded',
|
CUSTOM_TOUCH_LAYOUTS_LOADED: 'bx-custom-touch-layouts-loaded',
|
||||||
|
|
||||||
|
DATA_CHANNEL_CREATED: 'bx-data-channel-created',
|
||||||
|
|
||||||
|
dispatch: (target, eventName, data) => {
|
||||||
|
if (!eventName) {
|
||||||
|
alert('BxEvent.dispatch(): eventName is null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = new Event(eventName);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
for (const key in data) {
|
||||||
|
event[key] = data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Quickly create a tree of elements without having to use innerHTML
|
// Quickly create a tree of elements without having to use innerHTML
|
||||||
@@ -3068,7 +3096,29 @@ class RemotePlay {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GSSV_TOKEN = JSON.parse(localStorage.getItem('xboxcom_xbl_user_info')).tokens['http://gssv.xboxlive.com/'].token;
|
let GSSV_TOKEN;
|
||||||
|
try {
|
||||||
|
GSSV_TOKEN = JSON.parse(localStorage.getItem('xboxcom_xbl_user_info')).tokens['http://gssv.xboxlive.com/'].token;
|
||||||
|
} catch (e) {
|
||||||
|
for (let i = 0; i < localStorage.length; i++){
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if (!key.startsWith('Auth.User.')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = JSON.parse(localStorage.getItem(key));
|
||||||
|
for (const token of json.tokens) {
|
||||||
|
if (!token.relyingParty.includes('gssv.xboxlive.com')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSSV_TOKEN = token.tokenData.token;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fetch('https://xhome.gssv-play-prod.xboxlive.com/v2/login/user', {
|
fetch('https://xhome.gssv-play-prod.xboxlive.com/v2/login/user', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -3317,20 +3367,24 @@ class LoadingScreen {
|
|||||||
LoadingScreen.#orgWebTitle && (document.title = LoadingScreen.#orgWebTitle);
|
LoadingScreen.#orgWebTitle && (document.title = LoadingScreen.#orgWebTitle);
|
||||||
LoadingScreen.#$waitTimeBox && LoadingScreen.#$waitTimeBox.classList.add('bx-gone');
|
LoadingScreen.#$waitTimeBox && LoadingScreen.#$waitTimeBox.classList.add('bx-gone');
|
||||||
|
|
||||||
const $rocketBg = document.querySelector('#game-stream rect[width="800"]');
|
if (LoadingScreen.#$bgStyle) {
|
||||||
$rocketBg && $rocketBg.addEventListener('transitionend', e => {
|
const $rocketBg = document.querySelector('#game-stream rect[width="800"]');
|
||||||
LoadingScreen.#$bgStyle.textContent += `
|
$rocketBg && $rocketBg.addEventListener('transitionend', e => {
|
||||||
|
LoadingScreen.#$bgStyle.textContent += `
|
||||||
#game-stream {
|
#game-stream {
|
||||||
background: #000 !important;
|
background: #000 !important;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|
||||||
LoadingScreen.#$bgStyle.textContent += `
|
LoadingScreen.#$bgStyle.textContent += `
|
||||||
#game-stream rect[width="800"] {
|
#game-stream rect[width="800"] {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadingScreen.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
static reset() {
|
static reset() {
|
||||||
@@ -3386,12 +3440,12 @@ class TouchController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static #show() {
|
static #show() {
|
||||||
TouchController.loadCustomLayout(GAME_XBOX_TITLE_ID, TouchController.#currentLayoutId, 0);
|
document.querySelector('#BabylonCanvasContainer-main').parentElement.classList.remove('bx-offscreen');
|
||||||
TouchController.#showing = true;
|
TouchController.#showing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #hide() {
|
static #hide() {
|
||||||
TouchController.#dispatchMessage(TouchController.#EVENT_HIDE_CONTROLLER);
|
document.querySelector('#BabylonCanvasContainer-main').parentElement.classList.add('bx-offscreen');
|
||||||
TouchController.#showing = false;
|
TouchController.#showing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3403,8 +3457,8 @@ class TouchController {
|
|||||||
TouchController.#showing ? TouchController.#hide() : TouchController.#show();
|
TouchController.#showing ? TouchController.#hide() : TouchController.#show();
|
||||||
}
|
}
|
||||||
|
|
||||||
static enableBar() {
|
static #toggleBar(value) {
|
||||||
TouchController.#$bar && TouchController.#$bar.setAttribute('data-showing', true);
|
TouchController.#$bar && TouchController.#$bar.setAttribute('data-showing', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static reset() {
|
static reset() {
|
||||||
@@ -3424,9 +3478,9 @@ class TouchController {
|
|||||||
|
|
||||||
static getCustomLayouts(xboxTitleId) {
|
static getCustomLayouts(xboxTitleId) {
|
||||||
const dispatchLayouts = data => {
|
const dispatchLayouts = data => {
|
||||||
const event = new Event(BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED);
|
BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, {
|
||||||
event.data = data;
|
data: data,
|
||||||
window.dispatchEvent(event);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
xboxTitleId = '' + xboxTitleId;
|
xboxTitleId = '' + xboxTitleId;
|
||||||
@@ -3442,11 +3496,7 @@ class TouchController {
|
|||||||
url += `${xboxTitleId}.json`;
|
url += `${xboxTitleId}.json`;
|
||||||
}
|
}
|
||||||
NATIVE_FETCH(url)
|
NATIVE_FETCH(url)
|
||||||
.then(resp => resp.json(), () => {
|
.then(resp => resp.json())
|
||||||
TouchController.#customLayouts[xboxTitleId] = null;
|
|
||||||
// Wait for BX_EXPOSED.touch_layout_manager
|
|
||||||
setTimeout(() => dispatchLayouts(null), 1000);
|
|
||||||
})
|
|
||||||
.then(json => {
|
.then(json => {
|
||||||
// Normalize data
|
// Normalize data
|
||||||
const schema_version = json.schema_version || 1;
|
const schema_version = json.schema_version || 1;
|
||||||
@@ -3468,6 +3518,11 @@ class TouchController {
|
|||||||
|
|
||||||
// Wait for BX_EXPOSED.touch_layout_manager
|
// Wait for BX_EXPOSED.touch_layout_manager
|
||||||
setTimeout(() => dispatchLayouts(json), 1000);
|
setTimeout(() => dispatchLayouts(json), 1000);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
TouchController.#customLayouts[xboxTitleId] = null;
|
||||||
|
// Wait for BX_EXPOSED.touch_layout_manager
|
||||||
|
setTimeout(() => dispatchLayouts(null), 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3503,11 +3558,14 @@ class TouchController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static setup() {
|
static setup() {
|
||||||
|
const $fragment = document.createDocumentFragment();
|
||||||
const $style = document.createElement('style');
|
const $style = document.createElement('style');
|
||||||
document.documentElement.appendChild($style);
|
$fragment.appendChild($style);
|
||||||
|
|
||||||
const $bar = createElement('div', {'id': 'bx-touch-controller-bar'});
|
const $bar = createElement('div', {'id': 'bx-touch-controller-bar'});
|
||||||
document.documentElement.appendChild($bar);
|
$fragment.appendChild($bar);
|
||||||
|
|
||||||
|
document.documentElement.appendChild($fragment);
|
||||||
|
|
||||||
// Setup double-tap event
|
// Setup double-tap event
|
||||||
let clickTimeout;
|
let clickTimeout;
|
||||||
@@ -3531,11 +3589,10 @@ class TouchController {
|
|||||||
const PREF_STYLE_STANDARD = getPref(Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD);
|
const PREF_STYLE_STANDARD = getPref(Preferences.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD);
|
||||||
const PREF_STYLE_CUSTOM = getPref(Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM);
|
const PREF_STYLE_CUSTOM = getPref(Preferences.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM);
|
||||||
|
|
||||||
const nativeCreateDataChannel = RTCPeerConnection.prototype.createDataChannel;
|
window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, e => {
|
||||||
RTCPeerConnection.prototype.createDataChannel = function() {
|
const dataChannel = e.dataChannel;
|
||||||
const dataChannel = nativeCreateDataChannel.apply(this, arguments);
|
if (!dataChannel || dataChannel.label !== 'message') {
|
||||||
if (dataChannel.label !== 'message') {
|
return;
|
||||||
return dataChannel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply touch controller's style
|
// Apply touch controller's style
|
||||||
@@ -3552,6 +3609,8 @@ class TouchController {
|
|||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
$style.textContent = `#babylon-canvas { filter: ${filter} !important; }`;
|
$style.textContent = `#babylon-canvas { filter: ${filter} !important; }`;
|
||||||
|
} else {
|
||||||
|
$style.textContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
TouchController.#dataChannel = dataChannel;
|
TouchController.#dataChannel = dataChannel;
|
||||||
@@ -3576,14 +3635,19 @@ class TouchController {
|
|||||||
try {
|
try {
|
||||||
if (msg.data.includes('/titleinfo')) {
|
if (msg.data.includes('/titleinfo')) {
|
||||||
const json = JSON.parse(JSON.parse(msg.data).content);
|
const json = JSON.parse(JSON.parse(msg.data).content);
|
||||||
const xboxTitleId = parseInt(json.titleid, 16);
|
TouchController.#toggleBar(json.focused);
|
||||||
GAME_XBOX_TITLE_ID = xboxTitleId;
|
|
||||||
}
|
|
||||||
} catch (e) { console.log(e) }
|
|
||||||
});
|
|
||||||
|
|
||||||
return dataChannel;
|
if (!json.focused) {
|
||||||
};
|
TouchController.#show();
|
||||||
|
}
|
||||||
|
|
||||||
|
GAME_XBOX_TITLE_ID = parseInt(json.titleid, 16);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4725,8 +4789,8 @@ class MkbHandler {
|
|||||||
this.#$message.addEventListener('click', this.#onActivatePointerLock);
|
this.#$message.addEventListener('click', this.#onActivatePointerLock);
|
||||||
document.documentElement.appendChild(this.#$message);
|
document.documentElement.appendChild(this.#$message);
|
||||||
|
|
||||||
window.addEventListener('bx-stream-menu-shown', this.#onStreamMenuShown);
|
window.addEventListener(BxEvent.STREAM_MENU_SHOWN, this.#onStreamMenuShown);
|
||||||
window.addEventListener('bx-stream-menu-hidden', this.#onStreamMenuHidden);
|
window.addEventListener(BxEvent.STREAM_MENU_HIDDEN, this.#onStreamMenuHidden);
|
||||||
|
|
||||||
this.#waitForPointerLock(true);
|
this.#waitForPointerLock(true);
|
||||||
}
|
}
|
||||||
@@ -4743,8 +4807,8 @@ class MkbHandler {
|
|||||||
document.removeEventListener('pointerlockchange', this.#onPointerLockChange);
|
document.removeEventListener('pointerlockchange', this.#onPointerLockChange);
|
||||||
document.removeEventListener('pointerlockerror', this.#onPointerLockError);
|
document.removeEventListener('pointerlockerror', this.#onPointerLockError);
|
||||||
|
|
||||||
window.removeEventListener('bx-stream-menu-shown', this.#onStreamMenuShown);
|
window.removeEventListener(BxEvent.STREAM_MENU_SHOWN, this.#onStreamMenuShown);
|
||||||
window.removeEventListener('bx-stream-menu-hidden', this.#onStreamMenuHidden);
|
window.removeEventListener(BxEvent.STREAM_MENU_HIDDEN, this.#onStreamMenuHidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
start = () => {
|
start = () => {
|
||||||
@@ -4765,9 +4829,9 @@ class MkbHandler {
|
|||||||
virtualGamepad.connected = true;
|
virtualGamepad.connected = true;
|
||||||
virtualGamepad.timestamp = performance.now();
|
virtualGamepad.timestamp = performance.now();
|
||||||
|
|
||||||
const event = new Event('gamepadconnected');
|
BxEvent.dispatch(window, 'gamepadconnected', {
|
||||||
event.gamepad = virtualGamepad;
|
gamepad: virtualGamepad,
|
||||||
window.dispatchEvent(event);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stop = () => {
|
stop = () => {
|
||||||
@@ -4777,9 +4841,9 @@ class MkbHandler {
|
|||||||
virtualGamepad.connected = false;
|
virtualGamepad.connected = false;
|
||||||
virtualGamepad.timestamp = performance.now();
|
virtualGamepad.timestamp = performance.now();
|
||||||
|
|
||||||
const event = new Event('gamepaddisconnected');
|
BxEvent.dispatch(window, 'gamepaddisconnected', {
|
||||||
event.gamepad = virtualGamepad;
|
gamepad: virtualGamepad,
|
||||||
window.dispatchEvent(event);
|
});
|
||||||
|
|
||||||
window.navigator.getGamepads = this.#nativeGetGamepads;
|
window.navigator.getGamepads = this.#nativeGetGamepads;
|
||||||
|
|
||||||
@@ -4793,6 +4857,16 @@ class MkbHandler {
|
|||||||
window.removeEventListener('wheel', this.#onWheelEvent);
|
window.removeEventListener('wheel', this.#onWheelEvent);
|
||||||
window.removeEventListener('contextmenu', this.#disableContextMenu);
|
window.removeEventListener('contextmenu', this.#disableContextMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static setupEvents() {
|
||||||
|
window.addEventListener(BxEvent.STREAM_PLAYING, e => {
|
||||||
|
// Enable MKB
|
||||||
|
if (getPref(Preferences.MKB_ENABLED) && (!ENABLE_NATIVE_MKB_BETA || !window.NATIVE_MKB_TITLES.includes(GAME_PRODUCT_ID))) {
|
||||||
|
console.log('Emulate MKB');
|
||||||
|
MkbHandler.INSTANCE.init();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5518,11 +5592,10 @@ class VibrationManager {
|
|||||||
|
|
||||||
VibrationManager.updateGlobalVars();
|
VibrationManager.updateGlobalVars();
|
||||||
|
|
||||||
const orgCreateDataChannel = RTCPeerConnection.prototype.createDataChannel;
|
window.addEventListener(BxEvent.DATA_CHANNEL_CREATED, e => {
|
||||||
RTCPeerConnection.prototype.createDataChannel = function() {
|
const dataChannel = e.dataChannel;
|
||||||
const dataChannel = orgCreateDataChannel.apply(this, arguments);
|
if (!dataChannel || dataChannel.label !== 'input') {
|
||||||
if (dataChannel.label !== 'input') {
|
return;
|
||||||
return dataChannel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VIBRATION_DATA_MAP = {
|
const VIBRATION_DATA_MAP = {
|
||||||
@@ -5581,9 +5654,7 @@ class VibrationManager {
|
|||||||
|
|
||||||
VibrationManager.#playDeviceVibration(data);
|
VibrationManager.#playDeviceVibration(data);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
return dataChannel;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5828,6 +5899,22 @@ class StreamBadges {
|
|||||||
|
|
||||||
return $wrapper;
|
return $wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static setupEvents() {
|
||||||
|
window.addEventListener(BxEvent.STREAM_PLAYING, e => {
|
||||||
|
const $video = e.$video;
|
||||||
|
|
||||||
|
StreamBadges.resolution = {width: $video.videoWidth, height: $video.videoHeight};
|
||||||
|
StreamBadges.startTimestamp = +new Date;
|
||||||
|
|
||||||
|
// Get battery level
|
||||||
|
try {
|
||||||
|
navigator.getBattery && navigator.getBattery().then(bm => {
|
||||||
|
StreamBadges.startBatteryLevel = Math.round(bm.level * 100);
|
||||||
|
});
|
||||||
|
} catch(e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -6034,6 +6121,92 @@ class StreamStats {
|
|||||||
|
|
||||||
StreamStats.refreshStyles();
|
StreamStats.refreshStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getServerStats() {
|
||||||
|
STREAM_WEBRTC && STREAM_WEBRTC.getStats().then(stats => {
|
||||||
|
const allVideoCodecs = {};
|
||||||
|
let videoCodecId;
|
||||||
|
|
||||||
|
const allAudioCodecs = {};
|
||||||
|
let audioCodecId;
|
||||||
|
|
||||||
|
const allCandidates = {};
|
||||||
|
let candidateId;
|
||||||
|
|
||||||
|
stats.forEach(stat => {
|
||||||
|
if (stat.type == 'codec') {
|
||||||
|
const mimeType = stat.mimeType.split('/');
|
||||||
|
if (mimeType[0] === 'video') {
|
||||||
|
// Store all video stats
|
||||||
|
allVideoCodecs[stat.id] = stat;
|
||||||
|
} else if (mimeType[0] === 'audio') {
|
||||||
|
// Store all audio stats
|
||||||
|
allAudioCodecs[stat.id] = stat;
|
||||||
|
}
|
||||||
|
} else if (stat.type === 'inbound-rtp' && stat.packetsReceived > 0) {
|
||||||
|
// Get the codecId of the video/audio track currently being used
|
||||||
|
if (stat.kind === 'video') {
|
||||||
|
videoCodecId = stat.codecId;
|
||||||
|
} else if (stat.kind === 'audio') {
|
||||||
|
audioCodecId = stat.codecId;
|
||||||
|
}
|
||||||
|
} else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') {
|
||||||
|
candidateId = stat.remoteCandidateId;
|
||||||
|
} else if (stat.type === 'remote-candidate') {
|
||||||
|
allCandidates[stat.id] = stat.address;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get video codec from codecId
|
||||||
|
if (videoCodecId) {
|
||||||
|
const videoStat = allVideoCodecs[videoCodecId];
|
||||||
|
const video = {
|
||||||
|
codec: videoStat.mimeType.substring(6),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (video.codec === 'H264') {
|
||||||
|
const match = /profile-level-id=([0-9a-f]{6})/.exec(videoStat.sdpFmtpLine);
|
||||||
|
video.profile = match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamBadges.video = video;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get audio codec from codecId
|
||||||
|
if (audioCodecId) {
|
||||||
|
const audioStat = allAudioCodecs[audioCodecId];
|
||||||
|
StreamBadges.audio = {
|
||||||
|
codec: audioStat.mimeType.substring(6),
|
||||||
|
bitrate: audioStat.clockRate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get server type
|
||||||
|
if (candidateId) {
|
||||||
|
console.log('candidate', candidateId, allCandidates);
|
||||||
|
StreamBadges.ipv6 = allCandidates[candidateId].includes(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getPref(Preferences.STATS_SHOW_WHEN_PLAYING)) {
|
||||||
|
StreamStats.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static setupEvents() {
|
||||||
|
window.addEventListener(BxEvent.STREAM_PLAYING, e => {
|
||||||
|
const PREF_STATS_QUICK_GLANCE = getPref(Preferences.STATS_QUICK_GLANCE);
|
||||||
|
const PREF_STATS_SHOW_WHEN_PLAYING = getPref(Preferences.STATS_SHOW_WHEN_PLAYING);
|
||||||
|
|
||||||
|
StreamStats.getServerStats();
|
||||||
|
// Setup Stat's Quick Glance mode
|
||||||
|
if (PREF_STATS_QUICK_GLANCE) {
|
||||||
|
StreamStats.quickGlanceSetup();
|
||||||
|
// Show stats bar
|
||||||
|
!PREF_STATS_SHOW_WHEN_PLAYING && StreamStats.start(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserAgent {
|
class UserAgent {
|
||||||
@@ -8785,8 +8958,11 @@ function interceptHttpRequests() {
|
|||||||
let url = (typeof request === 'string') ? request : request.url;
|
let url = (typeof request === 'string') ? request : request.url;
|
||||||
|
|
||||||
if (url.endsWith('/play')) {
|
if (url.endsWith('/play')) {
|
||||||
// Setup UI
|
BxEvent.dispatch(window, BxEvent.STREAM_LOADING);
|
||||||
setupBxUi();
|
}
|
||||||
|
|
||||||
|
if (url.endsWith('/configuration')) {
|
||||||
|
BxEvent.dispatch(window, BxEvent.STREAM_STARTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_REMOTE_PLAYING && (url.includes('/sessions/home') || url.includes('inputconfigs'))) {
|
if (IS_REMOTE_PLAYING && (url.includes('/sessions/home') || url.includes('inputconfigs'))) {
|
||||||
@@ -8855,9 +9031,9 @@ function interceptHttpRequests() {
|
|||||||
if (obj[0].supportedTabs.length > 0) {
|
if (obj[0].supportedTabs.length > 0) {
|
||||||
TouchController.disable();
|
TouchController.disable();
|
||||||
|
|
||||||
const event = new Event(BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED);
|
BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, {
|
||||||
event.data = null;
|
data: null,
|
||||||
window.dispatchEvent(event);
|
});
|
||||||
} else {
|
} else {
|
||||||
TouchController.enable();
|
TouchController.enable();
|
||||||
|
|
||||||
@@ -8966,9 +9142,6 @@ function interceptHttpRequests() {
|
|||||||
|
|
||||||
// Get region
|
// Get region
|
||||||
if (url.endsWith('/sessions/cloud/play')) {
|
if (url.endsWith('/sessions/cloud/play')) {
|
||||||
// Setup loading screen
|
|
||||||
PREF_UI_LOADING_SCREEN_GAME_ART && LoadingScreen.setup();
|
|
||||||
|
|
||||||
// Start hiding cursor
|
// Start hiding cursor
|
||||||
if (!getPref(Preferences.MKB_ENABLED) && getPref(Preferences.MKB_HIDE_IDLE_CURSOR)) {
|
if (!getPref(Preferences.MKB_ENABLED) && getPref(Preferences.MKB_HIDE_IDLE_CURSOR)) {
|
||||||
MouseCursorHider.start();
|
MouseCursorHider.start();
|
||||||
@@ -9023,8 +9196,6 @@ function interceptHttpRequests() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (url.endsWith('/configuration') && url.includes('/sessions/cloud/') && request.method === 'GET') {
|
if (url.endsWith('/configuration') && url.includes('/sessions/cloud/') && request.method === 'GET') {
|
||||||
PREF_UI_LOADING_SCREEN_GAME_ART && LoadingScreen.hide();
|
|
||||||
|
|
||||||
const promise = NATIVE_FETCH(...arg);
|
const promise = NATIVE_FETCH(...arg);
|
||||||
|
|
||||||
// Touch controller for all games
|
// Touch controller for all games
|
||||||
@@ -9438,8 +9609,9 @@ function getVideoPlayerFilterStyle() {
|
|||||||
function updateVideoPlayerCss() {
|
function updateVideoPlayerCss() {
|
||||||
let $elm = document.getElementById('bx-video-css');
|
let $elm = document.getElementById('bx-video-css');
|
||||||
if (!$elm) {
|
if (!$elm) {
|
||||||
|
const $fragment = document.createDocumentFragment();
|
||||||
$elm = CE('style', {id: 'bx-video-css'});
|
$elm = CE('style', {id: 'bx-video-css'});
|
||||||
document.documentElement.appendChild($elm);
|
$fragment.appendChild($elm);
|
||||||
|
|
||||||
// Setup SVG filters
|
// Setup SVG filters
|
||||||
const $svg = CE('svg', {
|
const $svg = CE('svg', {
|
||||||
@@ -9451,7 +9623,8 @@ function updateVideoPlayerCss() {
|
|||||||
CE('feConvolveMatrix', {'id': 'bx-filter-clarity-matrix', 'order': '3', 'xmlns': 'http://www.w3.org/2000/svg'}))
|
CE('feConvolveMatrix', {'id': 'bx-filter-clarity-matrix', 'order': '3', 'xmlns': 'http://www.w3.org/2000/svg'}))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
document.documentElement.appendChild($svg);
|
$fragment.appendChild($svg);
|
||||||
|
document.documentElement.appendChild($fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
let filters = getVideoPlayerFilterStyle();
|
let filters = getVideoPlayerFilterStyle();
|
||||||
@@ -9625,13 +9798,13 @@ function injectStreamMenuButtons() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
item.removedNodes.forEach($node => {
|
item.removedNodes.forEach($node => {
|
||||||
if (!$node.className || !$node.className.startsWith) {
|
if (!$node || !$node.className || !$node.className.startsWith) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node.className.startsWith('StreamMenu')) {
|
if ($node.className.startsWith('StreamMenu')) {
|
||||||
if (!document.querySelector('div[class^=PureInStreamConfirmationModal]')) {
|
if (!document.querySelector('div[class^=PureInStreamConfirmationModal]')) {
|
||||||
window.dispatchEvent(new Event('bx-stream-menu-hidden'));
|
BxEvent.dispatch(window, BxEvent.STREAM_MENU_HIDDEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -9641,6 +9814,12 @@ function injectStreamMenuButtons() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error Page: .PureErrorPage.ErrorScreen
|
||||||
|
if ($node.className.includes('PureErrorPage')) {
|
||||||
|
BxEvent.dispatch(window, BxEvent.STREAM_ERROR_PAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (PREF_DISABLE_FEEDBACK_DIALOG && $node.className.startsWith('PostStreamFeedbackScreen')) {
|
if (PREF_DISABLE_FEEDBACK_DIALOG && $node.className.startsWith('PostStreamFeedbackScreen')) {
|
||||||
const $btnClose = $node.querySelector('button');
|
const $btnClose = $node.querySelector('button');
|
||||||
$btnClose && $btnClose.click();
|
$btnClose && $btnClose.click();
|
||||||
@@ -9649,7 +9828,7 @@ function injectStreamMenuButtons() {
|
|||||||
|
|
||||||
// Render badges
|
// Render badges
|
||||||
if ($node.className.startsWith('StreamMenu')) {
|
if ($node.className.startsWith('StreamMenu')) {
|
||||||
window.dispatchEvent(new Event('bx-stream-menu-shown'));
|
BxEvent.dispatch(window, BxEvent.STREAM_MENU_SHOWN);
|
||||||
|
|
||||||
// Hide Quick bar when closing HUD
|
// Hide Quick bar when closing HUD
|
||||||
const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
|
const $btnCloseHud = document.querySelector('button[class*=StreamMenu-module__backButton]');
|
||||||
@@ -9795,13 +9974,13 @@ function patchVideoApi() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onStreamStarted(this);
|
BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, {
|
||||||
|
$video: this,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const nativePlay = HTMLMediaElement.prototype.play;
|
const nativePlay = HTMLMediaElement.prototype.play;
|
||||||
HTMLMediaElement.prototype.play = function() {
|
HTMLMediaElement.prototype.play = function() {
|
||||||
LoadingScreen.reset();
|
|
||||||
|
|
||||||
if (this.className && this.className.startsWith('XboxSplashVideo')) {
|
if (this.className && this.className.startsWith('XboxSplashVideo')) {
|
||||||
if (PREF_SKIP_SPLASH_VIDEO) {
|
if (PREF_SKIP_SPLASH_VIDEO) {
|
||||||
this.volume = 0;
|
this.volume = 0;
|
||||||
@@ -10282,24 +10461,19 @@ function patchHistoryMethod(type) {
|
|||||||
const orig = window.history[type];
|
const orig = window.history[type];
|
||||||
|
|
||||||
return function(...args) {
|
return function(...args) {
|
||||||
const event = new Event(BxEvent.POPSTATE);
|
BxEvent.dispatch(window, BxEvent.POPSTATE, {
|
||||||
event.arguments = args;
|
arguments: args,
|
||||||
window.dispatchEvent(event);
|
});
|
||||||
|
|
||||||
return orig.apply(this, arguments);
|
return orig.apply(this, arguments);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function onHistoryChanged(e) {
|
function onHistoryChanged(e) {
|
||||||
if (e.arguments && e.arguments[0] && e.arguments[0].origin === 'better-xcloud') {
|
if (e && e.arguments && e.arguments[0] && e.arguments[0].origin === 'better-xcloud') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop MKB listeners
|
|
||||||
MkbHandler.INSTANCE.destroy();
|
|
||||||
|
|
||||||
IS_PLAYING = false;
|
|
||||||
setTimeout(RemotePlay.detect, 10);
|
setTimeout(RemotePlay.detect, 10);
|
||||||
|
|
||||||
const $settings = document.querySelector('.better_xcloud_settings');
|
const $settings = document.querySelector('.better_xcloud_settings');
|
||||||
@@ -10307,165 +10481,10 @@ function onHistoryChanged(e) {
|
|||||||
$settings.classList.add('bx-gone');
|
$settings.classList.add('bx-gone');
|
||||||
}
|
}
|
||||||
|
|
||||||
const $quickBar = document.querySelector('.bx-quick-settings-bar');
|
|
||||||
if ($quickBar) {
|
|
||||||
$quickBar.classList.add('bx-gone');
|
|
||||||
}
|
|
||||||
|
|
||||||
STREAM_AUDIO_GAIN_NODE = null;
|
|
||||||
$STREAM_VIDEO = null;
|
|
||||||
StreamStats.onStoppedPlaying();
|
|
||||||
|
|
||||||
const $screenshotBtn = document.querySelector('.bx-screenshot-button');
|
|
||||||
if ($screenshotBtn) {
|
|
||||||
$screenshotBtn.style = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseCursorHider.stop();
|
|
||||||
TouchController.reset();
|
|
||||||
|
|
||||||
LoadingScreen.reset();
|
LoadingScreen.reset();
|
||||||
|
|
||||||
GamepadHandler.stopPolling();
|
|
||||||
|
|
||||||
setTimeout(checkHeader, 2000);
|
setTimeout(checkHeader, 2000);
|
||||||
}
|
|
||||||
|
|
||||||
|
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED);
|
||||||
function onStreamStarted($video) {
|
|
||||||
IS_PLAYING = true;
|
|
||||||
|
|
||||||
// Get title ID for screenshot's name
|
|
||||||
if (window.location.pathname.includes('/launch/')) {
|
|
||||||
const matches = /\/launch\/(?<title_id>[^\/]+)\/(?<product_id>\w+)/.exec(window.location.pathname);
|
|
||||||
GAME_TITLE_ID = matches.groups.title_id;
|
|
||||||
GAME_PRODUCT_ID = matches.groups.product_id;
|
|
||||||
} else {
|
|
||||||
GAME_TITLE_ID = 'remote-play';
|
|
||||||
GAME_PRODUCT_ID = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable MKB
|
|
||||||
if (getPref(Preferences.MKB_ENABLED) && (!ENABLE_NATIVE_MKB_BETA || !window.NATIVE_MKB_TITLES.includes(GAME_PRODUCT_ID))) {
|
|
||||||
console.log('Emulate MKB');
|
|
||||||
MkbHandler.INSTANCE.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TouchController.isEnabled()) {
|
|
||||||
TouchController.enableBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (getPref(Preferences.CONTROLLER_ENABLE_SHORTCUTS)) {
|
|
||||||
GamepadHandler.startPolling();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const PREF_SCREENSHOT_BUTTON_POSITION = getPref(Preferences.SCREENSHOT_BUTTON_POSITION);
|
|
||||||
const PREF_STATS_QUICK_GLANCE = getPref(Preferences.STATS_QUICK_GLANCE);
|
|
||||||
const PREF_STATS_SHOW_WHEN_PLAYING = getPref(Preferences.STATS_SHOW_WHEN_PLAYING);
|
|
||||||
|
|
||||||
// Setup Stat's Quick Glance mode
|
|
||||||
if (PREF_STATS_QUICK_GLANCE) {
|
|
||||||
StreamStats.quickGlanceSetup();
|
|
||||||
// Show stats bar
|
|
||||||
!PREF_STATS_SHOW_WHEN_PLAYING && StreamStats.start(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$STREAM_VIDEO = $video;
|
|
||||||
$SCREENSHOT_CANVAS.width = $video.videoWidth;
|
|
||||||
$SCREENSHOT_CANVAS.height = $video.videoHeight;
|
|
||||||
|
|
||||||
StreamBadges.resolution = {width: $video.videoWidth, height: $video.videoHeight};
|
|
||||||
StreamBadges.startTimestamp = +new Date;
|
|
||||||
|
|
||||||
// Get battery level
|
|
||||||
try {
|
|
||||||
navigator.getBattery && navigator.getBattery().then(bm => {
|
|
||||||
StreamBadges.startBatteryLevel = Math.round(bm.level * 100);
|
|
||||||
});
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
STREAM_WEBRTC.getStats().then(stats => {
|
|
||||||
const allVideoCodecs = {};
|
|
||||||
let videoCodecId;
|
|
||||||
|
|
||||||
const allAudioCodecs = {};
|
|
||||||
let audioCodecId;
|
|
||||||
|
|
||||||
const allCandidates = {};
|
|
||||||
let candidateId;
|
|
||||||
|
|
||||||
stats.forEach(stat => {
|
|
||||||
if (stat.type == 'codec') {
|
|
||||||
const mimeType = stat.mimeType.split('/');
|
|
||||||
if (mimeType[0] === 'video') {
|
|
||||||
// Store all video stats
|
|
||||||
allVideoCodecs[stat.id] = stat;
|
|
||||||
} else if (mimeType[0] === 'audio') {
|
|
||||||
// Store all audio stats
|
|
||||||
allAudioCodecs[stat.id] = stat;
|
|
||||||
}
|
|
||||||
} else if (stat.type === 'inbound-rtp' && stat.packetsReceived > 0) {
|
|
||||||
// Get the codecId of the video/audio track currently being used
|
|
||||||
if (stat.kind === 'video') {
|
|
||||||
videoCodecId = stat.codecId;
|
|
||||||
} else if (stat.kind === 'audio') {
|
|
||||||
audioCodecId = stat.codecId;
|
|
||||||
}
|
|
||||||
} else if (stat.type === 'candidate-pair' && stat.packetsReceived > 0 && stat.state === 'succeeded') {
|
|
||||||
candidateId = stat.remoteCandidateId;
|
|
||||||
} else if (stat.type === 'remote-candidate') {
|
|
||||||
allCandidates[stat.id] = stat.address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get video codec from codecId
|
|
||||||
if (videoCodecId) {
|
|
||||||
const videoStat = allVideoCodecs[videoCodecId];
|
|
||||||
const video = {
|
|
||||||
codec: videoStat.mimeType.substring(6),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (video.codec === 'H264') {
|
|
||||||
const match = /profile-level-id=([0-9a-f]{6})/.exec(videoStat.sdpFmtpLine);
|
|
||||||
video.profile = match ? match[1] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamBadges.video = video;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get audio codec from codecId
|
|
||||||
if (audioCodecId) {
|
|
||||||
const audioStat = allAudioCodecs[audioCodecId];
|
|
||||||
StreamBadges.audio = {
|
|
||||||
codec: audioStat.mimeType.substring(6),
|
|
||||||
bitrate: audioStat.clockRate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get server type
|
|
||||||
if (candidateId) {
|
|
||||||
console.log(candidateId, allCandidates);
|
|
||||||
StreamBadges.ipv6 = allCandidates[candidateId].includes(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PREF_STATS_SHOW_WHEN_PLAYING) {
|
|
||||||
StreamStats.start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setup screenshot button
|
|
||||||
if (PREF_SCREENSHOT_BUTTON_POSITION !== 'none') {
|
|
||||||
const $btn = document.querySelector('.bx-screenshot-button');
|
|
||||||
$btn.style.display = 'block';
|
|
||||||
|
|
||||||
if (PREF_SCREENSHOT_BUTTON_POSITION === 'bottom-right') {
|
|
||||||
$btn.style.right = '0';
|
|
||||||
} else {
|
|
||||||
$btn.style.left = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -10508,6 +10527,93 @@ window.addEventListener('popstate', onHistoryChanged);
|
|||||||
window.history.pushState = patchHistoryMethod('pushState');
|
window.history.pushState = patchHistoryMethod('pushState');
|
||||||
window.history.replaceState = patchHistoryMethod('replaceState');
|
window.history.replaceState = patchHistoryMethod('replaceState');
|
||||||
|
|
||||||
|
window.addEventListener(BxEvent.STREAM_LOADING, e => {
|
||||||
|
// Get title ID for screenshot's name
|
||||||
|
if (window.location.pathname.includes('/launch/')) {
|
||||||
|
const matches = /\/launch\/(?<title_id>[^\/]+)\/(?<product_id>\w+)/.exec(window.location.pathname);
|
||||||
|
GAME_TITLE_ID = matches.groups.title_id;
|
||||||
|
GAME_PRODUCT_ID = matches.groups.product_id;
|
||||||
|
} else {
|
||||||
|
GAME_TITLE_ID = 'remote-play';
|
||||||
|
GAME_PRODUCT_ID = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup UI
|
||||||
|
setupBxUi();
|
||||||
|
|
||||||
|
// Setup loading screen
|
||||||
|
getPref(Preferences.UI_LOADING_SCREEN_GAME_ART) && LoadingScreen.setup();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener(BxEvent.STREAM_STARTING, e => {
|
||||||
|
// Hide loading screen
|
||||||
|
getPref(Preferences.UI_LOADING_SCREEN_GAME_ART) && LoadingScreen.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener(BxEvent.STREAM_PLAYING, e => {
|
||||||
|
const $video = e.$video;
|
||||||
|
$STREAM_VIDEO = $video;
|
||||||
|
|
||||||
|
IS_PLAYING = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (getPref(Preferences.CONTROLLER_ENABLE_SHORTCUTS)) {
|
||||||
|
GamepadHandler.startPolling();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const PREF_SCREENSHOT_BUTTON_POSITION = getPref(Preferences.SCREENSHOT_BUTTON_POSITION);
|
||||||
|
$SCREENSHOT_CANVAS.width = $video.videoWidth;
|
||||||
|
$SCREENSHOT_CANVAS.height = $video.videoHeight;
|
||||||
|
|
||||||
|
// Setup screenshot button
|
||||||
|
if (PREF_SCREENSHOT_BUTTON_POSITION !== 'none') {
|
||||||
|
const $btn = document.querySelector('.bx-screenshot-button');
|
||||||
|
$btn.style.display = 'block';
|
||||||
|
|
||||||
|
if (PREF_SCREENSHOT_BUTTON_POSITION === 'bottom-right') {
|
||||||
|
$btn.style.right = '0';
|
||||||
|
} else {
|
||||||
|
$btn.style.left = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener(BxEvent.STREAM_ERROR_PAGE, e => {
|
||||||
|
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener(BxEvent.STREAM_STOPPED, e => {
|
||||||
|
if (!IS_PLAYING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IS_PLAYING = false;
|
||||||
|
|
||||||
|
// Stop MKB listeners
|
||||||
|
MkbHandler.INSTANCE.destroy();
|
||||||
|
|
||||||
|
const $quickBar = document.querySelector('.bx-quick-settings-bar');
|
||||||
|
if ($quickBar) {
|
||||||
|
$quickBar.classList.add('bx-gone');
|
||||||
|
}
|
||||||
|
|
||||||
|
STREAM_AUDIO_GAIN_NODE = null;
|
||||||
|
$STREAM_VIDEO = null;
|
||||||
|
StreamStats.onStoppedPlaying();
|
||||||
|
|
||||||
|
const $screenshotBtn = document.querySelector('.bx-screenshot-button');
|
||||||
|
if ($screenshotBtn) {
|
||||||
|
$screenshotBtn.style = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseCursorHider.stop();
|
||||||
|
TouchController.reset();
|
||||||
|
|
||||||
|
GamepadHandler.stopPolling();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
PreloadedState.override();
|
PreloadedState.override();
|
||||||
|
|
||||||
// Check for Update
|
// Check for Update
|
||||||
@@ -10562,11 +10668,21 @@ if (getPref(Preferences.STREAM_TOUCH_CONTROLLER) === 'all') {
|
|||||||
|
|
||||||
VibrationManager.initialSetup();
|
VibrationManager.initialSetup();
|
||||||
|
|
||||||
|
const nativeCreateDataChannel = RTCPeerConnection.prototype.createDataChannel;
|
||||||
|
RTCPeerConnection.prototype.createDataChannel = function() {
|
||||||
|
const dataChannel = nativeCreateDataChannel.apply(this, arguments);
|
||||||
|
|
||||||
|
BxEvent.dispatch(window, BxEvent.DATA_CHANNEL_CREATED, {
|
||||||
|
dataChannel: dataChannel,
|
||||||
|
});
|
||||||
|
|
||||||
|
return dataChannel;
|
||||||
|
}
|
||||||
|
|
||||||
const OrgRTCPeerConnection = window.RTCPeerConnection;
|
const OrgRTCPeerConnection = window.RTCPeerConnection;
|
||||||
window.RTCPeerConnection = function() {
|
window.RTCPeerConnection = function() {
|
||||||
const peer = new OrgRTCPeerConnection();
|
STREAM_WEBRTC = new OrgRTCPeerConnection();
|
||||||
STREAM_WEBRTC = peer;
|
return STREAM_WEBRTC;
|
||||||
return peer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
patchRtcCodecs();
|
patchRtcCodecs();
|
||||||
@@ -10589,3 +10705,7 @@ if (getPref(Preferences.CONTROLLER_ENABLE_SHORTCUTS)) {
|
|||||||
Patcher.initialize();
|
Patcher.initialize();
|
||||||
|
|
||||||
RemotePlay.detect();
|
RemotePlay.detect();
|
||||||
|
|
||||||
|
StreamBadges.setupEvents();
|
||||||
|
StreamStats.setupEvents();
|
||||||
|
MkbHandler.setupEvents();
|
||||||
|
Reference in New Issue
Block a user