mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-07-06 22:31:44 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
fa19a5a68e | |||
3f834f74b6 | |||
749d5d720e | |||
b969d52a3c | |||
e5bd7e64a7 | |||
82ee00b4ae | |||
8e88af5f8c | |||
927eae3f2f | |||
9f440e9cf4 | |||
1acb30e3af | |||
34159fad22 |
9
build.ts
9
build.ts
@ -87,6 +87,15 @@ const postProcess = (str: string): string => {
|
|||||||
return p1.toUpperCase();
|
return p1.toUpperCase();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Replace " (e) =>" to " e =>"
|
||||||
|
// str = str.replaceAll(/ \(([^\s,.$()]+)\) =>/g, ' $1 =>');
|
||||||
|
|
||||||
|
// Set indent to 1 space
|
||||||
|
str = str.replaceAll(/\n(\s+)/g, (match, p1) => {
|
||||||
|
const len = p1.length / 2;
|
||||||
|
return '\n' + ' '.repeat(len);
|
||||||
|
});
|
||||||
|
|
||||||
assert(str.includes('/* ADDITIONAL CODE */'));
|
assert(str.includes('/* ADDITIONAL CODE */'));
|
||||||
assert(str.includes('window.BX_EXPOSED = BxExposed'));
|
assert(str.includes('window.BX_EXPOSED = BxExposed'));
|
||||||
assert(str.includes('window.BxEvent = BxEvent'));
|
assert(str.includes('window.BxEvent = BxEvent'));
|
||||||
|
116
dist/better-xcloud.lite.user.js
vendored
116
dist/better-xcloud.lite.user.js
vendored
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud (Lite)
|
// @name Better xCloud (Lite)
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 5.8.4-beta
|
// @version 5.8.5
|
||||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||||
// @author redphx
|
// @author redphx
|
||||||
// @license MIT
|
// @license MIT
|
||||||
@ -111,7 +111,7 @@ function deepClone(obj) {
|
|||||||
if (!obj) return {};
|
if (!obj) return {};
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
var SCRIPT_VERSION = "5.8.4-beta", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface;
|
var SCRIPT_VERSION = "5.8.5", SCRIPT_VARIANT = "lite", AppInterface = window.AppInterface;
|
||||||
UserAgent.init();
|
UserAgent.init();
|
||||||
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = {
|
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = {
|
||||||
supportedRegion: !0,
|
supportedRegion: !0,
|
||||||
@ -936,10 +936,7 @@ class BaseSettingsStore {
|
|||||||
}
|
}
|
||||||
class StreamStatsCollector {
|
class StreamStatsCollector {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => StreamStatsCollector.instance ?? (StreamStatsCollector.instance = new StreamStatsCollector);
|
||||||
if (!StreamStatsCollector.instance) StreamStatsCollector.instance = new StreamStatsCollector;
|
|
||||||
return StreamStatsCollector.instance;
|
|
||||||
}
|
|
||||||
static INTERVAL_BACKGROUND = 60000;
|
static INTERVAL_BACKGROUND = 60000;
|
||||||
calculateGrade(value, grades) {
|
calculateGrade(value, grades) {
|
||||||
return value > grades[2] ? "bad" : value > grades[1] ? "ok" : value > grades[0] ? "good" : "";
|
return value > grades[2] ? "bad" : value > grades[1] ? "ok" : value > grades[0] ? "good" : "";
|
||||||
@ -962,7 +959,8 @@ class StreamStatsCollector {
|
|||||||
fps: {
|
fps: {
|
||||||
current: 0,
|
current: 0,
|
||||||
toString() {
|
toString() {
|
||||||
return this.current.toString();
|
const maxFps = getPref("video_max_fps");
|
||||||
|
return maxFps < 60 ? `${maxFps}/${this.current}` : this.current.toString();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
btr: {
|
btr: {
|
||||||
@ -1807,10 +1805,7 @@ var MouseMapTo;
|
|||||||
})(MouseMapTo ||= {});
|
})(MouseMapTo ||= {});
|
||||||
class StreamStats {
|
class StreamStats {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => StreamStats.instance ?? (StreamStats.instance = new StreamStats);
|
||||||
if (!StreamStats.instance) StreamStats.instance = new StreamStats;
|
|
||||||
return StreamStats.instance;
|
|
||||||
}
|
|
||||||
intervalId;
|
intervalId;
|
||||||
REFRESH_INTERVAL = 1000;
|
REFRESH_INTERVAL = 1000;
|
||||||
stats = {
|
stats = {
|
||||||
@ -2141,14 +2136,13 @@ function onChangeVideoPlayerType() {
|
|||||||
else if ($videoProcessing.value = "usm", setPref("video_processing", "usm"), $optCas && ($optCas.disabled = !0), UserAgent.isSafari()) isDisabled = !0;
|
else if ($videoProcessing.value = "usm", setPref("video_processing", "usm"), $optCas && ($optCas.disabled = !0), UserAgent.isSafari()) isDisabled = !0;
|
||||||
$videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), $videoMaxFps.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), updateVideoPlayer();
|
$videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), $videoMaxFps.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), updateVideoPlayer();
|
||||||
}
|
}
|
||||||
function limitVideoPlayerFps() {
|
function limitVideoPlayerFps(targetFps) {
|
||||||
const targetFps = getPref("video_max_fps");
|
|
||||||
STATES.currentStream.streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
|
STATES.currentStream.streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
|
||||||
}
|
}
|
||||||
function updateVideoPlayer() {
|
function updateVideoPlayer() {
|
||||||
const streamPlayer = STATES.currentStream.streamPlayer;
|
const streamPlayer = STATES.currentStream.streamPlayer;
|
||||||
if (!streamPlayer) return;
|
if (!streamPlayer) return;
|
||||||
limitVideoPlayerFps();
|
limitVideoPlayerFps(getPref("video_max_fps"));
|
||||||
const options = {
|
const options = {
|
||||||
processing: getPref("video_processing"),
|
processing: getPref("video_processing"),
|
||||||
sharpness: getPref("video_sharpness"),
|
sharpness: getPref("video_sharpness"),
|
||||||
@ -2381,10 +2375,7 @@ class KeyHelper {
|
|||||||
var LOG_TAG = "PointerClient";
|
var LOG_TAG = "PointerClient";
|
||||||
class PointerClient {
|
class PointerClient {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => PointerClient.instance ?? (PointerClient.instance = new PointerClient);
|
||||||
if (!PointerClient.instance) PointerClient.instance = new PointerClient;
|
|
||||||
return PointerClient.instance;
|
|
||||||
}
|
|
||||||
socket;
|
socket;
|
||||||
mkbHandler;
|
mkbHandler;
|
||||||
start(port, mkbHandler) {
|
start(port, mkbHandler) {
|
||||||
@ -2516,11 +2507,8 @@ class PointerLockMouseDataProvider extends MouseDataProvider {
|
|||||||
#disableContextMenu = (e) => e.preventDefault();
|
#disableContextMenu = (e) => e.preventDefault();
|
||||||
}
|
}
|
||||||
class EmulatedMkbHandler extends MkbHandler {
|
class EmulatedMkbHandler extends MkbHandler {
|
||||||
static #instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => EmulatedMkbHandler.instance ?? (EmulatedMkbHandler.instance = new EmulatedMkbHandler);
|
||||||
if (!EmulatedMkbHandler.#instance) EmulatedMkbHandler.#instance = new EmulatedMkbHandler;
|
|
||||||
return EmulatedMkbHandler.#instance;
|
|
||||||
}
|
|
||||||
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
||||||
static DEFAULT_PANNING_SENSITIVITY = 0.001;
|
static DEFAULT_PANNING_SENSITIVITY = 0.001;
|
||||||
static DEFAULT_DEADZONE_COUNTERWEIGHT = 0.01;
|
static DEFAULT_DEADZONE_COUNTERWEIGHT = 0.01;
|
||||||
@ -2798,10 +2786,7 @@ class NavigationDialog {
|
|||||||
}
|
}
|
||||||
class NavigationDialogManager {
|
class NavigationDialogManager {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => NavigationDialogManager.instance ?? (NavigationDialogManager.instance = new NavigationDialogManager);
|
||||||
if (!NavigationDialogManager.instance) NavigationDialogManager.instance = new NavigationDialogManager;
|
|
||||||
return NavigationDialogManager.instance;
|
|
||||||
}
|
|
||||||
static GAMEPAD_POLLING_INTERVAL = 50;
|
static GAMEPAD_POLLING_INTERVAL = 50;
|
||||||
static GAMEPAD_KEYS = [
|
static GAMEPAD_KEYS = [
|
||||||
12,
|
12,
|
||||||
@ -3171,10 +3156,7 @@ if (getPref("block_social_features")) FeatureGates.EnableGuideChatTab = !1;
|
|||||||
if (BX_FLAGS.FeatureGates) FeatureGates = Object.assign(BX_FLAGS.FeatureGates, FeatureGates);
|
if (BX_FLAGS.FeatureGates) FeatureGates = Object.assign(BX_FLAGS.FeatureGates, FeatureGates);
|
||||||
class FullscreenText {
|
class FullscreenText {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => FullscreenText.instance ?? (FullscreenText.instance = new FullscreenText);
|
||||||
if (!FullscreenText.instance) FullscreenText.instance = new FullscreenText;
|
|
||||||
return FullscreenText.instance;
|
|
||||||
}
|
|
||||||
$text;
|
$text;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.$text = CE("div", {
|
this.$text = CE("div", {
|
||||||
@ -3190,10 +3172,7 @@ class FullscreenText {
|
|||||||
}
|
}
|
||||||
class SettingsNavigationDialog extends NavigationDialog {
|
class SettingsNavigationDialog extends NavigationDialog {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => SettingsNavigationDialog.instance ?? (SettingsNavigationDialog.instance = new SettingsNavigationDialog);
|
||||||
if (!SettingsNavigationDialog.instance) SettingsNavigationDialog.instance = new SettingsNavigationDialog;
|
|
||||||
return SettingsNavigationDialog.instance;
|
|
||||||
}
|
|
||||||
$container;
|
$container;
|
||||||
$tabs;
|
$tabs;
|
||||||
$settings;
|
$settings;
|
||||||
@ -3458,7 +3437,9 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
onChange: onChangeVideoPlayerType
|
onChange: onChangeVideoPlayerType
|
||||||
}, {
|
}, {
|
||||||
pref: "video_max_fps",
|
pref: "video_max_fps",
|
||||||
onChange: limitVideoPlayerFps
|
onChange: (e) => {
|
||||||
|
limitVideoPlayerFps(parseInt(e.target.value));
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
pref: "video_power_preference",
|
pref: "video_power_preference",
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
@ -4185,10 +4166,7 @@ class HeaderSection {
|
|||||||
}
|
}
|
||||||
class RemotePlayNavigationDialog extends NavigationDialog {
|
class RemotePlayNavigationDialog extends NavigationDialog {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => RemotePlayNavigationDialog.instance ?? (RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog);
|
||||||
if (!RemotePlayNavigationDialog.instance) RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog;
|
|
||||||
return RemotePlayNavigationDialog.instance;
|
|
||||||
}
|
|
||||||
STATE_LABELS = {
|
STATE_LABELS = {
|
||||||
On: t("powered-on"),
|
On: t("powered-on"),
|
||||||
Off: t("powered-off"),
|
Off: t("powered-off"),
|
||||||
@ -4254,10 +4232,7 @@ class RemotePlayNavigationDialog extends NavigationDialog {
|
|||||||
var LOG_TAG2 = "RemotePlay";
|
var LOG_TAG2 = "RemotePlay";
|
||||||
class RemotePlayManager {
|
class RemotePlayManager {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => RemotePlayManager.instance ?? (RemotePlayManager.instance = new RemotePlayManager);
|
||||||
if (!this.instance) this.instance = new RemotePlayManager;
|
|
||||||
return this.instance;
|
|
||||||
}
|
|
||||||
isInitialized = !1;
|
isInitialized = !1;
|
||||||
XCLOUD_TOKEN;
|
XCLOUD_TOKEN;
|
||||||
XHOME_TOKEN;
|
XHOME_TOKEN;
|
||||||
@ -4544,10 +4519,7 @@ class GuideMenu {
|
|||||||
}
|
}
|
||||||
class StreamBadges {
|
class StreamBadges {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => StreamBadges.instance ?? (StreamBadges.instance = new StreamBadges);
|
||||||
if (!StreamBadges.instance) StreamBadges.instance = new StreamBadges;
|
|
||||||
return StreamBadges.instance;
|
|
||||||
}
|
|
||||||
serverInfo = {};
|
serverInfo = {};
|
||||||
badges = {
|
badges = {
|
||||||
playtime: {
|
playtime: {
|
||||||
@ -4718,17 +4690,7 @@ class StreamBadges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class XcloudInterceptor {
|
class XcloudInterceptor {
|
||||||
static async#handleLogin(request, init) {
|
static SERVER_EMOJIS = {
|
||||||
const bypassServer = getPref("server_bypass_restriction");
|
|
||||||
if (bypassServer !== "off") {
|
|
||||||
const ip = BypassServerIps[bypassServer];
|
|
||||||
ip && request.headers.set("X-Forwarded-For", ip);
|
|
||||||
}
|
|
||||||
const response = await NATIVE_FETCH(request, init);
|
|
||||||
if (response.status !== 200) return BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE), response;
|
|
||||||
const obj = await response.clone().json();
|
|
||||||
RemotePlayManager.getInstance().xcloudToken = obj.gsToken;
|
|
||||||
const serverEmojis = {
|
|
||||||
AustraliaEast: "🇦🇺",
|
AustraliaEast: "🇦🇺",
|
||||||
AustraliaSouthEast: "🇦🇺",
|
AustraliaSouthEast: "🇦🇺",
|
||||||
BrazilSouth: "🇧🇷",
|
BrazilSouth: "🇧🇷",
|
||||||
@ -4743,7 +4705,18 @@ class XcloudInterceptor {
|
|||||||
WestEurope: "🇪🇺",
|
WestEurope: "🇪🇺",
|
||||||
WestUS: "🇺🇸",
|
WestUS: "🇺🇸",
|
||||||
WestUS2: "🇺🇸"
|
WestUS2: "🇺🇸"
|
||||||
}, serverRegex = /\/\/(\w+)\./;
|
};
|
||||||
|
static async handleLogin(request, init) {
|
||||||
|
const bypassServer = getPref("server_bypass_restriction");
|
||||||
|
if (bypassServer !== "off") {
|
||||||
|
const ip = BypassServerIps[bypassServer];
|
||||||
|
ip && request.headers.set("X-Forwarded-For", ip);
|
||||||
|
}
|
||||||
|
const response = await NATIVE_FETCH(request, init);
|
||||||
|
if (response.status !== 200) return BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE), response;
|
||||||
|
const obj = await response.clone().json();
|
||||||
|
RemotePlayManager.getInstance().xcloudToken = obj.gsToken;
|
||||||
|
const serverRegex = /\/\/(\w+)\./, serverEmojis = XcloudInterceptor.SERVER_EMOJIS;
|
||||||
for (let region of obj.offeringSettings.regions) {
|
for (let region of obj.offeringSettings.regions) {
|
||||||
const regionName = region.name;
|
const regionName = region.name;
|
||||||
let shortName = region.name;
|
let shortName = region.name;
|
||||||
@ -4762,7 +4735,7 @@ class XcloudInterceptor {
|
|||||||
}
|
}
|
||||||
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
|
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
|
||||||
}
|
}
|
||||||
static async#handlePlay(request, init) {
|
static async handlePlay(request, init) {
|
||||||
const PREF_STREAM_TARGET_RESOLUTION = getPref("stream_target_resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream_preferred_locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url);
|
const PREF_STREAM_TARGET_RESOLUTION = getPref("stream_target_resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream_preferred_locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url);
|
||||||
let badgeRegion = parsedUrl.host.split(".", 1)[0];
|
let badgeRegion = parsedUrl.host.split(".", 1)[0];
|
||||||
for (let regionName in STATES.serverRegions) {
|
for (let regionName in STATES.serverRegions) {
|
||||||
@ -4784,7 +4757,7 @@ class XcloudInterceptor {
|
|||||||
});
|
});
|
||||||
return NATIVE_FETCH(newRequest);
|
return NATIVE_FETCH(newRequest);
|
||||||
}
|
}
|
||||||
static async#handleWaitTime(request, init) {
|
static async handleWaitTime(request, init) {
|
||||||
const response = await NATIVE_FETCH(request, init);
|
const response = await NATIVE_FETCH(request, init);
|
||||||
if (getPref("ui_loading_screen_wait_time")) {
|
if (getPref("ui_loading_screen_wait_time")) {
|
||||||
const json = await response.clone().json();
|
const json = await response.clone().json();
|
||||||
@ -4792,7 +4765,7 @@ class XcloudInterceptor {
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
static async#handleConfiguration(request, init) {
|
static async handleConfiguration(request, init) {
|
||||||
if (request.method !== "GET") return NATIVE_FETCH(request, init);
|
if (request.method !== "GET") return NATIVE_FETCH(request, init);
|
||||||
const response = await NATIVE_FETCH(request, init), text = await response.clone().text();
|
const response = await NATIVE_FETCH(request, init), text = await response.clone().text();
|
||||||
if (!text.length) return response;
|
if (!text.length) return response;
|
||||||
@ -4811,10 +4784,10 @@ class XcloudInterceptor {
|
|||||||
}
|
}
|
||||||
static async handle(request, init) {
|
static async handle(request, init) {
|
||||||
let url = typeof request === "string" ? request : request.url;
|
let url = typeof request === "string" ? request : request.url;
|
||||||
if (url.endsWith("/v2/login/user")) return XcloudInterceptor.#handleLogin(request, init);
|
if (url.endsWith("/v2/login/user")) return XcloudInterceptor.handleLogin(request, init);
|
||||||
else if (url.endsWith("/sessions/cloud/play")) return XcloudInterceptor.#handlePlay(request, init);
|
else if (url.endsWith("/sessions/cloud/play")) return XcloudInterceptor.handlePlay(request, init);
|
||||||
else if (url.includes("xboxlive.com") && url.includes("/waittime/")) return XcloudInterceptor.#handleWaitTime(request, init);
|
else if (url.includes("xboxlive.com") && url.includes("/waittime/")) return XcloudInterceptor.handleWaitTime(request, init);
|
||||||
else if (url.endsWith("/configuration")) return XcloudInterceptor.#handleConfiguration(request, init);
|
else if (url.endsWith("/configuration")) return XcloudInterceptor.handleConfiguration(request, init);
|
||||||
else if (url && url.endsWith("/ice") && url.includes("/sessions/") && request.method === "GET") return patchIceCandidates(request);
|
else if (url && url.endsWith("/ice") && url.includes("/sessions/") && request.method === "GET") return patchIceCandidates(request);
|
||||||
return NATIVE_FETCH(request, init);
|
return NATIVE_FETCH(request, init);
|
||||||
}
|
}
|
||||||
@ -5083,7 +5056,7 @@ class WebGL2Player {
|
|||||||
saturation: 0
|
saturation: 0
|
||||||
};
|
};
|
||||||
targetFps = 60;
|
targetFps = 60;
|
||||||
frameInterval = Math.ceil(1000 / this.targetFps);
|
frameInterval = 0;
|
||||||
lastFrameTime = 0;
|
lastFrameTime = 0;
|
||||||
animFrameId = null;
|
animFrameId = null;
|
||||||
constructor($video) {
|
constructor($video) {
|
||||||
@ -5107,7 +5080,7 @@ class WebGL2Player {
|
|||||||
this.options.saturation = 1 + (saturation - 100) / 100, update && this.updateCanvas();
|
this.options.saturation = 1 + (saturation - 100) / 100, update && this.updateCanvas();
|
||||||
}
|
}
|
||||||
setTargetFps(target) {
|
setTargetFps(target) {
|
||||||
this.targetFps = target, this.frameInterval = Math.ceil(1000 / target);
|
this.targetFps = target, this.lastFrameTime = 0, this.frameInterval = target ? Math.floor(1000 / target) : 0;
|
||||||
}
|
}
|
||||||
getCanvas() {
|
getCanvas() {
|
||||||
return this.$canvas;
|
return this.$canvas;
|
||||||
@ -5117,6 +5090,7 @@ class WebGL2Player {
|
|||||||
gl.uniform2f(gl.getUniformLocation(program, "iResolution"), this.$canvas.width, this.$canvas.height), gl.uniform1i(gl.getUniformLocation(program, "filterId"), this.options.filterId), gl.uniform1f(gl.getUniformLocation(program, "sharpenFactor"), this.options.sharpenFactor), gl.uniform1f(gl.getUniformLocation(program, "brightness"), this.options.brightness), gl.uniform1f(gl.getUniformLocation(program, "contrast"), this.options.contrast), gl.uniform1f(gl.getUniformLocation(program, "saturation"), this.options.saturation);
|
gl.uniform2f(gl.getUniformLocation(program, "iResolution"), this.$canvas.width, this.$canvas.height), gl.uniform1i(gl.getUniformLocation(program, "filterId"), this.options.filterId), gl.uniform1f(gl.getUniformLocation(program, "sharpenFactor"), this.options.sharpenFactor), gl.uniform1f(gl.getUniformLocation(program, "brightness"), this.options.brightness), gl.uniform1f(gl.getUniformLocation(program, "contrast"), this.options.contrast), gl.uniform1f(gl.getUniformLocation(program, "saturation"), this.options.saturation);
|
||||||
}
|
}
|
||||||
drawFrame() {
|
drawFrame() {
|
||||||
|
if (this.targetFps === 0) return;
|
||||||
if (this.targetFps < 60) {
|
if (this.targetFps < 60) {
|
||||||
const currentTime = performance.now();
|
const currentTime = performance.now();
|
||||||
if (currentTime - this.lastFrameTime < this.frameInterval) return;
|
if (currentTime - this.lastFrameTime < this.frameInterval) return;
|
||||||
@ -5171,9 +5145,9 @@ class WebGL2Player {
|
|||||||
BxLogger.info(this.LOG_TAG, "Destroy"), this.stop();
|
BxLogger.info(this.LOG_TAG, "Destroy"), this.stop();
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
if (gl) {
|
if (gl) {
|
||||||
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
gl.getExtension("WEBGL_lose_context")?.loseContext(), gl.useProgram(null);
|
||||||
for (let resource of this.resources)
|
for (let resource of this.resources)
|
||||||
if (resource instanceof WebGLProgram) gl.useProgram(null), gl.deleteProgram(resource);
|
if (resource instanceof WebGLProgram) gl.deleteProgram(resource);
|
||||||
else if (resource instanceof WebGLShader) gl.deleteShader(resource);
|
else if (resource instanceof WebGLShader) gl.deleteShader(resource);
|
||||||
else if (resource instanceof WebGLTexture) gl.deleteTexture(resource);
|
else if (resource instanceof WebGLTexture) gl.deleteTexture(resource);
|
||||||
else if (resource instanceof WebGLBuffer) gl.deleteBuffer(resource);
|
else if (resource instanceof WebGLBuffer) gl.deleteBuffer(resource);
|
||||||
|
2
dist/better-xcloud.meta.js
vendored
2
dist/better-xcloud.meta.js
vendored
@ -1,5 +1,5 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 5.8.4
|
// @version 5.8.5
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
216
dist/better-xcloud.user.js
vendored
216
dist/better-xcloud.user.js
vendored
@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 5.8.4
|
// @version 5.8.5
|
||||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||||
// @author redphx
|
// @author redphx
|
||||||
// @license MIT
|
// @license MIT
|
||||||
@ -113,7 +113,7 @@ function deepClone(obj) {
|
|||||||
if (!obj) return {};
|
if (!obj) return {};
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
var SCRIPT_VERSION = "5.8.4", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
|
var SCRIPT_VERSION = "5.8.5", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
|
||||||
UserAgent.init();
|
UserAgent.init();
|
||||||
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = {
|
var userAgent = window.navigator.userAgent.toLowerCase(), isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent), isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"), browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0, userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport, supportMkb = AppInterface || !userAgent.match(/(android|iphone|ipad)/), STATES = {
|
||||||
supportedRegion: !0,
|
supportedRegion: !0,
|
||||||
@ -963,10 +963,7 @@ class BaseSettingsStore {
|
|||||||
}
|
}
|
||||||
class StreamStatsCollector {
|
class StreamStatsCollector {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => StreamStatsCollector.instance ?? (StreamStatsCollector.instance = new StreamStatsCollector);
|
||||||
if (!StreamStatsCollector.instance) StreamStatsCollector.instance = new StreamStatsCollector;
|
|
||||||
return StreamStatsCollector.instance;
|
|
||||||
}
|
|
||||||
static INTERVAL_BACKGROUND = 60000;
|
static INTERVAL_BACKGROUND = 60000;
|
||||||
calculateGrade(value, grades) {
|
calculateGrade(value, grades) {
|
||||||
return value > grades[2] ? "bad" : value > grades[1] ? "ok" : value > grades[0] ? "good" : "";
|
return value > grades[2] ? "bad" : value > grades[1] ? "ok" : value > grades[0] ? "good" : "";
|
||||||
@ -989,7 +986,8 @@ class StreamStatsCollector {
|
|||||||
fps: {
|
fps: {
|
||||||
current: 0,
|
current: 0,
|
||||||
toString() {
|
toString() {
|
||||||
return this.current.toString();
|
const maxFps = getPref("video_max_fps");
|
||||||
|
return maxFps < 60 ? `${maxFps}/${this.current}` : this.current.toString();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
btr: {
|
btr: {
|
||||||
@ -1878,10 +1876,7 @@ var MouseMapTo;
|
|||||||
})(MouseMapTo ||= {});
|
})(MouseMapTo ||= {});
|
||||||
class StreamStats {
|
class StreamStats {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => StreamStats.instance ?? (StreamStats.instance = new StreamStats);
|
||||||
if (!StreamStats.instance) StreamStats.instance = new StreamStats;
|
|
||||||
return StreamStats.instance;
|
|
||||||
}
|
|
||||||
intervalId;
|
intervalId;
|
||||||
REFRESH_INTERVAL = 1000;
|
REFRESH_INTERVAL = 1000;
|
||||||
stats = {
|
stats = {
|
||||||
@ -2258,14 +2253,13 @@ function onChangeVideoPlayerType() {
|
|||||||
else if ($videoProcessing.value = "usm", setPref("video_processing", "usm"), $optCas && ($optCas.disabled = !0), UserAgent.isSafari()) isDisabled = !0;
|
else if ($videoProcessing.value = "usm", setPref("video_processing", "usm"), $optCas && ($optCas.disabled = !0), UserAgent.isSafari()) isDisabled = !0;
|
||||||
$videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), $videoMaxFps.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), updateVideoPlayer();
|
$videoProcessing.disabled = isDisabled, $videoSharpness.dataset.disabled = isDisabled.toString(), $videoPowerPreference.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), $videoMaxFps.closest(".bx-settings-row").classList.toggle("bx-gone", playerType !== "webgl2"), updateVideoPlayer();
|
||||||
}
|
}
|
||||||
function limitVideoPlayerFps() {
|
function limitVideoPlayerFps(targetFps) {
|
||||||
const targetFps = getPref("video_max_fps");
|
|
||||||
STATES.currentStream.streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
|
STATES.currentStream.streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
|
||||||
}
|
}
|
||||||
function updateVideoPlayer() {
|
function updateVideoPlayer() {
|
||||||
const streamPlayer = STATES.currentStream.streamPlayer;
|
const streamPlayer = STATES.currentStream.streamPlayer;
|
||||||
if (!streamPlayer) return;
|
if (!streamPlayer) return;
|
||||||
limitVideoPlayerFps();
|
limitVideoPlayerFps(getPref("video_max_fps"));
|
||||||
const options = {
|
const options = {
|
||||||
processing: getPref("video_processing"),
|
processing: getPref("video_processing"),
|
||||||
sharpness: getPref("video_sharpness"),
|
sharpness: getPref("video_sharpness"),
|
||||||
@ -2498,10 +2492,7 @@ class KeyHelper {
|
|||||||
var LOG_TAG = "PointerClient";
|
var LOG_TAG = "PointerClient";
|
||||||
class PointerClient {
|
class PointerClient {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => PointerClient.instance ?? (PointerClient.instance = new PointerClient);
|
||||||
if (!PointerClient.instance) PointerClient.instance = new PointerClient;
|
|
||||||
return PointerClient.instance;
|
|
||||||
}
|
|
||||||
socket;
|
socket;
|
||||||
mkbHandler;
|
mkbHandler;
|
||||||
start(port, mkbHandler) {
|
start(port, mkbHandler) {
|
||||||
@ -2575,6 +2566,7 @@ class MouseDataProvider {
|
|||||||
class MkbHandler {}
|
class MkbHandler {}
|
||||||
class NativeMkbHandler extends MkbHandler {
|
class NativeMkbHandler extends MkbHandler {
|
||||||
static instance;
|
static instance;
|
||||||
|
static getInstance = () => NativeMkbHandler.instance ?? (NativeMkbHandler.instance = new NativeMkbHandler);
|
||||||
#pointerClient;
|
#pointerClient;
|
||||||
#enabled = !1;
|
#enabled = !1;
|
||||||
#mouseButtonsPressed = 0;
|
#mouseButtonsPressed = 0;
|
||||||
@ -2584,10 +2576,6 @@ class NativeMkbHandler extends MkbHandler {
|
|||||||
#mouseHorizontalMultiply = 0;
|
#mouseHorizontalMultiply = 0;
|
||||||
#inputSink;
|
#inputSink;
|
||||||
#$message;
|
#$message;
|
||||||
static getInstance() {
|
|
||||||
if (!NativeMkbHandler.instance) NativeMkbHandler.instance = new NativeMkbHandler;
|
|
||||||
return NativeMkbHandler.instance;
|
|
||||||
}
|
|
||||||
#onKeyboardEvent(e) {
|
#onKeyboardEvent(e) {
|
||||||
if (e.type === "keyup" && e.code === "F8") {
|
if (e.type === "keyup" && e.code === "F8") {
|
||||||
e.preventDefault(), this.toggle();
|
e.preventDefault(), this.toggle();
|
||||||
@ -2793,11 +2781,8 @@ class PointerLockMouseDataProvider extends MouseDataProvider {
|
|||||||
#disableContextMenu = (e) => e.preventDefault();
|
#disableContextMenu = (e) => e.preventDefault();
|
||||||
}
|
}
|
||||||
class EmulatedMkbHandler extends MkbHandler {
|
class EmulatedMkbHandler extends MkbHandler {
|
||||||
static #instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => EmulatedMkbHandler.instance ?? (EmulatedMkbHandler.instance = new EmulatedMkbHandler);
|
||||||
if (!EmulatedMkbHandler.#instance) EmulatedMkbHandler.#instance = new EmulatedMkbHandler;
|
|
||||||
return EmulatedMkbHandler.#instance;
|
|
||||||
}
|
|
||||||
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
||||||
static DEFAULT_PANNING_SENSITIVITY = 0.001;
|
static DEFAULT_PANNING_SENSITIVITY = 0.001;
|
||||||
static DEFAULT_DEADZONE_COUNTERWEIGHT = 0.01;
|
static DEFAULT_DEADZONE_COUNTERWEIGHT = 0.01;
|
||||||
@ -3081,10 +3066,7 @@ class NavigationDialog {
|
|||||||
}
|
}
|
||||||
class NavigationDialogManager {
|
class NavigationDialogManager {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => NavigationDialogManager.instance ?? (NavigationDialogManager.instance = new NavigationDialogManager);
|
||||||
if (!NavigationDialogManager.instance) NavigationDialogManager.instance = new NavigationDialogManager;
|
|
||||||
return NavigationDialogManager.instance;
|
|
||||||
}
|
|
||||||
static GAMEPAD_POLLING_INTERVAL = 50;
|
static GAMEPAD_POLLING_INTERVAL = 50;
|
||||||
static GAMEPAD_KEYS = [
|
static GAMEPAD_KEYS = [
|
||||||
12,
|
12,
|
||||||
@ -4257,9 +4239,9 @@ true` + text;
|
|||||||
return str = str.replace(text, newCode), str;
|
return str = str.replace(text, newCode), str;
|
||||||
},
|
},
|
||||||
skipFeedbackDialog(str) {
|
skipFeedbackDialog(str) {
|
||||||
let text = "&&this.shouldTransitionToFeedback(";
|
let text = "shouldTransitionToFeedback(e){";
|
||||||
if (!str.includes(text)) return !1;
|
if (!str.includes(text)) return !1;
|
||||||
return str = str.replace(text, "&& false " + text), str;
|
return str = str.replace(text, text + "return !1;"), str;
|
||||||
},
|
},
|
||||||
enableNativeMkb(str) {
|
enableNativeMkb(str) {
|
||||||
let text = "e.mouseSupported&&e.keyboardSupported&&e.fullscreenSupported;";
|
let text = "e.mouseSupported&&e.keyboardSupported&&e.fullscreenSupported;";
|
||||||
@ -4551,10 +4533,7 @@ class PatcherCache {
|
|||||||
}
|
}
|
||||||
class FullscreenText {
|
class FullscreenText {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => FullscreenText.instance ?? (FullscreenText.instance = new FullscreenText);
|
||||||
if (!FullscreenText.instance) FullscreenText.instance = new FullscreenText;
|
|
||||||
return FullscreenText.instance;
|
|
||||||
}
|
|
||||||
$text;
|
$text;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.$text = CE("div", {
|
this.$text = CE("div", {
|
||||||
@ -4570,10 +4549,7 @@ class FullscreenText {
|
|||||||
}
|
}
|
||||||
class SettingsNavigationDialog extends NavigationDialog {
|
class SettingsNavigationDialog extends NavigationDialog {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => SettingsNavigationDialog.instance ?? (SettingsNavigationDialog.instance = new SettingsNavigationDialog);
|
||||||
if (!SettingsNavigationDialog.instance) SettingsNavigationDialog.instance = new SettingsNavigationDialog;
|
|
||||||
return SettingsNavigationDialog.instance;
|
|
||||||
}
|
|
||||||
$container;
|
$container;
|
||||||
$tabs;
|
$tabs;
|
||||||
$settings;
|
$settings;
|
||||||
@ -4838,7 +4814,9 @@ class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
onChange: onChangeVideoPlayerType
|
onChange: onChangeVideoPlayerType
|
||||||
}, {
|
}, {
|
||||||
pref: "video_max_fps",
|
pref: "video_max_fps",
|
||||||
onChange: limitVideoPlayerFps
|
onChange: (e) => {
|
||||||
|
limitVideoPlayerFps(parseInt(e.target.value));
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
pref: "video_power_preference",
|
pref: "video_power_preference",
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
@ -5832,10 +5810,7 @@ class HeaderSection {
|
|||||||
}
|
}
|
||||||
class RemotePlayNavigationDialog extends NavigationDialog {
|
class RemotePlayNavigationDialog extends NavigationDialog {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => RemotePlayNavigationDialog.instance ?? (RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog);
|
||||||
if (!RemotePlayNavigationDialog.instance) RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog;
|
|
||||||
return RemotePlayNavigationDialog.instance;
|
|
||||||
}
|
|
||||||
STATE_LABELS = {
|
STATE_LABELS = {
|
||||||
On: t("powered-on"),
|
On: t("powered-on"),
|
||||||
Off: t("powered-off"),
|
Off: t("powered-off"),
|
||||||
@ -5901,10 +5876,7 @@ class RemotePlayNavigationDialog extends NavigationDialog {
|
|||||||
var LOG_TAG5 = "RemotePlay";
|
var LOG_TAG5 = "RemotePlay";
|
||||||
class RemotePlayManager {
|
class RemotePlayManager {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => RemotePlayManager.instance ?? (RemotePlayManager.instance = new RemotePlayManager);
|
||||||
if (!this.instance) this.instance = new RemotePlayManager;
|
|
||||||
return this.instance;
|
|
||||||
}
|
|
||||||
isInitialized = !1;
|
isInitialized = !1;
|
||||||
XCLOUD_TOKEN;
|
XCLOUD_TOKEN;
|
||||||
XHOME_TOKEN;
|
XHOME_TOKEN;
|
||||||
@ -6017,7 +5989,7 @@ class RemotePlayManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class XhomeInterceptor {
|
class XhomeInterceptor {
|
||||||
static #consoleAddrs = {};
|
static consoleAddrs = {};
|
||||||
static BASE_DEVICE_INFO = {
|
static BASE_DEVICE_INFO = {
|
||||||
appInfo: {
|
appInfo: {
|
||||||
env: {
|
env: {
|
||||||
@ -6056,7 +6028,7 @@ class XhomeInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
static async#handleLogin(request) {
|
static async handleLogin(request) {
|
||||||
try {
|
try {
|
||||||
const obj = await request.clone().json();
|
const obj = await request.clone().json();
|
||||||
obj.offeringId = "xhome", request = new Request("https://xhome.gssv-play-prod.xboxlive.com/v2/login/user", {
|
obj.offeringId = "xhome", request = new Request("https://xhome.gssv-play-prod.xboxlive.com/v2/login/user", {
|
||||||
@ -6071,19 +6043,19 @@ class XhomeInterceptor {
|
|||||||
}
|
}
|
||||||
return NATIVE_FETCH(request);
|
return NATIVE_FETCH(request);
|
||||||
}
|
}
|
||||||
static async#handleConfiguration(request) {
|
static async handleConfiguration(request) {
|
||||||
const response = await NATIVE_FETCH(request), obj = await response.clone().json();
|
const response = await NATIVE_FETCH(request), obj = await response.clone().json();
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
const processPorts = (port) => {
|
const processPorts = (port) => {
|
||||||
const ports = new Set;
|
const ports = new Set;
|
||||||
return port && ports.add(port), ports.add(9002), Array.from(ports);
|
return port && ports.add(port), ports.add(9002), Array.from(ports);
|
||||||
}, serverDetails = obj.serverDetails;
|
}, serverDetails = obj.serverDetails;
|
||||||
if (serverDetails.ipAddress) XhomeInterceptor.#consoleAddrs[serverDetails.ipAddress] = processPorts(serverDetails.port);
|
if (serverDetails.ipAddress) XhomeInterceptor.consoleAddrs[serverDetails.ipAddress] = processPorts(serverDetails.port);
|
||||||
if (serverDetails.ipV4Address) XhomeInterceptor.#consoleAddrs[serverDetails.ipV4Address] = processPorts(serverDetails.ipV4Port);
|
if (serverDetails.ipV4Address) XhomeInterceptor.consoleAddrs[serverDetails.ipV4Address] = processPorts(serverDetails.ipV4Port);
|
||||||
if (serverDetails.ipV6Address) XhomeInterceptor.#consoleAddrs[serverDetails.ipV6Address] = processPorts(serverDetails.ipV6Port);
|
if (serverDetails.ipV6Address) XhomeInterceptor.consoleAddrs[serverDetails.ipV6Address] = processPorts(serverDetails.ipV6Port);
|
||||||
return response.json = () => Promise.resolve(obj), response.text = () => Promise.resolve(JSON.stringify(obj)), response;
|
return response.json = () => Promise.resolve(obj), response.text = () => Promise.resolve(JSON.stringify(obj)), response;
|
||||||
}
|
}
|
||||||
static async#handleInputConfigs(request, opts) {
|
static async handleInputConfigs(request, opts) {
|
||||||
const response = await NATIVE_FETCH(request);
|
const response = await NATIVE_FETCH(request);
|
||||||
if (getPref("stream_touch_controller") !== "all") return response;
|
if (getPref("stream_touch_controller") !== "all") return response;
|
||||||
const obj = await response.clone().json(), xboxTitleId = JSON.parse(opts.body).titleIds[0];
|
const obj = await response.clone().json(), xboxTitleId = JSON.parse(opts.body).titleIds[0];
|
||||||
@ -6100,7 +6072,7 @@ class XhomeInterceptor {
|
|||||||
else TouchController.enable(), TouchController.requestCustomLayouts(xboxTitleId);
|
else TouchController.enable(), TouchController.requestCustomLayouts(xboxTitleId);
|
||||||
return response.json = () => Promise.resolve(obj), response.text = () => Promise.resolve(JSON.stringify(obj)), response;
|
return response.json = () => Promise.resolve(obj), response.text = () => Promise.resolve(JSON.stringify(obj)), response;
|
||||||
}
|
}
|
||||||
static async#handleTitles(request) {
|
static async handleTitles(request) {
|
||||||
const clone = request.clone(), headers = {};
|
const clone = request.clone(), headers = {};
|
||||||
for (let pair of clone.headers.entries())
|
for (let pair of clone.headers.entries())
|
||||||
headers[pair[0]] = pair[1];
|
headers[pair[0]] = pair[1];
|
||||||
@ -6112,7 +6084,7 @@ class XhomeInterceptor {
|
|||||||
headers
|
headers
|
||||||
}), NATIVE_FETCH(request);
|
}), NATIVE_FETCH(request);
|
||||||
}
|
}
|
||||||
static async#handlePlay(request) {
|
static async handlePlay(request) {
|
||||||
const body = await request.clone().json(), newRequest = new Request(request, {
|
const body = await request.clone().json(), newRequest = new Request(request, {
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
@ -6139,12 +6111,12 @@ class XhomeInterceptor {
|
|||||||
}
|
}
|
||||||
request = new Request(newUrl, opts);
|
request = new Request(newUrl, opts);
|
||||||
let url = typeof request === "string" ? request : request.url;
|
let url = typeof request === "string" ? request : request.url;
|
||||||
if (url.includes("/configuration")) return XhomeInterceptor.#handleConfiguration(request);
|
if (url.includes("/configuration")) return XhomeInterceptor.handleConfiguration(request);
|
||||||
else if (url.endsWith("/sessions/home/play")) return XhomeInterceptor.#handlePlay(request);
|
else if (url.endsWith("/sessions/home/play")) return XhomeInterceptor.handlePlay(request);
|
||||||
else if (url.includes("inputconfigs")) return XhomeInterceptor.#handleInputConfigs(request, opts);
|
else if (url.includes("inputconfigs")) return XhomeInterceptor.handleInputConfigs(request, opts);
|
||||||
else if (url.includes("/login/user")) return XhomeInterceptor.#handleLogin(request);
|
else if (url.includes("/login/user")) return XhomeInterceptor.handleLogin(request);
|
||||||
else if (url.endsWith("/titles")) return XhomeInterceptor.#handleTitles(request);
|
else if (url.endsWith("/titles")) return XhomeInterceptor.handleTitles(request);
|
||||||
else if (url && url.endsWith("/ice") && url.includes("/sessions/") && request.method === "GET") return patchIceCandidates(request, XhomeInterceptor.#consoleAddrs);
|
else if (url && url.endsWith("/ice") && url.includes("/sessions/") && request.method === "GET") return patchIceCandidates(request, XhomeInterceptor.consoleAddrs);
|
||||||
return await NATIVE_FETCH(request);
|
return await NATIVE_FETCH(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6413,10 +6385,7 @@ class GuideMenu {
|
|||||||
}
|
}
|
||||||
class StreamBadges {
|
class StreamBadges {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => StreamBadges.instance ?? (StreamBadges.instance = new StreamBadges);
|
||||||
if (!StreamBadges.instance) StreamBadges.instance = new StreamBadges;
|
|
||||||
return StreamBadges.instance;
|
|
||||||
}
|
|
||||||
serverInfo = {};
|
serverInfo = {};
|
||||||
badges = {
|
badges = {
|
||||||
playtime: {
|
playtime: {
|
||||||
@ -6581,17 +6550,7 @@ class StreamBadges {
|
|||||||
static setupEvents() {}
|
static setupEvents() {}
|
||||||
}
|
}
|
||||||
class XcloudInterceptor {
|
class XcloudInterceptor {
|
||||||
static async#handleLogin(request, init) {
|
static SERVER_EMOJIS = {
|
||||||
const bypassServer = getPref("server_bypass_restriction");
|
|
||||||
if (bypassServer !== "off") {
|
|
||||||
const ip = BypassServerIps[bypassServer];
|
|
||||||
ip && request.headers.set("X-Forwarded-For", ip);
|
|
||||||
}
|
|
||||||
const response = await NATIVE_FETCH(request, init);
|
|
||||||
if (response.status !== 200) return BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE), response;
|
|
||||||
const obj = await response.clone().json();
|
|
||||||
RemotePlayManager.getInstance().xcloudToken = obj.gsToken;
|
|
||||||
const serverEmojis = {
|
|
||||||
AustraliaEast: "🇦🇺",
|
AustraliaEast: "🇦🇺",
|
||||||
AustraliaSouthEast: "🇦🇺",
|
AustraliaSouthEast: "🇦🇺",
|
||||||
BrazilSouth: "🇧🇷",
|
BrazilSouth: "🇧🇷",
|
||||||
@ -6606,7 +6565,18 @@ class XcloudInterceptor {
|
|||||||
WestEurope: "🇪🇺",
|
WestEurope: "🇪🇺",
|
||||||
WestUS: "🇺🇸",
|
WestUS: "🇺🇸",
|
||||||
WestUS2: "🇺🇸"
|
WestUS2: "🇺🇸"
|
||||||
}, serverRegex = /\/\/(\w+)\./;
|
};
|
||||||
|
static async handleLogin(request, init) {
|
||||||
|
const bypassServer = getPref("server_bypass_restriction");
|
||||||
|
if (bypassServer !== "off") {
|
||||||
|
const ip = BypassServerIps[bypassServer];
|
||||||
|
ip && request.headers.set("X-Forwarded-For", ip);
|
||||||
|
}
|
||||||
|
const response = await NATIVE_FETCH(request, init);
|
||||||
|
if (response.status !== 200) return BxEvent.dispatch(window, BxEvent.XCLOUD_SERVERS_UNAVAILABLE), response;
|
||||||
|
const obj = await response.clone().json();
|
||||||
|
RemotePlayManager.getInstance().xcloudToken = obj.gsToken;
|
||||||
|
const serverRegex = /\/\/(\w+)\./, serverEmojis = XcloudInterceptor.SERVER_EMOJIS;
|
||||||
for (let region of obj.offeringSettings.regions) {
|
for (let region of obj.offeringSettings.regions) {
|
||||||
const regionName = region.name;
|
const regionName = region.name;
|
||||||
let shortName = region.name;
|
let shortName = region.name;
|
||||||
@ -6625,7 +6595,7 @@ class XcloudInterceptor {
|
|||||||
}
|
}
|
||||||
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
|
return STATES.gsToken = obj.gsToken, response.json = () => Promise.resolve(obj), response;
|
||||||
}
|
}
|
||||||
static async#handlePlay(request, init) {
|
static async handlePlay(request, init) {
|
||||||
const PREF_STREAM_TARGET_RESOLUTION = getPref("stream_target_resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream_preferred_locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url);
|
const PREF_STREAM_TARGET_RESOLUTION = getPref("stream_target_resolution"), PREF_STREAM_PREFERRED_LOCALE = getPref("stream_preferred_locale"), url = typeof request === "string" ? request : request.url, parsedUrl = new URL(url);
|
||||||
let badgeRegion = parsedUrl.host.split(".", 1)[0];
|
let badgeRegion = parsedUrl.host.split(".", 1)[0];
|
||||||
for (let regionName in STATES.serverRegions) {
|
for (let regionName in STATES.serverRegions) {
|
||||||
@ -6647,7 +6617,7 @@ class XcloudInterceptor {
|
|||||||
});
|
});
|
||||||
return NATIVE_FETCH(newRequest);
|
return NATIVE_FETCH(newRequest);
|
||||||
}
|
}
|
||||||
static async#handleWaitTime(request, init) {
|
static async handleWaitTime(request, init) {
|
||||||
const response = await NATIVE_FETCH(request, init);
|
const response = await NATIVE_FETCH(request, init);
|
||||||
if (getPref("ui_loading_screen_wait_time")) {
|
if (getPref("ui_loading_screen_wait_time")) {
|
||||||
const json = await response.clone().json();
|
const json = await response.clone().json();
|
||||||
@ -6655,7 +6625,7 @@ class XcloudInterceptor {
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
static async#handleConfiguration(request, init) {
|
static async handleConfiguration(request, init) {
|
||||||
if (request.method !== "GET") return NATIVE_FETCH(request, init);
|
if (request.method !== "GET") return NATIVE_FETCH(request, init);
|
||||||
if (getPref("stream_touch_controller") === "all") if (STATES.currentStream.titleInfo?.details.hasTouchSupport) TouchController.disable();
|
if (getPref("stream_touch_controller") === "all") if (STATES.currentStream.titleInfo?.details.hasTouchSupport) TouchController.disable();
|
||||||
else TouchController.enable();
|
else TouchController.enable();
|
||||||
@ -6677,10 +6647,10 @@ class XcloudInterceptor {
|
|||||||
}
|
}
|
||||||
static async handle(request, init) {
|
static async handle(request, init) {
|
||||||
let url = typeof request === "string" ? request : request.url;
|
let url = typeof request === "string" ? request : request.url;
|
||||||
if (url.endsWith("/v2/login/user")) return XcloudInterceptor.#handleLogin(request, init);
|
if (url.endsWith("/v2/login/user")) return XcloudInterceptor.handleLogin(request, init);
|
||||||
else if (url.endsWith("/sessions/cloud/play")) return XcloudInterceptor.#handlePlay(request, init);
|
else if (url.endsWith("/sessions/cloud/play")) return XcloudInterceptor.handlePlay(request, init);
|
||||||
else if (url.includes("xboxlive.com") && url.includes("/waittime/")) return XcloudInterceptor.#handleWaitTime(request, init);
|
else if (url.includes("xboxlive.com") && url.includes("/waittime/")) return XcloudInterceptor.handleWaitTime(request, init);
|
||||||
else if (url.endsWith("/configuration")) return XcloudInterceptor.#handleConfiguration(request, init);
|
else if (url.endsWith("/configuration")) return XcloudInterceptor.handleConfiguration(request, init);
|
||||||
else if (url && url.endsWith("/ice") && url.includes("/sessions/") && request.method === "GET") return patchIceCandidates(request);
|
else if (url && url.endsWith("/ice") && url.includes("/sessions/") && request.method === "GET") return patchIceCandidates(request);
|
||||||
return NATIVE_FETCH(request, init);
|
return NATIVE_FETCH(request, init);
|
||||||
}
|
}
|
||||||
@ -6991,7 +6961,7 @@ class WebGL2Player {
|
|||||||
saturation: 0
|
saturation: 0
|
||||||
};
|
};
|
||||||
targetFps = 60;
|
targetFps = 60;
|
||||||
frameInterval = Math.ceil(1000 / this.targetFps);
|
frameInterval = 0;
|
||||||
lastFrameTime = 0;
|
lastFrameTime = 0;
|
||||||
animFrameId = null;
|
animFrameId = null;
|
||||||
constructor($video) {
|
constructor($video) {
|
||||||
@ -7015,7 +6985,7 @@ class WebGL2Player {
|
|||||||
this.options.saturation = 1 + (saturation - 100) / 100, update && this.updateCanvas();
|
this.options.saturation = 1 + (saturation - 100) / 100, update && this.updateCanvas();
|
||||||
}
|
}
|
||||||
setTargetFps(target) {
|
setTargetFps(target) {
|
||||||
this.targetFps = target, this.frameInterval = Math.ceil(1000 / target);
|
this.targetFps = target, this.lastFrameTime = 0, this.frameInterval = target ? Math.floor(1000 / target) : 0;
|
||||||
}
|
}
|
||||||
getCanvas() {
|
getCanvas() {
|
||||||
return this.$canvas;
|
return this.$canvas;
|
||||||
@ -7025,6 +6995,7 @@ class WebGL2Player {
|
|||||||
gl.uniform2f(gl.getUniformLocation(program, "iResolution"), this.$canvas.width, this.$canvas.height), gl.uniform1i(gl.getUniformLocation(program, "filterId"), this.options.filterId), gl.uniform1f(gl.getUniformLocation(program, "sharpenFactor"), this.options.sharpenFactor), gl.uniform1f(gl.getUniformLocation(program, "brightness"), this.options.brightness), gl.uniform1f(gl.getUniformLocation(program, "contrast"), this.options.contrast), gl.uniform1f(gl.getUniformLocation(program, "saturation"), this.options.saturation);
|
gl.uniform2f(gl.getUniformLocation(program, "iResolution"), this.$canvas.width, this.$canvas.height), gl.uniform1i(gl.getUniformLocation(program, "filterId"), this.options.filterId), gl.uniform1f(gl.getUniformLocation(program, "sharpenFactor"), this.options.sharpenFactor), gl.uniform1f(gl.getUniformLocation(program, "brightness"), this.options.brightness), gl.uniform1f(gl.getUniformLocation(program, "contrast"), this.options.contrast), gl.uniform1f(gl.getUniformLocation(program, "saturation"), this.options.saturation);
|
||||||
}
|
}
|
||||||
drawFrame() {
|
drawFrame() {
|
||||||
|
if (this.targetFps === 0) return;
|
||||||
if (this.targetFps < 60) {
|
if (this.targetFps < 60) {
|
||||||
const currentTime = performance.now();
|
const currentTime = performance.now();
|
||||||
if (currentTime - this.lastFrameTime < this.frameInterval) return;
|
if (currentTime - this.lastFrameTime < this.frameInterval) return;
|
||||||
@ -7079,9 +7050,9 @@ class WebGL2Player {
|
|||||||
BxLogger.info(this.LOG_TAG, "Destroy"), this.stop();
|
BxLogger.info(this.LOG_TAG, "Destroy"), this.stop();
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
if (gl) {
|
if (gl) {
|
||||||
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
gl.getExtension("WEBGL_lose_context")?.loseContext(), gl.useProgram(null);
|
||||||
for (let resource of this.resources)
|
for (let resource of this.resources)
|
||||||
if (resource instanceof WebGLProgram) gl.useProgram(null), gl.deleteProgram(resource);
|
if (resource instanceof WebGLProgram) gl.deleteProgram(resource);
|
||||||
else if (resource instanceof WebGLShader) gl.deleteShader(resource);
|
else if (resource instanceof WebGLShader) gl.deleteShader(resource);
|
||||||
else if (resource instanceof WebGLTexture) gl.deleteTexture(resource);
|
else if (resource instanceof WebGLTexture) gl.deleteTexture(resource);
|
||||||
else if (resource instanceof WebGLBuffer) gl.deleteBuffer(resource);
|
else if (resource instanceof WebGLBuffer) gl.deleteBuffer(resource);
|
||||||
@ -7343,6 +7314,9 @@ class BaseGameBarAction {
|
|||||||
onClick(e) {
|
onClick(e) {
|
||||||
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
||||||
}
|
}
|
||||||
|
render() {
|
||||||
|
return this.$content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class ScreenshotAction extends BaseGameBarAction {
|
class ScreenshotAction extends BaseGameBarAction {
|
||||||
$content;
|
$content;
|
||||||
@ -7358,9 +7332,6 @@ class ScreenshotAction extends BaseGameBarAction {
|
|||||||
onClick(e) {
|
onClick(e) {
|
||||||
super.onClick(e), Screenshot.takeScreenshot();
|
super.onClick(e), Screenshot.takeScreenshot();
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
class TouchControlAction extends BaseGameBarAction {
|
class TouchControlAction extends BaseGameBarAction {
|
||||||
$content;
|
$content;
|
||||||
@ -7378,23 +7349,19 @@ class TouchControlAction extends BaseGameBarAction {
|
|||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
classes: ["bx-activated"]
|
classes: ["bx-activated"]
|
||||||
});
|
});
|
||||||
this.$content = CE("div", {}, $btnEnable, $btnDisable), this.reset();
|
this.$content = CE("div", {}, $btnEnable, $btnDisable);
|
||||||
}
|
}
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
super.onClick(e);
|
super.onClick(e);
|
||||||
const isVisible = TouchController.toggleVisibility();
|
const isVisible = TouchController.toggleVisibility();
|
||||||
this.$content.dataset.activated = (!isVisible).toString();
|
this.$content.dataset.activated = (!isVisible).toString();
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
reset() {
|
reset() {
|
||||||
this.$content.dataset.activated = "false";
|
this.$content.dataset.activated = "false";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class MicrophoneAction extends BaseGameBarAction {
|
class MicrophoneAction extends BaseGameBarAction {
|
||||||
$content;
|
$content;
|
||||||
visible = !1;
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const $btnDefault = createButton({
|
const $btnDefault = createButton({
|
||||||
@ -7407,7 +7374,7 @@ class MicrophoneAction extends BaseGameBarAction {
|
|||||||
icon: BxIcon.MICROPHONE_MUTED,
|
icon: BxIcon.MICROPHONE_MUTED,
|
||||||
onClick: this.onClick.bind(this)
|
onClick: this.onClick.bind(this)
|
||||||
});
|
});
|
||||||
this.$content = CE("div", {}, $btnMuted, $btnDefault), this.reset(), window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, (e) => {
|
this.$content = CE("div", {}, $btnMuted, $btnDefault), window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, (e) => {
|
||||||
const enabled = e.microphoneState === "Enabled";
|
const enabled = e.microphoneState === "Enabled";
|
||||||
this.$content.dataset.activated = enabled.toString(), this.$content.classList.remove("bx-gone");
|
this.$content.dataset.activated = enabled.toString(), this.$content.classList.remove("bx-gone");
|
||||||
});
|
});
|
||||||
@ -7417,11 +7384,8 @@ class MicrophoneAction extends BaseGameBarAction {
|
|||||||
const enabled = MicrophoneShortcut.toggle(!1);
|
const enabled = MicrophoneShortcut.toggle(!1);
|
||||||
this.$content.dataset.activated = enabled.toString();
|
this.$content.dataset.activated = enabled.toString();
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
reset() {
|
reset() {
|
||||||
this.visible = !1, this.$content.classList.add("bx-gone"), this.$content.dataset.activated = "false";
|
this.$content.classList.add("bx-gone"), this.$content.dataset.activated = "false";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class TrueAchievementsAction extends BaseGameBarAction {
|
class TrueAchievementsAction extends BaseGameBarAction {
|
||||||
@ -7431,16 +7395,12 @@ class TrueAchievementsAction extends BaseGameBarAction {
|
|||||||
this.$content = createButton({
|
this.$content = createButton({
|
||||||
style: 4,
|
style: 4,
|
||||||
icon: BxIcon.TRUE_ACHIEVEMENTS,
|
icon: BxIcon.TRUE_ACHIEVEMENTS,
|
||||||
title: t("true-achievements"),
|
|
||||||
onClick: this.onClick.bind(this)
|
onClick: this.onClick.bind(this)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
super.onClick(e), TrueAchievements.open(!1);
|
super.onClick(e), TrueAchievements.open(!1);
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
class SpeakerAction extends BaseGameBarAction {
|
class SpeakerAction extends BaseGameBarAction {
|
||||||
$content;
|
$content;
|
||||||
@ -7456,7 +7416,7 @@ class SpeakerAction extends BaseGameBarAction {
|
|||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
classes: ["bx-activated"]
|
classes: ["bx-activated"]
|
||||||
});
|
});
|
||||||
this.$content = CE("div", {}, $btnEnable, $btnMuted), this.reset(), window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, (e) => {
|
this.$content = CE("div", {}, $btnEnable, $btnMuted), window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, (e) => {
|
||||||
const enabled = e.speakerState === 0;
|
const enabled = e.speakerState === 0;
|
||||||
this.$content.dataset.activated = (!enabled).toString();
|
this.$content.dataset.activated = (!enabled).toString();
|
||||||
});
|
});
|
||||||
@ -7464,9 +7424,6 @@ class SpeakerAction extends BaseGameBarAction {
|
|||||||
onClick(e) {
|
onClick(e) {
|
||||||
super.onClick(e), SoundShortcut.muteUnmute();
|
super.onClick(e), SoundShortcut.muteUnmute();
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
reset() {
|
reset() {
|
||||||
this.$content.dataset.activated = "false";
|
this.$content.dataset.activated = "false";
|
||||||
}
|
}
|
||||||
@ -7475,7 +7432,9 @@ class RendererShortcut {
|
|||||||
static toggleVisibility() {
|
static toggleVisibility() {
|
||||||
const $mediaContainer = document.querySelector('#game-stream div[data-testid="media-container"]');
|
const $mediaContainer = document.querySelector('#game-stream div[data-testid="media-container"]');
|
||||||
if (!$mediaContainer) return !0;
|
if (!$mediaContainer) return !0;
|
||||||
return $mediaContainer.classList.toggle("bx-gone"), !$mediaContainer.classList.contains("bx-gone");
|
$mediaContainer.classList.toggle("bx-gone");
|
||||||
|
const isShowing = !$mediaContainer.classList.contains("bx-gone");
|
||||||
|
return limitVideoPlayerFps(isShowing ? getPref("video_max_fps") : 0), isShowing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class RendererAction extends BaseGameBarAction {
|
class RendererAction extends BaseGameBarAction {
|
||||||
@ -7492,26 +7451,20 @@ class RendererAction extends BaseGameBarAction {
|
|||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
classes: ["bx-activated"]
|
classes: ["bx-activated"]
|
||||||
});
|
});
|
||||||
this.$content = CE("div", {}, $btnDefault, $btnActivated), this.reset();
|
this.$content = CE("div", {}, $btnDefault, $btnActivated);
|
||||||
}
|
}
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
super.onClick(e);
|
super.onClick(e);
|
||||||
const isVisible = RendererShortcut.toggleVisibility();
|
const isVisible = RendererShortcut.toggleVisibility();
|
||||||
this.$content.dataset.activated = (!isVisible).toString();
|
this.$content.dataset.activated = (!isVisible).toString();
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
reset() {
|
reset() {
|
||||||
this.$content.dataset.activated = "false";
|
this.$content.dataset.activated = "false";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class GameBar {
|
class GameBar {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => GameBar.instance ?? (GameBar.instance = new GameBar);
|
||||||
if (!GameBar.instance) GameBar.instance = new GameBar;
|
|
||||||
return GameBar.instance;
|
|
||||||
}
|
|
||||||
static VISIBLE_DURATION = 2000;
|
static VISIBLE_DURATION = 2000;
|
||||||
$gameBar;
|
$gameBar;
|
||||||
$container;
|
$container;
|
||||||
@ -7535,8 +7488,7 @@ class GameBar {
|
|||||||
if (e.target !== $gameBar) return;
|
if (e.target !== $gameBar) return;
|
||||||
$container.classList.contains("bx-show") ? this.hideBar() : this.showBar();
|
$container.classList.contains("bx-show") ? this.hideBar() : this.showBar();
|
||||||
}), window.addEventListener(BxEvent.GAME_BAR_ACTION_ACTIVATED, this.hideBar.bind(this)), $container.addEventListener("pointerover", this.clearHideTimeout.bind(this)), $container.addEventListener("pointerout", this.beginHideTimeout.bind(this)), $container.addEventListener("transitionend", (e) => {
|
}), window.addEventListener(BxEvent.GAME_BAR_ACTION_ACTIVATED, this.hideBar.bind(this)), $container.addEventListener("pointerover", this.clearHideTimeout.bind(this)), $container.addEventListener("pointerout", this.beginHideTimeout.bind(this)), $container.addEventListener("transitionend", (e) => {
|
||||||
const classList = $container.classList;
|
$container.classList.replace("bx-hide", "bx-offscreen");
|
||||||
if (classList.contains("bx-hide")) classList.remove("bx-hide"), classList.add("bx-offscreen");
|
|
||||||
}), document.documentElement.appendChild($gameBar), this.$gameBar = $gameBar, this.$container = $container, getPref("game_bar_position") !== "off" && window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, ((e) => {
|
}), document.documentElement.appendChild($gameBar), this.$gameBar = $gameBar, this.$container = $container, getPref("game_bar_position") !== "off" && window.addEventListener(BxEvent.XCLOUD_POLLING_MODE_CHANGED, ((e) => {
|
||||||
if (!STATES.isPlaying) {
|
if (!STATES.isPlaying) {
|
||||||
this.disable();
|
this.disable();
|
||||||
@ -7554,30 +7506,24 @@ class GameBar {
|
|||||||
this.timeoutId && clearTimeout(this.timeoutId), this.timeoutId = null;
|
this.timeoutId && clearTimeout(this.timeoutId), this.timeoutId = null;
|
||||||
}
|
}
|
||||||
enable() {
|
enable() {
|
||||||
this.$gameBar && this.$gameBar.classList.remove("bx-gone");
|
this.$gameBar.classList.remove("bx-gone");
|
||||||
}
|
}
|
||||||
disable() {
|
disable() {
|
||||||
this.hideBar(), this.$gameBar && this.$gameBar.classList.add("bx-gone");
|
this.hideBar(), this.$gameBar.classList.add("bx-gone");
|
||||||
}
|
}
|
||||||
showBar() {
|
showBar() {
|
||||||
if (!this.$container) return;
|
|
||||||
this.$container.classList.remove("bx-offscreen", "bx-hide", "bx-gone"), this.$container.classList.add("bx-show"), this.beginHideTimeout();
|
this.$container.classList.remove("bx-offscreen", "bx-hide", "bx-gone"), this.$container.classList.add("bx-show"), this.beginHideTimeout();
|
||||||
}
|
}
|
||||||
hideBar() {
|
hideBar() {
|
||||||
if (this.clearHideTimeout(), clearFocus(), !this.$container) return;
|
this.clearHideTimeout(), clearFocus(), this.$container.classList.replace("bx-show", "bx-hide");
|
||||||
this.$container.classList.remove("bx-show"), this.$container.classList.add("bx-hide");
|
|
||||||
}
|
}
|
||||||
reset() {
|
reset() {
|
||||||
for (let action of this.actions)
|
this.actions.forEach((action) => action.reset());
|
||||||
action.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class XcloudApi {
|
class XcloudApi {
|
||||||
static instance;
|
static instance;
|
||||||
static getInstance() {
|
static getInstance = () => XcloudApi.instance ?? (XcloudApi.instance = new XcloudApi);
|
||||||
if (!XcloudApi.instance) XcloudApi.instance = new XcloudApi;
|
|
||||||
return XcloudApi.instance;
|
|
||||||
}
|
|
||||||
CACHE_TITLES = {};
|
CACHE_TITLES = {};
|
||||||
CACHE_WAIT_TIME = {};
|
CACHE_WAIT_TIME = {};
|
||||||
async getTitleInfo(id2) {
|
async getTitleInfo(id2) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { BxEvent } from "@/utils/bx-event";
|
import { BxEvent } from "@/utils/bx-event";
|
||||||
|
|
||||||
export abstract class BaseGameBarAction {
|
export abstract class BaseGameBarAction {
|
||||||
|
abstract $content: HTMLElement;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
reset() {}
|
reset() {}
|
||||||
|
|
||||||
@ -8,5 +10,7 @@ export abstract class BaseGameBarAction {
|
|||||||
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
||||||
};
|
};
|
||||||
|
|
||||||
abstract render(): HTMLElement;
|
render(): HTMLElement {
|
||||||
|
return this.$content;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@ import { MicrophoneShortcut, MicrophoneState } from "../shortcuts/shortcut-micro
|
|||||||
export class MicrophoneAction extends BaseGameBarAction {
|
export class MicrophoneAction extends BaseGameBarAction {
|
||||||
$content: HTMLElement;
|
$content: HTMLElement;
|
||||||
|
|
||||||
visible: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -26,12 +24,7 @@ export class MicrophoneAction extends BaseGameBarAction {
|
|||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnMuted, $btnDefault);
|
||||||
$btnMuted,
|
|
||||||
$btnDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, e => {
|
window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, e => {
|
||||||
const microphoneState = (e as any).microphoneState;
|
const microphoneState = (e as any).microphoneState;
|
||||||
@ -49,12 +42,7 @@ export class MicrophoneAction extends BaseGameBarAction {
|
|||||||
this.$content.dataset.activated = enabled.toString();
|
this.$content.dataset.activated = enabled.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.visible = false;
|
|
||||||
this.$content.classList.add('bx-gone');
|
this.$content.classList.add('bx-gone');
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,7 @@ export class RendererAction extends BaseGameBarAction {
|
|||||||
classes: ['bx-activated'],
|
classes: ['bx-activated'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnDefault, $btnActivated);
|
||||||
$btnDefault,
|
|
||||||
$btnActivated,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(e: Event) {
|
onClick(e: Event) {
|
||||||
@ -37,10 +32,6 @@ export class RendererAction extends BaseGameBarAction {
|
|||||||
this.$content.dataset.activated = (!isVisible).toString();
|
this.$content.dataset.activated = (!isVisible).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,4 @@ export class ScreenshotAction extends BaseGameBarAction {
|
|||||||
super.onClick(e);
|
super.onClick(e);
|
||||||
Screenshot.takeScreenshot();
|
Screenshot.takeScreenshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,7 @@ export class SpeakerAction extends BaseGameBarAction {
|
|||||||
classes: ['bx-activated'],
|
classes: ['bx-activated'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnEnable, $btnMuted);
|
||||||
$btnEnable,
|
|
||||||
$btnMuted,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, e => {
|
window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, e => {
|
||||||
const speakerState = (e as any).speakerState;
|
const speakerState = (e as any).speakerState;
|
||||||
@ -44,10 +39,6 @@ export class SpeakerAction extends BaseGameBarAction {
|
|||||||
SoundShortcut.muteUnmute();
|
SoundShortcut.muteUnmute();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,7 @@ export class TouchControlAction extends BaseGameBarAction {
|
|||||||
classes: ['bx-activated'],
|
classes: ['bx-activated'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnEnable, $btnDisable);
|
||||||
$btnEnable,
|
|
||||||
$btnDisable,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(e: Event) {
|
onClick(e: Event) {
|
||||||
@ -39,10 +34,6 @@ export class TouchControlAction extends BaseGameBarAction {
|
|||||||
this.$content.dataset.activated = (!isVisible).toString();
|
this.$content.dataset.activated = (!isVisible).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BxIcon } from "@/utils/bx-icon";
|
import { BxIcon } from "@/utils/bx-icon";
|
||||||
import { createButton, ButtonStyle } from "@/utils/html";
|
import { createButton, ButtonStyle } from "@/utils/html";
|
||||||
import { t } from "@/utils/translation";
|
|
||||||
import { BaseGameBarAction } from "./action-base";
|
import { BaseGameBarAction } from "./action-base";
|
||||||
import { TrueAchievements } from "@/utils/true-achievements";
|
import { TrueAchievements } from "@/utils/true-achievements";
|
||||||
|
|
||||||
@ -13,7 +12,6 @@ export class TrueAchievementsAction extends BaseGameBarAction {
|
|||||||
this.$content = createButton({
|
this.$content = createButton({
|
||||||
style: ButtonStyle.GHOST,
|
style: ButtonStyle.GHOST,
|
||||||
icon: BxIcon.TRUE_ACHIEVEMENTS,
|
icon: BxIcon.TRUE_ACHIEVEMENTS,
|
||||||
title: t('true-achievements'),
|
|
||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -22,8 +20,4 @@ export class TrueAchievementsAction extends BaseGameBarAction {
|
|||||||
super.onClick(e);
|
super.onClick(e);
|
||||||
TrueAchievements.open(false);
|
TrueAchievements.open(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import type { BaseGameBarAction } from "./action-base";
|
|||||||
import { STATES } from "@utils/global";
|
import { STATES } from "@utils/global";
|
||||||
import { MicrophoneAction } from "./action-microphone";
|
import { MicrophoneAction } from "./action-microphone";
|
||||||
import { PrefKey } from "@/enums/pref-keys";
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { getPref, StreamTouchController } from "@/utils/settings-storages/global-settings-storage";
|
import { getPref, StreamTouchController, type GameBarPosition } from "@/utils/settings-storages/global-settings-storage";
|
||||||
import { TrueAchievementsAction } from "./action-true-achievements";
|
import { TrueAchievementsAction } from "./action-true-achievements";
|
||||||
import { SpeakerAction } from "./action-speaker";
|
import { SpeakerAction } from "./action-speaker";
|
||||||
import { RendererAction } from "./action-renderer";
|
import { RendererAction } from "./action-renderer";
|
||||||
@ -15,13 +15,7 @@ import { RendererAction } from "./action-renderer";
|
|||||||
|
|
||||||
export class GameBar {
|
export class GameBar {
|
||||||
private static instance: GameBar;
|
private static instance: GameBar;
|
||||||
public static getInstance(): GameBar {
|
public static getInstance = () => GameBar.instance ?? (GameBar.instance = new GameBar());
|
||||||
if (!GameBar.instance) {
|
|
||||||
GameBar.instance = new GameBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
return GameBar.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly VISIBLE_DURATION = 2000;
|
private static readonly VISIBLE_DURATION = 2000;
|
||||||
|
|
||||||
@ -35,7 +29,7 @@ export class GameBar {
|
|||||||
private constructor() {
|
private constructor() {
|
||||||
let $container;
|
let $container;
|
||||||
|
|
||||||
const position = getPref(PrefKey.GAME_BAR_POSITION);
|
const position = getPref(PrefKey.GAME_BAR_POSITION) as GameBarPosition;
|
||||||
|
|
||||||
const $gameBar = CE('div', {id: 'bx-game-bar', class: 'bx-gone', 'data-position': position},
|
const $gameBar = CE('div', {id: 'bx-game-bar', class: 'bx-gone', 'data-position': position},
|
||||||
$container = CE('div', {class: 'bx-game-bar-container bx-offscreen'}),
|
$container = CE('div', {class: 'bx-game-bar-container bx-offscreen'}),
|
||||||
@ -78,11 +72,7 @@ export class GameBar {
|
|||||||
|
|
||||||
// Add animation when hiding game bar
|
// Add animation when hiding game bar
|
||||||
$container.addEventListener('transitionend', e => {
|
$container.addEventListener('transitionend', e => {
|
||||||
const classList = $container.classList;
|
$container.classList.replace('bx-hide', 'bx-offscreen');
|
||||||
if (classList.contains('bx-hide')) {
|
|
||||||
classList.remove('bx-hide');
|
|
||||||
classList.add('bx-offscreen');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.documentElement.appendChild($gameBar);
|
document.documentElement.appendChild($gameBar);
|
||||||
@ -117,19 +107,15 @@ export class GameBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
this.$gameBar && this.$gameBar.classList.remove('bx-gone');
|
this.$gameBar.classList.remove('bx-gone');
|
||||||
}
|
}
|
||||||
|
|
||||||
disable() {
|
disable() {
|
||||||
this.hideBar();
|
this.hideBar();
|
||||||
this.$gameBar && this.$gameBar.classList.add('bx-gone');
|
this.$gameBar.classList.add('bx-gone');
|
||||||
}
|
}
|
||||||
|
|
||||||
showBar() {
|
showBar() {
|
||||||
if (!this.$container) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$container.classList.remove('bx-offscreen', 'bx-hide' , 'bx-gone');
|
this.$container.classList.remove('bx-offscreen', 'bx-hide' , 'bx-gone');
|
||||||
this.$container.classList.add('bx-show');
|
this.$container.classList.add('bx-show');
|
||||||
|
|
||||||
@ -142,18 +128,11 @@ export class GameBar {
|
|||||||
// Stop focusing Game Bar
|
// Stop focusing Game Bar
|
||||||
clearFocus();
|
clearFocus();
|
||||||
|
|
||||||
if (!this.$container) {
|
this.$container.classList.replace('bx-show', 'bx-hide');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$container.classList.remove('bx-show');
|
|
||||||
this.$container.classList.add('bx-hide');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all states
|
// Reset all states
|
||||||
reset() {
|
reset() {
|
||||||
for (const action of this.actions) {
|
this.actions.forEach(action => action.reset());
|
||||||
action.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,14 +124,8 @@ This class uses some code from Yuzu emulator to handle mouse's movements
|
|||||||
Source: https://github.com/yuzu-emu/yuzu-mainline/blob/master/src/input_common/drivers/mouse.cpp
|
Source: https://github.com/yuzu-emu/yuzu-mainline/blob/master/src/input_common/drivers/mouse.cpp
|
||||||
*/
|
*/
|
||||||
export class EmulatedMkbHandler extends MkbHandler {
|
export class EmulatedMkbHandler extends MkbHandler {
|
||||||
static #instance: EmulatedMkbHandler;
|
private static instance: EmulatedMkbHandler;
|
||||||
public static getInstance(): EmulatedMkbHandler {
|
public static getInstance = () => EmulatedMkbHandler.instance ?? (EmulatedMkbHandler.instance = new EmulatedMkbHandler());
|
||||||
if (!EmulatedMkbHandler.#instance) {
|
|
||||||
EmulatedMkbHandler.#instance = new EmulatedMkbHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmulatedMkbHandler.#instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
#CURRENT_PRESET_DATA = MkbPreset.convert(MkbPreset.DEFAULT_PRESET);
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ type XcloudInputSink = {
|
|||||||
|
|
||||||
export class NativeMkbHandler extends MkbHandler {
|
export class NativeMkbHandler extends MkbHandler {
|
||||||
private static instance: NativeMkbHandler;
|
private static instance: NativeMkbHandler;
|
||||||
|
public static getInstance = () => NativeMkbHandler.instance ?? (NativeMkbHandler.instance = new NativeMkbHandler());
|
||||||
|
|
||||||
#pointerClient: PointerClient | undefined;
|
#pointerClient: PointerClient | undefined;
|
||||||
#enabled: boolean = false;
|
#enabled: boolean = false;
|
||||||
|
|
||||||
@ -37,14 +39,6 @@ export class NativeMkbHandler extends MkbHandler {
|
|||||||
|
|
||||||
#$message?: HTMLElement;
|
#$message?: HTMLElement;
|
||||||
|
|
||||||
public static getInstance(): NativeMkbHandler {
|
|
||||||
if (!NativeMkbHandler.instance) {
|
|
||||||
NativeMkbHandler.instance = new NativeMkbHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
return NativeMkbHandler.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
#onKeyboardEvent(e: KeyboardEvent) {
|
#onKeyboardEvent(e: KeyboardEvent) {
|
||||||
if (e.type === 'keyup' && e.code === 'F8') {
|
if (e.type === 'keyup' && e.code === 'F8') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -15,13 +15,7 @@ enum PointerAction {
|
|||||||
|
|
||||||
export class PointerClient {
|
export class PointerClient {
|
||||||
private static instance: PointerClient;
|
private static instance: PointerClient;
|
||||||
public static getInstance(): PointerClient {
|
public static getInstance = () => PointerClient.instance ?? (PointerClient.instance = new PointerClient());
|
||||||
if (!PointerClient.instance) {
|
|
||||||
PointerClient.instance = new PointerClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
return PointerClient.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private socket: WebSocket | undefined | null;
|
private socket: WebSocket | undefined | null;
|
||||||
private mkbHandler: MkbHandler | undefined;
|
private mkbHandler: MkbHandler | undefined;
|
||||||
|
@ -632,12 +632,12 @@ true` + text;
|
|||||||
},
|
},
|
||||||
|
|
||||||
skipFeedbackDialog(str: string) {
|
skipFeedbackDialog(str: string) {
|
||||||
let text = '&&this.shouldTransitionToFeedback(';
|
let text = 'shouldTransitionToFeedback(e){';
|
||||||
if (!str.includes(text)) {
|
if (!str.includes(text)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = str.replace(text, '&& false ' + text);
|
str = str.replace(text, text + 'return !1;');
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export class WebGL2Player {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private targetFps = 60;
|
private targetFps = 60;
|
||||||
private frameInterval = Math.ceil(1000 / this.targetFps);
|
private frameInterval = 0;
|
||||||
private lastFrameTime = 0;
|
private lastFrameTime = 0;
|
||||||
|
|
||||||
private animFrameId: number | null = null;
|
private animFrameId: number | null = null;
|
||||||
@ -73,7 +73,8 @@ export class WebGL2Player {
|
|||||||
|
|
||||||
setTargetFps(target: number) {
|
setTargetFps(target: number) {
|
||||||
this.targetFps = target;
|
this.targetFps = target;
|
||||||
this.frameInterval = Math.ceil(1000 / target);
|
this.lastFrameTime = 0;
|
||||||
|
this.frameInterval = target ? Math.floor(1000 / target) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCanvas() {
|
getCanvas() {
|
||||||
@ -94,6 +95,11 @@ export class WebGL2Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawFrame() {
|
drawFrame() {
|
||||||
|
// Don't draw when FPS is 0
|
||||||
|
if (this.targetFps === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Limit FPS
|
// Limit FPS
|
||||||
if (this.targetFps < 60) {
|
if (this.targetFps < 60) {
|
||||||
const currentTime = performance.now();
|
const currentTime = performance.now();
|
||||||
@ -233,10 +239,10 @@ export class WebGL2Player {
|
|||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
if (gl) {
|
if (gl) {
|
||||||
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
gl.getExtension('WEBGL_lose_context')?.loseContext();
|
||||||
|
gl.useProgram(null);
|
||||||
|
|
||||||
for (const resource of this.resources) {
|
for (const resource of this.resources) {
|
||||||
if (resource instanceof WebGLProgram) {
|
if (resource instanceof WebGLProgram) {
|
||||||
gl.useProgram(null);
|
|
||||||
gl.deleteProgram(resource);
|
gl.deleteProgram(resource);
|
||||||
} else if (resource instanceof WebGLShader) {
|
} else if (resource instanceof WebGLShader) {
|
||||||
gl.deleteShader(resource);
|
gl.deleteShader(resource);
|
||||||
|
@ -37,13 +37,7 @@ type RemotePlayConsole = {
|
|||||||
|
|
||||||
export class RemotePlayManager {
|
export class RemotePlayManager {
|
||||||
private static instance: RemotePlayManager;
|
private static instance: RemotePlayManager;
|
||||||
public static getInstance(): RemotePlayManager {
|
public static getInstance = () => RemotePlayManager.instance ?? (RemotePlayManager.instance = new RemotePlayManager());
|
||||||
if (!this.instance) {
|
|
||||||
this.instance = new RemotePlayManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isInitialized = false;
|
private isInitialized = false;
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
|
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
||||||
|
import { limitVideoPlayerFps } from "../stream/stream-settings-utils";
|
||||||
|
|
||||||
export class RendererShortcut {
|
export class RendererShortcut {
|
||||||
static toggleVisibility(): boolean {
|
static toggleVisibility(): boolean {
|
||||||
const $mediaContainer = document.querySelector('#game-stream div[data-testid="media-container"]');
|
const $mediaContainer = document.querySelector('#game-stream div[data-testid="media-container"]');
|
||||||
@ -6,6 +10,9 @@ export class RendererShortcut {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$mediaContainer.classList.toggle('bx-gone');
|
$mediaContainer.classList.toggle('bx-gone');
|
||||||
return !$mediaContainer.classList.contains('bx-gone');
|
const isShowing = !$mediaContainer.classList.contains('bx-gone');
|
||||||
|
// Switch FPS
|
||||||
|
limitVideoPlayerFps(isShowing ? getPref(PrefKey.VIDEO_MAX_FPS) : 0);
|
||||||
|
return isShowing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,7 @@ enum StreamBadge {
|
|||||||
|
|
||||||
export class StreamBadges {
|
export class StreamBadges {
|
||||||
private static instance: StreamBadges;
|
private static instance: StreamBadges;
|
||||||
public static getInstance(): StreamBadges {
|
public static getInstance = () => StreamBadges.instance ?? (StreamBadges.instance = new StreamBadges());
|
||||||
if (!StreamBadges.instance) {
|
|
||||||
StreamBadges.instance = new StreamBadges();
|
|
||||||
}
|
|
||||||
|
|
||||||
return StreamBadges.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private serverInfo: StreamServerInfo = {};
|
private serverInfo: StreamServerInfo = {};
|
||||||
|
|
||||||
|
@ -45,8 +45,7 @@ export function onChangeVideoPlayerType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function limitVideoPlayerFps() {
|
export function limitVideoPlayerFps(targetFps: number) {
|
||||||
const targetFps = getPref(PrefKey.VIDEO_MAX_FPS);
|
|
||||||
const streamPlayer = STATES.currentStream.streamPlayer;
|
const streamPlayer = STATES.currentStream.streamPlayer;
|
||||||
streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
|
streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
|
||||||
}
|
}
|
||||||
@ -58,7 +57,7 @@ export function updateVideoPlayer() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
limitVideoPlayerFps();
|
limitVideoPlayerFps(getPref(PrefKey.VIDEO_MAX_FPS));
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
processing: getPref(PrefKey.VIDEO_PROCESSING),
|
processing: getPref(PrefKey.VIDEO_PROCESSING),
|
||||||
|
@ -9,13 +9,7 @@ import { StreamStat, StreamStatsCollector, type StreamStatGrade } from "@/utils/
|
|||||||
|
|
||||||
export class StreamStats {
|
export class StreamStats {
|
||||||
private static instance: StreamStats;
|
private static instance: StreamStats;
|
||||||
public static getInstance(): StreamStats {
|
public static getInstance = () => StreamStats.instance ?? (StreamStats.instance = new StreamStats());
|
||||||
if (!StreamStats.instance) {
|
|
||||||
StreamStats.instance = new StreamStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
return StreamStats.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private intervalId?: number | null;
|
private intervalId?: number | null;
|
||||||
private readonly REFRESH_INTERVAL = 1 * 1000;
|
private readonly REFRESH_INTERVAL = 1 * 1000;
|
||||||
|
@ -88,12 +88,7 @@ export abstract class NavigationDialog {
|
|||||||
|
|
||||||
export class NavigationDialogManager {
|
export class NavigationDialogManager {
|
||||||
private static instance: NavigationDialogManager;
|
private static instance: NavigationDialogManager;
|
||||||
public static getInstance(): NavigationDialogManager {
|
public static getInstance = () => NavigationDialogManager.instance ?? (NavigationDialogManager.instance = new NavigationDialogManager());
|
||||||
if (!NavigationDialogManager.instance) {
|
|
||||||
NavigationDialogManager.instance = new NavigationDialogManager();
|
|
||||||
}
|
|
||||||
return NavigationDialogManager.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly GAMEPAD_POLLING_INTERVAL = 50;
|
private static readonly GAMEPAD_POLLING_INTERVAL = 50;
|
||||||
private static readonly GAMEPAD_KEYS = [
|
private static readonly GAMEPAD_KEYS = [
|
||||||
|
@ -11,12 +11,7 @@ import { BxEvent } from "@/utils/bx-event";
|
|||||||
|
|
||||||
export class RemotePlayNavigationDialog extends NavigationDialog {
|
export class RemotePlayNavigationDialog extends NavigationDialog {
|
||||||
private static instance: RemotePlayNavigationDialog;
|
private static instance: RemotePlayNavigationDialog;
|
||||||
public static getInstance(): RemotePlayNavigationDialog {
|
public static getInstance = () => RemotePlayNavigationDialog.instance ?? (RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog());
|
||||||
if (!RemotePlayNavigationDialog.instance) {
|
|
||||||
RemotePlayNavigationDialog.instance = new RemotePlayNavigationDialog();
|
|
||||||
}
|
|
||||||
return RemotePlayNavigationDialog.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly STATE_LABELS: Record<RemotePlayConsoleState, string> = {
|
private readonly STATE_LABELS: Record<RemotePlayConsoleState, string> = {
|
||||||
[RemotePlayConsoleState.ON]: t('powered-on'),
|
[RemotePlayConsoleState.ON]: t('powered-on'),
|
||||||
|
@ -64,12 +64,7 @@ type SettingTab = {
|
|||||||
|
|
||||||
export class SettingsNavigationDialog extends NavigationDialog {
|
export class SettingsNavigationDialog extends NavigationDialog {
|
||||||
private static instance: SettingsNavigationDialog;
|
private static instance: SettingsNavigationDialog;
|
||||||
public static getInstance(): SettingsNavigationDialog {
|
public static getInstance = () => SettingsNavigationDialog.instance ?? (SettingsNavigationDialog.instance = new SettingsNavigationDialog());
|
||||||
if (!SettingsNavigationDialog.instance) {
|
|
||||||
SettingsNavigationDialog.instance = new SettingsNavigationDialog();
|
|
||||||
}
|
|
||||||
return SettingsNavigationDialog.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
$container!: HTMLElement;
|
$container!: HTMLElement;
|
||||||
private $tabs!: HTMLElement;
|
private $tabs!: HTMLElement;
|
||||||
@ -409,7 +404,9 @@ export class SettingsNavigationDialog extends NavigationDialog {
|
|||||||
onChange: onChangeVideoPlayerType,
|
onChange: onChangeVideoPlayerType,
|
||||||
}, {
|
}, {
|
||||||
pref: PrefKey.VIDEO_MAX_FPS,
|
pref: PrefKey.VIDEO_MAX_FPS,
|
||||||
onChange: limitVideoPlayerFps,
|
onChange: e => {
|
||||||
|
limitVideoPlayerFps(parseInt(e.target.value));
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
pref: PrefKey.VIDEO_POWER_PREFERENCE,
|
pref: PrefKey.VIDEO_POWER_PREFERENCE,
|
||||||
onChange: () => {
|
onChange: () => {
|
||||||
|
@ -2,12 +2,7 @@ import { CE } from "@/utils/html";
|
|||||||
|
|
||||||
export class FullscreenText {
|
export class FullscreenText {
|
||||||
private static instance: FullscreenText;
|
private static instance: FullscreenText;
|
||||||
public static getInstance(): FullscreenText {
|
public static getInstance = () => FullscreenText.instance ?? (FullscreenText.instance = new FullscreenText());
|
||||||
if (!FullscreenText.instance) {
|
|
||||||
FullscreenText.instance = new FullscreenText();
|
|
||||||
}
|
|
||||||
return FullscreenText.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
$text: HTMLElement;
|
$text: HTMLElement;
|
||||||
|
|
||||||
|
@ -39,6 +39,10 @@ export const enum ControllerDeviceVibration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type GameBarPosition = 'bottom-left' | 'bottom-right' | 'off';
|
||||||
|
export type GameBarPositionOptions = Record<GameBarPosition, string>;
|
||||||
|
|
||||||
|
|
||||||
function getSupportedCodecProfiles() {
|
function getSupportedCodecProfiles() {
|
||||||
const options: PartialRecord<CodecProfile, string> = {
|
const options: PartialRecord<CodecProfile, string> = {
|
||||||
default: t('default'),
|
default: t('default'),
|
||||||
@ -323,12 +327,12 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
[PrefKey.GAME_BAR_POSITION]: {
|
[PrefKey.GAME_BAR_POSITION]: {
|
||||||
requiredVariants: 'full',
|
requiredVariants: 'full',
|
||||||
label: t('position'),
|
label: t('position'),
|
||||||
default: 'bottom-left',
|
default: 'bottom-left' satisfies GameBarPosition,
|
||||||
options: {
|
options: {
|
||||||
'bottom-left': t('bottom-left'),
|
'bottom-left': t('bottom-left'),
|
||||||
'bottom-right': t('bottom-right'),
|
'bottom-right': t('bottom-right'),
|
||||||
'off': t('off'),
|
'off': t('off'),
|
||||||
},
|
} satisfies GameBarPositionOptions,
|
||||||
},
|
},
|
||||||
|
|
||||||
[PrefKey.LOCAL_CO_OP_ENABLED]: {
|
[PrefKey.LOCAL_CO_OP_ENABLED]: {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { BxEvent } from "./bx-event";
|
import { BxEvent } from "./bx-event";
|
||||||
import { STATES } from "./global";
|
import { STATES } from "./global";
|
||||||
import { humanFileSize, secondsToHm } from "./html";
|
import { humanFileSize, secondsToHm } from "./html";
|
||||||
|
import { getPref } from "./settings-storages/global-settings-storage";
|
||||||
|
|
||||||
export enum StreamStat {
|
export enum StreamStat {
|
||||||
PING = 'ping',
|
PING = 'ping',
|
||||||
@ -92,13 +94,7 @@ type CurrentStats = {
|
|||||||
|
|
||||||
export class StreamStatsCollector {
|
export class StreamStatsCollector {
|
||||||
private static instance: StreamStatsCollector;
|
private static instance: StreamStatsCollector;
|
||||||
public static getInstance(): StreamStatsCollector {
|
public static getInstance = () => StreamStatsCollector.instance ?? (StreamStatsCollector.instance = new StreamStatsCollector());
|
||||||
if (!StreamStatsCollector.instance) {
|
|
||||||
StreamStatsCollector.instance = new StreamStatsCollector();
|
|
||||||
}
|
|
||||||
|
|
||||||
return StreamStatsCollector.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect in background - 60 seconds
|
// Collect in background - 60 seconds
|
||||||
static readonly INTERVAL_BACKGROUND = 60 * 1000;
|
static readonly INTERVAL_BACKGROUND = 60 * 1000;
|
||||||
@ -127,7 +123,8 @@ export class StreamStatsCollector {
|
|||||||
[StreamStat.FPS]: {
|
[StreamStat.FPS]: {
|
||||||
current: 0,
|
current: 0,
|
||||||
toString() {
|
toString() {
|
||||||
return this.current.toString();
|
const maxFps = getPref(PrefKey.VIDEO_MAX_FPS);
|
||||||
|
return maxFps < 60 ? `${maxFps}/${this.current}` : this.current.toString();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,13 +3,7 @@ import { STATES } from "./global";
|
|||||||
|
|
||||||
export class XcloudApi {
|
export class XcloudApi {
|
||||||
private static instance: XcloudApi;
|
private static instance: XcloudApi;
|
||||||
public static getInstance(): XcloudApi {
|
public static getInstance = () => XcloudApi.instance ?? (XcloudApi.instance = new XcloudApi());
|
||||||
if (!XcloudApi.instance) {
|
|
||||||
XcloudApi.instance = new XcloudApi();
|
|
||||||
}
|
|
||||||
|
|
||||||
return XcloudApi.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CACHE_TITLES: {[key: string]: XcloudTitleInfo} = {};
|
private CACHE_TITLES: {[key: string]: XcloudTitleInfo} = {};
|
||||||
private CACHE_WAIT_TIME: {[key: string]: XcloudWaitTimeInfo} = {};
|
private CACHE_WAIT_TIME: {[key: string]: XcloudWaitTimeInfo} = {};
|
||||||
|
@ -13,9 +13,25 @@ import { BypassServerIps } from "@/enums/bypass-servers";
|
|||||||
import { PrefKey } from "@/enums/pref-keys";
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { getPref, StreamResolution, StreamTouchController } from "./settings-storages/global-settings-storage";
|
import { getPref, StreamResolution, StreamTouchController } from "./settings-storages/global-settings-storage";
|
||||||
|
|
||||||
export
|
export class XcloudInterceptor {
|
||||||
class XcloudInterceptor {
|
private static readonly SERVER_EMOJIS = {
|
||||||
static async #handleLogin(request: RequestInfo | URL, init?: RequestInit) {
|
AustraliaEast: '🇦🇺',
|
||||||
|
AustraliaSouthEast: '🇦🇺',
|
||||||
|
BrazilSouth: '🇧🇷',
|
||||||
|
EastUS: '🇺🇸',
|
||||||
|
EastUS2: '🇺🇸',
|
||||||
|
JapanEast: '🇯🇵',
|
||||||
|
KoreaCentral: '🇰🇷',
|
||||||
|
MexicoCentral: '🇲🇽',
|
||||||
|
NorthCentralUs: '🇺🇸',
|
||||||
|
SouthCentralUS: '🇺🇸',
|
||||||
|
UKSouth: '🇬🇧',
|
||||||
|
WestEurope: '🇪🇺',
|
||||||
|
WestUS: '🇺🇸',
|
||||||
|
WestUS2: '🇺🇸',
|
||||||
|
};
|
||||||
|
|
||||||
|
private static async handleLogin(request: RequestInfo | URL, init?: RequestInit) {
|
||||||
const bypassServer = getPref(PrefKey.SERVER_BYPASS_RESTRICTION);
|
const bypassServer = getPref(PrefKey.SERVER_BYPASS_RESTRICTION);
|
||||||
if (bypassServer !== 'off') {
|
if (bypassServer !== 'off') {
|
||||||
const ip = BypassServerIps[bypassServer as keyof typeof BypassServerIps];
|
const ip = BypassServerIps[bypassServer as keyof typeof BypassServerIps];
|
||||||
@ -35,24 +51,8 @@ class XcloudInterceptor {
|
|||||||
RemotePlayManager.getInstance().xcloudToken = obj.gsToken;
|
RemotePlayManager.getInstance().xcloudToken = obj.gsToken;
|
||||||
|
|
||||||
// Get server list
|
// Get server list
|
||||||
const serverEmojis = {
|
|
||||||
AustraliaEast: '🇦🇺',
|
|
||||||
AustraliaSouthEast: '🇦🇺',
|
|
||||||
BrazilSouth: '🇧🇷',
|
|
||||||
EastUS: '🇺🇸',
|
|
||||||
EastUS2: '🇺🇸',
|
|
||||||
JapanEast: '🇯🇵',
|
|
||||||
KoreaCentral: '🇰🇷',
|
|
||||||
MexicoCentral: '🇲🇽',
|
|
||||||
NorthCentralUs: '🇺🇸',
|
|
||||||
SouthCentralUS: '🇺🇸',
|
|
||||||
UKSouth: '🇬🇧',
|
|
||||||
WestEurope: '🇪🇺',
|
|
||||||
WestUS: '🇺🇸',
|
|
||||||
WestUS2: '🇺🇸',
|
|
||||||
};
|
|
||||||
|
|
||||||
const serverRegex = /\/\/(\w+)\./;
|
const serverRegex = /\/\/(\w+)\./;
|
||||||
|
const serverEmojis = XcloudInterceptor.SERVER_EMOJIS;
|
||||||
|
|
||||||
for (let region of obj.offeringSettings.regions) {
|
for (let region of obj.offeringSettings.regions) {
|
||||||
const regionName = region.name as keyof typeof serverEmojis;
|
const regionName = region.name as keyof typeof serverEmojis;
|
||||||
@ -91,7 +91,7 @@ class XcloudInterceptor {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handlePlay(request: RequestInfo | URL, init?: RequestInit) {
|
private static async handlePlay(request: RequestInfo | URL, init?: RequestInit) {
|
||||||
const PREF_STREAM_TARGET_RESOLUTION = getPref(PrefKey.STREAM_TARGET_RESOLUTION);
|
const PREF_STREAM_TARGET_RESOLUTION = getPref(PrefKey.STREAM_TARGET_RESOLUTION);
|
||||||
const PREF_STREAM_PREFERRED_LOCALE = getPref(PrefKey.STREAM_PREFERRED_LOCALE);
|
const PREF_STREAM_PREFERRED_LOCALE = getPref(PrefKey.STREAM_PREFERRED_LOCALE);
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ class XcloudInterceptor {
|
|||||||
return NATIVE_FETCH(newRequest);
|
return NATIVE_FETCH(newRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handleWaitTime(request: RequestInfo | URL, init?: RequestInit) {
|
private static async handleWaitTime(request: RequestInfo | URL, init?: RequestInit) {
|
||||||
const response = await NATIVE_FETCH(request, init);
|
const response = await NATIVE_FETCH(request, init);
|
||||||
|
|
||||||
if (getPref(PrefKey.UI_LOADING_SCREEN_WAIT_TIME)) {
|
if (getPref(PrefKey.UI_LOADING_SCREEN_WAIT_TIME)) {
|
||||||
@ -143,7 +143,7 @@ class XcloudInterceptor {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handleConfiguration(request: RequestInfo | URL, init?: RequestInit) {
|
private static async handleConfiguration(request: RequestInfo | URL, init?: RequestInit) {
|
||||||
if ((request as Request).method !== 'GET') {
|
if ((request as Request).method !== 'GET') {
|
||||||
return NATIVE_FETCH(request, init);
|
return NATIVE_FETCH(request, init);
|
||||||
}
|
}
|
||||||
@ -213,13 +213,13 @@ class XcloudInterceptor {
|
|||||||
|
|
||||||
// Server list
|
// Server list
|
||||||
if (url.endsWith('/v2/login/user')) {
|
if (url.endsWith('/v2/login/user')) {
|
||||||
return XcloudInterceptor.#handleLogin(request, init);
|
return XcloudInterceptor.handleLogin(request, init);
|
||||||
} else if (url.endsWith('/sessions/cloud/play')) { // Get session
|
} else if (url.endsWith('/sessions/cloud/play')) { // Get session
|
||||||
return XcloudInterceptor.#handlePlay(request, init);
|
return XcloudInterceptor.handlePlay(request, init);
|
||||||
} else if (url.includes('xboxlive.com') && url.includes('/waittime/')) {
|
} else if (url.includes('xboxlive.com') && url.includes('/waittime/')) {
|
||||||
return XcloudInterceptor.#handleWaitTime(request, init);
|
return XcloudInterceptor.handleWaitTime(request, init);
|
||||||
} else if (url.endsWith('/configuration')) {
|
} else if (url.endsWith('/configuration')) {
|
||||||
return XcloudInterceptor.#handleConfiguration(request, init);
|
return XcloudInterceptor.handleConfiguration(request, init);
|
||||||
} else if (url && url.endsWith('/ice') && url.includes('/sessions/') && (request as Request).method === 'GET') {
|
} else if (url && url.endsWith('/ice') && url.includes('/sessions/') && (request as Request).method === 'GET') {
|
||||||
return patchIceCandidates(request as Request);
|
return patchIceCandidates(request as Request);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import type { RemotePlayConsoleAddresses } from "@/types/network";
|
|||||||
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
||||||
|
|
||||||
export class XhomeInterceptor {
|
export class XhomeInterceptor {
|
||||||
static #consoleAddrs: RemotePlayConsoleAddresses = {};
|
private static consoleAddrs: RemotePlayConsoleAddresses = {};
|
||||||
|
|
||||||
private static readonly BASE_DEVICE_INFO = {
|
private static readonly BASE_DEVICE_INFO = {
|
||||||
appInfo: {
|
appInfo: {
|
||||||
@ -52,7 +52,7 @@ export class XhomeInterceptor {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static async #handleLogin(request: Request) {
|
private static async handleLogin(request: Request) {
|
||||||
try {
|
try {
|
||||||
const clone = (request as Request).clone();
|
const clone = (request as Request).clone();
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export class XhomeInterceptor {
|
|||||||
return NATIVE_FETCH(request);
|
return NATIVE_FETCH(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handleConfiguration(request: Request | URL) {
|
private static async handleConfiguration(request: Request | URL) {
|
||||||
const response = await NATIVE_FETCH(request);
|
const response = await NATIVE_FETCH(request);
|
||||||
|
|
||||||
const obj = await response.clone().json()
|
const obj = await response.clone().json()
|
||||||
@ -90,15 +90,15 @@ export class XhomeInterceptor {
|
|||||||
|
|
||||||
const serverDetails = obj.serverDetails;
|
const serverDetails = obj.serverDetails;
|
||||||
if (serverDetails.ipAddress) {
|
if (serverDetails.ipAddress) {
|
||||||
XhomeInterceptor.#consoleAddrs[serverDetails.ipAddress] = processPorts(serverDetails.port);
|
XhomeInterceptor.consoleAddrs[serverDetails.ipAddress] = processPorts(serverDetails.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverDetails.ipV4Address) {
|
if (serverDetails.ipV4Address) {
|
||||||
XhomeInterceptor.#consoleAddrs[serverDetails.ipV4Address] = processPorts(serverDetails.ipV4Port);
|
XhomeInterceptor.consoleAddrs[serverDetails.ipV4Address] = processPorts(serverDetails.ipV4Port);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverDetails.ipV6Address) {
|
if (serverDetails.ipV6Address) {
|
||||||
XhomeInterceptor.#consoleAddrs[serverDetails.ipV6Address] = processPorts(serverDetails.ipV6Port);
|
XhomeInterceptor.consoleAddrs[serverDetails.ipV6Address] = processPorts(serverDetails.ipV6Port);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json = () => Promise.resolve(obj);
|
response.json = () => Promise.resolve(obj);
|
||||||
@ -107,7 +107,7 @@ export class XhomeInterceptor {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handleInputConfigs(request: Request | URL, opts: {[index: string]: any}) {
|
private static async handleInputConfigs(request: Request | URL, opts: {[index: string]: any}) {
|
||||||
const response = await NATIVE_FETCH(request);
|
const response = await NATIVE_FETCH(request);
|
||||||
|
|
||||||
if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== StreamTouchController.ALL) {
|
if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== StreamTouchController.ALL) {
|
||||||
@ -144,7 +144,7 @@ export class XhomeInterceptor {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handleTitles(request: Request) {
|
private static async handleTitles(request: Request) {
|
||||||
const clone = request.clone();
|
const clone = request.clone();
|
||||||
|
|
||||||
const headers: {[index: string]: any} = {};
|
const headers: {[index: string]: any} = {};
|
||||||
@ -163,7 +163,7 @@ export class XhomeInterceptor {
|
|||||||
return NATIVE_FETCH(request);
|
return NATIVE_FETCH(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async #handlePlay(request: RequestInfo | URL) {
|
private static async handlePlay(request: RequestInfo | URL) {
|
||||||
const clone = (request as Request).clone();
|
const clone = (request as Request).clone();
|
||||||
const body = await clone.json();
|
const body = await clone.json();
|
||||||
|
|
||||||
@ -216,17 +216,17 @@ export class XhomeInterceptor {
|
|||||||
|
|
||||||
// Get console IP
|
// Get console IP
|
||||||
if (url.includes('/configuration')) {
|
if (url.includes('/configuration')) {
|
||||||
return XhomeInterceptor.#handleConfiguration(request);
|
return XhomeInterceptor.handleConfiguration(request);
|
||||||
} else if (url.endsWith('/sessions/home/play')) {
|
} else if (url.endsWith('/sessions/home/play')) {
|
||||||
return XhomeInterceptor.#handlePlay(request);
|
return XhomeInterceptor.handlePlay(request);
|
||||||
} else if (url.includes('inputconfigs')) {
|
} else if (url.includes('inputconfigs')) {
|
||||||
return XhomeInterceptor.#handleInputConfigs(request, opts);
|
return XhomeInterceptor.handleInputConfigs(request, opts);
|
||||||
} else if (url.includes('/login/user')) {
|
} else if (url.includes('/login/user')) {
|
||||||
return XhomeInterceptor.#handleLogin(request);
|
return XhomeInterceptor.handleLogin(request);
|
||||||
} else if (url.endsWith('/titles')) {
|
} else if (url.endsWith('/titles')) {
|
||||||
return XhomeInterceptor.#handleTitles(request);
|
return XhomeInterceptor.handleTitles(request);
|
||||||
} else if (url && url.endsWith('/ice') && url.includes('/sessions/') && (request as Request).method === 'GET') {
|
} else if (url && url.endsWith('/ice') && url.includes('/sessions/') && (request as Request).method === 'GET') {
|
||||||
return patchIceCandidates(request, XhomeInterceptor.#consoleAddrs);
|
return patchIceCandidates(request, XhomeInterceptor.consoleAddrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await NATIVE_FETCH(request);
|
return await NATIVE_FETCH(request);
|
||||||
|
Reference in New Issue
Block a user