Compare commits

..

12 Commits

Author SHA1 Message Date
f71904c30b Bump version to 4.7.1 2024-06-10 08:29:53 +07:00
3a16187504 Update Guide menu detection 2024-06-10 08:03:13 +07:00
00ebb3f672 Fix dispatching STREAM_PLAYING event when playing normal video 2024-06-09 18:31:57 +07:00
ebb4d3c141 Add FeatureGates 2024-06-09 18:31:15 +07:00
902918d7fb Update URLs 2024-06-09 15:56:32 +07:00
b780e4e63b Minor fix 2024-06-09 15:45:41 +07:00
32889e0cf1 Minor fix 2024-06-09 15:43:19 +07:00
d8b9fcc951 Update better-xcloud.user.js 2024-06-09 15:40:15 +07:00
c7734245ae Don't check update for beta version 2024-06-09 11:50:46 +07:00
35e7fdacb5 Disable context menu on devices with touch support by default 2024-06-09 11:48:12 +07:00
504f16b802 Rename "hasTouchSupport" to "userAgentHasTouchSupport" 2024-06-09 11:47:08 +07:00
a3a7a57b51 Get PointerServer's port from the app 2024-06-09 11:41:00 +07:00
22 changed files with 177 additions and 125 deletions

View File

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

View File

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Better xCloud // @name Better xCloud
// @namespace https://github.com/redphx // @namespace https://github.com/redphx
// @version 4.7.0 // @version 4.7.1
// @description Improve Xbox Cloud Gaming (xCloud) experience // @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx // @author redphx
// @license MIT // @license MIT
@ -104,23 +104,23 @@ class UserAgent {
} }
// src/utils/global.ts // src/utils/global.ts
var SCRIPT_VERSION = "4.7.0"; var SCRIPT_VERSION = "4.7.1";
var SCRIPT_HOME = "https://github.com/redphx/better-xcloud";
var AppInterface = window.AppInterface; var AppInterface = window.AppInterface;
UserAgent.init(); UserAgent.init();
var userAgent = window.navigator.userAgent.toLowerCase(); var userAgent = window.navigator.userAgent.toLowerCase();
var isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent); var isTv = userAgent.includes("smart-tv") || userAgent.includes("smarttv") || /\baft.*\b/.test(userAgent);
var isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser"); var isVr = window.navigator.userAgent.includes("VR") && window.navigator.userAgent.includes("OculusBrowser");
var browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0; var browserHasTouchSupport = "ontouchstart" in window || navigator.maxTouchPoints > 0;
var hasTouchSupport = !isTv && !isVr && browserHasTouchSupport; var userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport;
var STATES = { var STATES = {
isPlaying: false, isPlaying: false,
appContext: {}, appContext: {},
serverRegions: {}, serverRegions: {},
hasTouchSupport, userAgentHasTouchSupport,
browserHasTouchSupport, browserHasTouchSupport,
currentStream: {}, currentStream: {},
remotePlay: {} remotePlay: {},
pointerServerPort: 9269
}; };
// src/utils/bx-event.ts // src/utils/bx-event.ts
@ -171,8 +171,8 @@ var XcloudEvent;
event[key] = data[key]; event[key] = data[key];
} }
} }
AppInterface && AppInterface.onEvent(eventName);
target.dispatchEvent(event); target.dispatchEvent(event);
AppInterface && AppInterface.onEvent(eventName);
} }
BxEvent.dispatch = dispatch; BxEvent.dispatch = dispatch;
})(BxEvent || (BxEvent = {})); })(BxEvent || (BxEvent = {}));
@ -189,7 +189,8 @@ var DEFAULT_FLAGS = {
EnableXcloudLogging: false, EnableXcloudLogging: false,
SafariWorkaround: true, SafariWorkaround: true,
UseDevTouchLayout: false, UseDevTouchLayout: false,
ForceNativeMkbTitles: [] ForceNativeMkbTitles: [],
FeatureGates: null
}; };
var BX_FLAGS = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {}); var BX_FLAGS = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {});
try { try {
@ -1590,7 +1591,7 @@ class Preferences {
all: t("tc-all-games"), all: t("tc-all-games"),
off: t("off") off: t("off")
}, },
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
ready: (setting) => { ready: (setting) => {
if (setting.unsupported) { if (setting.unsupported) {
setting.default = "default"; setting.default = "default";
@ -1600,7 +1601,7 @@ class Preferences {
[PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: { [PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: {
label: t("tc-auto-off"), label: t("tc-auto-off"),
default: false, default: false,
unsupported: !STATES.hasTouchSupport unsupported: !STATES.userAgentHasTouchSupport
}, },
[PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY]: { [PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY]: {
type: SettingElementType.NUMBER_STEPPER, type: SettingElementType.NUMBER_STEPPER,
@ -1614,7 +1615,7 @@ class Preferences {
ticks: 10, ticks: 10,
hideSlider: true hideSlider: true
}, },
unsupported: !STATES.hasTouchSupport unsupported: !STATES.userAgentHasTouchSupport
}, },
[PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: { [PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
label: t("tc-standard-layout-style"), label: t("tc-standard-layout-style"),
@ -1624,7 +1625,7 @@ class Preferences {
white: t("tc-all-white"), white: t("tc-all-white"),
muted: t("tc-muted-colors") muted: t("tc-muted-colors")
}, },
unsupported: !STATES.hasTouchSupport unsupported: !STATES.userAgentHasTouchSupport
}, },
[PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: { [PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: {
label: t("tc-custom-layout-style"), label: t("tc-custom-layout-style"),
@ -1633,7 +1634,7 @@ class Preferences {
default: t("default"), default: t("default"),
muted: t("tc-muted-colors") muted: t("tc-muted-colors")
}, },
unsupported: !STATES.hasTouchSupport unsupported: !STATES.userAgentHasTouchSupport
}, },
[PrefKey.STREAM_SIMPLIFY_MENU]: { [PrefKey.STREAM_SIMPLIFY_MENU]: {
label: t("simplify-stream-menu"), label: t("simplify-stream-menu"),
@ -1842,7 +1843,7 @@ class Preferences {
}, },
[PrefKey.UI_HOME_CONTEXT_MENU_DISABLED]: { [PrefKey.UI_HOME_CONTEXT_MENU_DISABLED]: {
label: t("disable-home-context-menu"), label: t("disable-home-context-menu"),
default: false default: STATES.browserHasTouchSupport
}, },
[PrefKey.BLOCK_SOCIAL_FEATURES]: { [PrefKey.BLOCK_SOCIAL_FEATURES]: {
label: t("disable-social-features"), label: t("disable-social-features"),
@ -2968,7 +2969,6 @@ var PointerAction;
})(PointerAction || (PointerAction = {})); })(PointerAction || (PointerAction = {}));
class PointerClient { class PointerClient {
static #PORT = 9269;
static instance; static instance;
static getInstance() { static getInstance() {
if (!PointerClient.instance) { if (!PointerClient.instance) {
@ -2978,9 +2978,12 @@ class PointerClient {
} }
#socket; #socket;
#mkbHandler; #mkbHandler;
start(mkbHandler) { start(port, mkbHandler) {
if (!port) {
throw new Error("PointerServer port is 0");
}
this.#mkbHandler = mkbHandler; this.#mkbHandler = mkbHandler;
this.#socket = new WebSocket(`ws://localhost:${PointerClient.#PORT}`); this.#socket = new WebSocket(`ws://localhost:${port}`);
this.#socket.binaryType = "arraybuffer"; this.#socket.binaryType = "arraybuffer";
this.#socket.addEventListener("open", (event) => { this.#socket.addEventListener("open", (event) => {
BxLogger.info(LOG_TAG, "connected"); BxLogger.info(LOG_TAG, "connected");
@ -3159,7 +3162,7 @@ class NativeMkbHandler extends MkbHandler {
this.#inputSink = window.BX_EXPOSED.inputSink; this.#inputSink = window.BX_EXPOSED.inputSink;
this.#updateInputConfigurationAsync(false); this.#updateInputConfigurationAsync(false);
try { try {
this.#pointerClient.start(this); this.#pointerClient.start(STATES.pointerServerPort, this);
} catch (e) { } catch (e) {
Toast.show("Cannot enable Mouse & Keyboard feature"); Toast.show("Cannot enable Mouse & Keyboard feature");
} }
@ -3313,7 +3316,7 @@ class WebSocketMouseDataProvider extends MouseDataProvider {
this.#pointerClient = PointerClient.getInstance(); this.#pointerClient = PointerClient.getInstance();
this.#connected = false; this.#connected = false;
try { try {
this.#pointerClient.start(this.mkbHandler); this.#pointerClient.start(STATES.pointerServerPort, this.mkbHandler);
this.#connected = true; this.#connected = true;
} catch (e) { } catch (e) {
Toast.show("Cannot enable Mouse & Keyboard feature"); Toast.show("Cannot enable Mouse & Keyboard feature");
@ -3837,6 +3840,9 @@ class StreamUiShortcut {
// src/utils/utils.ts // src/utils/utils.ts
function checkForUpdate() { function checkForUpdate() {
if (SCRIPT_VERSION.includes("beta")) {
return;
}
const CHECK_INTERVAL_SECONDS = 7200; const CHECK_INTERVAL_SECONDS = 7200;
const currentVersion = getPref(PrefKey.CURRENT_VERSION); const currentVersion = getPref(PrefKey.CURRENT_VERSION);
const lastCheck = getPref(PrefKey.LAST_UPDATE_CHECK); const lastCheck = getPref(PrefKey.LAST_UPDATE_CHECK);
@ -4221,14 +4227,14 @@ var BxExposed = {
modifyTitleInfo: (titleInfo) => { modifyTitleInfo: (titleInfo) => {
titleInfo = structuredClone(titleInfo); titleInfo = structuredClone(titleInfo);
let supportedInputTypes = titleInfo.details.supportedInputTypes; let supportedInputTypes = titleInfo.details.supportedInputTypes;
if (BX_FLAGS.ForceNativeMkbTitles.includes(titleInfo.details.productId)) { if (BX_FLAGS.ForceNativeMkbTitles?.includes(titleInfo.details.productId)) {
supportedInputTypes.push(InputType.MKB); supportedInputTypes.push(InputType.MKB);
} }
if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "off") { if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "off") {
supportedInputTypes = supportedInputTypes.filter((i) => i !== InputType.MKB); supportedInputTypes = supportedInputTypes.filter((i) => i !== InputType.MKB);
} }
titleInfo.details.hasMkbSupport = supportedInputTypes.includes(InputType.MKB); titleInfo.details.hasMkbSupport = supportedInputTypes.includes(InputType.MKB);
if (STATES.hasTouchSupport) { if (STATES.userAgentHasTouchSupport) {
let touchControllerAvailability = getPref(PrefKey.STREAM_TOUCH_CONTROLLER); let touchControllerAvailability = getPref(PrefKey.STREAM_TOUCH_CONTROLLER);
if (touchControllerAvailability !== "off" && getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) { if (touchControllerAvailability !== "off" && getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) {
const gamepads = window.navigator.getGamepads(); const gamepads = window.navigator.getGamepads();
@ -5333,7 +5339,7 @@ var setupStreamSettingsDialog = function() {
} }
] ]
}, },
STATES.hasTouchSupport && { STATES.userAgentHasTouchSupport && {
group: "touch-controller", group: "touch-controller",
label: t("touch-controller"), label: t("touch-controller"),
items: [ items: [
@ -5899,6 +5905,20 @@ var GamePassCloudGallery;
GamePassCloudGallery2["TOUCH"] = "9c86f07a-f3e8-45ad-82a0-a1f759597059"; GamePassCloudGallery2["TOUCH"] = "9c86f07a-f3e8-45ad-82a0-a1f759597059";
})(GamePassCloudGallery || (GamePassCloudGallery = {})); })(GamePassCloudGallery || (GamePassCloudGallery = {}));
// src/utils/feature-gates.ts
var FeatureGates = {
PwaPrompt: false
};
if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED)) {
FeatureGates["EnableHomeContextMenu"] = false;
}
if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES)) {
FeatureGates["EnableGuideChatTab"] = false;
}
if (BX_FLAGS.FeatureGates) {
FeatureGates = Object.assign(BX_FLAGS.FeatureGates, FeatureGates);
}
// src/utils/network.ts // src/utils/network.ts
var clearApplicationInsightsBuffers = function() { var clearApplicationInsightsBuffers = function() {
window.sessionStorage.removeItem("AI_buffer"); window.sessionStorage.removeItem("AI_buffer");
@ -6042,12 +6062,8 @@ function interceptHttpRequests() {
try { try {
const response = await NATIVE_FETCH(request, init); const response = await NATIVE_FETCH(request, init);
const json = await response.json(); const json = await response.json();
const overrideTreatments = {}; for (const key in FeatureGates) {
if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED)) { json.exp.treatments[key] = FeatureGates[key];
overrideTreatments["EnableHomeContextMenu"] = false;
}
for (const key in overrideTreatments) {
json.exp.treatments[key] = overrideTreatments[key];
} }
response.json = () => Promise.resolve(json); response.json = () => Promise.resolve(json);
return response; return response;
@ -6055,7 +6071,7 @@ function interceptHttpRequests() {
console.log(e); console.log(e);
} }
} }
if (STATES.hasTouchSupport && url.includes("catalog.gamepass.com/sigls/")) { if (STATES.userAgentHasTouchSupport && url.includes("catalog.gamepass.com/sigls/")) {
const response = await NATIVE_FETCH(request, init); const response = await NATIVE_FETCH(request, init);
const obj = await response.clone().json(); const obj = await response.clone().json();
if (url.includes(GamePassCloudGallery.ALL)) { if (url.includes(GamePassCloudGallery.ALL)) {
@ -6149,12 +6165,12 @@ class XhomeInterceptor {
const xboxTitleId = JSON.parse(opts.body).titleIds[0]; const xboxTitleId = JSON.parse(opts.body).titleIds[0];
STATES.currentStream.xboxTitleId = xboxTitleId; STATES.currentStream.xboxTitleId = xboxTitleId;
const inputConfigs = obj[0]; const inputConfigs = obj[0];
let hasTouchSupport2 = inputConfigs.supportedTabs.length > 0; let hasTouchSupport = inputConfigs.supportedTabs.length > 0;
if (!hasTouchSupport2) { if (!hasTouchSupport) {
const supportedInputTypes = inputConfigs.supportedInputTypes; const supportedInputTypes = inputConfigs.supportedInputTypes;
hasTouchSupport2 = supportedInputTypes.includes(InputType.NATIVE_TOUCH) || supportedInputTypes.includes(InputType.CUSTOM_TOUCH_OVERLAY); hasTouchSupport = supportedInputTypes.includes(InputType.NATIVE_TOUCH) || supportedInputTypes.includes(InputType.CUSTOM_TOUCH_OVERLAY);
} }
if (hasTouchSupport2) { if (hasTouchSupport) {
TouchController.disable(); TouchController.disable();
BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, { BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, {
data: null data: null
@ -6340,7 +6356,7 @@ class XcloudInterceptor {
overrides.inputConfiguration = overrides.inputConfiguration || {}; overrides.inputConfiguration = overrides.inputConfiguration || {};
overrides.inputConfiguration.enableVibration = true; overrides.inputConfiguration.enableVibration = true;
let overrideMkb = null; let overrideMkb = null;
if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "on" || BX_FLAGS.ForceNativeMkbTitles.includes(STATES.currentStream.titleInfo.details.productId)) { if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "on" || BX_FLAGS.ForceNativeMkbTitles?.includes(STATES.currentStream.titleInfo.details.productId)) {
overrideMkb = true; overrideMkb = true;
} }
if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "off") { if (getPref(PrefKey.NATIVE_MKB_ENABLED) === "off") {
@ -8023,10 +8039,9 @@ if (!!window.BX_REMOTE_PLAY_CONFIG) {
return false; return false;
} }
const endIndex = str2.indexOf("},", index); const endIndex = str2.indexOf("},", index);
const newSettings = [ let newSettings = JSON.stringify(FeatureGates);
"PwaPrompt: false" newSettings = newSettings.substring(1, newSettings.length - 1);
]; const newCode = newSettings;
const newCode = newSettings.join(",");
str2 = str2.substring(0, endIndex) + "," + newCode + str2.substring(endIndex); str2 = str2.substring(0, endIndex) + "," + newCode + str2.substring(endIndex);
return str2; return str2;
}, },
@ -8356,7 +8371,7 @@ var PATCH_ORDERS = [
"remotePlayKeepAlive", "remotePlayKeepAlive",
"remotePlayDirectConnectUrl", "remotePlayDirectConnectUrl",
"remotePlayDisableAchievementToast", "remotePlayDisableAchievementToast",
STATES.hasTouchSupport && "patchUpdateInputConfigurationAsync" STATES.userAgentHasTouchSupport && "patchUpdateInputConfigurationAsync"
] : [], ] : [],
...BX_FLAGS.EnableXcloudLogging ? [ ...BX_FLAGS.EnableXcloudLogging ? [
"enableConsoleLogging", "enableConsoleLogging",
@ -8371,7 +8386,7 @@ var PLAYING_PATCH_ORDERS = [
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && "patchAudioMediaStream", getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && !getPref(PrefKey.STREAM_COMBINE_SOURCES) && "patchAudioMediaStream",
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && getPref(PrefKey.STREAM_COMBINE_SOURCES) && "patchCombinedAudioVideoMediaStream", getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && getPref(PrefKey.STREAM_COMBINE_SOURCES) && "patchCombinedAudioVideoMediaStream",
getPref(PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG) && "skipFeedbackDialog", getPref(PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG) && "skipFeedbackDialog",
...STATES.hasTouchSupport ? [ ...STATES.userAgentHasTouchSupport ? [
getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all" && "patchShowSensorControls", getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all" && "patchShowSensorControls",
getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all" && "exposeTouchLayoutManager", getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all" && "exposeTouchLayoutManager",
(getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "off" || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && "disableTakRenderer", (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "off" || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && "disableTakRenderer",
@ -8560,7 +8575,7 @@ function setupSettingsUi() {
let $updateAvailable; let $updateAvailable;
const $wrapper = CE("div", { class: "bx-settings-wrapper" }, CE("div", { class: "bx-settings-title-wrapper" }, CE("a", { const $wrapper = CE("div", { class: "bx-settings-wrapper" }, CE("div", { class: "bx-settings-title-wrapper" }, CE("a", {
class: "bx-settings-title", class: "bx-settings-title",
href: SCRIPT_HOME, href: "https://github.com/redphx/better-xcloud/releases",
target: "_blank" target: "_blank"
}, "Better xCloud " + SCRIPT_VERSION), createButton({ }, "Better xCloud " + SCRIPT_VERSION), createButton({
icon: BxIcon.QUESTION, icon: BxIcon.QUESTION,
@ -8570,11 +8585,11 @@ function setupSettingsUi() {
}))); })));
$updateAvailable = CE("a", { $updateAvailable = CE("a", {
class: "bx-settings-update bx-gone", class: "bx-settings-update bx-gone",
href: "https://github.com/redphx/better-xcloud/releases", href: "https://github.com/redphx/better-xcloud/releases/latest",
target: "_blank" target: "_blank"
}); });
$wrapper.appendChild($updateAvailable); $wrapper.appendChild($updateAvailable);
if (PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) { if (!SCRIPT_VERSION.includes("beta") && PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) {
$updateAvailable.textContent = `🌟 Version ${PREF_LATEST_VERSION} available`; $updateAvailable.textContent = `🌟 Version ${PREF_LATEST_VERSION} available`;
$updateAvailable.classList.remove("bx-gone"); $updateAvailable.classList.remove("bx-gone");
} }
@ -8811,8 +8826,8 @@ var SETTINGS_UI = {
] ]
}, },
[t("touch-controller")]: { [t("touch-controller")]: {
note: !STATES.hasTouchSupport ? "⚠️ " + t("device-unsupported-touch") : null, note: !STATES.userAgentHasTouchSupport ? "⚠️ " + t("device-unsupported-touch") : null,
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
items: [ items: [
PrefKey.STREAM_TOUCH_CONTROLLER, PrefKey.STREAM_TOUCH_CONTROLLER,
PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF, PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF,
@ -8884,7 +8899,7 @@ var injectSettingsButton = function($parent) {
document.activeElement && document.activeElement.blur(); document.activeElement && document.activeElement.blur();
} }
}); });
if (PREF_LATEST_VERSION && PREF_LATEST_VERSION !== SCRIPT_VERSION) { if (!SCRIPT_VERSION.includes("beta") && PREF_LATEST_VERSION && PREF_LATEST_VERSION !== SCRIPT_VERSION) {
$settingsBtn.setAttribute("data-update-available", "true"); $settingsBtn.setAttribute("data-update-available", "true");
} }
$headerFragment.appendChild($settingsBtn); $headerFragment.appendChild($settingsBtn);
@ -8950,7 +8965,7 @@ function overridePreloadState() {
} catch (e) { } catch (e) {
BxLogger.error(LOG_TAG6, e); BxLogger.error(LOG_TAG6, e);
} }
if (STATES.hasTouchSupport) { if (STATES.userAgentHasTouchSupport) {
try { try {
const sigls = state.xcloud.sigls; const sigls = state.xcloud.sigls;
if (GamePassCloudGallery.TOUCH in sigls) { if (GamePassCloudGallery.TOUCH in sigls) {
@ -9050,10 +9065,10 @@ function patchVideoApi() {
} }
return nativePlay.apply(this); return nativePlay.apply(this);
} }
if (!!this.src) { const $parent = this.parentElement;
return nativePlay.apply(this); if (!this.src && $parent.dataset.testid === "media-container") {
this.addEventListener("playing", showFunc);
} }
this.addEventListener("playing", showFunc);
return nativePlay.apply(this); return nativePlay.apply(this);
}; };
} }
@ -9339,7 +9354,7 @@ class GameBar {
const $gameBar = CE("div", { id: "bx-game-bar", class: "bx-gone", "data-position": position }, $container = CE("div", { class: "bx-game-bar-container bx-offscreen" }), createSvgIcon(position === "bottom-left" ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT)); const $gameBar = CE("div", { id: "bx-game-bar", class: "bx-gone", "data-position": position }, $container = CE("div", { class: "bx-game-bar-container bx-offscreen" }), createSvgIcon(position === "bottom-left" ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT));
this.actions = [ this.actions = [
new ScreenshotAction, new ScreenshotAction,
...STATES.hasTouchSupport && getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== "off" ? [new TouchControlAction] : [], ...STATES.userAgentHasTouchSupport && getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== "off" ? [new TouchControlAction] : [],
new MicrophoneAction new MicrophoneAction
]; ];
if (position === "bottom-right") { if (position === "bottom-right") {
@ -9513,14 +9528,16 @@ var observeRootDialog = function($root) {
const $addedElm = mutation.addedNodes[0]; const $addedElm = mutation.addedNodes[0];
if ($addedElm instanceof HTMLElement && $addedElm.className) { if ($addedElm instanceof HTMLElement && $addedElm.className) {
if ($addedElm.className.startsWith("NavigationAnimation") || $addedElm.className.startsWith("DialogRoutes") || $addedElm.className.startsWith("Dialog-module__container")) { if ($addedElm.className.startsWith("NavigationAnimation") || $addedElm.className.startsWith("DialogRoutes") || $addedElm.className.startsWith("Dialog-module__container")) {
const $selectedTab = $addedElm.querySelector("div[class^=NavigationMenu] button[aria-selected=true"); if (document.querySelector("#gamepass-dialog-root div[class*=GuideDialog]")) {
if ($selectedTab) { const $selectedTab = $addedElm.querySelector("div[class^=NavigationMenu] button[aria-selected=true");
let $elm = $selectedTab; if ($selectedTab) {
let index; let $elm = $selectedTab;
for (index = 0;$elm = $elm?.previousElementSibling; index++) let index;
; for (index = 0;$elm = $elm?.previousElementSibling; index++)
if (index === 0) { ;
BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, { where: GuideMenuTab.HOME }); if (index === 0) {
BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, { where: GuideMenuTab.HOME });
}
} }
} }
} }
@ -9561,7 +9578,7 @@ var main = function() {
AppInterface && patchPointerLockApi(); AppInterface && patchPointerLockApi();
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && patchAudioContext(); getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && patchAudioContext();
getPref(PrefKey.BLOCK_TRACKING) && patchMeControl(); getPref(PrefKey.BLOCK_TRACKING) && patchMeControl();
STATES.hasTouchSupport && TouchController.updateCustomList(); STATES.userAgentHasTouchSupport && TouchController.updateCustomList();
overridePreloadState(); overridePreloadState();
VibrationManager.initialSetup(); VibrationManager.initialSetup();
BX_FLAGS.CheckForUpdate && checkForUpdate(); BX_FLAGS.CheckForUpdate && checkForUpdate();
@ -9583,7 +9600,10 @@ var main = function() {
if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all") { if (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === "all") {
TouchController.setup(); TouchController.setup();
} }
getPref(PrefKey.MKB_ENABLED) && AppInterface && AppInterface.startPointerServer(); if (getPref(PrefKey.MKB_ENABLED) && AppInterface) {
STATES.pointerServerPort = AppInterface.startPointerServer() || 9269;
BxLogger.info("startPointerServer", "Port", STATES.pointerServerPort.toString());
}
}; };
if (window.location.pathname.includes("/auth/msa")) { if (window.location.pathname.includes("/auth/msa")) {
window.addEventListener("load", (e) => { window.addEventListener("load", (e) => {

View File

@ -219,15 +219,18 @@ function observeRootDialog($root: HTMLElement) {
const $addedElm = mutation.addedNodes[0]; const $addedElm = mutation.addedNodes[0];
if ($addedElm instanceof HTMLElement && $addedElm.className) { if ($addedElm instanceof HTMLElement && $addedElm.className) {
if ($addedElm.className.startsWith('NavigationAnimation') || $addedElm.className.startsWith('DialogRoutes') || $addedElm.className.startsWith('Dialog-module__container')) { if ($addedElm.className.startsWith('NavigationAnimation') || $addedElm.className.startsWith('DialogRoutes') || $addedElm.className.startsWith('Dialog-module__container')) {
// Find navigation bar // Make sure it's Guide dialog
const $selectedTab = $addedElm.querySelector('div[class^=NavigationMenu] button[aria-selected=true'); if (document.querySelector('#gamepass-dialog-root div[class*=GuideDialog]')) {
if ($selectedTab) { // Find navigation bar
let $elm: Element | null = $selectedTab; const $selectedTab = $addedElm.querySelector('div[class^=NavigationMenu] button[aria-selected=true');
let index; if ($selectedTab) {
for (index = 0; ($elm = $elm?.previousElementSibling); index++); let $elm: Element | null = $selectedTab;
let index;
for (index = 0; ($elm = $elm?.previousElementSibling); index++);
if (index === 0) { if (index === 0) {
BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, {where: GuideMenuTab.HOME}); BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, {where: GuideMenuTab.HOME});
}
} }
} }
} }
@ -277,7 +280,7 @@ function main() {
getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && patchAudioContext(); getPref(PrefKey.AUDIO_ENABLE_VOLUME_CONTROL) && patchAudioContext();
getPref(PrefKey.BLOCK_TRACKING) && patchMeControl(); getPref(PrefKey.BLOCK_TRACKING) && patchMeControl();
STATES.hasTouchSupport && TouchController.updateCustomList(); STATES.userAgentHasTouchSupport && TouchController.updateCustomList();
overridePreloadState(); overridePreloadState();
VibrationManager.initialSetup(); VibrationManager.initialSetup();
@ -314,7 +317,10 @@ function main() {
} }
// Start PointerProviderServer // Start PointerProviderServer
(getPref(PrefKey.MKB_ENABLED)) && AppInterface && AppInterface.startPointerServer(); if (getPref(PrefKey.MKB_ENABLED) && AppInterface) {
STATES.pointerServerPort = AppInterface.startPointerServer() || 9269;
BxLogger.info('startPointerServer', 'Port', STATES.pointerServerPort.toString());
}
} }
main(); main();

View File

@ -41,7 +41,7 @@ export class GameBar {
this.actions = [ this.actions = [
new ScreenshotAction(), new ScreenshotAction(),
...(STATES.hasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== 'off') ? [new TouchControlAction()] : []), ...(STATES.userAgentHasTouchSupport && (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) !== 'off') ? [new TouchControlAction()] : []),
new MicrophoneAction(), new MicrophoneAction(),
]; ];

View File

@ -33,7 +33,7 @@ class WebSocketMouseDataProvider extends MouseDataProvider {
this.#pointerClient = PointerClient.getInstance(); this.#pointerClient = PointerClient.getInstance();
this.#connected = false; this.#connected = false;
try { try {
this.#pointerClient.start(this.mkbHandler); this.#pointerClient.start(STATES.pointerServerPort, this.mkbHandler);
this.#connected = true; this.#connected = true;
} catch (e) { } catch (e) {
Toast.show('Cannot enable Mouse & Keyboard feature'); Toast.show('Cannot enable Mouse & Keyboard feature');

View File

@ -1,6 +1,6 @@
import { Toast } from "@/utils/toast"; import { Toast } from "@/utils/toast";
import { PointerClient } from "./pointer-client"; import { PointerClient } from "./pointer-client";
import { AppInterface } from "@/utils/global"; import { AppInterface, STATES } from "@/utils/global";
import { MkbHandler } from "./base-mkb-handler"; import { MkbHandler } from "./base-mkb-handler";
import { t } from "@/utils/translation"; import { t } from "@/utils/translation";
import { BxEvent } from "@/utils/bx-event"; import { BxEvent } from "@/utils/bx-event";
@ -149,7 +149,7 @@ export class NativeMkbHandler extends MkbHandler {
this.#updateInputConfigurationAsync(false); this.#updateInputConfigurationAsync(false);
try { try {
this.#pointerClient.start(this); this.#pointerClient.start(STATES.pointerServerPort, this);
} catch (e) { } catch (e) {
Toast.show('Cannot enable Mouse & Keyboard feature'); Toast.show('Cannot enable Mouse & Keyboard feature');
} }

View File

@ -14,8 +14,6 @@ enum PointerAction {
export class PointerClient { export class PointerClient {
static #PORT = 9269;
private static instance: PointerClient; private static instance: PointerClient;
public static getInstance(): PointerClient { public static getInstance(): PointerClient {
if (!PointerClient.instance) { if (!PointerClient.instance) {
@ -28,11 +26,15 @@ export class PointerClient {
#socket: WebSocket | undefined | null; #socket: WebSocket | undefined | null;
#mkbHandler: MkbHandler | undefined; #mkbHandler: MkbHandler | undefined;
start(mkbHandler: MkbHandler) { start(port: number, mkbHandler: MkbHandler) {
if (!port) {
throw new Error('PointerServer port is 0');
}
this.#mkbHandler = mkbHandler; this.#mkbHandler = mkbHandler;
// Create WebSocket connection. // Create WebSocket connection.
this.#socket = new WebSocket(`ws://localhost:${PointerClient.#PORT}`); this.#socket = new WebSocket(`ws://localhost:${port}`);
this.#socket.binaryType = 'arraybuffer'; this.#socket.binaryType = 'arraybuffer';
// Connection opened // Connection opened

View File

@ -11,6 +11,7 @@ import codeLocalCoOpEnable from "./patches/local-co-op-enable.js" with { type: "
import codeRemotePlayEnable from "./patches/remote-play-enable.js" with { type: "text" }; import codeRemotePlayEnable from "./patches/remote-play-enable.js" with { type: "text" };
import codeRemotePlayKeepAlive from "./patches/remote-play-keep-alive.js" with { type: "text" }; import codeRemotePlayKeepAlive from "./patches/remote-play-keep-alive.js" with { type: "text" };
import codeVibrationAdjust from "./patches/vibration-adjust.js" with { type: "text" }; import codeVibrationAdjust from "./patches/vibration-adjust.js" with { type: "text" };
import { FeatureGates } from "@/utils/feature-gates.js";
type PatchArray = (keyof typeof PATCHES)[]; type PatchArray = (keyof typeof PATCHES)[];
@ -228,12 +229,10 @@ if (!!window.BX_REMOTE_PLAY_CONFIG) {
// Find the next "}," // Find the next "},"
const endIndex = str.indexOf('},', index); const endIndex = str.indexOf('},', index);
const newSettings = [ let newSettings = JSON.stringify(FeatureGates);
// 'EnableStreamGate: false', newSettings = newSettings.substring(1, newSettings.length - 1);
'PwaPrompt: false',
];
const newCode = newSettings.join(','); const newCode = newSettings;
str = str.substring(0, endIndex) + ',' + newCode + str.substring(endIndex); str = str.substring(0, endIndex) + ',' + newCode + str.substring(endIndex);
return str; return str;
@ -672,7 +671,7 @@ let PATCH_ORDERS: PatchArray = [
'remotePlayKeepAlive', 'remotePlayKeepAlive',
'remotePlayDirectConnectUrl', 'remotePlayDirectConnectUrl',
'remotePlayDisableAchievementToast', 'remotePlayDisableAchievementToast',
STATES.hasTouchSupport && 'patchUpdateInputConfigurationAsync', STATES.userAgentHasTouchSupport && 'patchUpdateInputConfigurationAsync',
] : []), ] : []),
...(BX_FLAGS.EnableXcloudLogging ? [ ...(BX_FLAGS.EnableXcloudLogging ? [
@ -698,7 +697,7 @@ let PLAYING_PATCH_ORDERS: PatchArray = [
// Skip feedback dialog // Skip feedback dialog
getPref(PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG) && 'skipFeedbackDialog', getPref(PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG) && 'skipFeedbackDialog',
...(STATES.hasTouchSupport ? [ ...(STATES.userAgentHasTouchSupport ? [
getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'patchShowSensorControls', getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'patchShowSensorControls',
getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'exposeTouchLayoutManager', getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'all' && 'exposeTouchLayoutManager',
(getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'off' || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer', (getPref(PrefKey.STREAM_TOUCH_CONTROLLER) === 'off' || getPref(PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer',

View File

@ -1,4 +1,4 @@
import { STATES, AppInterface, SCRIPT_HOME, SCRIPT_VERSION } from "@utils/global"; import { STATES, AppInterface, SCRIPT_VERSION } from "@utils/global";
import { CE, createButton, ButtonStyle } from "@utils/html"; import { CE, createButton, ButtonStyle } from "@utils/html";
import { BxIcon } from "@utils/bx-icon"; import { BxIcon } from "@utils/bx-icon";
import { getPreferredServerRegion } from "@utils/region"; import { getPreferredServerRegion } from "@utils/region";
@ -62,8 +62,8 @@ const SETTINGS_UI = {
}, },
[t('touch-controller')]: { [t('touch-controller')]: {
note: !STATES.hasTouchSupport ? '⚠️ ' + t('device-unsupported-touch') : null, note: !STATES.userAgentHasTouchSupport ? '⚠️ ' + t('device-unsupported-touch') : null,
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
items: [ items: [
PrefKey.STREAM_TOUCH_CONTROLLER, PrefKey.STREAM_TOUCH_CONTROLLER,
PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF, PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF,
@ -130,7 +130,7 @@ export function setupSettingsUi() {
CE<HTMLElement>('div', {'class': 'bx-settings-title-wrapper'}, CE<HTMLElement>('div', {'class': 'bx-settings-title-wrapper'},
CE('a', { CE('a', {
'class': 'bx-settings-title', 'class': 'bx-settings-title',
'href': SCRIPT_HOME, 'href': 'https://github.com/redphx/better-xcloud/releases',
'target': '_blank', 'target': '_blank',
}, 'Better xCloud ' + SCRIPT_VERSION), }, 'Better xCloud ' + SCRIPT_VERSION),
createButton({ createButton({
@ -143,14 +143,14 @@ export function setupSettingsUi() {
); );
$updateAvailable = CE('a', { $updateAvailable = CE('a', {
'class': 'bx-settings-update bx-gone', 'class': 'bx-settings-update bx-gone',
'href': 'https://github.com/redphx/better-xcloud/releases', 'href': 'https://github.com/redphx/better-xcloud/releases/latest',
'target': '_blank', 'target': '_blank',
}); });
$wrapper.appendChild($updateAvailable); $wrapper.appendChild($updateAvailable);
// Show new version indicator // Show new version indicator
if (PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) { if (!SCRIPT_VERSION.includes('beta') && PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) {
$updateAvailable.textContent = `🌟 Version ${PREF_LATEST_VERSION} available`; $updateAvailable.textContent = `🌟 Version ${PREF_LATEST_VERSION} available`;
$updateAvailable.classList.remove('bx-gone'); $updateAvailable.classList.remove('bx-gone');
} }

View File

@ -48,7 +48,7 @@ function injectSettingsButton($parent?: HTMLElement) {
}); });
// Show new update status // Show new update status
if (PREF_LATEST_VERSION && PREF_LATEST_VERSION !== SCRIPT_VERSION) { if (!SCRIPT_VERSION.includes('beta') && PREF_LATEST_VERSION && PREF_LATEST_VERSION !== SCRIPT_VERSION) {
$settingsBtn.setAttribute('data-update-available', 'true'); $settingsBtn.setAttribute('data-update-available', 'true');
} }

View File

@ -167,7 +167,7 @@ function setupStreamSettingsDialog() {
], ],
}, },
STATES.hasTouchSupport && { STATES.userAgentHasTouchSupport && {
group: 'touch-controller', group: 'touch-controller',
label: t('touch-controller'), label: t('touch-controller'),
items: [ items: [

View File

@ -28,7 +28,7 @@ type BxStates = {
appContext: any | null; appContext: any | null;
serverRegions: any; serverRegions: any;
hasTouchSupport: boolean; userAgentHasTouchSupport: boolean;
browserHasTouchSupport: boolean; browserHasTouchSupport: boolean;
currentStream: Partial<{ currentStream: Partial<{
@ -51,6 +51,8 @@ type BxStates = {
serverId: string; serverId: string;
}; };
}>; }>;
pointerServerPort: number;
} }
type DualEnum = {[index: string]: number} & {[index: number]: string}; type DualEnum = {[index: string]: number} & {[index: number]: string};

View File

@ -66,8 +66,8 @@ export namespace BxEvent {
} }
} }
AppInterface && AppInterface.onEvent(eventName);
target.dispatchEvent(event); target.dispatchEvent(event);
AppInterface && AppInterface.onEvent(eventName);
} }
} }

View File

@ -23,7 +23,7 @@ export const BxExposed = {
let supportedInputTypes = titleInfo.details.supportedInputTypes; let supportedInputTypes = titleInfo.details.supportedInputTypes;
if (BX_FLAGS.ForceNativeMkbTitles.includes(titleInfo.details.productId)) { if (BX_FLAGS.ForceNativeMkbTitles?.includes(titleInfo.details.productId)) {
supportedInputTypes.push(InputType.MKB); supportedInputTypes.push(InputType.MKB);
} }
@ -34,7 +34,7 @@ export const BxExposed = {
titleInfo.details.hasMkbSupport = supportedInputTypes.includes(InputType.MKB); titleInfo.details.hasMkbSupport = supportedInputTypes.includes(InputType.MKB);
if (STATES.hasTouchSupport) { if (STATES.userAgentHasTouchSupport) {
let touchControllerAvailability = getPref(PrefKey.STREAM_TOUCH_CONTROLLER); let touchControllerAvailability = getPref(PrefKey.STREAM_TOUCH_CONTROLLER);
// Disable touch control when gamepad found // Disable touch control when gamepad found

View File

@ -8,6 +8,7 @@ type BxFlags = Partial<{
UseDevTouchLayout: boolean; UseDevTouchLayout: boolean;
ForceNativeMkbTitles: string[]; ForceNativeMkbTitles: string[];
FeatureGates: {[key: string]: boolean} | null,
}> }>
// Setup flags // Setup flags
@ -21,9 +22,10 @@ const DEFAULT_FLAGS: BxFlags = {
UseDevTouchLayout: false, UseDevTouchLayout: false,
ForceNativeMkbTitles: [], ForceNativeMkbTitles: [],
FeatureGates: null,
} }
export const BX_FLAGS = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {}); export const BX_FLAGS: BxFlags = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {});
try { try {
delete window.BX_FLAGS; delete window.BX_FLAGS;
} catch (e) {} } catch (e) {}

View File

@ -0,0 +1,20 @@
import { BX_FLAGS } from "./bx-flags";
import { getPref, PrefKey } from "./preferences";
export let FeatureGates: {[key: string]: boolean} = {
'PwaPrompt': false,
};
// Disable context menu in Home page
if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED)) {
FeatureGates['EnableHomeContextMenu'] = false;
}
// Disable chat feature
if (getPref(PrefKey.BLOCK_SOCIAL_FEATURES)) {
FeatureGates['EnableGuideChatTab'] = false;
}
if (BX_FLAGS.FeatureGates) {
FeatureGates = Object.assign(BX_FLAGS.FeatureGates, FeatureGates);
}

View File

@ -1,7 +1,6 @@
import { UserAgent } from "./user-agent"; import { UserAgent } from "./user-agent";
export const SCRIPT_VERSION = Bun.env.SCRIPT_VERSION; export const SCRIPT_VERSION = Bun.env.SCRIPT_VERSION!;
export const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
export const AppInterface = window.AppInterface; export const AppInterface = window.AppInterface;
@ -11,15 +10,17 @@ const userAgent = window.navigator.userAgent.toLowerCase();
const isTv = userAgent.includes('smart-tv') || userAgent.includes('smarttv') || /\baft.*\b/.test(userAgent); const isTv = userAgent.includes('smart-tv') || userAgent.includes('smarttv') || /\baft.*\b/.test(userAgent);
const isVr = window.navigator.userAgent.includes('VR') && window.navigator.userAgent.includes('OculusBrowser'); const isVr = window.navigator.userAgent.includes('VR') && window.navigator.userAgent.includes('OculusBrowser');
const browserHasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0; const browserHasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
const hasTouchSupport = !isTv && !isVr && browserHasTouchSupport; const userAgentHasTouchSupport = !isTv && !isVr && browserHasTouchSupport;
export const STATES: BxStates = { export const STATES: BxStates = {
isPlaying: false, isPlaying: false,
appContext: {}, appContext: {},
serverRegions: {}, serverRegions: {},
hasTouchSupport: hasTouchSupport, userAgentHasTouchSupport: userAgentHasTouchSupport,
browserHasTouchSupport: browserHasTouchSupport, browserHasTouchSupport: browserHasTouchSupport,
currentStream: {}, currentStream: {},
remotePlay: {}, remotePlay: {},
pointerServerPort: 9269,
}; };

View File

@ -35,12 +35,12 @@ export function patchVideoApi() {
return nativePlay.apply(this); return nativePlay.apply(this);
} }
if (!!this.src) { const $parent = this.parentElement!!;
return nativePlay.apply(this); // Video tag is stream player
if (!this.src && $parent.dataset.testid === 'media-container') {
this.addEventListener('playing', showFunc);
} }
this.addEventListener('playing', showFunc);
return nativePlay.apply(this); return nativePlay.apply(this);
}; };
} }

View File

@ -9,6 +9,7 @@ import { STATES } from "@utils/global";
import { getPreferredServerRegion } from "@utils/region"; import { getPreferredServerRegion } from "@utils/region";
import { GamePassCloudGallery } from "./gamepass-gallery"; import { GamePassCloudGallery } from "./gamepass-gallery";
import { InputType } from "./bx-exposed"; import { InputType } from "./bx-exposed";
import { FeatureGates } from "./feature-gates";
enum RequestType { enum RequestType {
XCLOUD = 'xcloud', XCLOUD = 'xcloud',
@ -440,7 +441,7 @@ class XcloudInterceptor {
let overrideMkb: boolean | null = null; let overrideMkb: boolean | null = null;
if (getPref(PrefKey.NATIVE_MKB_ENABLED) === 'on' || BX_FLAGS.ForceNativeMkbTitles.includes(STATES.currentStream.titleInfo!.details.productId)) { if (getPref(PrefKey.NATIVE_MKB_ENABLED) === 'on' || BX_FLAGS.ForceNativeMkbTitles?.includes(STATES.currentStream.titleInfo!.details.productId)) {
overrideMkb = true; overrideMkb = true;
} }
@ -578,14 +579,8 @@ export function interceptHttpRequests() {
const response = await NATIVE_FETCH(request, init); const response = await NATIVE_FETCH(request, init);
const json = await response.json(); const json = await response.json();
const overrideTreatments: {[key: string]: boolean} = {}; for (const key in FeatureGates) {
json.exp.treatments[key] = FeatureGates[key]
if (getPref(PrefKey.UI_HOME_CONTEXT_MENU_DISABLED)) {
overrideTreatments['EnableHomeContextMenu'] = false;
}
for (const key in overrideTreatments) {
json.exp.treatments[key] = overrideTreatments[key]
} }
response.json = () => Promise.resolve(json); response.json = () => Promise.resolve(json);
@ -596,7 +591,7 @@ export function interceptHttpRequests() {
} }
// Add list of games with custom layouts to the official list // Add list of games with custom layouts to the official list
if (STATES.hasTouchSupport && url.includes('catalog.gamepass.com/sigls/')) { if (STATES.userAgentHasTouchSupport && url.includes('catalog.gamepass.com/sigls/')) {
const response = await NATIVE_FETCH(request, init); const response = await NATIVE_FETCH(request, init);
const obj = await response.clone().json(); const obj = await response.clone().json();

View File

@ -263,7 +263,7 @@ export class Preferences {
all: t('tc-all-games'), all: t('tc-all-games'),
off: t('off'), off: t('off'),
}, },
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
ready: (setting: PreferenceSetting) => { ready: (setting: PreferenceSetting) => {
if (setting.unsupported) { if (setting.unsupported) {
setting.default = 'default'; setting.default = 'default';
@ -273,7 +273,7 @@ export class Preferences {
[PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: { [PrefKey.STREAM_TOUCH_CONTROLLER_AUTO_OFF]: {
label: t('tc-auto-off'), label: t('tc-auto-off'),
default: false, default: false,
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
}, },
[PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY]: { [PrefKey.STREAM_TOUCH_CONTROLLER_DEFAULT_OPACITY]: {
type: SettingElementType.NUMBER_STEPPER, type: SettingElementType.NUMBER_STEPPER,
@ -287,7 +287,7 @@ export class Preferences {
ticks: 10, ticks: 10,
hideSlider: true, hideSlider: true,
}, },
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
}, },
[PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: { [PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_STANDARD]: {
label: t('tc-standard-layout-style'), label: t('tc-standard-layout-style'),
@ -297,7 +297,7 @@ export class Preferences {
white: t('tc-all-white'), white: t('tc-all-white'),
muted: t('tc-muted-colors'), muted: t('tc-muted-colors'),
}, },
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
}, },
[PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: { [PrefKey.STREAM_TOUCH_CONTROLLER_STYLE_CUSTOM]: {
label: t('tc-custom-layout-style'), label: t('tc-custom-layout-style'),
@ -306,7 +306,7 @@ export class Preferences {
default: t('default'), default: t('default'),
muted: t('tc-muted-colors'), muted: t('tc-muted-colors'),
}, },
unsupported: !STATES.hasTouchSupport, unsupported: !STATES.userAgentHasTouchSupport,
}, },
[PrefKey.STREAM_SIMPLIFY_MENU]: { [PrefKey.STREAM_SIMPLIFY_MENU]: {
@ -544,7 +544,7 @@ export class Preferences {
[PrefKey.UI_HOME_CONTEXT_MENU_DISABLED]: { [PrefKey.UI_HOME_CONTEXT_MENU_DISABLED]: {
label: t('disable-home-context-menu'), label: t('disable-home-context-menu'),
default: false, default: STATES.browserHasTouchSupport,
}, },
[PrefKey.BLOCK_SOCIAL_FEATURES]: { [PrefKey.BLOCK_SOCIAL_FEATURES]: {

View File

@ -24,7 +24,7 @@ export function overridePreloadState() {
} }
// Add list of games with custom layouts to the official list // Add list of games with custom layouts to the official list
if (STATES.hasTouchSupport) { if (STATES.userAgentHasTouchSupport) {
try { try {
const sigls = state.xcloud.sigls; const sigls = state.xcloud.sigls;
if (GamePassCloudGallery.TOUCH in sigls) { if (GamePassCloudGallery.TOUCH in sigls) {

View File

@ -7,6 +7,11 @@ import { Translations } from "./translation";
* Check for update * Check for update
*/ */
export function checkForUpdate() { export function checkForUpdate() {
// Don't check update for beta version
if (SCRIPT_VERSION.includes('beta')) {
return;
}
const CHECK_INTERVAL_SECONDS = 2 * 3600; // check every 2 hours const CHECK_INTERVAL_SECONDS = 2 * 3600; // check every 2 hours
const currentVersion = getPref(PrefKey.CURRENT_VERSION); const currentVersion = getPref(PrefKey.CURRENT_VERSION);