Compare commits

...

6 Commits

Author SHA1 Message Date
528c6774fe Bump version to 6.4.10 2025-04-16 16:43:40 +07:00
3c8a35d441 Fix "disableTouchContextMenu" patch 2025-04-16 16:42:59 +07:00
544ededb64 Bump version to 6.4.9 2025-04-15 04:52:46 +07:00
f4f88f688b Fix script running on non-xCloud pages (#698) 2025-04-15 04:52:21 +07:00
1fb1a64767 Fix Remote Play stopped working with xCloud 29.1.60 2025-04-15 04:39:50 +07:00
769649a376 Upgrade bun 2025-04-15 04:36:54 +07:00
9 changed files with 39 additions and 26 deletions

View File

@ -3,11 +3,11 @@
"workspaces": {
"": {
"devDependencies": {
"@types/bun": "^1.2.5",
"@types/node": "^22.13.10",
"@types/bun": "^1.2.6",
"@types/node": "^22.13.13",
"@types/stylus": "^0.48.43",
"@webgpu/types": "^0.1.57",
"eslint": "^9.22.0",
"@webgpu/types": "^0.1.59",
"eslint": "^9.23.0",
"eslint-plugin-compat": "^6.0.2",
"stylus": "^0.64.0",
},
@ -23,7 +23,7 @@
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
"@eslint/config-array": ["@eslint/config-array@0.19.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
"@eslint/config-array": ["@eslint/config-array@0.20.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.2.0", "", {}, "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ=="],
@ -31,7 +31,7 @@
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
"@eslint/js": ["@eslint/js@9.23.0", "", {}, "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw=="],
"@eslint/js": ["@eslint/js@9.24.0", "", {}, "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
@ -51,19 +51,19 @@
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@types/bun": ["@types/bun@1.2.6", "", { "dependencies": { "bun-types": "1.2.6" } }, "sha512-fY9CAmTdJH1Llx7rugB0FpgWK2RKuHCs3g2cFDYXUutIy1QGiPQxKkGY8owhfZ4MXWNfxwIbQLChgH5gDsY7vw=="],
"@types/bun": ["@types/bun@1.2.9", "", { "dependencies": { "bun-types": "1.2.9" } }, "sha512-epShhLGQYc4Bv/aceHbmBhOz1XgUnuTZgcxjxk+WXwNyDXavv5QHD1QEFV0FwbTSQtNq6g4ZcV6y0vZakTjswg=="],
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@22.13.13", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ=="],
"@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
"@types/stylus": ["@types/stylus@0.48.43", "", { "dependencies": { "@types/node": "*" } }, "sha512-72dv/zdhuyXWVHUXG2VTPEQdOG+oen95/DNFx2aMFFaY6LoITI6PwEqf5x31JF49kp2w9hvUzkNfTGBIeg61LQ=="],
"@types/ws": ["@types/ws@8.5.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A=="],
"@webgpu/types": ["@webgpu/types@0.1.59", "", {}, "sha512-jZJ6ipNli+rn++/GAPqsZXfsgjx951wlCW7vNAg+oGdp0ZYidTOkbVTVeK2frzowuD5ch7MRz7leOEX1PMv43A=="],
"@webgpu/types": ["@webgpu/types@0.1.60", "", {}, "sha512-8B/tdfRFKdrnejqmvq95ogp8tf52oZ51p3f4QD5m5Paey/qlX4Rhhy5Y8tgFMi7Ms70HzcMMw3EQjH/jdhTwlA=="],
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
@ -85,7 +85,7 @@
"browserslist": ["browserslist@4.24.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA=="],
"bun-types": ["bun-types@1.2.6", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-FbCKyr5KDiPULUzN/nm5oqQs9nXCHD8dVc64BArxJadCvbNzAI6lUWGh9fSJZWeDIRD38ikceBU8Kj/Uh+53oQ=="],
"bun-types": ["bun-types@1.2.9", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-dk/kOEfQbajENN/D6FyiSgOKEuUi9PWfqKQJEgwKrCMWbjS/S6tEXp178mWvWAcUSYm9ArDlWHZKO3T/4cLXiw=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
@ -115,7 +115,7 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.23.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw=="],
"eslint": ["eslint@9.24.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.24.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ=="],
"eslint-plugin-compat": ["eslint-plugin-compat@6.0.2", "", { "dependencies": { "@mdn/browser-compat-data": "^5.5.35", "ast-metadata-inferer": "^0.8.1", "browserslist": "^4.24.2", "caniuse-lite": "^1.0.30001687", "find-up": "^5.0.0", "globals": "^15.7.0", "lodash.memoize": "^4.1.2", "semver": "^7.6.2" }, "peerDependencies": { "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-1ME+YfJjmOz1blH0nPZpHgjMGK4kjgEeoYqGCqoBPQ/mGu/dJzdoP0f1C8H2jcWZjzhZjAMccbM/VdXhPORIfA=="],
@ -257,7 +257,7 @@
"typescript": ["typescript@5.7.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg=="],
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"update-browserslist-db": ["update-browserslist-db@1.1.1", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A=="],

View File

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

View File

@ -1,12 +1,13 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 6.4.8
// @version 6.4.10
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
// @match https://www.xbox.com/*/play*
// @match https://www.xbox.com/*/auth/msa?*loggedIn*
// @exclude https://www.xbox.com/*/xbox-game-pass/play-day-one
// @run-at document-start
// @grant none
// @updateURL https://raw.githubusercontent.com/redphx/better-xcloud/typescript/dist/better-xcloud.meta.js
@ -192,7 +193,7 @@ class UserAgent {
});
}
}
var SCRIPT_VERSION = "6.4.8", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
var SCRIPT_VERSION = "6.4.10", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
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, STATES = {
supportedRegion: !0,
@ -5548,7 +5549,7 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
return str = PatcherUtils.replaceWith(str, index, '"All"', '"Locked"'), str;
},
disableTouchContextMenu(str) {
let index = str.indexOf('"ContextualCardActions-module__container');
let index = str.indexOf("arguments.length>2&&void 0!==arguments[2]?arguments[2]:500;");
if (index >= 0 && (index = str.indexOf('addEventListener("touchstart"', index)), index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return ", index, 50)), index < 0) return !1;
return str = PatcherUtils.replaceWith(str, index, "return", "return () => {};"), str;
},
@ -8158,7 +8159,8 @@ var FeatureGates = {
ShowForcedUpdateScreen: !1,
EnableTakControlResizing: !0,
EnableLazyLoadedHome: !1,
EnableRemotePlay: getGlobalPref("xhome.enabled")
EnableRemotePlay: getGlobalPref("xhome.enabled"),
EnableConsoles: getGlobalPref("xhome.enabled")
}, nativeMkbMode = getGlobalPref("nativeMkb.mode");
if (nativeMkbMode !== "default") FeatureGates.EnableMouseAndKeyboard = nativeMkbMode === "on";
var blockFeatures = getGlobalPref("block.features");
@ -10241,6 +10243,7 @@ if (BX_FLAGS.SafariWorkaround && document.readyState !== "loading") {
class: "bx-reload-overlay"
}, CE("div", !1, CE("p", !1, t("load-failed-message")), $secondaryAction))), document.documentElement.appendChild($fragment), isSafari && window.location.reload(!0), new Error("[Better xCloud] Executing workaround for Safari");
}
if (!window.location.pathname.match(/^\/[a-zA-Z]{2}-[a-zA-Z]{2}\/play/)) throw new Error("[Better xCloud] Not xCloud page");
window.addEventListener("load", (e) => {
window.setTimeout(() => {
if (document.body.classList.contains("legacyBackground")) window.stop(), window.location.reload(!0);

View File

@ -1,12 +1,13 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 6.4.8
// @version 6.4.10
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
// @match https://www.xbox.com/*/play*
// @match https://www.xbox.com/*/auth/msa?*loggedIn*
// @exclude https://www.xbox.com/*/xbox-game-pass/play-day-one
// @run-at document-start
// @grant none
// @updateURL https://raw.githubusercontent.com/redphx/better-xcloud/typescript/dist/better-xcloud.meta.js
@ -25,7 +26,7 @@ var ALL_PREFS = {global: ["audio.mic.onPlaying","audio.volume.booster.enabled","
var SMART_TV_UNIQUE_ID = "FC4A1DA2-711C-4E9C-BC7F-047AF8A672EA", CHROMIUM_VERSION = "125.0.0.0";
if (!!window.chrome || window.navigator.userAgent.includes("Chrome")) {let match = window.navigator.userAgent.match(/\s(?:Chrome|Edg)\/([\d\.]+)/);if (match) CHROMIUM_VERSION = match[1];}
class UserAgent {static STORAGE_KEY = "BetterXcloud.UserAgent";static #config;static #isMobile = null;static #isSafari = null;static #isSafariMobile = null;static #USER_AGENTS = {"windows-edge": `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROMIUM_VERSION} Safari/537.36 Edg/${CHROMIUM_VERSION}`,"macos-safari": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.2 Safari/605.1.1","smarttv-generic": `${window.navigator.userAgent} Smart-TV`,"smarttv-tizen": `Mozilla/5.0 (SMART-TV; LINUX; Tizen 7.0) AppleWebKit/537.36 (KHTML, like Gecko) ${CHROMIUM_VERSION}/7.0 TV Safari/537.36 ${SMART_TV_UNIQUE_ID}`,"vr-oculus": window.navigator.userAgent + " OculusBrowser VR"};static init() {if (UserAgent.#config = JSON.parse(window.localStorage.getItem(UserAgent.STORAGE_KEY) || "{}"), !UserAgent.#config.profile) UserAgent.#config.profile = BX_FLAGS.DeviceInfo.deviceType === "android-tv" || BX_FLAGS.DeviceInfo.deviceType === "webos" ? "vr-oculus" : "default";if (!UserAgent.#config.custom) UserAgent.#config.custom = "";UserAgent.spoof();}static updateStorage(profile, custom) {let config = UserAgent.#config;if (config.profile = profile, profile === "custom" && typeof custom !== "undefined") config.custom = custom;window.localStorage.setItem(UserAgent.STORAGE_KEY, JSON.stringify(config));}static getDefault() {return window.navigator.orgUserAgent || window.navigator.userAgent;}static get(profile) {let defaultUserAgent = window.navigator.userAgent;switch (profile) {case "default":return defaultUserAgent;case "custom":return UserAgent.#config.custom || defaultUserAgent;default:return UserAgent.#USER_AGENTS[profile] || defaultUserAgent;}}static isSafari() {if (this.#isSafari !== null) return this.#isSafari;let userAgent = UserAgent.getDefault().toLowerCase(), result = userAgent.includes("safari") && !userAgent.includes("chrom");return this.#isSafari = result, result;}static isSafariMobile() {if (this.#isSafariMobile !== null) return this.#isSafariMobile;let userAgent = UserAgent.getDefault().toLowerCase(), result = this.isSafari() && userAgent.includes("mobile");return this.#isSafariMobile = result, result;}static isMobile() {if (this.#isMobile !== null) return this.#isMobile;let userAgent = UserAgent.getDefault().toLowerCase(), result = /iphone|ipad|android/.test(userAgent);return this.#isMobile = result, result;}static spoof() {let profile = UserAgent.#config.profile;if (profile === "default") return;let newUserAgent = UserAgent.get(profile);if ("userAgentData" in window.navigator) window.navigator.orgUserAgentData = window.navigator.userAgentData, Object.defineProperty(window.navigator, "userAgentData", {});window.navigator.orgUserAgent = window.navigator.userAgent, Object.defineProperty(window.navigator, "userAgent", {value: newUserAgent});}}
var SCRIPT_VERSION = "6.4.8", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
var SCRIPT_VERSION = "6.4.10", SCRIPT_VARIANT = "full", AppInterface = window.AppInterface;
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, STATES = {supportedRegion: !0,serverRegions: {},selectedRegion: {},gsToken: "",isSignedIn: !1,isPlaying: !1,browser: {capabilities: {touch: browserHasTouchSupport,batteryApi: "getBattery" in window.navigator,deviceVibration: !!window.navigator.vibrate,mkb: AppInterface || !UserAgent.getDefault().toLowerCase().match(/(android|iphone|ipad)/),emulatedNativeMkb: !!AppInterface}},userAgent: {isTv,capabilities: {touch: userAgentHasTouchSupport,mkb: AppInterface || !userAgent.match(/(android|iphone|ipad)/)}},currentStream: {},remotePlay: {},pointerServerPort: 9269};
function deepClone(obj) {if (!obj) return {};if ("structuredClone" in window) return structuredClone(obj);return JSON.parse(JSON.stringify(obj));}
@ -192,7 +193,7 @@ if (e && e.id) {const siglId = e.id;if (${siglIds.map((item2) => `siglId === "${
`;return str = PatcherUtils.insertAt(str, index, newCode), str;},ignoreGenresSection(str) {let index = str.indexOf('="GenresRow"');if (index > -1 && (index = PatcherUtils.lastIndexOf(str, "{", index)), index < 0) return !1;return str = PatcherUtils.insertAt(str, index + 1, "return null;"), str;},overrideStorageGetSettings(str) {let text = "}getSetting(e){";if (!str.includes(text)) return !1;let newCode = `
// console.log('setting', this.baseStorageKey, e);
if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {const settings = window.BX_EXPOSED.overrideSettings[this.baseStorageKey];if (e in settings) {return settings[e];}}
`;return str = str.replace(text, text + newCode), str;},alwaysShowStreamHud(str) {let index = str.indexOf(",{onShowStreamMenu:");if (index < 0) return !1;if (index = str.indexOf("&&(0,", index - 100), index < 0) return !1;let commaIndex = str.indexOf(",", index - 10);return str = str.substring(0, commaIndex) + ",true" + str.substring(index), str;},patchSetCurrentFocus(str) {let index = str.indexOf(".setCurrentFocus=(");if (index < 0) return !1;return index = str.indexOf("{", index) + 1, str = PatcherUtils.insertAt(str, index, "e && BxEvent.dispatch(window, BxEvent.NAVIGATION_FOCUS_CHANGED, { element: e });"), str;},detectProductDetailPage(str) {let index = str.indexOf('{location:"ProductDetailPage",');if (index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return", index, 200)), index < 0) return !1;return str = str.substring(0, index) + 'BxEvent.dispatch(window, BxEvent.XCLOUD_RENDERING_COMPONENT, { component: "product-detail" });' + str.substring(index), str;},detectBrowserRouterReady(str) {let index = str.indexOf("{history:this.history,");if (index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return", index, 100)), index < 0) return !1;return str = PatcherUtils.insertAt(str, index, "window.BxEvent.dispatch(window, window.BxEvent.XCLOUD_ROUTER_HISTORY_READY, {history: this.history});"), str;},guideAchievementsDefaultLocked(str) {let index = str.indexOf("FilterButton-module__container");if (index >= 0 && (index = PatcherUtils.lastIndexOf(str, '"All"', index, 150)), index < 0) return !1;if (str = PatcherUtils.replaceWith(str, index, '"All"', '"Locked"'), index = str.indexOf('"Guide_Achievements_Unlocked_Empty","Guide_Achievements_Locked_Empty"'), index >= 0 && (index = PatcherUtils.indexOf(str, '"All"', index, 250)), index < 0) return !1;return str = PatcherUtils.replaceWith(str, index, '"All"', '"Locked"'), str;},disableTouchContextMenu(str) {let index = str.indexOf('"ContextualCardActions-module__container');if (index >= 0 && (index = str.indexOf('addEventListener("touchstart"', index)), index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return ", index, 50)), index < 0) return !1;return str = PatcherUtils.replaceWith(str, index, "return", "return () => {};"), str;},modifyPreloadedState(str) {let text = "=window.__PRELOADED_STATE__;";if (!str.includes(text)) return !1;return str = str.replace(text, "=window.BX_EXPOSED.modifyPreloadedState(window.__PRELOADED_STATE__);"), str;},homePageBeforeLoad(str) {return PatcherUtils.patchBeforePageLoad(str, "home");},productDetailPageBeforeLoad(str) {return PatcherUtils.patchBeforePageLoad(str, "product-detail");},streamPageBeforeLoad(str) {return PatcherUtils.patchBeforePageLoad(str, "stream");},disableAbsoluteMouse(str) {let text = "sendAbsoluteMouseCapableMessage(e){";if (!str.includes(text)) return !1;return str = str.replace(text, text + "return;"), str;},changeNotificationsSubscription(str) {let text = ";buildSubscriptionQueryParamsForNotifications(", index = str.indexOf(text);if (index < 0) return !1;index += text.length;let subsVar = str[index];index = str.indexOf("{", index) + 1;let blockFeatures = getGlobalPref("block.features"), filters = [];if (blockFeatures.includes("notifications-invites")) filters.push("GameInvite", "PartyInvite");if (blockFeatures.includes("friends")) filters.push("Follower");if (blockFeatures.includes("notifications-achievements")) filters.push("AchievementUnlock");let newCode = `
`;return str = str.replace(text, text + newCode), str;},alwaysShowStreamHud(str) {let index = str.indexOf(",{onShowStreamMenu:");if (index < 0) return !1;if (index = str.indexOf("&&(0,", index - 100), index < 0) return !1;let commaIndex = str.indexOf(",", index - 10);return str = str.substring(0, commaIndex) + ",true" + str.substring(index), str;},patchSetCurrentFocus(str) {let index = str.indexOf(".setCurrentFocus=(");if (index < 0) return !1;return index = str.indexOf("{", index) + 1, str = PatcherUtils.insertAt(str, index, "e && BxEvent.dispatch(window, BxEvent.NAVIGATION_FOCUS_CHANGED, { element: e });"), str;},detectProductDetailPage(str) {let index = str.indexOf('{location:"ProductDetailPage",');if (index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return", index, 200)), index < 0) return !1;return str = str.substring(0, index) + 'BxEvent.dispatch(window, BxEvent.XCLOUD_RENDERING_COMPONENT, { component: "product-detail" });' + str.substring(index), str;},detectBrowserRouterReady(str) {let index = str.indexOf("{history:this.history,");if (index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return", index, 100)), index < 0) return !1;return str = PatcherUtils.insertAt(str, index, "window.BxEvent.dispatch(window, window.BxEvent.XCLOUD_ROUTER_HISTORY_READY, {history: this.history});"), str;},guideAchievementsDefaultLocked(str) {let index = str.indexOf("FilterButton-module__container");if (index >= 0 && (index = PatcherUtils.lastIndexOf(str, '"All"', index, 150)), index < 0) return !1;if (str = PatcherUtils.replaceWith(str, index, '"All"', '"Locked"'), index = str.indexOf('"Guide_Achievements_Unlocked_Empty","Guide_Achievements_Locked_Empty"'), index >= 0 && (index = PatcherUtils.indexOf(str, '"All"', index, 250)), index < 0) return !1;return str = PatcherUtils.replaceWith(str, index, '"All"', '"Locked"'), str;},disableTouchContextMenu(str) {let index = str.indexOf("arguments.length>2&&void 0!==arguments[2]?arguments[2]:500;");if (index >= 0 && (index = str.indexOf('addEventListener("touchstart"', index)), index >= 0 && (index = PatcherUtils.lastIndexOf(str, "return ", index, 50)), index < 0) return !1;return str = PatcherUtils.replaceWith(str, index, "return", "return () => {};"), str;},modifyPreloadedState(str) {let text = "=window.__PRELOADED_STATE__;";if (!str.includes(text)) return !1;return str = str.replace(text, "=window.BX_EXPOSED.modifyPreloadedState(window.__PRELOADED_STATE__);"), str;},homePageBeforeLoad(str) {return PatcherUtils.patchBeforePageLoad(str, "home");},productDetailPageBeforeLoad(str) {return PatcherUtils.patchBeforePageLoad(str, "product-detail");},streamPageBeforeLoad(str) {return PatcherUtils.patchBeforePageLoad(str, "stream");},disableAbsoluteMouse(str) {let text = "sendAbsoluteMouseCapableMessage(e){";if (!str.includes(text)) return !1;return str = str.replace(text, text + "return;"), str;},changeNotificationsSubscription(str) {let text = ";buildSubscriptionQueryParamsForNotifications(", index = str.indexOf(text);if (index < 0) return !1;index += text.length;let subsVar = str[index];index = str.indexOf("{", index) + 1;let blockFeatures = getGlobalPref("block.features"), filters = [];if (blockFeatures.includes("notifications-invites")) filters.push("GameInvite", "PartyInvite");if (blockFeatures.includes("friends")) filters.push("Follower");if (blockFeatures.includes("notifications-achievements")) filters.push("AchievementUnlock");let newCode = `
let subs = ${subsVar};
subs = subs.filter(val => !${JSON.stringify(filters)}.includes(val));
${subsVar} = subs;
@ -223,7 +224,7 @@ class TrueAchievements {static instance;static getInstance = () => TrueAchieveme
class VirtualControllerShortcut {static pressXboxButton() {let streamSession = window.BX_EXPOSED.streamSession;if (!streamSession) return;let released = generateVirtualControllerMapping(0), pressed = generateVirtualControllerMapping(0, {Nexus: 1,VirtualPhysicality: 1024});streamSession.onVirtualGamepadInput("systemMenu", performance.now(), [pressed]), setTimeout(() => {streamSession.onVirtualGamepadInput("systemMenu", performance.now(), [released]);}, 100);}}
class ShortcutHandler {static runAction(action) {switch (action) {case "bx.settings.show":SettingsDialog.getInstance().show();break;case "stream.screenshot.capture":ScreenshotManager.getInstance().takeScreenshot();break;case "stream.video.toggle":RendererShortcut.toggleVisibility();break;case "stream.stats.toggle":StreamStats.getInstance().toggle();break;case "stream.microphone.toggle":MicrophoneShortcut.toggle();break;case "stream.menu.show":StreamUiShortcut.showHideStreamMenu();break;case "stream.sound.toggle":SoundShortcut.muteUnmute();break;case "stream.volume.inc":SoundShortcut.adjustGainNodeVolume(10);break;case "stream.volume.dec":SoundShortcut.adjustGainNodeVolume(-10);break;case "device.brightness.inc":case "device.brightness.dec":case "device.sound.toggle":case "device.volume.inc":case "device.volume.dec":AppInterface && AppInterface.runShortcut && AppInterface.runShortcut(action);break;case "mkb.toggle":if (STATES.currentStream.titleInfo?.details.hasMkbSupport) NativeMkbHandler.getInstance()?.toggle();else EmulatedMkbHandler.getInstance()?.toggle();break;case "ta.open":TrueAchievements.getInstance().open(!1);break;case "controller.xbox.press":VirtualControllerShortcut.pressXboxButton();break;}}}
class ControllerShortcut {static buttonsCache = {};static buttonsStatus = {};static reset(index) {ControllerShortcut.buttonsCache[index] = [], ControllerShortcut.buttonsStatus[index] = [];}static handle(gamepad) {let controllerSettings = window.BX_STREAM_SETTINGS.controllers[gamepad.id];if (!controllerSettings) return !1;let actions = controllerSettings.shortcuts;if (!actions) return !1;let gamepadIndex = gamepad.index;ControllerShortcut.buttonsCache[gamepadIndex] = ControllerShortcut.buttonsStatus[gamepadIndex].slice(0), ControllerShortcut.buttonsStatus[gamepadIndex] = [];let pressed = [], otherButtonPressed = !1, entries = gamepad.buttons.entries(), index, button;for ([index, button] of entries)if (button.pressed && index !== 16) {if (otherButtonPressed = !0, pressed[index] = !0, actions[index] && !ControllerShortcut.buttonsCache[gamepadIndex][index]) {let idx = index;setTimeout(() => ShortcutHandler.runAction(actions[idx]), 0);}}return ControllerShortcut.buttonsStatus[gamepadIndex] = pressed, otherButtonPressed;}}
var FeatureGates = {PwaPrompt: !1,EnableWifiWarnings: !1,EnableUpdateRequiredPage: !1,ShowForcedUpdateScreen: !1,EnableTakControlResizing: !0,EnableLazyLoadedHome: !1,EnableRemotePlay: getGlobalPref("xhome.enabled")}, nativeMkbMode = getGlobalPref("nativeMkb.mode");
var FeatureGates = {PwaPrompt: !1,EnableWifiWarnings: !1,EnableUpdateRequiredPage: !1,ShowForcedUpdateScreen: !1,EnableTakControlResizing: !0,EnableLazyLoadedHome: !1,EnableRemotePlay: getGlobalPref("xhome.enabled"),EnableConsoles: getGlobalPref("xhome.enabled")}, nativeMkbMode = getGlobalPref("nativeMkb.mode");
if (nativeMkbMode !== "default") FeatureGates.EnableMouseAndKeyboard = nativeMkbMode === "on";
var blockFeatures = getGlobalPref("block.features");
if (blockFeatures.includes("chat")) FeatureGates.EnableGuideChatTab = !1;
@ -294,6 +295,7 @@ SettingsManager.getInstance();
if (window.location.pathname.includes("/auth/msa")) {let nativePushState = window.history.pushState;throw window.history.pushState = function(...args) {let url = args[2];if (url && (url.startsWith("/play") || url.substring(6).startsWith("/play"))) {console.log("Redirecting to xbox.com/play"), window.stop(), window.location.href = "https://www.xbox.com" + url;return;}return nativePushState.apply(this, arguments);}, new Error("[Better xCloud] Refreshing the page after logging in");}
BxLogger.info("readyState", document.readyState);
if (BX_FLAGS.SafariWorkaround && document.readyState !== "loading") {window.stop();let css = "";css += '.bx-reload-overlay{position:fixed;top:0;bottom:0;left:0;right:0;display:flex;align-items:center;background:rgba(0,0,0,0.8);z-index:9999;color:#fff;text-align:center;font-weight:400;font-family:"Segoe UI",Arial,Helvetica,sans-serif;font-size:1.3rem}.bx-reload-overlay *:focus{outline:none !important}.bx-reload-overlay > div{margin:0 auto}.bx-reload-overlay a{text-decoration:none;display:inline-block;background:#107c10;color:#fff;border-radius:4px;padding:6px}';let isSafari = UserAgent.isSafari(), $secondaryAction;if (isSafari) $secondaryAction = CE("p", !1, t("settings-reloading"));else $secondaryAction = CE("a", {href: "https://better-xcloud.github.io/troubleshooting",target: "_blank"}, "🤓 " + t("how-to-fix"));let $fragment = document.createDocumentFragment();throw $fragment.appendChild(CE("style", !1, css)), $fragment.appendChild(CE("div", {class: "bx-reload-overlay"}, CE("div", !1, CE("p", !1, t("load-failed-message")), $secondaryAction))), document.documentElement.appendChild($fragment), isSafari && window.location.reload(!0), new Error("[Better xCloud] Executing workaround for Safari");}
if (!window.location.pathname.match(/^\/[a-zA-Z]{2}-[a-zA-Z]{2}\/play/)) throw new Error("[Better xCloud] Not xCloud page");
window.addEventListener("load", (e) => {window.setTimeout(() => {if (document.body.classList.contains("legacyBackground")) window.stop(), window.location.reload(!0);}, 3000);});
document.addEventListener("readystatechange", (e) => {if (document.readyState !== "interactive") return;if (STATES.isSignedIn = !!window.xbcUser?.isSignedIn, STATES.isSignedIn) RemotePlayManager.getInstance()?.initialize();if (getGlobalPref("ui.hideSections").includes("friends") || getGlobalPref("block.features").includes("friends")) {let $parent = document.querySelector("div[class*=PlayWithFriendsSkeleton]")?.closest("div[class*=HomePage-module]");$parent && ($parent.style.display = "none");}preloadFonts();});
window.BX_EXPOSED = BxExposed;

View File

@ -10,11 +10,11 @@
"build": "build.ts"
},
"devDependencies": {
"@types/bun": "^1.2.6",
"@types/node": "^22.13.13",
"@types/bun": "^1.2.9",
"@types/node": "^22.14.1",
"@types/stylus": "^0.48.43",
"@webgpu/types": "^0.1.59",
"eslint": "^9.23.0",
"@webgpu/types": "^0.1.60",
"eslint": "^9.24.0",
"eslint-plugin-compat": "^6.0.2",
"stylus": "^0.64.0"
},

View File

@ -7,6 +7,7 @@
// @license MIT
// @match https://www.xbox.com/*/play*
// @match https://www.xbox.com/*/auth/msa?*loggedIn*
// @exclude https://www.xbox.com/*/xbox-game-pass/play-day-one
// @run-at document-start
// @grant none
// @updateURL https://raw.githubusercontent.com/redphx/better-xcloud/typescript/dist/better-xcloud.meta.js

View File

@ -147,6 +147,12 @@ if (isFullVersion() && BX_FLAGS.SafariWorkaround && document.readyState !== 'loa
throw new Error('[Better xCloud] Executing workaround for Safari');
}
// Make sure it only run on /play
if (!window.location.pathname.match(/^\/[a-zA-Z]{2}-[a-zA-Z]{2}\/play/)) {
throw new Error('[Better xCloud] Not xCloud page');
}
window.addEventListener('load', e => {
// Automatically reload the page when running into the "We are sorry..." error message
window.setTimeout(() => {

View File

@ -903,7 +903,7 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
// Disable long touch activating context menu
disableTouchContextMenu(str: string) {
let index = str.indexOf('"ContextualCardActions-module__container');
let index = str.indexOf('arguments.length>2&&void 0!==arguments[2]?arguments[2]:500;');
index >= 0 && (index = str.indexOf('addEventListener("touchstart"', index));
index >= 0 && (index = PatcherUtils.lastIndexOf(str, 'return ', index, 50));
if (index < 0) {

View File

@ -11,6 +11,7 @@ export let FeatureGates: { [key: string]: boolean } = {
EnableTakControlResizing: true, // Experimenting
EnableLazyLoadedHome: false,
EnableRemotePlay: getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED),
EnableConsoles: getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED),
};
// Enable Native Mouse & Keyboard