Update dist

This commit is contained in:
redphx 2024-09-30 17:12:22 +07:00
parent bd58355ef5
commit 086afafedf
2 changed files with 7221 additions and 206 deletions

6944
dist/better-xcloud.lite.user.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
// ==UserScript==
// @name Better xCloud
// @namespace https://github.com/redphx
// @version 5.7.7
// @version 5.7.8-beta
// @description Improve Xbox Cloud Gaming (xCloud) experience
// @author redphx
// @license MIT
@ -120,7 +120,7 @@ function deepClone(obj) {
if (!obj) return {};
return JSON.parse(JSON.stringify(obj));
}
var SCRIPT_VERSION = "5.7.7", AppInterface = window.AppInterface;
var SCRIPT_VERSION = "5.7.8-beta", 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,
@ -995,6 +995,7 @@ class BaseSettingsStore {
let settingId;
for (settingId in definitions) {
const setting = definitions[settingId];
if (typeof setting.requiredVariants === "string") setting.requiredVariants = [setting.requiredVariants];
setting.ready && setting.ready.call(this, setting);
}
this.definitions = definitions, this._settings = null;
@ -1016,7 +1017,9 @@ class BaseSettingsStore {
debugger;
return;
}
if (checkUnsupported && this.definitions[key].unsupported) return this.definitions[key].default;
const definition = this.definitions[key];
if (definition.requiredVariants && !definition.requiredVariants.includes(SCRIPT_VARIANT)) return definition.default;
if (checkUnsupported && definition.unsupported) return definition.default;
if (!(key in this.settings)) this.settings[key] = this.validateValue(key, null);
return this.settings[key];
}
@ -1183,6 +1186,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
screenshot_apply_filters: {
requiredVariants: "full",
label: t("screenshot-apply-filters"),
default: !1
},
@ -1195,12 +1199,14 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
stream_combine_sources: {
requiredVariants: "full",
label: t("combine-audio-video-streams"),
default: !1,
experimental: !0,
note: t("combine-audio-video-streams-summary")
},
stream_touch_controller: {
requiredVariants: "full",
label: t("tc-availability"),
default: "all",
options: {
@ -1214,11 +1220,13 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
stream_touch_controller_auto_off: {
requiredVariants: "full",
label: t("tc-auto-off"),
default: !1,
unsupported: !STATES.userAgent.capabilities.touch
},
stream_touch_controller_default_opacity: {
requiredVariants: "full",
type: "number-stepper",
label: t("tc-default-opacity"),
default: 100,
@ -1233,6 +1241,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
unsupported: !STATES.userAgent.capabilities.touch
},
stream_touch_controller_style_standard: {
requiredVariants: "full",
label: t("tc-standard-layout-style"),
default: "default",
options: {
@ -1243,6 +1252,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
unsupported: !STATES.userAgent.capabilities.touch
},
stream_touch_controller_style_custom: {
requiredVariants: "full",
label: t("tc-custom-layout-style"),
default: "default",
options: {
@ -1256,14 +1266,17 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
mkb_hide_idle_cursor: {
requiredVariants: "full",
label: t("hide-idle-cursor"),
default: !1
},
stream_disable_feedback_dialog: {
requiredVariants: "full",
label: t("disable-post-stream-feedback-dialog"),
default: !1
},
bitrate_video_max: {
requiredVariants: "full",
type: "number-stepper",
label: t("bitrate-video-maximum"),
note: "⚠️ " + t("unexpected-behavior"),
@ -1283,6 +1296,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
game_bar_position: {
requiredVariants: "full",
label: t("position"),
default: "bottom-left",
options: {
@ -1292,6 +1306,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
local_co_op_enabled: {
requiredVariants: "full",
label: t("enable-local-co-op-support"),
default: !1,
note: CE("a", {
@ -1304,13 +1319,16 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !0
},
controller_enable_shortcuts: {
requiredVariants: "full",
default: !1
},
controller_enable_vibration: {
requiredVariants: "full",
label: t("controller-vibration"),
default: !0
},
controller_device_vibration: {
requiredVariants: "full",
label: t("device-vibration"),
default: "off",
options: {
@ -1320,6 +1338,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
controller_vibration_intensity: {
requiredVariants: "full",
label: t("vibration-intensity"),
type: "number-stepper",
default: 100,
@ -1332,6 +1351,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
mkb_enabled: {
requiredVariants: "full",
label: t("enable-mkb"),
default: !1,
unsupported: (() => {
@ -1349,6 +1369,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
native_mkb_enabled: {
requiredVariants: "full",
label: t("native-mkb"),
default: "default",
options: {
@ -1363,6 +1384,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
native_mkb_scroll_x_sensitivity: {
requiredVariants: "full",
label: t("horizontal-scroll-sensitivity"),
type: "number-stepper",
default: 0,
@ -1378,6 +1400,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
native_mkb_scroll_y_sensitivity: {
requiredVariants: "full",
label: t("vertical-scroll-sensitivity"),
type: "number-stepper",
default: 0,
@ -1393,9 +1416,11 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
mkb_default_preset_id: {
requiredVariants: "full",
default: 0
},
mkb_absolute_mouse: {
requiredVariants: "full",
default: !1
},
reduce_animations: {
@ -1403,6 +1428,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
ui_loading_screen_game_art: {
requiredVariants: "full",
label: t("show-game-art"),
default: !0
},
@ -1424,6 +1450,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: BX_FLAGS.DeviceInfo.deviceType !== "unknown"
},
ui_layout: {
requiredVariants: "full",
label: t("layout"),
default: "default",
options: {
@ -1437,10 +1464,12 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
ui_home_context_menu_disabled: {
requiredVariants: "full",
label: t("disable-home-context-menu"),
default: STATES.browser.capabilities.touch
},
ui_hide_sections: {
requiredVariants: "full",
label: t("hide-sections"),
default: [],
multipleOptions: {
@ -1456,6 +1485,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
ui_game_card_show_wait_time: {
requiredVariants: "full",
label: t("show-wait-time-in-game-card"),
default: !1
},
@ -1585,6 +1615,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
audio_enable_volume_control: {
requiredVariants: "full",
label: t("enable-volume-control"),
default: !1
},
@ -1662,10 +1693,12 @@ class GlobalSettingsStorage extends BaseSettingsStore {
default: !1
},
xhome_enabled: {
requiredVariants: "full",
label: t("enable-remote-play-feature"),
default: !1
},
xhome_resolution: {
requiredVariants: "full",
default: "1080p",
options: {
"1080p": "1080p",
@ -1673,6 +1706,7 @@ class GlobalSettingsStorage extends BaseSettingsStore {
}
},
game_fortnite_force_console: {
requiredVariants: "full",
label: "🎮 " + t("fortnite-force-console-version"),
default: !1,
note: t("fortnite-allow-stw-mode")
@ -1699,7 +1733,7 @@ class Screenshot {
if ($canvas) $canvas.width = width, $canvas.height = height;
}
static updateCanvasFilters(filters) {
Screenshot.#canvasContext.filter = filters;
Screenshot.#canvasContext && (Screenshot.#canvasContext.filter = filters);
}
static #onAnimationEnd(e) {
e.target.classList.remove("bx-taking-screenshot");
@ -4065,12 +4099,14 @@ class SettingsNavigationDialog extends NavigationDialog {
"stream_combine_sources"
]
}, {
requiredVariants: "full",
group: "co-op",
label: t("local-co-op"),
items: [
"local_co_op_enabled"
]
}, {
requiredVariants: "full",
group: "mkb",
label: t("mouse-and-keyboard"),
items: [
@ -4079,6 +4115,7 @@ class SettingsNavigationDialog extends NavigationDialog {
"mkb_hide_idle_cursor"
]
}, {
requiredVariants: "full",
group: "touch-control",
label: t("touch-controller"),
note: !STATES.userAgent.capabilities.touch ? "⚠️ " + t("device-unsupported-touch") : null,
@ -4107,6 +4144,7 @@ class SettingsNavigationDialog extends NavigationDialog {
"ui_hide_sections"
]
}, {
requiredVariants: "full",
group: "game-bar",
label: t("game-bar"),
items: [
@ -4193,6 +4231,7 @@ class SettingsNavigationDialog extends NavigationDialog {
]
}];
TAB_DISPLAY_ITEMS = [{
requiredVariants: "full",
group: "audio",
label: t("audio"),
helpUrl: "https://better-xcloud.github.io/ingame-features/#audio",
@ -4309,6 +4348,7 @@ class SettingsNavigationDialog extends NavigationDialog {
content: MkbRemapper.INSTANCE.render()
}];
TAB_NATIVE_MKB_ITEMS = [{
requiredVariants: "full",
group: "native-mkb",
label: t("native-mkb"),
items: [{
@ -4324,6 +4364,7 @@ class SettingsNavigationDialog extends NavigationDialog {
}]
}];
TAB_SHORTCUTS_ITEMS = [{
requiredVariants: "full",
group: "controller-shortcuts",
label: t("controller-shortcuts"),
content: ControllerShortcut.renderSettings()
@ -4383,22 +4424,26 @@ class SettingsNavigationDialog extends NavigationDialog {
{
icon: BxIcon.CONTROLLER,
group: "controller",
items: this.TAB_CONTROLLER_ITEMS
items: this.TAB_CONTROLLER_ITEMS,
requiredVariants: "full"
},
getPref("mkb_enabled") && {
icon: BxIcon.VIRTUAL_CONTROLLER,
group: "mkb",
items: this.TAB_VIRTUAL_CONTROLLER_ITEMS
items: this.TAB_VIRTUAL_CONTROLLER_ITEMS,
requiredVariants: "full"
},
AppInterface && getPref("native_mkb_enabled") === "on" && {
icon: BxIcon.NATIVE_MKB,
group: "native-mkb",
items: this.TAB_NATIVE_MKB_ITEMS
items: this.TAB_NATIVE_MKB_ITEMS,
requiredVariants: "full"
},
{
icon: BxIcon.COMMAND,
group: "shortcuts",
items: this.TAB_SHORTCUTS_ITEMS
items: this.TAB_SHORTCUTS_ITEMS,
requiredVariants: "full"
},
{
icon: BxIcon.STREAM_STATS,
@ -4457,6 +4502,10 @@ class SettingsNavigationDialog extends NavigationDialog {
if (!(prefKey in this.suggestedSettings.default)) this.suggestedSettings.default[prefKey] = getPrefDefinition(prefKey).default;
}
}
isSupportedVariant(requiredVariants) {
if (typeof requiredVariants === "undefined") return !0;
return requiredVariants = typeof requiredVariants === "string" ? [requiredVariants] : requiredVariants, requiredVariants.includes(SCRIPT_VARIANT);
}
async renderSuggestions(e) {
const $btnSuggest = e.target.closest("div");
$btnSuggest.toggleAttribute("bx-open");
@ -4645,6 +4694,7 @@ class SettingsNavigationDialog extends NavigationDialog {
}
let prefDefinition = null;
if (pref) prefDefinition = getPrefDefinition(pref);
if (prefDefinition && !this.isSupportedVariant(prefDefinition.requiredVariants)) return;
let label = prefDefinition?.label || setting.label, note = prefDefinition?.note || setting.note;
const experimental = prefDefinition?.experimental || setting.experimental;
if (settingTabContent.label && setting.pref) {
@ -4718,6 +4768,7 @@ class SettingsNavigationDialog extends NavigationDialog {
});
for (let settingTab of this.SETTINGS_UI) {
if (!settingTab) continue;
if (!this.isSupportedVariant(settingTab.requiredVariants)) continue;
if (settingTab.group !== "global" && !this.renderFullSettings) continue;
const $svg = this.renderTab(settingTab);
$tabs.appendChild($svg);
@ -4727,13 +4778,17 @@ class SettingsNavigationDialog extends NavigationDialog {
});
for (let settingTabContent of settingTab.items) {
if (settingTabContent === !1) continue;
if (!this.isSupportedVariant(settingTabContent.requiredVariants)) continue;
if (!this.renderFullSettings && settingTab.group === "global" && settingTabContent.group !== "general" && settingTabContent.group !== "footer") continue;
let label = settingTabContent.label;
if (label === t("better-xcloud")) label += " " + SCRIPT_VERSION, label = createButton({
if (label === t("better-xcloud")) {
if (label += " " + SCRIPT_VERSION, SCRIPT_VARIANT === "lite") label += " (Lite)";
label = createButton({
label,
url: "https://github.com/redphx/better-xcloud/releases",
style: 1024 | 8 | 32
});
}
if (label) {
const $title = CE("h2", {
_nearby: {
@ -5908,6 +5963,211 @@ class LoadingScreen {
LoadingScreen.#$bgStyle && (LoadingScreen.#$bgStyle.textContent = ""), LoadingScreen.#$waitTimeBox && LoadingScreen.#$waitTimeBox.classList.add("bx-gone"), LoadingScreen.#waitTimeInterval && clearInterval(LoadingScreen.#waitTimeInterval), LoadingScreen.#waitTimeInterval = null;
}
}
class TrueAchievements {
static $link = createButton({
label: t("true-achievements"),
url: "#",
icon: BxIcon.TRUE_ACHIEVEMENTS,
style: 32 | 4 | 64 | 2048,
onClick: TrueAchievements.onClick
});
static $button = createButton({
label: t("true-achievements"),
title: t("true-achievements"),
icon: BxIcon.TRUE_ACHIEVEMENTS,
style: 32,
onClick: TrueAchievements.onClick
});
static onClick(e) {
e.preventDefault();
const dataset = TrueAchievements.$link.dataset;
TrueAchievements.open(!0, dataset.xboxTitleId, dataset.id), window.BX_EXPOSED.dialogRoutes?.closeAll();
}
static $hiddenLink = CE("a", {
target: "_blank"
});
static updateIds(xboxTitleId, id2) {
const { $link, $button } = TrueAchievements;
if (clearDataSet($link), clearDataSet($button), xboxTitleId) $link.dataset.xboxTitleId = xboxTitleId, $button.dataset.xboxTitleId = xboxTitleId;
if (id2) $link.dataset.id = id2, $button.dataset.id = id2;
}
static injectAchievementsProgress($elm) {
if (SCRIPT_VARIANT !== "full") return;
const $parent = $elm.parentElement, $div = CE("div", {
class: "bx-guide-home-achievements-progress"
}, $elm);
let xboxTitleId;
try {
const $container = $parent.closest("div[class*=AchievementsPreview-module__container]");
if ($container) xboxTitleId = getReactProps($container).children.props.data.data.xboxTitleId;
} catch (e) {}
if (!xboxTitleId) xboxTitleId = TrueAchievements.getStreamXboxTitleId();
if (typeof xboxTitleId !== "undefined") xboxTitleId = xboxTitleId.toString();
if (TrueAchievements.updateIds(xboxTitleId), document.documentElement.dataset.xdsPlatform === "tv") $div.appendChild(TrueAchievements.$link);
else $div.appendChild(TrueAchievements.$button);
$parent.appendChild($div);
}
static injectAchievementDetailPage($parent) {
if (SCRIPT_VARIANT !== "full") return;
const props = getReactProps($parent);
if (!props) return;
try {
const achievementList = props.children.props.data.data, $header = $parent.querySelector("div[class*=AchievementDetailHeader]"), achievementName = getReactProps($header).children[0].props.achievementName;
let id2, xboxTitleId;
for (let achiev of achievementList)
if (achiev.name === achievementName) {
id2 = achiev.id, xboxTitleId = achiev.title.id;
break;
}
if (id2) TrueAchievements.updateIds(xboxTitleId, id2), $parent.appendChild(TrueAchievements.$link);
} catch (e) {}
}
static getStreamXboxTitleId() {
return STATES.currentStream.xboxTitleId || STATES.currentStream.titleInfo?.details.xboxTitleId;
}
static open(override, xboxTitleId, id2) {
if (!xboxTitleId || xboxTitleId === "undefined") xboxTitleId = TrueAchievements.getStreamXboxTitleId();
if (AppInterface && AppInterface.openTrueAchievementsLink) {
AppInterface.openTrueAchievementsLink(override, xboxTitleId?.toString(), id2?.toString());
return;
}
let url = "https://www.trueachievements.com";
if (xboxTitleId) {
if (url += `/deeplink/${xboxTitleId}`, id2) url += `/${id2}`;
}
TrueAchievements.$hiddenLink.href = url, TrueAchievements.$hiddenLink.click();
}
}
class GuideMenu {
static #BUTTONS = {
scriptSettings: createButton({
label: t("better-xcloud"),
style: 64 | 32 | 1,
onClick: (e) => {
window.addEventListener(BxEvent.XCLOUD_DIALOG_DISMISSED, (e2) => {
setTimeout(() => SettingsNavigationDialog.getInstance().show(), 50);
}, { once: !0 }), GuideMenu.#closeGuideMenu();
}
}),
closeApp: AppInterface && createButton({
icon: BxIcon.POWER,
label: t("close-app"),
title: t("close-app"),
style: 64 | 32 | 2,
onClick: (e) => {
AppInterface.closeApp();
},
attributes: {
"data-state": "normal"
}
}),
reloadPage: createButton({
icon: BxIcon.REFRESH,
label: t("reload-page"),
title: t("reload-page"),
style: 64 | 32,
onClick: (e) => {
if (STATES.isPlaying) confirm(t("confirm-reload-stream")) && window.location.reload();
else window.location.reload();
GuideMenu.#closeGuideMenu();
}
}),
backToHome: createButton({
icon: BxIcon.HOME,
label: t("back-to-home"),
title: t("back-to-home"),
style: 64 | 32,
onClick: (e) => {
confirm(t("back-to-home-confirm")) && (window.location.href = window.location.href.substring(0, 31)), GuideMenu.#closeGuideMenu();
},
attributes: {
"data-state": "playing"
}
})
};
static #$renderedButtons;
static #closeGuideMenu() {
if (window.BX_EXPOSED.dialogRoutes) {
window.BX_EXPOSED.dialogRoutes.closeAll();
return;
}
const $btnClose = document.querySelector("#gamepass-dialog-root button[class^=Header-module__closeButton]");
$btnClose && $btnClose.click();
}
static #renderButtons() {
if (GuideMenu.#$renderedButtons) return GuideMenu.#$renderedButtons;
const $div = CE("div", {
class: "bx-guide-home-buttons"
}), buttons = [
GuideMenu.#BUTTONS.scriptSettings,
[
GuideMenu.#BUTTONS.backToHome,
GuideMenu.#BUTTONS.reloadPage,
GuideMenu.#BUTTONS.closeApp
]
];
for (let $button of buttons) {
if (!$button) continue;
if ($button instanceof HTMLElement) $div.appendChild($button);
else if (Array.isArray($button)) {
const $wrapper = CE("div", {});
for (let $child of $button)
$child && $wrapper.appendChild($child);
$div.appendChild($wrapper);
}
}
return GuideMenu.#$renderedButtons = $div, $div;
}
static #injectHome($root, isPlaying = !1) {
{
const $achievementsProgress = $root.querySelector("button[class*=AchievementsButton-module__progressBarContainer]");
if ($achievementsProgress) TrueAchievements.injectAchievementsProgress($achievementsProgress);
}
let $target = null;
if (isPlaying) {
$target = $root.querySelector("a[class*=QuitGameButton]");
const $btnXcloudHome = $root.querySelector("div[class^=HomeButtonWithDivider]");
$btnXcloudHome && ($btnXcloudHome.style.display = "none");
} else {
const $dividers = $root.querySelectorAll("div[class*=Divider-module__divider]");
if ($dividers) $target = $dividers[$dividers.length - 1];
}
if (!$target) return !1;
const $buttons = GuideMenu.#renderButtons();
$buttons.dataset.isPlaying = isPlaying.toString(), $target.insertAdjacentElement("afterend", $buttons);
}
static async#onShown(e) {
if (e.where === "home") {
const $root = document.querySelector("#gamepass-dialog-root div[role=dialog] div[role=tabpanel] div[class*=HomeLandingPage]");
$root && GuideMenu.#injectHome($root, STATES.isPlaying);
}
}
static addEventListeners() {
window.addEventListener(BxEvent.XCLOUD_GUIDE_MENU_SHOWN, GuideMenu.#onShown);
}
static observe($addedElm) {
const className = $addedElm.className;
if (className.includes("AchievementsButton-module__progressBarContainer")) {
TrueAchievements.injectAchievementsProgress($addedElm);
return;
}
if (!className.startsWith("NavigationAnimation") && !className.startsWith("DialogRoutes") && !className.startsWith("Dialog-module__container")) return;
{
const $achievDetailPage = $addedElm.querySelector("div[class*=AchievementDetailPage]");
if ($achievDetailPage) {
TrueAchievements.injectAchievementDetailPage($achievDetailPage);
return;
}
}
const $selectedTab = $addedElm.querySelector("div[class^=NavigationMenu] button[aria-selected=true");
if ($selectedTab) {
let $elm = $selectedTab, index;
for (index = 0;$elm = $elm?.previousElementSibling; index++)
;
if (index === 0) BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, { where: "home" });
}
}
}
var StreamBadgeIcon = {
playtime: BxIcon.PLAYTIME,
video: BxIcon.DISPLAY,
@ -6929,79 +7189,6 @@ class MicrophoneAction extends BaseGameBarAction {
this.visible = !1, this.$content.classList.add("bx-gone"), this.$content.setAttribute("data-enabled", "false");
}
}
class TrueAchievements {
static $link = createButton({
label: t("true-achievements"),
url: "#",
icon: BxIcon.TRUE_ACHIEVEMENTS,
style: 32 | 4 | 64 | 2048,
onClick: TrueAchievements.onClick
});
static $button = createButton({
label: t("true-achievements"),
title: t("true-achievements"),
icon: BxIcon.TRUE_ACHIEVEMENTS,
style: 32,
onClick: TrueAchievements.onClick
});
static onClick(e) {
e.preventDefault();
const dataset = TrueAchievements.$link.dataset;
TrueAchievements.open(!0, dataset.xboxTitleId, dataset.id), window.BX_EXPOSED.dialogRoutes.closeAll();
}
static $hiddenLink = CE("a", {
target: "_blank"
});
static updateIds(xboxTitleId, id2) {
const { $link, $button } = TrueAchievements;
if (clearDataSet($link), clearDataSet($button), xboxTitleId) $link.dataset.xboxTitleId = xboxTitleId, $button.dataset.xboxTitleId = xboxTitleId;
if (id2) $link.dataset.id = id2, $button.dataset.id = id2;
}
static injectAchievementsProgress($elm) {
const $parent = $elm.parentElement, $div = CE("div", {
class: "bx-guide-home-achievements-progress"
}, $elm);
let xboxTitleId;
try {
const $container = $parent.closest("div[class*=AchievementsPreview-module__container]");
if ($container) xboxTitleId = getReactProps($container).children.props.data.data.xboxTitleId;
} catch (e) {}
if (!xboxTitleId) xboxTitleId = TrueAchievements.getStreamXboxTitleId();
if (typeof xboxTitleId !== "undefined") xboxTitleId = xboxTitleId.toString();
if (TrueAchievements.updateIds(xboxTitleId), document.documentElement.dataset.xdsPlatform === "tv") $div.appendChild(TrueAchievements.$link);
else $div.appendChild(TrueAchievements.$button);
$parent.appendChild($div);
}
static injectAchievementDetailPage($parent) {
const props = getReactProps($parent);
if (!props) return;
try {
const achievementList = props.children.props.data.data, $header = $parent.querySelector("div[class*=AchievementDetailHeader]"), achievementName = getReactProps($header).children[0].props.achievementName;
let id2, xboxTitleId;
for (let achiev of achievementList)
if (achiev.name === achievementName) {
id2 = achiev.id, xboxTitleId = achiev.title.id;
break;
}
if (id2) TrueAchievements.updateIds(xboxTitleId, id2), $parent.appendChild(TrueAchievements.$link);
} catch (e) {}
}
static getStreamXboxTitleId() {
return STATES.currentStream.xboxTitleId || STATES.currentStream.titleInfo?.details.xboxTitleId;
}
static open(override, xboxTitleId, id2) {
if (!xboxTitleId || xboxTitleId === "undefined") xboxTitleId = TrueAchievements.getStreamXboxTitleId();
if (AppInterface && AppInterface.openTrueAchievementsLink) {
AppInterface.openTrueAchievementsLink(override, xboxTitleId?.toString(), id2?.toString());
return;
}
let url = "https://www.trueachievements.com";
if (xboxTitleId) {
if (url += `/deeplink/${xboxTitleId}`, id2) url += `/${id2}`;
}
TrueAchievements.$hiddenLink.href = url, TrueAchievements.$hiddenLink.click();
}
}
class TrueAchievementsAction extends BaseGameBarAction {
$content;
constructor() {
@ -7113,124 +7300,6 @@ class GameBar {
action.reset();
}
}
class GuideMenu {
static #BUTTONS = {
scriptSettings: createButton({
label: t("better-xcloud"),
style: 64 | 32 | 1,
onClick: (e) => {
window.addEventListener(BxEvent.XCLOUD_DIALOG_DISMISSED, (e2) => {
setTimeout(() => SettingsNavigationDialog.getInstance().show(), 50);
}, { once: !0 }), window.BX_EXPOSED.dialogRoutes.closeAll();
}
}),
closeApp: AppInterface && createButton({
icon: BxIcon.POWER,
label: t("close-app"),
title: t("close-app"),
style: 64 | 32 | 2,
onClick: (e) => {
AppInterface.closeApp();
},
attributes: {
"data-state": "normal"
}
}),
reloadPage: createButton({
icon: BxIcon.REFRESH,
label: t("reload-page"),
title: t("reload-page"),
style: 64 | 32,
onClick: (e) => {
if (STATES.isPlaying) confirm(t("confirm-reload-stream")) && window.location.reload();
else window.location.reload();
window.BX_EXPOSED.dialogRoutes.closeAll();
}
}),
backToHome: createButton({
icon: BxIcon.HOME,
label: t("back-to-home"),
title: t("back-to-home"),
style: 64 | 32,
onClick: (e) => {
confirm(t("back-to-home-confirm")) && (window.location.href = window.location.href.substring(0, 31)), window.BX_EXPOSED.dialogRoutes.closeAll();
},
attributes: {
"data-state": "playing"
}
})
};
static #$renderedButtons;
static #renderButtons() {
if (GuideMenu.#$renderedButtons) return GuideMenu.#$renderedButtons;
const $div = CE("div", {
class: "bx-guide-home-buttons"
}), buttons = [
GuideMenu.#BUTTONS.scriptSettings,
[
GuideMenu.#BUTTONS.backToHome,
GuideMenu.#BUTTONS.reloadPage,
GuideMenu.#BUTTONS.closeApp
]
];
for (let $button of buttons) {
if (!$button) continue;
if ($button instanceof HTMLElement) $div.appendChild($button);
else if (Array.isArray($button)) {
const $wrapper = CE("div", {});
for (let $child of $button)
$child && $wrapper.appendChild($child);
$div.appendChild($wrapper);
}
}
return GuideMenu.#$renderedButtons = $div, $div;
}
static #injectHome($root, isPlaying = !1) {
const $achievementsProgress = $root.querySelector("button[class*=AchievementsButton-module__progressBarContainer]");
if ($achievementsProgress) TrueAchievements.injectAchievementsProgress($achievementsProgress);
let $target = null;
if (isPlaying) {
$target = $root.querySelector("a[class*=QuitGameButton]");
const $btnXcloudHome = $root.querySelector("div[class^=HomeButtonWithDivider]");
$btnXcloudHome && ($btnXcloudHome.style.display = "none");
} else {
const $dividers = $root.querySelectorAll("div[class*=Divider-module__divider]");
if ($dividers) $target = $dividers[$dividers.length - 1];
}
if (!$target) return !1;
const $buttons = GuideMenu.#renderButtons();
$buttons.dataset.isPlaying = isPlaying.toString(), $target.insertAdjacentElement("afterend", $buttons);
}
static async#onShown(e) {
if (e.where === "home") {
const $root = document.querySelector("#gamepass-dialog-root div[role=dialog] div[role=tabpanel] div[class*=HomeLandingPage]");
$root && GuideMenu.#injectHome($root, STATES.isPlaying);
}
}
static addEventListeners() {
window.addEventListener(BxEvent.XCLOUD_GUIDE_MENU_SHOWN, GuideMenu.#onShown);
}
static observe($addedElm) {
const className = $addedElm.className;
if (className.includes("AchievementsButton-module__progressBarContainer")) {
TrueAchievements.injectAchievementsProgress($addedElm);
return;
}
if (!className.startsWith("NavigationAnimation") && !className.startsWith("DialogRoutes") && !className.startsWith("Dialog-module__container")) return;
const $achievDetailPage = $addedElm.querySelector("div[class*=AchievementDetailPage]");
if ($achievDetailPage) {
TrueAchievements.injectAchievementDetailPage($achievDetailPage);
return;
}
const $selectedTab = $addedElm.querySelector("div[class^=NavigationMenu] button[aria-selected=true");
if ($selectedTab) {
let $elm = $selectedTab, index;
for (index = 0;$elm = $elm?.previousElementSibling; index++)
;
if (index === 0) BxEvent.dispatch(window, BxEvent.XCLOUD_GUIDE_MENU_SHOWN, { where: "home" });
}
}
}
class XcloudApi {
static instance;
static getInstance() {
@ -7532,12 +7601,11 @@ function waitForRootDialog() {
observer.observe(document.documentElement, { subtree: !0, childList: !0 });
}
function main() {
if (waitForRootDialog(), patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), AppInterface && patchPointerLockApi(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking")) patchMeControl(), disableAdobeAudienceManager();
if (STATES.userAgent.capabilities.touch && TouchController.updateCustomList(), overridePreloadState(), VibrationManager.initialSetup(), BX_FLAGS.CheckForUpdate && checkForUpdate(), addCss(), Toast.setup(), getPref("game_bar_position") !== "off" && GameBar.getInstance(), Screenshot.setup(), GuideMenu.addEventListeners(), StreamBadges.setupEvents(), StreamStats.setupEvents(), EmulatedMkbHandler.setupEvents(), Patcher.init(), disablePwa(), getPref("controller_show_connection_status")) window.addEventListener("gamepadconnected", (e) => showGamepadToast(e.gamepad)), window.addEventListener("gamepaddisconnected", (e) => showGamepadToast(e.gamepad));
if (getPref("xhome_enabled")) RemotePlayManager.detect();
if (patchRtcPeerConnection(), patchRtcCodecs(), interceptHttpRequests(), patchVideoApi(), patchCanvasContext(), AppInterface && patchPointerLockApi(), getPref("audio_enable_volume_control") && patchAudioContext(), getPref("block_tracking")) patchMeControl(), disableAdobeAudienceManager();
if (waitForRootDialog(), addCss(), Toast.setup(), GuideMenu.addEventListeners(), StreamBadges.setupEvents(), StreamStats.setupEvents(), getPref("game_bar_position") !== "off" && GameBar.getInstance(), Screenshot.setup(), STATES.userAgent.capabilities.touch && TouchController.updateCustomList(), overridePreloadState(), VibrationManager.initialSetup(), BX_FLAGS.CheckForUpdate && checkForUpdate(), Patcher.init(), disablePwa(), getPref("xhome_enabled")) RemotePlayManager.detect();
if (getPref("stream_touch_controller") === "all") TouchController.setup();
if (getPref("mkb_enabled") && AppInterface) STATES.pointerServerPort = AppInterface.startPointerServer() || 9269, BxLogger.info("startPointerServer", "Port", STATES.pointerServerPort.toString());
getPref("ui_game_card_show_wait_time") && GameTile.setup();
if (getPref("ui_game_card_show_wait_time") && GameTile.setup(), EmulatedMkbHandler.setupEvents(), getPref("controller_show_connection_status")) window.addEventListener("gamepadconnected", (e) => showGamepadToast(e.gamepad)), window.addEventListener("gamepaddisconnected", (e) => showGamepadToast(e.gamepad));
}
if (window.location.pathname.includes("/auth/msa")) {
const nativePushState = window.history.pushState;
@ -7606,8 +7674,11 @@ window.addEventListener(BxEvent.STREAM_PLAYING, (e) => {
const gameBar = GameBar.getInstance();
gameBar.reset(), gameBar.enable(), gameBar.showBar();
}
const $video = e.$video;
Screenshot.updateCanvasSize($video.videoWidth, $video.videoHeight), updateVideoPlayer();
{
const $video = e.$video;
Screenshot.updateCanvasSize($video.videoWidth, $video.videoHeight);
}
updateVideoPlayer();
});
window.addEventListener(BxEvent.STREAM_ERROR_PAGE, (e) => {
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED);