mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 23:57:19 +02:00
Refactor + bug fixes (#245)
* Create DATA_CHANNEL_CREATED event * Add BxEvent.dispatch() * Dispatch STREAM_WEBRTC_CONNECTED event * Fix not being able to hide touch control in Remote Play * Fix touch bar again * Listen to STREAM_LOADING & STREAM_STARTING events * Add setupEvents() functions * Show/hide touch controller using CSS instead * Fix exception in LoadingScreen class * Fix Remote Play stopped working
This commit is contained in:
parent
6f517d85b3
commit
46647dbffd
@ -44,11 +44,30 @@ 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_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) => {
|
||||||
|
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 +3087,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 +3358,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 +3431,14 @@ class TouchController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static #show() {
|
static #show() {
|
||||||
TouchController.loadCustomLayout(GAME_XBOX_TITLE_ID, TouchController.#currentLayoutId, 0);
|
document.querySelector('#BabylonCanvasContainer-main').parentElement.classList.remove('bx-gone');
|
||||||
|
// TouchController.loadCustomLayout(GAME_XBOX_TITLE_ID, TouchController.#currentLayoutId, 0);
|
||||||
TouchController.#showing = true;
|
TouchController.#showing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #hide() {
|
static #hide() {
|
||||||
TouchController.#dispatchMessage(TouchController.#EVENT_HIDE_CONTROLLER);
|
document.querySelector('#BabylonCanvasContainer-main').parentElement.classList.add('bx-gone');
|
||||||
|
// TouchController.#dispatchMessage(TouchController.#EVENT_HIDE_CONTROLLER);
|
||||||
TouchController.#showing = false;
|
TouchController.#showing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3403,8 +3450,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 +3471,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;
|
||||||
@ -3503,11 +3550,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 +3581,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 +3601,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 +3627,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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4765,9 +4821,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 +4833,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 +4849,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 +5584,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 +5646,7 @@ class VibrationManager {
|
|||||||
|
|
||||||
VibrationManager.#playDeviceVibration(data);
|
VibrationManager.#playDeviceVibration(data);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
return dataChannel;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5828,6 +5891,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 +6113,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 +8950,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 +9023,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 +9134,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 +9188,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 +9601,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 +9615,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();
|
||||||
@ -9795,13 +9960,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,10 +10447,9 @@ 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);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -10332,143 +10496,6 @@ function onHistoryChanged(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function disablePwa() {
|
function disablePwa() {
|
||||||
const userAgent = (window.navigator.orgUserAgent || window.navigator.userAgent || '').toLowerCase();
|
const userAgent = (window.navigator.orgUserAgent || window.navigator.userAgent || '').toLowerCase();
|
||||||
if (!userAgent) {
|
if (!userAgent) {
|
||||||
@ -10508,6 +10535,59 @@ 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
PreloadedState.override();
|
PreloadedState.override();
|
||||||
|
|
||||||
// Check for Update
|
// Check for Update
|
||||||
@ -10562,11 +10642,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 +10679,7 @@ if (getPref(Preferences.CONTROLLER_ENABLE_SHORTCUTS)) {
|
|||||||
Patcher.initialize();
|
Patcher.initialize();
|
||||||
|
|
||||||
RemotePlay.detect();
|
RemotePlay.detect();
|
||||||
|
|
||||||
|
StreamBadges.setupEvents();
|
||||||
|
StreamStats.setupEvents();
|
||||||
|
MkbHandler.setupEvents();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user