mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-07-06 06:11:43 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
f7266d6361 | |||
4bd96de89e | |||
4011eb402a | |||
557a38214d | |||
4648126f03 | |||
07b2e47757 | |||
cf4609d87b | |||
1ca2b771e7 | |||
fe98a1165f | |||
4777f90a53 | |||
1ea1afe4d4 |
125
dist/better-xcloud.lite.user.js
vendored
125
dist/better-xcloud.lite.user.js
vendored
File diff suppressed because one or more lines are too long
2
dist/better-xcloud.meta.js
vendored
2
dist/better-xcloud.meta.js
vendored
@ -1,5 +1,5 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Better xCloud
|
// @name Better xCloud
|
||||||
// @namespace https://github.com/redphx
|
// @namespace https://github.com/redphx
|
||||||
// @version 6.0.0
|
// @version 6.0.1
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
203
dist/better-xcloud.user.js
vendored
203
dist/better-xcloud.user.js
vendored
File diff suppressed because one or more lines are too long
@ -47,7 +47,41 @@ body[data-media-type=tv] .bx-stream-home-button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div[data-testid=media-container] {
|
div[data-testid=media-container] {
|
||||||
|
&[data-position=center] {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-position=top] {
|
||||||
|
video, canvas {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-position=bottom] {
|
||||||
|
video, canvas {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#game-stream {
|
||||||
|
video {
|
||||||
|
margin: auto;
|
||||||
|
align-self: center;
|
||||||
|
background: #000;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
align-self: center;
|
||||||
|
margin: auto;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.bx-taking-screenshot:before {
|
&.bx-taking-screenshot:before {
|
||||||
animation: bx-anim-taking-screenshot 0.5s ease;
|
animation: bx-anim-taking-screenshot 0.5s ease;
|
||||||
@ -59,21 +93,6 @@ div[data-testid=media-container] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#game-stream video {
|
|
||||||
margin: auto;
|
|
||||||
align-self: center;
|
|
||||||
background: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-stream canvas {
|
|
||||||
position: absolute;
|
|
||||||
align-self: center;
|
|
||||||
margin: auto;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gamepass-dialog-root div[class^=Guide-module__guide] {
|
#gamepass-dialog-root div[class^=Guide-module__guide] {
|
||||||
.bx-button {
|
.bx-button {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
@ -47,7 +47,7 @@ export const enum PrefKey {
|
|||||||
CONTROLLER_POLLING_RATE = 'controller.pollingRate',
|
CONTROLLER_POLLING_RATE = 'controller.pollingRate',
|
||||||
|
|
||||||
NATIVE_MKB_MODE = 'nativeMkb.mode',
|
NATIVE_MKB_MODE = 'nativeMkb.mode',
|
||||||
FORCE_NATIVE_MKB_GAMES = 'nativeMkb.forcedGames',
|
NATIVE_MKB_FORCED_GAMES = 'nativeMkb.forcedGames',
|
||||||
NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityX',
|
NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityX',
|
||||||
NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityY',
|
NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY = 'nativeMkb.scroll.sensitivityY',
|
||||||
|
|
||||||
@ -93,6 +93,7 @@ export const enum PrefKey {
|
|||||||
VIDEO_BRIGHTNESS = 'video.brightness',
|
VIDEO_BRIGHTNESS = 'video.brightness',
|
||||||
VIDEO_CONTRAST = 'video.contrast',
|
VIDEO_CONTRAST = 'video.contrast',
|
||||||
VIDEO_SATURATION = 'video.saturation',
|
VIDEO_SATURATION = 'video.saturation',
|
||||||
|
VIDEO_POSITION = 'video.position',
|
||||||
|
|
||||||
AUDIO_MIC_ON_PLAYING = 'audio.mic.onPlaying',
|
AUDIO_MIC_ON_PLAYING = 'audio.mic.onPlaying',
|
||||||
AUDIO_VOLUME_CONTROL_ENABLED = 'audio.volume.booster.enabled',
|
AUDIO_VOLUME_CONTROL_ENABLED = 'audio.volume.booster.enabled',
|
||||||
|
@ -93,6 +93,14 @@ export const enum VideoRatio {
|
|||||||
FILL = 'fill',
|
FILL = 'fill',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum VideoPosition {
|
||||||
|
CENTER = 'center',
|
||||||
|
TOP = 'top',
|
||||||
|
TOP_HALF = 'top-half',
|
||||||
|
BOTTOM = 'bottom',
|
||||||
|
BOTTOM_HALF = 'bottom-half',
|
||||||
|
}
|
||||||
|
|
||||||
export const enum StreamPlayerType {
|
export const enum StreamPlayerType {
|
||||||
VIDEO = 'default',
|
VIDEO = 'default',
|
||||||
WEBGL2 = 'webgl2',
|
WEBGL2 = 'webgl2',
|
||||||
|
16
src/index.ts
16
src/index.ts
@ -1,4 +1,4 @@
|
|||||||
import { compressCss, isFullVersion } from "@macros/build" with {type: "macro"};
|
import { compressCss, isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import "@utils/global";
|
import "@utils/global";
|
||||||
import { BxEvent } from "@utils/bx-event";
|
import { BxEvent } from "@utils/bx-event";
|
||||||
@ -16,7 +16,7 @@ import { LoadingScreen } from "@modules/loading-screen";
|
|||||||
import { MouseCursorHider } from "@modules/mkb/mouse-cursor-hider";
|
import { MouseCursorHider } from "@modules/mkb/mouse-cursor-hider";
|
||||||
import { TouchController } from "@modules/touch-controller";
|
import { TouchController } from "@modules/touch-controller";
|
||||||
import { checkForUpdate, disablePwa, productTitleToSlug } from "@utils/utils";
|
import { checkForUpdate, disablePwa, productTitleToSlug } from "@utils/utils";
|
||||||
import { Patcher } from "@modules/patcher";
|
import { Patcher } from "@/modules/patcher/patcher";
|
||||||
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
||||||
import { onHistoryChanged, patchHistoryMethod } from "@utils/history";
|
import { onHistoryChanged, patchHistoryMethod } from "@utils/history";
|
||||||
import { disableAdobeAudienceManager, patchAudioContext, patchCanvasContext, patchMeControl, patchPointerLockApi, patchRtcCodecs, patchRtcPeerConnection, patchVideoApi } from "@utils/monkey-patches";
|
import { disableAdobeAudienceManager, patchAudioContext, patchCanvasContext, patchMeControl, patchPointerLockApi, patchRtcCodecs, patchRtcPeerConnection, patchVideoApi } from "@utils/monkey-patches";
|
||||||
@ -199,7 +199,7 @@ window.addEventListener(BxEvent.XCLOUD_SERVERS_UNAVAILABLE, e => {
|
|||||||
if ($unsupportedPage) {
|
if ($unsupportedPage) {
|
||||||
SettingsDialog.getInstance().show();
|
SettingsDialog.getInstance().show();
|
||||||
}
|
}
|
||||||
}, {once: true});
|
}, { once: true });
|
||||||
|
|
||||||
window.addEventListener(BxEvent.XCLOUD_SERVERS_READY, e => {
|
window.addEventListener(BxEvent.XCLOUD_SERVERS_READY, e => {
|
||||||
STATES.isSignedIn = true;
|
STATES.isSignedIn = true;
|
||||||
@ -266,9 +266,9 @@ window.addEventListener(BxEvent.STREAM_ERROR_PAGE, e => {
|
|||||||
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED);
|
BxEvent.dispatch(window, BxEvent.STREAM_STOPPED);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, e => {
|
isFullVersion() && window.addEventListener(BxEvent.XCLOUD_RENDERING_COMPONENT, e => {
|
||||||
const component = (e as any).component;
|
const component = (e as any).component;
|
||||||
if (component === 'product-details') {
|
if (component === 'product-detail') {
|
||||||
ProductDetailsPage.injectButtons();
|
ProductDetailsPage.injectButtons();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -353,8 +353,8 @@ isFullVersion() && window.addEventListener(BxEvent.CAPTURE_SCREENSHOT, e => {
|
|||||||
function main() {
|
function main() {
|
||||||
GhPagesUtils.fetchLatestCommit();
|
GhPagesUtils.fetchLatestCommit();
|
||||||
|
|
||||||
if (getPref<NativeMkbMode>(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON) {
|
if (getPref<NativeMkbMode>(PrefKey.NATIVE_MKB_MODE) !== NativeMkbMode.OFF) {
|
||||||
const customList = getPref<string[]>(PrefKey.FORCE_NATIVE_MKB_GAMES);
|
const customList = getPref<string[]>(PrefKey.NATIVE_MKB_FORCED_GAMES);
|
||||||
BX_FLAGS.ForceNativeMkbTitles.push(...customList);
|
BX_FLAGS.ForceNativeMkbTitles.push(...customList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +406,7 @@ function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start PointerProviderServer
|
// Start PointerProviderServer
|
||||||
if (getPref(PrefKey.MKB_ENABLED) && AppInterface) {
|
if (AppInterface && (getPref(PrefKey.MKB_ENABLED) || getPref<NativeMkbMode>(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON)) {
|
||||||
STATES.pointerServerPort = AppInterface.startPointerServer() || 9269;
|
STATES.pointerServerPort = AppInterface.startPointerServer() || 9269;
|
||||||
BxLogger.info('startPointerServer', 'Port', STATES.pointerServerPort.toString());
|
BxLogger.info('startPointerServer', 'Port', STATES.pointerServerPort.toString());
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ import { ShortcutHandler } from "@/utils/shortcut-handler";
|
|||||||
|
|
||||||
|
|
||||||
export class ControllerShortcut {
|
export class ControllerShortcut {
|
||||||
private static buttonsCache: {[key: string]: boolean[]} = {};
|
private static buttonsCache: { [key: string]: boolean[] } = {};
|
||||||
private static buttonsStatus: {[key: string]: boolean[]} = {};
|
private static buttonsStatus: { [key: string]: boolean[] } = {};
|
||||||
|
|
||||||
static reset(index: number) {
|
static reset(index: number) {
|
||||||
ControllerShortcut.buttonsCache[index] = [];
|
ControllerShortcut.buttonsCache[index] = [];
|
||||||
|
@ -47,8 +47,8 @@ export class GameBar {
|
|||||||
|
|
||||||
const position = getPref<GameBarPosition>(PrefKey.GAME_BAR_POSITION);
|
const position = getPref<GameBarPosition>(PrefKey.GAME_BAR_POSITION);
|
||||||
|
|
||||||
const $gameBar = CE('div', {id: 'bx-game-bar', class: 'bx-gone', 'data-position': position},
|
const $gameBar = CE('div', { id: 'bx-game-bar', class: 'bx-gone', 'data-position': position },
|
||||||
$container = CE('div', {class: 'bx-game-bar-container bx-offscreen'}),
|
$container = CE('div', { class: 'bx-game-bar-container bx-offscreen' }),
|
||||||
createSvgIcon(position === 'bottom-left' ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT),
|
createSvgIcon(position === 'bottom-left' ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { t } from "@utils/translation";
|
|||||||
import { STATES } from "@utils/global";
|
import { STATES } from "@utils/global";
|
||||||
import { PrefKey } from "@/enums/pref-keys";
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
||||||
import { compressCss } from "@macros/build" with {type: "macro"};
|
import { compressCss } from "@macros/build" with { type: "macro" };
|
||||||
import { LoadingScreenRocket } from "@/enums/pref-values";
|
import { LoadingScreenRocket } from "@/enums/pref-values";
|
||||||
|
|
||||||
export class LoadingScreen {
|
export class LoadingScreen {
|
||||||
|
@ -95,7 +95,7 @@ export class KeyHelper {
|
|||||||
const tmp = str.split(':');
|
const tmp = str.split(':');
|
||||||
|
|
||||||
const code = tmp[0] as KeyEventInfo['code'];
|
const code = tmp[0] as KeyEventInfo['code'];
|
||||||
const modifiers = parseInt(tmp[1]);
|
const modifiers = parseInt(tmp[1] as string);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { MkbPresetKey, MouseConstant, MouseMapTo, WheelCode } from "@/enums/mkb";
|
import { MkbPresetKey, MouseConstant, MouseMapTo, WheelCode } from "@/enums/mkb";
|
||||||
import { BxEvent } from "@utils/bx-event";
|
import { BxEvent } from "@utils/bx-event";
|
||||||
@ -16,6 +16,8 @@ import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
|||||||
import { GamepadKey, GamepadStick } from "@/enums/gamepad";
|
import { GamepadKey, GamepadStick } from "@/enums/gamepad";
|
||||||
import { MkbPopup } from "./mkb-popup";
|
import { MkbPopup } from "./mkb-popup";
|
||||||
import type { MkbConvertedPresetData } from "@/types/presets";
|
import type { MkbConvertedPresetData } from "@/types/presets";
|
||||||
|
import { StreamSettings } from "@/utils/stream-settings";
|
||||||
|
import { ShortcutAction } from "@/enums/shortcut-actions";
|
||||||
|
|
||||||
const PointerToMouseButton = {
|
const PointerToMouseButton = {
|
||||||
1: 0,
|
1: 0,
|
||||||
@ -168,7 +170,7 @@ export class EmulatedMkbHandler extends MkbHandler {
|
|||||||
|
|
||||||
private popup: MkbPopup;
|
private popup: MkbPopup;
|
||||||
|
|
||||||
private STICK_MAP: {[key in GamepadKey]?: [GamepadKey[], number, number]} = {
|
private STICK_MAP: { [key in GamepadKey]?: [GamepadKey[], number, number] } = {
|
||||||
[GamepadKey.LS_LEFT]: [this.LEFT_STICK_X, 0, -1],
|
[GamepadKey.LS_LEFT]: [this.LEFT_STICK_X, 0, -1],
|
||||||
[GamepadKey.LS_RIGHT]: [this.LEFT_STICK_X, 0, 1],
|
[GamepadKey.LS_RIGHT]: [this.LEFT_STICK_X, 0, 1],
|
||||||
[GamepadKey.LS_UP]: [this.LEFT_STICK_Y, 1, -1],
|
[GamepadKey.LS_UP]: [this.LEFT_STICK_Y, 1, -1],
|
||||||
@ -529,7 +531,12 @@ export class EmulatedMkbHandler extends MkbHandler {
|
|||||||
MkbPopup.getInstance().reset();
|
MkbPopup.getInstance().reset();
|
||||||
|
|
||||||
if (AppInterface) {
|
if (AppInterface) {
|
||||||
Toast.show(t('press-key-to-toggle-mkb', {key: `<b>F8</b>`}), t('virtual-controller'), {html: true});
|
const shortcutKey = StreamSettings.findKeyboardShortcut(ShortcutAction.MKB_TOGGLE);
|
||||||
|
if (shortcutKey) {
|
||||||
|
const msg = t('press-key-to-toggle-mkb', { key: `<b>${KeyHelper.codeToKeyName(shortcutKey)}</b>` });
|
||||||
|
Toast.show(msg, t('native-mkb'), { html: true });
|
||||||
|
}
|
||||||
|
|
||||||
this.waitForMouseData(false);
|
this.waitForMouseData(false);
|
||||||
} else {
|
} else {
|
||||||
this.waitForMouseData(true);
|
this.waitForMouseData(true);
|
||||||
@ -627,7 +634,7 @@ export class EmulatedMkbHandler extends MkbHandler {
|
|||||||
this.waitForMouseData(true);
|
this.waitForMouseData(true);
|
||||||
this.mouseDataProvider?.stop();
|
this.mouseDataProvider?.stop();
|
||||||
|
|
||||||
// Toast.show(t('virtual-controller'), t('disabled'), {instant: true});
|
// Toast.show(t('virtual-controller'), t('disabled'), { instant: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
static setupEvents() {
|
static setupEvents() {
|
||||||
|
@ -182,7 +182,7 @@ export class NativeMkbHandler extends MkbHandler {
|
|||||||
window.BX_EXPOSED.stopTakRendering = true;
|
window.BX_EXPOSED.stopTakRendering = true;
|
||||||
this.waitForMouseData(false);
|
this.waitForMouseData(false);
|
||||||
|
|
||||||
Toast.show(t('native-mkb'), t('enabled'), {instant: true});
|
Toast.show(t('native-mkb'), t('enabled'), { instant: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
45
src/modules/patcher/patcher-utils.ts
Normal file
45
src/modules/patcher/patcher-utils.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import type { PatchArray, PatchName, PatchPage } from "./patcher";
|
||||||
|
|
||||||
|
export class PatcherUtils {
|
||||||
|
static indexOf(txt: string, searchString: string, startIndex: number, maxRange: number): number {
|
||||||
|
const index = txt.indexOf(searchString, startIndex);
|
||||||
|
if (index < 0 || (maxRange && index - startIndex > maxRange)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lastIndexOf(txt: string, searchString: string, startIndex: number, maxRange: number): number {
|
||||||
|
const index = txt.lastIndexOf(searchString, startIndex);
|
||||||
|
if (index < 0 || (maxRange && startIndex - index > maxRange)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static insertAt(txt: string, index: number, insertString: string): string {
|
||||||
|
return txt.substring(0, index) + insertString + txt.substring(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static replaceWith(txt: string, index: number, fromString: string, toString: string): string {
|
||||||
|
return txt.substring(0, index) + toString + txt.substring(index + fromString.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static filterPatches(patches: Array<string | false>): PatchArray {
|
||||||
|
return patches.filter((item): item is PatchName => !!item);
|
||||||
|
}
|
||||||
|
|
||||||
|
static patchBeforePageLoad(str: string, page: PatchPage): string | false {
|
||||||
|
let text = `chunkName:()=>"${page}-page",`;
|
||||||
|
if (!str.includes(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.replace('requireAsync(e){', `requireAsync(e){window.BX_EXPOSED.beforePageLoad("${page}");`);
|
||||||
|
str = str.replace('requireSync(e){', `requireSync(e){window.BX_EXPOSED.beforePageLoad("${page}");`);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { SCRIPT_VERSION, STATES } from "@utils/global";
|
import { AppInterface, SCRIPT_VERSION, STATES } from "@utils/global";
|
||||||
import { BX_FLAGS } from "@utils/bx-flags";
|
import { BX_FLAGS } from "@utils/bx-flags";
|
||||||
import { BxLogger } from "@utils/bx-logger";
|
import { BxLogger } from "@utils/bx-logger";
|
||||||
import { hashCode, renderString } from "@utils/utils";
|
import { hashCode, renderString } from "@utils/utils";
|
||||||
@ -17,39 +17,12 @@ import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
|||||||
import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
|
import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
|
||||||
import { t } from "@/utils/translation";
|
import { t } from "@/utils/translation";
|
||||||
import { NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values";
|
import { NativeMkbMode, TouchControllerMode, UiLayout, UiSection } from "@/enums/pref-values";
|
||||||
|
import { PatcherUtils } from "./patcher-utils.js";
|
||||||
|
|
||||||
type PathName = keyof typeof PATCHES;
|
export type PatchName = keyof typeof PATCHES;
|
||||||
type PatchArray = PathName[];
|
export type PatchArray = PatchName[];
|
||||||
|
export type PatchPage = 'home' | 'stream' | 'product-detail';
|
||||||
|
|
||||||
class PatcherUtils {
|
|
||||||
static indexOf(txt: string, searchString: string, startIndex: number, maxRange: number): number {
|
|
||||||
const index = txt.indexOf(searchString, startIndex);
|
|
||||||
if (index < 0 || (maxRange && index - startIndex > maxRange)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static lastIndexOf(txt: string, searchString: string, startIndex: number, maxRange: number): number {
|
|
||||||
const index = txt.lastIndexOf(searchString, startIndex);
|
|
||||||
if (index < 0 || (maxRange && startIndex - index > maxRange)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static insertAt(txt: string, index: number, insertString: string): string {
|
|
||||||
return txt.substring(0, index) + insertString + txt.substring(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
static replaceWith(txt: string, index: number, fromString: string, toString: string): string {
|
|
||||||
return txt.substring(0, index) + toString + txt.substring(index + fromString.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ENDING_CHUNKS_PATCH_NAME = 'loadingEndingChunks';
|
|
||||||
const LOG_TAG = 'Patcher';
|
const LOG_TAG = 'Patcher';
|
||||||
|
|
||||||
const PATCHES = {
|
const PATCHES = {
|
||||||
@ -314,19 +287,6 @@ logFunc(logTag, '//', logMessage);
|
|||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add patches that are only needed when start playing
|
|
||||||
loadingEndingChunks(str: string) {
|
|
||||||
let text = '"FamilySagaManager"';
|
|
||||||
if (!str.includes(text)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BxLogger.info(LOG_TAG, 'Remaining patches:', PATCH_ORDERS);
|
|
||||||
PATCH_ORDERS = PATCH_ORDERS.concat(PLAYING_PATCH_ORDERS);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Disable StreamGate
|
// Disable StreamGate
|
||||||
disableStreamGate(str: string) {
|
disableStreamGate(str: string) {
|
||||||
const index = str.indexOf('case"partially-ready":');
|
const index = str.indexOf('case"partially-ready":');
|
||||||
@ -520,6 +480,10 @@ BxEvent.dispatch(window, BxEvent.XCLOUD_POLLING_MODE_CHANGED);
|
|||||||
|
|
||||||
// Get param name
|
// Get param name
|
||||||
const params = str.substring(index, backetIndex).match(/\(([^)]+)\)/)![1];
|
const params = str.substring(index, backetIndex).match(/\(([^)]+)\)/)![1];
|
||||||
|
if (!params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const titleInfoVar = params.split(',')[0];
|
const titleInfoVar = params.split(',')[0];
|
||||||
|
|
||||||
const newCode = `
|
const newCode = `
|
||||||
@ -542,6 +506,10 @@ BxLogger.info('patchXcloudTitleInfo', ${titleInfoVar});
|
|||||||
|
|
||||||
// Get param name
|
// Get param name
|
||||||
const params = str.substring(index, backetIndex).match(/\(([^)]+)\)/)![1];
|
const params = str.substring(index, backetIndex).match(/\(([^)]+)\)/)![1];
|
||||||
|
if (!params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const configsVar = params.split(',')[1];
|
const configsVar = params.split(',')[1];
|
||||||
|
|
||||||
const newCode = `
|
const newCode = `
|
||||||
@ -719,30 +687,6 @@ true` + text;
|
|||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
|
||||||
(x.AW, {
|
|
||||||
path: V.LoginDeviceCode.path,
|
|
||||||
exact: !0,
|
|
||||||
render: () => (0, n.jsx)(qe, {
|
|
||||||
children: (0, n.jsx)(Et.R, {})
|
|
||||||
})
|
|
||||||
}, V.LoginDeviceCode.name),
|
|
||||||
|
|
||||||
const qe = e => {
|
|
||||||
let {
|
|
||||||
children: t
|
|
||||||
} = e;
|
|
||||||
const {
|
|
||||||
isTV: a,
|
|
||||||
isSupportedTVBrowser: r
|
|
||||||
} = (0, T.d)();
|
|
||||||
return a && r ? (0, n.jsx)(n.Fragment, {
|
|
||||||
children: t
|
|
||||||
}) : (0, n.jsx)(x.l_, {
|
|
||||||
to: V.Home.getLink()
|
|
||||||
})
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
enableTvRoutes(str: string) {
|
enableTvRoutes(str: string) {
|
||||||
let index = str.indexOf('.LoginDeviceCode.path,');
|
let index = str.indexOf('.LoginDeviceCode.path,');
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
@ -912,16 +856,15 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
|
|||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
// product-details-page.js#2388, 24.17.20
|
detectProductDetailPage(str: string) {
|
||||||
detectProductDetailsPage(str: string) {
|
|
||||||
let index = str.indexOf('{location:"ProductDetailPage",');
|
let index = str.indexOf('{location:"ProductDetailPage",');
|
||||||
index >= 0 && (index = PatcherUtils.lastIndexOf('return', str, index, 200));
|
index >= 0 && (index = PatcherUtils.lastIndexOf(str, 'return', index, 200));
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = str.substring(0, index) + 'BxEvent.dispatch(window, BxEvent.XCLOUD_RENDERING_COMPONENT, { component: "product-details" });' + str.substring(index);
|
str = str.substring(0, index) + 'BxEvent.dispatch(window, BxEvent.XCLOUD_RENDERING_COMPONENT, { component: "product-detail" });' + str.substring(index);
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -992,9 +935,21 @@ if (this.baseStorageKey in window.BX_EXPOSED.overrideSettings) {
|
|||||||
str = str.replace(text, '=window.BX_EXPOSED.modifyPreloadedState(window.__PRELOADED_STATE__);');
|
str = str.replace(text, '=window.BX_EXPOSED.modifyPreloadedState(window.__PRELOADED_STATE__);');
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
homePageBeforeLoad(str: string) {
|
||||||
|
return PatcherUtils.patchBeforePageLoad(str, 'home');
|
||||||
|
},
|
||||||
|
|
||||||
|
productDetailPageBeforeLoad(str: string) {
|
||||||
|
return PatcherUtils.patchBeforePageLoad(str, 'product-detail');
|
||||||
|
},
|
||||||
|
|
||||||
|
streamPageBeforeLoad(str: string) {
|
||||||
|
return PatcherUtils.patchBeforePageLoad(str, 'stream');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let PATCH_ORDERS: PatchArray = [
|
let PATCH_ORDERS = PatcherUtils.filterPatches([
|
||||||
...(getPref<NativeMkbMode>(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [
|
...(getPref<NativeMkbMode>(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON ? [
|
||||||
'enableNativeMkb',
|
'enableNativeMkb',
|
||||||
'exposeInputSink',
|
'exposeInputSink',
|
||||||
@ -1015,10 +970,13 @@ let PATCH_ORDERS: PatchArray = [
|
|||||||
'exposeStreamSession',
|
'exposeStreamSession',
|
||||||
'exposeDialogRoutes',
|
'exposeDialogRoutes',
|
||||||
|
|
||||||
|
'homePageBeforeLoad',
|
||||||
|
'productDetailPageBeforeLoad',
|
||||||
|
'streamPageBeforeLoad',
|
||||||
|
|
||||||
'guideAchievementsDefaultLocked',
|
'guideAchievementsDefaultLocked',
|
||||||
|
|
||||||
'enableTvRoutes',
|
'enableTvRoutes',
|
||||||
// AppInterface && 'detectProductDetailsPage',
|
|
||||||
|
|
||||||
'supportLocalCoOp',
|
'supportLocalCoOp',
|
||||||
'overrideStorageGetSettings',
|
'overrideStorageGetSettings',
|
||||||
@ -1027,11 +985,6 @@ let PATCH_ORDERS: PatchArray = [
|
|||||||
getPref<UiLayout>(PrefKey.UI_LAYOUT) !== UiLayout.DEFAULT && 'websiteLayout',
|
getPref<UiLayout>(PrefKey.UI_LAYOUT) !== UiLayout.DEFAULT && 'websiteLayout',
|
||||||
getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole',
|
getPref(PrefKey.GAME_FORTNITE_FORCE_CONSOLE) && 'forceFortniteConsole',
|
||||||
|
|
||||||
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection',
|
|
||||||
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.ALL_GAMES) && 'ignoreAllGamesSection',
|
|
||||||
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection',
|
|
||||||
(getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.NATIVE_MKB) || getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.MOST_POPULAR)) && 'ignoreSiglSections',
|
|
||||||
|
|
||||||
...(STATES.userAgent.capabilities.touch ? [
|
...(STATES.userAgent.capabilities.touch ? [
|
||||||
'disableTouchContextMenu',
|
'disableTouchContextMenu',
|
||||||
] : []),
|
] : []),
|
||||||
@ -1059,12 +1012,19 @@ let PATCH_ORDERS: PatchArray = [
|
|||||||
'enableConsoleLogging',
|
'enableConsoleLogging',
|
||||||
'enableXcloudLogger',
|
'enableXcloudLogger',
|
||||||
] : []),
|
] : []),
|
||||||
].filter((item): item is string => !!item) as PatchArray;
|
]);
|
||||||
|
|
||||||
|
let HOME_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([
|
||||||
|
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.FRIENDS) && 'ignorePlayWithFriendsSection',
|
||||||
|
getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.ALL_GAMES) && 'ignoreAllGamesSection',
|
||||||
|
STATES.browser.capabilities.touch && getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.TOUCH) && 'ignorePlayWithTouchSection',
|
||||||
|
(getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.NATIVE_MKB) || getPref<UiSection>(PrefKey.UI_HIDE_SECTIONS).includes(UiSection.MOST_POPULAR)) && 'ignoreSiglSections',
|
||||||
|
]);
|
||||||
|
|
||||||
// Only when playing
|
// Only when playing
|
||||||
// TODO: check this
|
// TODO: check this
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let PLAYING_PATCH_ORDERS: PatchArray = [
|
let STREAM_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([
|
||||||
'patchXcloudTitleInfo',
|
'patchXcloudTitleInfo',
|
||||||
'disableGamepadDisconnectedScreen',
|
'disableGamepadDisconnectedScreen',
|
||||||
'patchStreamHud',
|
'patchStreamHud',
|
||||||
@ -1085,7 +1045,7 @@ let PLAYING_PATCH_ORDERS: PatchArray = [
|
|||||||
...(STATES.userAgent.capabilities.touch ? [
|
...(STATES.userAgent.capabilities.touch ? [
|
||||||
getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'patchShowSensorControls',
|
getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'patchShowSensorControls',
|
||||||
getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'exposeTouchLayoutManager',
|
getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL && 'exposeTouchLayoutManager',
|
||||||
(getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF || getPref(PrefKey.TOUCH_CONTROLLER_AUTO_OFF) || !STATES.userAgent.capabilities.touch) && 'disableTakRenderer',
|
(getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.OFF || getPref(PrefKey.TOUCH_CONTROLLER_AUTO_OFF)) && 'disableTakRenderer',
|
||||||
getPref<TouchControllerDefaultOpacity>(PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY) !== 100 && 'patchTouchControlDefaultOpacity',
|
getPref<TouchControllerDefaultOpacity>(PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY) !== 100 && 'patchTouchControlDefaultOpacity',
|
||||||
'patchBabylonRendererClass',
|
'patchBabylonRendererClass',
|
||||||
] : []),
|
] : []),
|
||||||
@ -1106,12 +1066,32 @@ let PLAYING_PATCH_ORDERS: PatchArray = [
|
|||||||
'patchMouseAndKeyboardEnabled',
|
'patchMouseAndKeyboardEnabled',
|
||||||
'disableNativeRequestPointerLock',
|
'disableNativeRequestPointerLock',
|
||||||
] : []),
|
] : []),
|
||||||
].filter((item): item is string => !!item);
|
]);
|
||||||
|
|
||||||
const ALL_PATCHES = [...PATCH_ORDERS, ...PLAYING_PATCH_ORDERS];
|
let PRODUCT_DETAIL_PAGE_PATCH_ORDERS = PatcherUtils.filterPatches([
|
||||||
|
AppInterface && 'detectProductDetailPage',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const ALL_PATCHES = [...PATCH_ORDERS, ...HOME_PAGE_PATCH_ORDERS, ...STREAM_PAGE_PATCH_ORDERS, ...PRODUCT_DETAIL_PAGE_PATCH_ORDERS];
|
||||||
|
|
||||||
export class Patcher {
|
export class Patcher {
|
||||||
static #patchFunctionBind() {
|
private static remainingPatches: { [key in PatchPage]: PatchArray } = {
|
||||||
|
home: HOME_PAGE_PATCH_ORDERS,
|
||||||
|
stream: STREAM_PAGE_PATCH_ORDERS,
|
||||||
|
'product-detail': PRODUCT_DETAIL_PAGE_PATCH_ORDERS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static patchPage(page: PatchPage) {
|
||||||
|
const remaining = Patcher.remainingPatches[page];
|
||||||
|
if (!remaining) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PATCH_ORDERS = PATCH_ORDERS.concat(remaining);
|
||||||
|
delete Patcher.remainingPatches[page];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static patchNativeBind() {
|
||||||
const nativeBind = Function.prototype.bind;
|
const nativeBind = Function.prototype.bind;
|
||||||
Function.prototype.bind = function() {
|
Function.prototype.bind = function() {
|
||||||
let valid = false;
|
let valid = false;
|
||||||
@ -1132,8 +1112,6 @@ export class Patcher {
|
|||||||
return nativeBind.apply(this, arguments);
|
return nativeBind.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
PatcherCache.getInstance().init();
|
|
||||||
|
|
||||||
if (typeof arguments[1] === 'function') {
|
if (typeof arguments[1] === 'function') {
|
||||||
BxLogger.info(LOG_TAG, 'Restored Function.prototype.bind()');
|
BxLogger.info(LOG_TAG, 'Restored Function.prototype.bind()');
|
||||||
Function.prototype.bind = nativeBind;
|
Function.prototype.bind = nativeBind;
|
||||||
@ -1211,6 +1189,7 @@ export class Patcher {
|
|||||||
patchesToCheck.splice(patchIndex, 1);
|
patchesToCheck.splice(patchIndex, 1);
|
||||||
patchIndex--;
|
patchIndex--;
|
||||||
PATCH_ORDERS = PATCH_ORDERS.filter(item => item != patchName);
|
PATCH_ORDERS = PATCH_ORDERS.filter(item => item != patchName);
|
||||||
|
BxLogger.info(LOG_TAG, 'Remaining patches', PATCH_ORDERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply patched functions
|
// Apply patched functions
|
||||||
@ -1236,7 +1215,7 @@ export class Patcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
Patcher.#patchFunctionBind();
|
Patcher.patchNativeBind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1249,7 +1228,29 @@ export class PatcherCache {
|
|||||||
|
|
||||||
private CACHE!: { [key: string]: PatchArray };
|
private CACHE!: { [key: string]: PatchArray };
|
||||||
|
|
||||||
private isInitialized = false;
|
private constructor() {
|
||||||
|
this.checkSignature();
|
||||||
|
|
||||||
|
// Read cache from storage
|
||||||
|
this.CACHE = JSON.parse(window.localStorage.getItem(this.KEY_CACHE) || '{}');
|
||||||
|
BxLogger.info(LOG_TAG, 'Cache', this.CACHE);
|
||||||
|
|
||||||
|
const pathName = window.location.pathname;
|
||||||
|
if (pathName.includes('/play/launch/')) {
|
||||||
|
Patcher.patchPage('stream');
|
||||||
|
} else if (pathName.includes('/play/games/')) {
|
||||||
|
Patcher.patchPage('product-detail');
|
||||||
|
} else if (pathName.endsWith('/play') || pathName.endsWith('/play/')) {
|
||||||
|
Patcher.patchPage('home');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove cached patches from PATCH_ORDERS & PLAYING_PATCH_ORDERS
|
||||||
|
PATCH_ORDERS = this.cleanupPatches(PATCH_ORDERS);
|
||||||
|
STREAM_PAGE_PATCH_ORDERS = this.cleanupPatches(STREAM_PAGE_PATCH_ORDERS);
|
||||||
|
PRODUCT_DETAIL_PAGE_PATCH_ORDERS = this.cleanupPatches(PRODUCT_DETAIL_PAGE_PATCH_ORDERS);
|
||||||
|
|
||||||
|
BxLogger.info(LOG_TAG, 'PATCH_ORDERS', PATCH_ORDERS.slice(0));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get patch's signature
|
* Get patch's signature
|
||||||
@ -1333,30 +1334,4 @@ export class PatcherCache {
|
|||||||
// Save to storage
|
// Save to storage
|
||||||
window.localStorage.setItem(this.KEY_CACHE, JSON.stringify(this.CACHE));
|
window.localStorage.setItem(this.KEY_CACHE, JSON.stringify(this.CACHE));
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.isInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isInitialized = true;
|
|
||||||
|
|
||||||
this.checkSignature();
|
|
||||||
|
|
||||||
// Read cache from storage
|
|
||||||
this.CACHE = JSON.parse(window.localStorage.getItem(this.KEY_CACHE) || '{}');
|
|
||||||
BxLogger.info(LOG_TAG, this.CACHE);
|
|
||||||
|
|
||||||
if (window.location.pathname.includes('/play/')) {
|
|
||||||
PATCH_ORDERS.push(...PLAYING_PATCH_ORDERS);
|
|
||||||
} else {
|
|
||||||
PATCH_ORDERS.push(ENDING_CHUNKS_PATCH_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove cached patches from PATCH_ORDERS & PLAYING_PATCH_ORDERS
|
|
||||||
PATCH_ORDERS = this.cleanupPatches(PATCH_ORDERS);
|
|
||||||
PLAYING_PATCH_ORDERS = this.cleanupPatches(PLAYING_PATCH_ORDERS);
|
|
||||||
|
|
||||||
BxLogger.info(LOG_TAG, PATCH_ORDERS.slice(0));
|
|
||||||
BxLogger.info(LOG_TAG, PLAYING_PATCH_ORDERS.slice(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
e && BxEvent.dispatch(window, BxEvent.NAVIGATION_FOCUS_CHANGED, {element: e});
|
e && BxEvent.dispatch(window, BxEvent.NAVIGATION_FOCUS_CHANGED, { element: e });
|
@ -204,7 +204,7 @@ export class RemotePlayManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.consoles.length === 0) {
|
if (this.consoles.length === 0) {
|
||||||
Toast.show(t('no-consoles-found'), '', {instant: true});
|
Toast.show(t('no-consoles-found'), '', { instant: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ export class MicrophoneShortcut {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
window.BX_EXPOSED.streamSession.tryEnableChatAsync(enableMic);
|
window.BX_EXPOSED.streamSession.tryEnableChatAsync(enableMic);
|
||||||
showToast && Toast.show(t('microphone'), t(enableMic ? 'unmuted': 'muted'), {instant: true});
|
showToast && Toast.show(t('microphone'), t(enableMic ? 'unmuted': 'muted'), { instant: true });
|
||||||
|
|
||||||
return enableMic;
|
return enableMic;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -37,7 +37,7 @@ export class SoundShortcut {
|
|||||||
SoundShortcut.setGainNodeVolume(newValue);
|
SoundShortcut.setGainNodeVolume(newValue);
|
||||||
|
|
||||||
// Show toast
|
// Show toast
|
||||||
Toast.show(`${t('stream')} ❯ ${t('volume')}`, newValue + '%', {instant: true});
|
Toast.show(`${t('stream')} ❯ ${t('volume')}`, newValue + '%', { instant: true });
|
||||||
|
|
||||||
return newValue;
|
return newValue;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ export class SoundShortcut {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SoundShortcut.setGainNodeVolume(targetValue);
|
SoundShortcut.setGainNodeVolume(targetValue);
|
||||||
Toast.show(`${t('stream')} ❯ ${t('volume')}`, status, {instant: true});
|
Toast.show(`${t('stream')} ❯ ${t('volume')}`, status, { instant: true });
|
||||||
|
|
||||||
BxEvent.dispatch(window, BxEvent.SPEAKER_STATE_CHANGED, {
|
BxEvent.dispatch(window, BxEvent.SPEAKER_STATE_CHANGED, {
|
||||||
speakerState: targetValue === 0 ? SpeakerState.MUTED : SpeakerState.ENABLED,
|
speakerState: targetValue === 0 ? SpeakerState.MUTED : SpeakerState.ENABLED,
|
||||||
@ -82,7 +82,7 @@ export class SoundShortcut {
|
|||||||
$media.muted = !$media.muted;
|
$media.muted = !$media.muted;
|
||||||
|
|
||||||
const status = $media.muted ? t('muted') : t('unmuted');
|
const status = $media.muted ? t('muted') : t('unmuted');
|
||||||
Toast.show(`${t('stream')} ❯ ${t('volume')}`, status, {instant: true});
|
Toast.show(`${t('stream')} ❯ ${t('volume')}`, status, { instant: true });
|
||||||
|
|
||||||
BxEvent.dispatch(window, BxEvent.SPEAKER_STATE_CHANGED, {
|
BxEvent.dispatch(window, BxEvent.SPEAKER_STATE_CHANGED, {
|
||||||
speakerState: $media.muted ? SpeakerState.MUTED : SpeakerState.ENABLED,
|
speakerState: $media.muted ? SpeakerState.MUTED : SpeakerState.ENABLED,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { CE } from "@/utils/html";
|
import { CE } from "@/utils/html";
|
||||||
import { WebGL2Player } from "./player/webgl2-player";
|
import { WebGL2Player } from "./player/webgl2-player";
|
||||||
@ -7,7 +7,7 @@ import { STATES } from "@/utils/global";
|
|||||||
import { PrefKey } from "@/enums/pref-keys";
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
import { getPref } from "@/utils/settings-storages/global-settings-storage";
|
||||||
import { BX_FLAGS } from "@/utils/bx-flags";
|
import { BX_FLAGS } from "@/utils/bx-flags";
|
||||||
import { StreamPlayerType, StreamVideoProcessing, VideoRatio } from "@/enums/pref-values";
|
import { StreamPlayerType, StreamVideoProcessing, VideoPosition, VideoRatio } from "@/enums/pref-values";
|
||||||
|
|
||||||
export type StreamPlayerOptions = Partial<{
|
export type StreamPlayerOptions = Partial<{
|
||||||
processing: string,
|
processing: string,
|
||||||
@ -39,13 +39,12 @@ export class StreamPlayer {
|
|||||||
private setupVideoElements() {
|
private setupVideoElements() {
|
||||||
this.$videoCss = document.getElementById('bx-video-css') as HTMLStyleElement;
|
this.$videoCss = document.getElementById('bx-video-css') as HTMLStyleElement;
|
||||||
if (this.$videoCss) {
|
if (this.$videoCss) {
|
||||||
this.$usmMatrix = this.$videoCss.querySelector('#bx-filter-usm-matrix') as any;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const $fragment = document.createDocumentFragment();
|
const $fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
this.$videoCss = CE<HTMLStyleElement>('style', {id: 'bx-video-css'});
|
this.$videoCss = CE<HTMLStyleElement>('style', { id: 'bx-video-css' });
|
||||||
$fragment.appendChild(this.$videoCss);
|
$fragment.appendChild(this.$videoCss);
|
||||||
|
|
||||||
// Setup SVG filters
|
// Setup SVG filters
|
||||||
@ -53,7 +52,7 @@ export class StreamPlayer {
|
|||||||
id: 'bx-video-filters',
|
id: 'bx-video-filters',
|
||||||
xmlns: 'http://www.w3.org/2000/svg',
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
class: 'bx-gone',
|
class: 'bx-gone',
|
||||||
}, CE('defs', {xmlns: 'http://www.w3.org/2000/svg'},
|
}, CE('defs', { xmlns: 'http://www.w3.org/2000/svg' },
|
||||||
CE('filter', {
|
CE('filter', {
|
||||||
id: 'bx-filter-usm',
|
id: 'bx-filter-usm',
|
||||||
xmlns: 'http://www.w3.org/2000/svg',
|
xmlns: 'http://www.w3.org/2000/svg',
|
||||||
@ -141,6 +140,23 @@ export class StreamPlayer {
|
|||||||
$video.dataset.width = width.toString();
|
$video.dataset.width = width.toString();
|
||||||
$video.dataset.height = height.toString();
|
$video.dataset.height = height.toString();
|
||||||
|
|
||||||
|
// Set position
|
||||||
|
const $parent = $video.parentElement!;
|
||||||
|
const position = getPref<VideoPosition>(PrefKey.VIDEO_POSITION);
|
||||||
|
$parent.style.removeProperty('padding-top');
|
||||||
|
|
||||||
|
$parent.dataset.position = position;
|
||||||
|
if (position === VideoPosition.TOP_HALF || position === VideoPosition.BOTTOM_HALF) {
|
||||||
|
let padding = Math.floor((window.innerHeight - height) / 4);
|
||||||
|
if (padding > 0) {
|
||||||
|
if (position === VideoPosition.BOTTOM_HALF) {
|
||||||
|
padding *= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent.style.paddingTop = padding + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update size
|
// Update size
|
||||||
targetWidth = `${width}px`;
|
targetWidth = `${width}px`;
|
||||||
targetHeight = `${height}px`;
|
targetHeight = `${height}px`;
|
||||||
@ -164,6 +180,8 @@ export class StreamPlayer {
|
|||||||
$webGL2Canvas.style.width = targetWidth;
|
$webGL2Canvas.style.width = targetWidth;
|
||||||
$webGL2Canvas.style.height = targetHeight;
|
$webGL2Canvas.style.height = targetHeight;
|
||||||
$webGL2Canvas.style.objectFit = targetObjectFit;
|
$webGL2Canvas.style.objectFit = targetObjectFit;
|
||||||
|
|
||||||
|
$video.dispatchEvent(new Event('resize'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update video dimensions
|
// Update video dimensions
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isLiteVersion } from "@macros/build" with {type: "macro"};
|
import { isLiteVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { t } from "@utils/translation";
|
import { t } from "@utils/translation";
|
||||||
import { BxEvent } from "@utils/bx-event";
|
import { BxEvent } from "@utils/bx-event";
|
||||||
@ -118,9 +118,9 @@ export class StreamBadges {
|
|||||||
return $badge;
|
return $badge;
|
||||||
}
|
}
|
||||||
|
|
||||||
$badge = CE('div', {class: 'bx-badge', title: badgeInfo.name},
|
$badge = CE('div', { class: 'bx-badge', title: badgeInfo.name },
|
||||||
CE('span', {class: 'bx-badge-name'}, createSvgIcon(badgeInfo.icon)),
|
CE('span', { class: 'bx-badge-name' }, createSvgIcon(badgeInfo.icon)),
|
||||||
CE('span', {class: 'bx-badge-value', style: `background-color: ${badgeInfo.color}`}, value),
|
CE('span', { class: 'bx-badge-value', style: `background-color: ${badgeInfo.color}` }, value),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (name === StreamBadge.BATTERY) {
|
if (name === StreamBadge.BATTERY) {
|
||||||
@ -219,7 +219,7 @@ export class StreamBadges {
|
|||||||
this.serverInfo.audio ? this.badges.audio.$element : [StreamBadge.AUDIO, '?'],
|
this.serverInfo.audio ? this.badges.audio.$element : [StreamBadge.AUDIO, '?'],
|
||||||
];
|
];
|
||||||
|
|
||||||
const $container = CE('div', {class: 'bx-badges'});
|
const $container = CE('div', { class: 'bx-badges' });
|
||||||
|
|
||||||
for (const item of BADGES) {
|
for (const item of BADGES) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
@ -71,7 +71,6 @@ export function updateVideoPlayer() {
|
|||||||
streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE));
|
streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE));
|
||||||
streamPlayer.updateOptions(options);
|
streamPlayer.updateOptions(options);
|
||||||
streamPlayer.refreshPlayer();
|
streamPlayer.refreshPlayer();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', updateVideoPlayer);
|
window.addEventListener('resize', updateVideoPlayer);
|
||||||
|
@ -209,7 +209,7 @@ export class StreamStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async render() {
|
private async render() {
|
||||||
this.$container = CE('div', {class: 'bx-stats-bar bx-gone'});
|
this.$container = CE('div', { class: 'bx-stats-bar bx-gone' });
|
||||||
|
|
||||||
let statKey: keyof typeof this.stats;
|
let statKey: keyof typeof this.stats;
|
||||||
for (statKey in this.stats) {
|
for (statKey in this.stats) {
|
||||||
|
@ -267,7 +267,7 @@ export class StreamUiHandler {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
observer.observe($screen, {subtree: true, childList: true});
|
observer.observe($screen, { subtree: true, childList: true });
|
||||||
StreamUiHandler.observer = observer;
|
StreamUiHandler.observer = observer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,13 +233,13 @@ export class TouchController {
|
|||||||
let html = false;
|
let html = false;
|
||||||
if (layout.author) {
|
if (layout.author) {
|
||||||
const author = `<b>${escapeHtml(layout.author)}</b>`;
|
const author = `<b>${escapeHtml(layout.author)}</b>`;
|
||||||
msg = t('touch-control-layout-by', {name: author});
|
msg = t('touch-control-layout-by', { name: author });
|
||||||
html = true;
|
html = true;
|
||||||
} else {
|
} else {
|
||||||
msg = t('touch-control-layout');
|
msg = t('touch-control-layout');
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutChanged && Toast.show(msg, layout.name, {html: html});
|
layoutChanged && Toast.show(msg, layout.name, { html });
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
// Show gyroscope control in the "More options" dialog if this layout has gyroscope
|
// Show gyroscope control in the "More options" dialog if this layout has gyroscope
|
||||||
|
@ -154,7 +154,7 @@ export class NavigationDialogManager {
|
|||||||
private constructor() {
|
private constructor() {
|
||||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||||
|
|
||||||
this.$overlay = CE('div', {class: 'bx-navigation-dialog-overlay bx-gone'});
|
this.$overlay = CE('div', { class: 'bx-navigation-dialog-overlay bx-gone' });
|
||||||
this.$overlay.addEventListener('click', e => {
|
this.$overlay.addEventListener('click', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -164,7 +164,7 @@ export class NavigationDialogManager {
|
|||||||
|
|
||||||
document.documentElement.appendChild(this.$overlay);
|
document.documentElement.appendChild(this.$overlay);
|
||||||
|
|
||||||
this.$container = CE('div', {class: 'bx-navigation-dialog bx-gone'});
|
this.$container = CE('div', { class: 'bx-navigation-dialog bx-gone' });
|
||||||
document.documentElement.appendChild(this.$container);
|
document.documentElement.appendChild(this.$container);
|
||||||
|
|
||||||
// Hide dialog when the Guide menu is shown
|
// Hide dialog when the Guide menu is shown
|
||||||
@ -186,7 +186,7 @@ export class NavigationDialogManager {
|
|||||||
// Find un-calculated <select> elements
|
// Find un-calculated <select> elements
|
||||||
this.calculateSelectBoxes($dialog);
|
this.calculateSelectBoxes($dialog);
|
||||||
});
|
});
|
||||||
observer.observe(this.$container, {childList: true});
|
observer.observe(this.$container, { childList: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ export class NavigationDialogManager {
|
|||||||
} else if (keyCode === 'Enter' || keyCode === 'NumpadEnter' || keyCode === 'Space') {
|
} else if (keyCode === 'Enter' || keyCode === 'NumpadEnter' || keyCode === 'Space') {
|
||||||
if (!($target instanceof HTMLInputElement && $target.type === 'text')) {
|
if (!($target instanceof HTMLInputElement && $target.type === 'text')) {
|
||||||
handled = true;
|
handled = true;
|
||||||
$target.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
$target.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||||
}
|
}
|
||||||
} else if (keyCode === 'Escape') {
|
} else if (keyCode === 'Escape') {
|
||||||
handled = true;
|
handled = true;
|
||||||
@ -393,7 +393,7 @@ export class NavigationDialogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (releasedButton === GamepadKey.A) {
|
if (releasedButton === GamepadKey.A) {
|
||||||
document.activeElement && document.activeElement.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
document.activeElement?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||||
return;
|
return;
|
||||||
} else if (releasedButton === GamepadKey.B) {
|
} else if (releasedButton === GamepadKey.B) {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
@ -95,8 +95,8 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
fragment.appendChild(CE('p', {class: 'bx-shortcut-note'},
|
fragment.appendChild(CE('p', { class: 'bx-shortcut-note' },
|
||||||
CE('span', {class: 'bx-prompt'}, PrompFont.HOME),
|
CE('span', { class: 'bx-prompt' }, PrompFont.HOME),
|
||||||
': ' + t('controller-shortcuts-xbox-note'),
|
': ' + t('controller-shortcuts-xbox-note'),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
|
|||||||
orientation: 'horizontal',
|
orientation: 'horizontal',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const $label = CE('label', {class: 'bx-prompt'}, `${PrompFont.HOME}${prompt}`);
|
const $label = CE('label', { class: 'bx-prompt' }, `${PrompFont.HOME}${prompt}`);
|
||||||
const $div = CE('div', {class: 'bx-shortcut-actions'});
|
const $div = CE('div', { class: 'bx-shortcut-actions' });
|
||||||
|
|
||||||
let $fakeSelect: HTMLSelectElement | null = null;
|
let $fakeSelect: HTMLSelectElement | null = null;
|
||||||
if (!PREF_CONTROLLER_FRIENDLY_UI) {
|
if (!PREF_CONTROLLER_FRIENDLY_UI) {
|
||||||
|
@ -32,7 +32,7 @@ export class RemotePlayDialog extends NavigationDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setupDialog() {
|
private setupDialog() {
|
||||||
const $fragment = CE('div', {'class': 'bx-remote-play-container'});
|
const $fragment = CE('div', { class: 'bx-remote-play-container' });
|
||||||
|
|
||||||
const $settingNote = CE('p', {});
|
const $settingNote = CE('p', {});
|
||||||
|
|
||||||
@ -69,13 +69,13 @@ export class RemotePlayDialog extends NavigationDialog {
|
|||||||
const consoles = manager.getConsoles();
|
const consoles = manager.getConsoles();
|
||||||
|
|
||||||
for (let con of consoles) {
|
for (let con of consoles) {
|
||||||
const $child = CE('div', {class: 'bx-remote-play-device-wrapper'},
|
const $child = CE('div', { class: 'bx-remote-play-device-wrapper' },
|
||||||
CE('div', {class: 'bx-remote-play-device-info'},
|
CE('div', { class: 'bx-remote-play-device-info' },
|
||||||
CE('div', {},
|
CE('div', {},
|
||||||
CE('span', {class: 'bx-remote-play-device-name'}, con.deviceName),
|
CE('span', { class: 'bx-remote-play-device-name' }, con.deviceName),
|
||||||
CE('span', {class: 'bx-remote-play-console-type'}, con.consoleType.replace('Xbox', ''))
|
CE('span', { class: 'bx-remote-play-console-type' }, con.consoleType.replace('Xbox', ''))
|
||||||
),
|
),
|
||||||
CE('div', {class: 'bx-remote-play-power-state'}, this.STATE_LABELS[con.powerState]),
|
CE('div', { class: 'bx-remote-play-power-state' }, this.STATE_LABELS[con.powerState]),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Connect button
|
// Connect button
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { limitVideoPlayerFps, onChangeVideoPlayerType, updateVideoPlayer } from "@/modules/stream/stream-settings-utils";
|
import { limitVideoPlayerFps, onChangeVideoPlayerType, updateVideoPlayer } from "@/modules/stream/stream-settings-utils";
|
||||||
import { ButtonStyle, CE, createButton, createSettingRow, createSvgIcon, escapeCssSelector, type BxButtonOptions } from "@/utils/html";
|
import { ButtonStyle, CE, createButton, createSettingRow, createSvgIcon, escapeCssSelector, type BxButtonOptions } from "@/utils/html";
|
||||||
@ -12,7 +12,7 @@ import { STATES, AppInterface, deepClone, SCRIPT_VERSION, STORAGE, SCRIPT_VARIAN
|
|||||||
import { t, Translations } from "@/utils/translation";
|
import { t, Translations } from "@/utils/translation";
|
||||||
import { BxSelectElement } from "@/web-components/bx-select";
|
import { BxSelectElement } from "@/web-components/bx-select";
|
||||||
import { setNearby } from "@/utils/navigation-utils";
|
import { setNearby } from "@/utils/navigation-utils";
|
||||||
import { PatcherCache } from "@/modules/patcher";
|
import { PatcherCache } from "@/modules/patcher/patcher";
|
||||||
import { UserAgentProfile } from "@/enums/user-agent";
|
import { UserAgentProfile } from "@/enums/user-agent";
|
||||||
import { UserAgent } from "@/utils/user-agent";
|
import { UserAgent } from "@/utils/user-agent";
|
||||||
import { BX_FLAGS } from "@/utils/bx-flags";
|
import { BX_FLAGS } from "@/utils/bx-flags";
|
||||||
@ -39,7 +39,7 @@ type SettingTabSectionItem = Partial<{
|
|||||||
note: string | (() => HTMLElement);
|
note: string | (() => HTMLElement);
|
||||||
experimental: string;
|
experimental: string;
|
||||||
content: HTMLElement | (() => HTMLElement);
|
content: HTMLElement | (() => HTMLElement);
|
||||||
options: {[key: string]: string};
|
options: { [key: string]: string };
|
||||||
unsupported: boolean;
|
unsupported: boolean;
|
||||||
unsupportedNote: string;
|
unsupportedNote: string;
|
||||||
onChange: (e: any, value: number) => void;
|
onChange: (e: any, value: number) => void;
|
||||||
@ -112,7 +112,7 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
if (!SCRIPT_VERSION.includes('beta') && PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) {
|
if (!SCRIPT_VERSION.includes('beta') && PREF_LATEST_VERSION && PREF_LATEST_VERSION != SCRIPT_VERSION) {
|
||||||
// Show new version button
|
// Show new version button
|
||||||
const opts = {
|
const opts = {
|
||||||
label: '🌟 ' + t('new-version-available', {version: PREF_LATEST_VERSION}),
|
label: '🌟 ' + t('new-version-available', { version: PREF_LATEST_VERSION }),
|
||||||
style: ButtonStyle.PRIMARY | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
|
style: ButtonStyle.PRIMARY | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
|
||||||
} as BxButtonOptions;
|
} as BxButtonOptions;
|
||||||
|
|
||||||
@ -231,8 +231,9 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
items: [
|
items: [
|
||||||
PrefKey.NATIVE_MKB_MODE,
|
PrefKey.NATIVE_MKB_MODE,
|
||||||
{
|
{
|
||||||
pref: PrefKey.FORCE_NATIVE_MKB_GAMES,
|
pref: PrefKey.NATIVE_MKB_FORCED_GAMES,
|
||||||
multiLines: true,
|
multiLines: true,
|
||||||
|
note: CE('a', { href: 'https://github.com/redphx/better-xcloud/discussions/574', target: '_blank' }, t('unofficial-game-list')),
|
||||||
},
|
},
|
||||||
|
|
||||||
PrefKey.MKB_ENABLED,
|
PrefKey.MKB_ENABLED,
|
||||||
@ -475,6 +476,9 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
}, {
|
}, {
|
||||||
pref: PrefKey.VIDEO_RATIO,
|
pref: PrefKey.VIDEO_RATIO,
|
||||||
onChange: updateVideoPlayer,
|
onChange: updateVideoPlayer,
|
||||||
|
}, {
|
||||||
|
pref: PrefKey.VIDEO_POSITION,
|
||||||
|
onChange: updateVideoPlayer,
|
||||||
}, {
|
}, {
|
||||||
pref: PrefKey.VIDEO_SHARPNESS,
|
pref: PrefKey.VIDEO_SHARPNESS,
|
||||||
onChange: updateVideoPlayer,
|
onChange: updateVideoPlayer,
|
||||||
@ -545,7 +549,7 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
|
|
||||||
// If there is no custom layouts -> show only Default option
|
// If there is no custom layouts -> show only Default option
|
||||||
if (!customLayouts) {
|
if (!customLayouts) {
|
||||||
$elm.appendChild(CE('option', {value: ''}, t('default')));
|
$elm.appendChild(CE('option', { value: '' }, t('default')));
|
||||||
$elm.value = '';
|
$elm.value = '';
|
||||||
$elm.dispatchEvent(new Event('input'));
|
$elm.dispatchEvent(new Event('input'));
|
||||||
return;
|
return;
|
||||||
@ -563,7 +567,7 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
name = layout.name;
|
name = layout.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const $option = CE('option', {value: key}, name);
|
const $option = CE('option', { value: key }, name);
|
||||||
$fragment.appendChild($option);
|
$fragment.appendChild($option);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,7 +859,7 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
|
|
||||||
setting.options[value] = label;
|
setting.options[value] = label;
|
||||||
|
|
||||||
const $option = CE<HTMLOptionElement>('option', {value: value}, label);
|
const $option = CE<HTMLOptionElement>('option', { value }, label);
|
||||||
const continent = continents[region.contintent];
|
const continent = continents[region.contintent];
|
||||||
if (!continent.children) {
|
if (!continent.children) {
|
||||||
continent.children = [];
|
continent.children = [];
|
||||||
@ -998,9 +1002,9 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
|
|
||||||
let $note;
|
let $note;
|
||||||
if (unsupportedNote) {
|
if (unsupportedNote) {
|
||||||
$note = CE('div', {class: 'bx-settings-dialog-note'}, unsupportedNote);
|
$note = CE('div', { class: 'bx-settings-dialog-note' }, unsupportedNote);
|
||||||
} else if (note) {
|
} else if (note) {
|
||||||
$note = CE('div', {class: 'bx-settings-dialog-note'}, note);
|
$note = CE('div', { class: 'bx-settings-dialog-note' }, note);
|
||||||
}
|
}
|
||||||
|
|
||||||
const $row = createSettingRow(label, !prefDefinition?.unsupported && $control, {
|
const $row = createSettingRow(label, !prefDefinition?.unsupported && $control, {
|
||||||
@ -1079,7 +1083,7 @@ export class SettingsDialog extends NavigationDialog {
|
|||||||
|
|
||||||
// Add note
|
// Add note
|
||||||
if (section.unsupportedNote) {
|
if (section.unsupportedNote) {
|
||||||
const $note = CE('b', {class: 'bx-note-unsupported'}, section.unsupportedNote);
|
const $note = CE('b', { class: 'bx-note-unsupported' }, section.unsupportedNote);
|
||||||
|
|
||||||
$tabContent.appendChild($note);
|
$tabContent.appendChild($note);
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ export class ControllerExtraSettings extends HTMLElement {
|
|||||||
CE('div', { class: 'bx-controller-extra-wrapper' },
|
CE('div', { class: 'bx-controller-extra-wrapper' },
|
||||||
$selectControllers,
|
$selectControllers,
|
||||||
|
|
||||||
CE('div', {class: 'bx-sub-content-box'},
|
CE('div', { class: 'bx-sub-content-box' },
|
||||||
createSettingRow(
|
createSettingRow(
|
||||||
t('controller-shortcuts-in-game'),
|
t('controller-shortcuts-in-game'),
|
||||||
CE('div', {
|
CE('div', {
|
||||||
|
@ -91,12 +91,12 @@ export class SuggestionsSetting {
|
|||||||
SuggestionsSetting.generateDefaultSuggestedSettings.call(this);
|
SuggestionsSetting.generateDefaultSuggestedSettings.call(this);
|
||||||
|
|
||||||
// Start rendering
|
// Start rendering
|
||||||
const $suggestedSettings = CE('div', {class: 'bx-suggest-wrapper'});
|
const $suggestedSettings = CE('div', { class: 'bx-suggest-wrapper' });
|
||||||
const $select = CE<HTMLSelectElement>('select', {},
|
const $select = CE<HTMLSelectElement>('select', {},
|
||||||
hasRecommendedSettings && CE('option', {value: 'recommended'}, t('recommended')),
|
hasRecommendedSettings && CE('option', { value: 'recommended' }, t('recommended')),
|
||||||
!hasRecommendedSettings && CE('option', {value: 'highest'}, t('highest-quality')),
|
!hasRecommendedSettings && CE('option', { value: 'highest' }, t('highest-quality')),
|
||||||
CE('option', {value: 'default'}, t('default')),
|
CE('option', { value: 'default' }, t('default')),
|
||||||
CE('option', {value: 'lowest'}, t('lowest-quality')),
|
CE('option', { value: 'lowest' }, t('lowest-quality')),
|
||||||
);
|
);
|
||||||
$select.addEventListener('input', e => {
|
$select.addEventListener('input', e => {
|
||||||
const profile = $select.value as SuggestedSettingProfile;
|
const profile = $select.value as SuggestedSettingProfile;
|
||||||
@ -107,13 +107,13 @@ export class SuggestionsSetting {
|
|||||||
|
|
||||||
let note: HTMLElement | string | undefined;
|
let note: HTMLElement | string | undefined;
|
||||||
if (profile === 'recommended') {
|
if (profile === 'recommended') {
|
||||||
note = t('recommended-settings-for-device', {device: recommendedDevice});
|
note = t('recommended-settings-for-device', { device: recommendedDevice });
|
||||||
} else if (profile === 'highest') {
|
} else if (profile === 'highest') {
|
||||||
// Add note for "Highest quality" profile
|
// Add note for "Highest quality" profile
|
||||||
note = '⚠️ ' + t('highest-quality-note');
|
note = '⚠️ ' + t('highest-quality-note');
|
||||||
}
|
}
|
||||||
|
|
||||||
note && fragment.appendChild(CE('div', {class: 'bx-suggest-note'}, note));
|
note && fragment.appendChild(CE('div', { class: 'bx-suggest-note' }, note));
|
||||||
|
|
||||||
const settings = this.suggestedSettings[profile];
|
const settings = this.suggestedSettings[profile];
|
||||||
let prefKey: PrefKey;
|
let prefKey: PrefKey;
|
||||||
@ -265,7 +265,7 @@ export class SuggestionsSetting {
|
|||||||
|
|
||||||
// Get recommended settings from GitHub
|
// Get recommended settings from GitHub
|
||||||
try {
|
try {
|
||||||
let {brand, board, model} = androidInfo!;
|
let { brand, board, model } = androidInfo!;
|
||||||
brand = normalize(brand);
|
brand = normalize(brand);
|
||||||
board = normalize(board);
|
board = normalize(board);
|
||||||
model = normalize(model);
|
model = normalize(model);
|
||||||
|
@ -28,7 +28,7 @@ export class GameTile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof totalWaitTime === 'number' && isElementVisible($elm)) {
|
if (typeof totalWaitTime === 'number' && isElementVisible($elm)) {
|
||||||
const $div = CE('div', {'class': 'bx-game-tile-wait-time'},
|
const $div = CE('div', { class: 'bx-game-tile-wait-time' },
|
||||||
createSvgIcon(BxIcon.PLAYTIME),
|
createSvgIcon(BxIcon.PLAYTIME),
|
||||||
CE('span', {}, secondsToHms(totalWaitTime)),
|
CE('span', {}, secondsToHms(totalWaitTime)),
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { BxEvent } from "@/utils/bx-event";
|
import { BxEvent } from "@/utils/bx-event";
|
||||||
import { AppInterface, STATES } from "@/utils/global";
|
import { AppInterface, STATES } from "@/utils/global";
|
||||||
@ -42,7 +42,7 @@ export class GuideMenu {
|
|||||||
// Wait until the Guide dialog is closed
|
// Wait until the Guide dialog is closed
|
||||||
window.addEventListener(BxEvent.XCLOUD_DIALOG_DISMISSED, e => {
|
window.addEventListener(BxEvent.XCLOUD_DIALOG_DISMISSED, e => {
|
||||||
setTimeout(() => SettingsDialog.getInstance().show(), 50);
|
setTimeout(() => SettingsDialog.getInstance().show(), 50);
|
||||||
}, {once: true});
|
}, { once: true });
|
||||||
|
|
||||||
// Close all xCloud's dialogs
|
// Close all xCloud's dialogs
|
||||||
this.closeGuideMenu();
|
this.closeGuideMenu();
|
||||||
@ -218,7 +218,7 @@ export class GuideMenu {
|
|||||||
for (index = 0; ($elm = $elm?.previousElementSibling); 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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ export class HeaderSection {
|
|||||||
this.timeoutId && clearTimeout(this.timeoutId);
|
this.timeoutId && clearTimeout(this.timeoutId);
|
||||||
this.timeoutId = window.setTimeout(this.checkHeader, 2000);
|
this.timeoutId = window.setTimeout(this.checkHeader, 2000);
|
||||||
});
|
});
|
||||||
this.observer.observe($root, {subtree: true, childList: true});
|
this.observer.observe($root, { subtree: true, childList: true });
|
||||||
|
|
||||||
this.checkHeader();
|
this.checkHeader();
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export class ProductDetailsPage {
|
|||||||
$container.parentElement.appendChild(CE('div', {
|
$container.parentElement.appendChild(CE('div', {
|
||||||
class: 'bx-product-details-buttons',
|
class: 'bx-product-details-buttons',
|
||||||
},
|
},
|
||||||
BX_FLAGS.DeviceInfo.deviceType === 'android' && ProductDetailsPage.$btnShortcut,
|
['android-handheld', 'android'].includes(BX_FLAGS.DeviceInfo.deviceType) && ProductDetailsPage.$btnShortcut,
|
||||||
ProductDetailsPage.$btnWallpaper,
|
ProductDetailsPage.$btnWallpaper,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
2
src/types/global.d.ts
vendored
2
src/types/global.d.ts
vendored
@ -9,7 +9,7 @@ declare global {
|
|||||||
interface Window {
|
interface Window {
|
||||||
AppInterface: any;
|
AppInterface: any;
|
||||||
BX_FLAGS?: BxFlags;
|
BX_FLAGS?: BxFlags;
|
||||||
BX_CE: (elmName: string, props: {[index: string]: any}={}) => HTMLElement;
|
BX_CE: (elmName: string, props: { [index: string]: any }={}) => HTMLElement;
|
||||||
BX_EXPOSED: typeof BxExposed & Partial<{
|
BX_EXPOSED: typeof BxExposed & Partial<{
|
||||||
shouldShowSensorControls: boolean;
|
shouldShowSensorControls: boolean;
|
||||||
stopTakRendering: boolean;
|
stopTakRendering: boolean;
|
||||||
|
6
src/types/preferences.d.ts
vendored
6
src/types/preferences.d.ts
vendored
@ -1,8 +1,8 @@
|
|||||||
export type PreferenceSetting = {
|
export type PreferenceSetting = {
|
||||||
default: any;
|
default: any;
|
||||||
optionsGroup?: string;
|
optionsGroup?: string;
|
||||||
options?: {[index: string]: string};
|
options?: { [index: string]: string };
|
||||||
multipleOptions?: {[index: string]: string};
|
multipleOptions?: { [index: string]: string };
|
||||||
unsupported?: boolean;
|
unsupported?: boolean;
|
||||||
unsupportedNote?: string | (() => HTMLElement);
|
unsupportedNote?: string | (() => HTMLElement);
|
||||||
note?: string | (() => HTMLElement);
|
note?: string | (() => HTMLElement);
|
||||||
@ -17,4 +17,4 @@ export type PreferenceSetting = {
|
|||||||
label?: string;
|
label?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PreferenceSettings = {[index in PrefKey]: PreferenceSetting};
|
export type PreferenceSettings = { [index in PrefKey]: PreferenceSetting };
|
||||||
|
2
src/types/presets.d.ts
vendored
2
src/types/presets.d.ts
vendored
@ -65,7 +65,7 @@ type KeyboardShortcutConvertedPresetData = KeyboardShortcutPresetData & {
|
|||||||
interface AllPresets<T extends PresetRecord> {
|
interface AllPresets<T extends PresetRecord> {
|
||||||
default: Array<number>,
|
default: Array<number>,
|
||||||
custom: Array<number>,
|
custom: Array<number>,
|
||||||
data: {[key: string]: T},
|
data: { [key: string]: T },
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AllPresetsData<T extends PresetRecord> {
|
interface AllPresetsData<T extends PresetRecord> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { ControllerShortcut } from "@/modules/controller-shortcut";
|
import { ControllerShortcut } from "@/modules/controller-shortcut";
|
||||||
import { BxEvent } from "@utils/bx-event";
|
import { BxEvent } from "@utils/bx-event";
|
||||||
@ -11,6 +11,7 @@ import { getPref } from "./settings-storages/global-settings-storage";
|
|||||||
import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
|
import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
|
||||||
import { TouchController } from "@/modules/touch-controller";
|
import { TouchController } from "@/modules/touch-controller";
|
||||||
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
|
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
|
||||||
|
import { Patcher, type PatchPage } from "@/modules/patcher/patcher";
|
||||||
|
|
||||||
export enum SupportedInputType {
|
export enum SupportedInputType {
|
||||||
CONTROLLER = 'Controller',
|
CONTROLLER = 'Controller',
|
||||||
@ -208,5 +209,10 @@ export const BxExposed = {
|
|||||||
/ /g,
|
/ /g,
|
||||||
],
|
],
|
||||||
|
|
||||||
toggleLocalCoOp: (enable: boolean) => {},
|
toggleLocalCoOp(enable: boolean) {},
|
||||||
|
|
||||||
|
beforePageLoad: isFullVersion() ? (page: PatchPage) => {
|
||||||
|
BxLogger.info('beforePageLoad', page);
|
||||||
|
Patcher.patchPage(page);
|
||||||
|
} : () => {},
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ export type BxFlags = {
|
|||||||
SafariWorkaround: boolean;
|
SafariWorkaround: boolean;
|
||||||
|
|
||||||
ForceNativeMkbTitles: string[];
|
ForceNativeMkbTitles: string[];
|
||||||
FeatureGates: {[key: string]: boolean} | null,
|
FeatureGates: { [key: string]: boolean } | null,
|
||||||
|
|
||||||
DeviceInfo: {
|
DeviceInfo: {
|
||||||
deviceType: 'android' | 'android-tv' | 'android-handheld' | 'webos' | 'unknown',
|
deviceType: 'android' | 'android-tv' | 'android-handheld' | 'webos' | 'unknown',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CE } from "@utils/html";
|
import { CE } from "@utils/html";
|
||||||
import { compressCss, renderStylus } from "@macros/build" with {type: "macro"};
|
import { compressCss, renderStylus } from "@macros/build" with { type: "macro" };
|
||||||
import { UiSection } from "@/enums/pref-values";
|
import { UiSection } from "@/enums/pref-values";
|
||||||
import { PrefKey } from "@/enums/pref-keys";
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { getPref } from "./settings-storages/global-settings-storage";
|
import { getPref } from "./settings-storages/global-settings-storage";
|
||||||
|
@ -3,7 +3,7 @@ import { BX_FLAGS } from "./bx-flags";
|
|||||||
import { getPref } from "./settings-storages/global-settings-storage";
|
import { getPref } from "./settings-storages/global-settings-storage";
|
||||||
import { NativeMkbMode } from "@/enums/pref-values";
|
import { NativeMkbMode } from "@/enums/pref-values";
|
||||||
|
|
||||||
export let FeatureGates: {[key: string]: boolean} = {
|
export let FeatureGates: { [key: string]: boolean } = {
|
||||||
PwaPrompt: false,
|
PwaPrompt: false,
|
||||||
EnableWifiWarnings: false,
|
EnableWifiWarnings: false,
|
||||||
EnableUpdateRequiredPage: false,
|
EnableUpdateRequiredPage: false,
|
||||||
|
@ -31,7 +31,7 @@ export function showGamepadToast(gamepad: Gamepad) {
|
|||||||
status = t('disconnected');
|
status = t('disconnected');
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.show(text, status, {instant: false});
|
Toast.show(text, status, { instant: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUniqueGamepadNames() {
|
export function getUniqueGamepadNames() {
|
||||||
|
@ -47,7 +47,7 @@ export const STATES: BxStates = {
|
|||||||
pointerServerPort: 9269,
|
pointerServerPort: 9269,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const STORAGE: {[key: string]: BaseSettingsStore} = {};
|
export const STORAGE: { [key: string]: BaseSettingsStore } = {};
|
||||||
|
|
||||||
export function deepClone(obj: any): typeof obj | {} {
|
export function deepClone(obj: any): typeof obj | {} {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
@ -47,7 +47,7 @@ export type BxButtonOptions = Partial<{
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
onClick: EventListener;
|
onClick: EventListener;
|
||||||
tabIndex: number;
|
tabIndex: number;
|
||||||
attributes: {[key: string]: any},
|
attributes: { [key: string]: any },
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type SettingsRowOptions = Partial<{
|
export type SettingsRowOptions = Partial<{
|
||||||
@ -186,7 +186,7 @@ export function createSettingRow(label: string, $control: HTMLElement | false |
|
|||||||
let $label: HTMLElement;
|
let $label: HTMLElement;
|
||||||
|
|
||||||
const $row = CE<HTMLLabelElement>('label', { class: 'bx-settings-row' },
|
const $row = CE<HTMLLabelElement>('label', { class: 'bx-settings-row' },
|
||||||
$label = CE('span', {class: 'bx-settings-label'},
|
$label = CE('span', { class: 'bx-settings-label' },
|
||||||
label,
|
label,
|
||||||
options.$note,
|
options.$note,
|
||||||
),
|
),
|
||||||
@ -300,7 +300,7 @@ export function renderPresetsList<T extends PresetRecord>($select: HTMLSelectEle
|
|||||||
const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
|
const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
export function humanFileSize(size: number) {
|
export function humanFileSize(size: number) {
|
||||||
const i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
|
const i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
|
||||||
return (size / Math.pow(1024, i)).toFixed(2) + ' ' + FILE_SIZE_UNITS[i];
|
return (size / Math.pow(1024, i)).toFixed(1) + ' ' + FILE_SIZE_UNITS[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function secondsToHm(seconds: number) {
|
export function secondsToHm(seconds: number) {
|
||||||
|
@ -55,11 +55,11 @@ export class BaseLocalTable<T extends BaseRecord> {
|
|||||||
return this.call(table.get.bind(table), ...arguments);
|
return this.call(table.get.bind(table), ...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<{[key: string]: T}> {
|
async getAll(): Promise<{ [key: string]: T }> {
|
||||||
const table = await this.prepareTable();
|
const table = await this.prepareTable();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const all = await (this.call(table.getAll.bind(table), ...arguments) as Promise<T[]>);
|
const all = await (this.call(table.getAll.bind(table), ...arguments) as Promise<T[]>);
|
||||||
const results: {[key: string]: T} = {};
|
const results: { [key: string]: T } = {};
|
||||||
|
|
||||||
all.forEach(item => {
|
all.forEach(item => {
|
||||||
results[item.id as T['id']] = item;
|
results[item.id as T['id']] = item;
|
||||||
|
@ -23,9 +23,13 @@ export class ControllerSettingsTable extends BaseLocalTable<ControllerSettingsRe
|
|||||||
|
|
||||||
async getControllersData() {
|
async getControllersData() {
|
||||||
const all = await this.getAll();
|
const all = await this.getAll();
|
||||||
const results: {[key: string]: ControllerSettingsRecord['data']} = {};
|
const results: { [key: string]: ControllerSettingsRecord['data'] } = {};
|
||||||
|
|
||||||
for (const key in all) {
|
for (const key in all) {
|
||||||
|
if (!all[key]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const settings = all[key].data;
|
const settings = all[key].data;
|
||||||
// Pre-calculate virabtionIntensity
|
// Pre-calculate virabtionIntensity
|
||||||
settings.vibrationIntensity /= 100;
|
settings.vibrationIntensity /= 100;
|
||||||
|
@ -50,7 +50,7 @@ export function patchVideoApi() {
|
|||||||
const $parent = this.parentElement!!;
|
const $parent = this.parentElement!!;
|
||||||
// Video tag is stream player
|
// Video tag is stream player
|
||||||
if (!this.src && $parent.dataset.testid === 'media-container') {
|
if (!this.src && $parent.dataset.testid === 'media-container') {
|
||||||
this.addEventListener('loadedmetadata', showFunc, {once: true});
|
this.addEventListener('loadedmetadata', showFunc, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return nativePlay.apply(this);
|
return nativePlay.apply(this);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { BX_FLAGS, NATIVE_FETCH } from "@utils/bx-flags";
|
import { BX_FLAGS, NATIVE_FETCH } from "@utils/bx-flags";
|
||||||
import { TouchController } from "@modules/touch-controller";
|
import { TouchController } from "@modules/touch-controller";
|
||||||
@ -39,7 +39,7 @@ function clearAllLogs() {
|
|||||||
clearDbLogs('XCloudAppLogs', 'logs');
|
clearDbLogs('XCloudAppLogs', 'logs');
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateIceCandidates(candidates: any, options: {preferIpv6Server: boolean, consoleAddrs?: RemotePlayConsoleAddresses}) {
|
function updateIceCandidates(candidates: any, options: { preferIpv6Server: boolean, consoleAddrs?: RemotePlayConsoleAddresses }) {
|
||||||
const pattern = new RegExp(/a=candidate:(?<foundation>\d+) (?<component>\d+) UDP (?<priority>\d+) (?<ip>[^\s]+) (?<port>\d+) (?<the_rest>.*)/);
|
const pattern = new RegExp(/a=candidate:(?<foundation>\d+) (?<component>\d+) UDP (?<priority>\d+) (?<ip>[^\s]+) (?<port>\d+) (?<the_rest>.*)/);
|
||||||
|
|
||||||
const lst = [];
|
const lst = [];
|
||||||
@ -48,7 +48,7 @@ function updateIceCandidates(candidates: any, options: {preferIpv6Server: boolea
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groups: {[index: string]: string | number} = pattern.exec(item.candidate)!.groups!;
|
const groups: { [index: string]: string | number } = pattern.exec(item.candidate)!.groups!;
|
||||||
lst.push(groups);
|
lst.push(groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ export class RootDialogObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
observer.observe($root, {subtree: true, childList: true});
|
observer.observe($root, { subtree: true, childList: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static waitForRootDialog() {
|
public static waitForRootDialog() {
|
||||||
@ -107,6 +107,6 @@ export class RootDialogObserver {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
observer.observe(document.documentElement, {subtree: true, childList: true});
|
observer.observe(document.documentElement, { subtree: true, childList: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export class ScreenshotManager {
|
|||||||
|
|
||||||
this.$download = CE<HTMLAnchorElement>('a');
|
this.$download = CE<HTMLAnchorElement>('a');
|
||||||
|
|
||||||
this.$canvas = CE<HTMLCanvasElement>('canvas', {'class': 'bx-gone'});
|
this.$canvas = CE<HTMLCanvasElement>('canvas', { class: 'bx-gone' });
|
||||||
this.canvasContext = this.$canvas.getContext('2d', {
|
this.canvasContext = this.$canvas.getContext('2d', {
|
||||||
alpha: false,
|
alpha: false,
|
||||||
willReadFrequently: false,
|
willReadFrequently: false,
|
||||||
@ -59,8 +59,11 @@ export class ScreenshotManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$player.parentElement!.addEventListener('animationend', this.onAnimationEnd, { once: true });
|
const $gameStream = $player.closest('#game-stream');
|
||||||
$player.parentElement!.classList.add('bx-taking-screenshot');
|
if ($gameStream) {
|
||||||
|
$gameStream.addEventListener('animationend', this.onAnimationEnd, { once: true });
|
||||||
|
$gameStream.classList.add('bx-taking-screenshot');
|
||||||
|
}
|
||||||
|
|
||||||
const canvasContext = this.canvasContext;
|
const canvasContext = this.canvasContext;
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export class SettingElement {
|
|||||||
for (let value in setting.options) {
|
for (let value in setting.options) {
|
||||||
const label = setting.options[value];
|
const label = setting.options[value];
|
||||||
|
|
||||||
const $option = CE<HTMLOptionElement>('option', {value: value}, label);
|
const $option = CE<HTMLOptionElement>('option', { value }, label);
|
||||||
$parent.appendChild($option);
|
$parent.appendChild($option);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,13 +68,14 @@ export class SettingElement {
|
|||||||
tabindex: 0,
|
tabindex: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const size = params.size ? params.size : Object.keys(setting.multipleOptions!).length;
|
const totalOptions = Object.keys(setting.multipleOptions!).length;
|
||||||
|
const size = params.size ? Math.min(params.size, totalOptions) : totalOptions;
|
||||||
$control.setAttribute('size', size.toString());
|
$control.setAttribute('size', size.toString());
|
||||||
|
|
||||||
for (let value in setting.multipleOptions) {
|
for (const value in setting.multipleOptions) {
|
||||||
const label = setting.multipleOptions[value];
|
const label = setting.multipleOptions[value];
|
||||||
|
|
||||||
const $option = CE<HTMLOptionElement>('option', {value: value}, label) as HTMLOptionElement;
|
const $option = CE<HTMLOptionElement>('option', { value }, label) as HTMLOptionElement;
|
||||||
$option.selected = currentValue.indexOf(value) > -1;
|
$option.selected = currentValue.indexOf(value) > -1;
|
||||||
|
|
||||||
$option.addEventListener('mousedown', function(e) {
|
$option.addEventListener('mousedown', function(e) {
|
||||||
@ -110,7 +111,7 @@ export class SettingElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static renderCheckbox(key: string, setting: PreferenceSetting, currentValue: any, onChange: any) {
|
private static renderCheckbox(key: string, setting: PreferenceSetting, currentValue: any, onChange: any) {
|
||||||
const $control = CE('input', {type: 'checkbox', tabindex: 0}) as HTMLInputElement;
|
const $control = CE('input', { type: 'checkbox', tabindex: 0 }) as HTMLInputElement;
|
||||||
$control.checked = currentValue;
|
$control.checked = currentValue;
|
||||||
|
|
||||||
onChange && $control.addEventListener('input', e => {
|
onChange && $control.addEventListener('input', e => {
|
||||||
|
@ -8,7 +8,7 @@ import { CE } from "../html";
|
|||||||
import { t, SUPPORTED_LANGUAGES } from "../translation";
|
import { t, SUPPORTED_LANGUAGES } from "../translation";
|
||||||
import { UserAgent } from "../user-agent";
|
import { UserAgent } from "../user-agent";
|
||||||
import { BaseSettingsStore as BaseSettingsStorage } from "./base-settings-storage";
|
import { BaseSettingsStore as BaseSettingsStorage } from "./base-settings-storage";
|
||||||
import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerStyleStandard, TouchControllerStyleCustom, GameBarPosition, DeviceVibrationMode, NativeMkbMode, UiLayout, UiSection, StreamPlayerType, StreamVideoProcessing, VideoRatio, StreamStat } from "@/enums/pref-values";
|
import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerStyleStandard, TouchControllerStyleCustom, GameBarPosition, DeviceVibrationMode, NativeMkbMode, UiLayout, UiSection, StreamPlayerType, StreamVideoProcessing, VideoRatio, StreamStat, VideoPosition } from "@/enums/pref-values";
|
||||||
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
|
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
|
||||||
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
|
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
|
||||||
import { GhPagesUtils } from "../gh-pages";
|
import { GhPagesUtils } from "../gh-pages";
|
||||||
@ -89,7 +89,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
},
|
},
|
||||||
[PrefKey.SERVER_REGION]: {
|
[PrefKey.SERVER_REGION]: {
|
||||||
label: t('region'),
|
label: t('region'),
|
||||||
note: CE('a', {target: '_blank', href: 'https://umap.openstreetmap.fr/en/map/xbox-cloud-gaming-servers_1135022'}, t('server-locations')),
|
note: CE('a', { target: '_blank', href: 'https://umap.openstreetmap.fr/en/map/xbox-cloud-gaming-servers_1135022' }, t('server-locations')),
|
||||||
default: 'default',
|
default: 'default',
|
||||||
},
|
},
|
||||||
[PrefKey.SERVER_BYPASS_RESTRICTION]: {
|
[PrefKey.SERVER_BYPASS_RESTRICTION]: {
|
||||||
@ -424,7 +424,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[PrefKey.FORCE_NATIVE_MKB_GAMES]: {
|
[PrefKey.NATIVE_MKB_FORCED_GAMES]: {
|
||||||
label: t('force-native-mkb-games'),
|
label: t('force-native-mkb-games'),
|
||||||
default: [],
|
default: [],
|
||||||
unsupported: !AppInterface && UserAgent.isMobile(),
|
unsupported: !AppInterface && UserAgent.isMobile(),
|
||||||
@ -437,6 +437,9 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
params: {
|
||||||
|
size: 6,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
[PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: {
|
[PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: {
|
||||||
@ -681,10 +684,10 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
},
|
},
|
||||||
[PrefKey.VIDEO_RATIO]: {
|
[PrefKey.VIDEO_RATIO]: {
|
||||||
label: t('aspect-ratio'),
|
label: t('aspect-ratio'),
|
||||||
note: t('aspect-ratio-note'),
|
note: STATES.browser.capabilities.touch ? t('aspect-ratio-note') : undefined,
|
||||||
default: VideoRatio['16:9'],
|
default: VideoRatio['16:9'],
|
||||||
options: {
|
options: {
|
||||||
[VideoRatio['16:9']]: '16:9',
|
[VideoRatio['16:9']]: `16:9 (${t('default')})`,
|
||||||
[VideoRatio['18:9']]: '18:9',
|
[VideoRatio['18:9']]: '18:9',
|
||||||
[VideoRatio['21:9']]: '21:9',
|
[VideoRatio['21:9']]: '21:9',
|
||||||
[VideoRatio['16:10']]: '16:10',
|
[VideoRatio['16:10']]: '16:10',
|
||||||
@ -694,6 +697,19 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
//'cover': 'Cover',
|
//'cover': 'Cover',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[PrefKey.VIDEO_POSITION]: {
|
||||||
|
label: t('position'),
|
||||||
|
note: STATES.browser.capabilities.touch ? t('aspect-ratio-note') : undefined,
|
||||||
|
default: VideoPosition.CENTER,
|
||||||
|
options: {
|
||||||
|
[VideoPosition.TOP]: t('top'),
|
||||||
|
[VideoPosition.TOP_HALF]: t('top-half'),
|
||||||
|
[VideoPosition.CENTER]: `${t('center')} (${t('default')})`,
|
||||||
|
[VideoPosition.BOTTOM_HALF]: t('bottom-half'),
|
||||||
|
[VideoPosition.BOTTOM]: t('bottom'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
[PrefKey.VIDEO_SATURATION]: {
|
[PrefKey.VIDEO_SATURATION]: {
|
||||||
label: t('saturation'),
|
label: t('saturation'),
|
||||||
default: 100,
|
default: 100,
|
||||||
@ -746,7 +762,6 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
[PrefKey.STATS_ITEMS]: {
|
[PrefKey.STATS_ITEMS]: {
|
||||||
label: t('stats'),
|
label: t('stats'),
|
||||||
default: [StreamStat.PING, StreamStat.FPS, StreamStat.BITRATE, StreamStat.DECODE_TIME, StreamStat.PACKETS_LOST, StreamStat.FRAMES_LOST],
|
default: [StreamStat.PING, StreamStat.FPS, StreamStat.BITRATE, StreamStat.DECODE_TIME, StreamStat.PACKETS_LOST, StreamStat.FRAMES_LOST],
|
||||||
|
@ -104,7 +104,7 @@ export class StreamStatsCollector {
|
|||||||
current: 0,
|
current: 0,
|
||||||
grades: [30, 40, 60],
|
grades: [30, 40, 60],
|
||||||
toString() {
|
toString() {
|
||||||
return `${this.current.toFixed(2)}ms`;
|
return `${this.current.toFixed(1)}ms`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ export class StreamStatsCollector {
|
|||||||
[StreamStat.BITRATE]: {
|
[StreamStat.BITRATE]: {
|
||||||
current: 0,
|
current: 0,
|
||||||
toString() {
|
toString() {
|
||||||
return `${this.current.toFixed(2)} Mbps`;
|
return `${this.current.toFixed(1)} Mbps`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ export class StreamStatsCollector {
|
|||||||
received: 0,
|
received: 0,
|
||||||
dropped: 0,
|
dropped: 0,
|
||||||
toString() {
|
toString() {
|
||||||
const framesDroppedPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(2);
|
const framesDroppedPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(1);
|
||||||
return framesDroppedPercentage === '0.00' ? this.dropped.toString() : `${this.dropped} (${framesDroppedPercentage}%)`;
|
return framesDroppedPercentage === '0.00' ? this.dropped.toString() : `${this.dropped} (${framesDroppedPercentage}%)`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -136,7 +136,7 @@ export class StreamStatsCollector {
|
|||||||
received: 0,
|
received: 0,
|
||||||
dropped: 0,
|
dropped: 0,
|
||||||
toString() {
|
toString() {
|
||||||
const packetsLostPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(2);
|
const packetsLostPercentage = (this.dropped * 100 / ((this.dropped + this.received) || 1)).toFixed(1);
|
||||||
return packetsLostPercentage === '0.00' ? this.dropped.toString() : `${this.dropped} (${packetsLostPercentage}%)`;
|
return packetsLostPercentage === '0.00' ? this.dropped.toString() : `${this.dropped} (${packetsLostPercentage}%)`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -146,7 +146,7 @@ export class StreamStatsCollector {
|
|||||||
total: 0,
|
total: 0,
|
||||||
grades: [6, 9, 12],
|
grades: [6, 9, 12],
|
||||||
toString() {
|
toString() {
|
||||||
return isNaN(this.current) ? '??ms' : `${this.current.toFixed(2)}ms`;
|
return isNaN(this.current) ? '??ms' : `${this.current.toFixed(1)}ms`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ export class Toast {
|
|||||||
private constructor() {
|
private constructor() {
|
||||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||||
|
|
||||||
this.$wrapper = CE('div', {class: 'bx-toast bx-offscreen'},
|
this.$wrapper = CE('div', { class: 'bx-toast bx-offscreen' },
|
||||||
this.$msg = CE('span', {class: 'bx-toast-msg'}),
|
this.$msg = CE('span', { class: 'bx-toast-msg' }),
|
||||||
this.$status = CE('span', {class: 'bx-toast-status'}),
|
this.$status = CE('span', { class: 'bx-toast-status' }),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.$wrapper.addEventListener('transitionend', e => {
|
this.$wrapper.addEventListener('transitionend', e => {
|
||||||
|
@ -47,6 +47,8 @@ const Texts = {
|
|||||||
"better-xcloud": "Better xCloud",
|
"better-xcloud": "Better xCloud",
|
||||||
"bitrate-audio-maximum": "Maximum audio bitrate",
|
"bitrate-audio-maximum": "Maximum audio bitrate",
|
||||||
"bitrate-video-maximum": "Maximum video bitrate",
|
"bitrate-video-maximum": "Maximum video bitrate",
|
||||||
|
"bottom": "Bottom",
|
||||||
|
"bottom-half": "Bottom half",
|
||||||
"bottom-left": "Bottom-left",
|
"bottom-left": "Bottom-left",
|
||||||
"bottom-right": "Bottom-right",
|
"bottom-right": "Bottom-right",
|
||||||
"brazil": "Brazil",
|
"brazil": "Brazil",
|
||||||
@ -56,6 +58,7 @@ const Texts = {
|
|||||||
"can-stream-xbox-360-games": "Can stream Xbox 360 games",
|
"can-stream-xbox-360-games": "Can stream Xbox 360 games",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"cant-stream-xbox-360-games": "Can't stream Xbox 360 games",
|
"cant-stream-xbox-360-games": "Can't stream Xbox 360 games",
|
||||||
|
"center": "Center",
|
||||||
"clarity-boost": "Clarity boost",
|
"clarity-boost": "Clarity boost",
|
||||||
"clarity-boost-warning": "These settings don't work when the Clarity Boost mode is ON",
|
"clarity-boost-warning": "These settings don't work when the Clarity Boost mode is ON",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
@ -339,7 +342,9 @@ const Texts = {
|
|||||||
"tc-standard-layout-style": "Standard layout's button style",
|
"tc-standard-layout-style": "Standard layout's button style",
|
||||||
"text-size": "Text size",
|
"text-size": "Text size",
|
||||||
"toggle": "Toggle",
|
"toggle": "Toggle",
|
||||||
|
"top": "Top",
|
||||||
"top-center": "Top-center",
|
"top-center": "Top-center",
|
||||||
|
"top-half": "Top half",
|
||||||
"top-left": "Top-left",
|
"top-left": "Top-left",
|
||||||
"top-right": "Top-right",
|
"top-right": "Top-right",
|
||||||
"touch-control-layout": "Touch control layout",
|
"touch-control-layout": "Touch control layout",
|
||||||
|
@ -9,12 +9,12 @@ type UserAgentConfig = {
|
|||||||
|
|
||||||
const SMART_TV_UNIQUE_ID = 'FC4A1DA2-711C-4E9C-BC7F-047AF8A672EA';
|
const SMART_TV_UNIQUE_ID = 'FC4A1DA2-711C-4E9C-BC7F-047AF8A672EA';
|
||||||
|
|
||||||
let CHROMIUM_VERSION = '123.0.0.0';
|
let CHROMIUM_VERSION = '125.0.0.0';
|
||||||
if (!!(window as any).chrome || window.navigator.userAgent.includes('Chrome')) {
|
if (!!(window as any).chrome || window.navigator.userAgent.includes('Chrome')) {
|
||||||
// Get Chromium version in the original User-Agent value
|
// Get Chromium version in the original User-Agent value
|
||||||
const match = window.navigator.userAgent.match(/\s(?:Chrome|Edg)\/([\d\.]+)/);
|
const match = window.navigator.userAgent.match(/\s(?:Chrome|Edg)\/([\d\.]+)/);
|
||||||
if (match) {
|
if (match) {
|
||||||
CHROMIUM_VERSION = match[1];
|
CHROMIUM_VERSION = match[1] as string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,11 +102,11 @@ export function roundToNearest(value: number, interval: number): number {
|
|||||||
export async function copyToClipboard(text: string, showToast=true): Promise<boolean> {
|
export async function copyToClipboard(text: string, showToast=true): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
showToast && Toast.show('Copied to clipboard', '', {instant: true});
|
showToast && Toast.show('Copied to clipboard', '', { instant: true });
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to copy: ', err);
|
console.error('Failed to copy: ', err);
|
||||||
showToast && Toast.show('Failed to copy', '', {instant: true});
|
showToast && Toast.show('Failed to copy', '', { instant: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -128,10 +128,10 @@ export function parseDetailsPath(path: string) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const titleSlug = matches.groups.titleSlug.replaceAll('\%' + '7C', '-');
|
const titleSlug = matches.groups.titleSlug!.replaceAll('\%' + '7C', '-');
|
||||||
const productId = matches.groups.productId;
|
const productId = matches.groups.productId;
|
||||||
|
|
||||||
return {titleSlug, productId};
|
return { titleSlug, productId };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearAllData() {
|
export function clearAllData() {
|
||||||
|
@ -3,7 +3,7 @@ import { NATIVE_FETCH } from "./bx-flags"
|
|||||||
export class XboxApi {
|
export class XboxApi {
|
||||||
private static CACHED_TITLES: Record<string, string> = {};
|
private static CACHED_TITLES: Record<string, string> = {};
|
||||||
|
|
||||||
static async getProductTitle(xboxTitleId: number | string): Promise<string | null> {
|
static async getProductTitle(xboxTitleId: number | string): Promise<string | undefined> {
|
||||||
xboxTitleId = xboxTitleId.toString();
|
xboxTitleId = xboxTitleId.toString();
|
||||||
if (XboxApi.CACHED_TITLES[xboxTitleId]) {
|
if (XboxApi.CACHED_TITLES[xboxTitleId]) {
|
||||||
return XboxApi.CACHED_TITLES[xboxTitleId];
|
return XboxApi.CACHED_TITLES[xboxTitleId];
|
||||||
@ -20,6 +20,6 @@ export class XboxApi {
|
|||||||
return productTitle;
|
return productTitle;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,21 @@ export class XcloudApi {
|
|||||||
public static getInstance = () => XcloudApi.instance ?? (XcloudApi.instance = new XcloudApi());
|
public static getInstance = () => XcloudApi.instance ?? (XcloudApi.instance = new XcloudApi());
|
||||||
private readonly LOG_TAG = 'XcloudApi';
|
private readonly LOG_TAG = 'XcloudApi';
|
||||||
|
|
||||||
private CACHE_TITLES: {[key: string]: XcloudTitleInfo} = {};
|
private CACHE_TITLES: { [key: string]: XcloudTitleInfo } = {};
|
||||||
private CACHE_WAIT_TIME: {[key: string]: XcloudWaitTimeInfo} = {};
|
private CACHE_WAIT_TIME: { [key: string]: XcloudWaitTimeInfo } = {};
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
BxLogger.info(this.LOG_TAG, 'constructor()');
|
BxLogger.info(this.LOG_TAG, 'constructor()');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTitleInfo(id: string): Promise<XcloudTitleInfo | null> {
|
async getTitleInfo(id: string): Promise<XcloudTitleInfo | undefined> {
|
||||||
if (id in this.CACHE_TITLES) {
|
if (id in this.CACHE_TITLES) {
|
||||||
return this.CACHE_TITLES[id];
|
return this.CACHE_TITLES[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUri = STATES.selectedRegion.baseUri;
|
const baseUri = STATES.selectedRegion.baseUri;
|
||||||
if (!baseUri || !STATES.gsToken) {
|
if (!baseUri || !STATES.gsToken) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let json;
|
let json;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isFullVersion } from "@macros/build" with {type: "macro"};
|
import { isFullVersion } from "@macros/build" with { type: "macro" };
|
||||||
|
|
||||||
import { LoadingScreen } from "@modules/loading-screen";
|
import { LoadingScreen } from "@modules/loading-screen";
|
||||||
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
||||||
@ -116,7 +116,7 @@ export class XcloudInterceptor {
|
|||||||
|
|
||||||
let match = serverRegex.exec(region.baseUri);
|
let match = serverRegex.exec(region.baseUri);
|
||||||
if (match) {
|
if (match) {
|
||||||
shortName = match[1];
|
shortName = match[1] as string;
|
||||||
if (serverExtra[regionName]) {
|
if (serverExtra[regionName]) {
|
||||||
shortName = serverExtra[regionName][0] + ' ' + shortName;
|
shortName = serverExtra[regionName][0] + ' ' + shortName;
|
||||||
region.contintent = serverExtra[regionName][1];
|
region.contintent = serverExtra[regionName][1];
|
||||||
@ -155,10 +155,10 @@ export class XcloudInterceptor {
|
|||||||
const url = (typeof request === 'string') ? request : (request as Request).url;
|
const url = (typeof request === 'string') ? request : (request as Request).url;
|
||||||
const parsedUrl = new URL(url);
|
const parsedUrl = new URL(url);
|
||||||
|
|
||||||
let badgeRegion: string = parsedUrl.host.split('.', 1)[0];
|
let badgeRegion: string = parsedUrl.host.split('.', 1)[0] as string;
|
||||||
for (let regionName in STATES.serverRegions) {
|
for (let regionName in STATES.serverRegions) {
|
||||||
const region = STATES.serverRegions[regionName];
|
const region = STATES.serverRegions[regionName];
|
||||||
if (parsedUrl.origin == region.baseUri) {
|
if (region && parsedUrl.origin === region.baseUri) {
|
||||||
badgeRegion = regionName;
|
badgeRegion = regionName;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ export class XcloudInterceptor {
|
|||||||
const clone = (request as Request).clone();
|
const clone = (request as Request).clone();
|
||||||
const body = await clone.json();
|
const body = await clone.json();
|
||||||
|
|
||||||
const headers: {[index: string]: string} = {};
|
const headers: { [index: string]: string } = {};
|
||||||
for (const pair of (clone.headers as any).entries()) {
|
for (const pair of (clone.headers as any).entries()) {
|
||||||
headers[pair[0]] = pair[1];
|
headers[pair[0]] = pair[1];
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ export class XhomeInterceptor {
|
|||||||
XhomeInterceptor.consoleAddrs = {};
|
XhomeInterceptor.consoleAddrs = {};
|
||||||
for (const pair of pairs) {
|
for (const pair of pairs) {
|
||||||
const [keyAddr, keyPort] = pair;
|
const [keyAddr, keyPort] = pair;
|
||||||
if (serverDetails[keyAddr]) {
|
if (keyAddr && keyPort && serverDetails[keyAddr]) {
|
||||||
const port = serverDetails[keyPort];
|
const port = serverDetails[keyPort];
|
||||||
// Add port 9002 to the list of ports
|
// Add port 9002 to the list of ports
|
||||||
const ports = new Set<number>();
|
const ports = new Set<number>();
|
||||||
@ -107,7 +107,7 @@ export class XhomeInterceptor {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async handleInputConfigs(request: Request | URL, opts: {[index: string]: any}) {
|
private static async handleInputConfigs(request: Request | URL, opts: { [index: string]: any }) {
|
||||||
const response = await NATIVE_FETCH(request);
|
const response = await NATIVE_FETCH(request);
|
||||||
|
|
||||||
if (getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.ALL) {
|
if (getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.ALL) {
|
||||||
@ -147,7 +147,7 @@ export class XhomeInterceptor {
|
|||||||
private static async handleTitles(request: Request) {
|
private static async handleTitles(request: Request) {
|
||||||
const clone = request.clone();
|
const clone = request.clone();
|
||||||
|
|
||||||
const headers: {[index: string]: any} = {};
|
const headers: { [index: string]: any } = {};
|
||||||
for (const pair of (clone.headers as any).entries()) {
|
for (const pair of (clone.headers as any).entries()) {
|
||||||
headers[pair[0]] = pair[1];
|
headers[pair[0]] = pair[1];
|
||||||
}
|
}
|
||||||
@ -183,7 +183,7 @@ export class XhomeInterceptor {
|
|||||||
|
|
||||||
const clone = request.clone();
|
const clone = request.clone();
|
||||||
|
|
||||||
const headers: {[index: string]: string} = {};
|
const headers: { [index: string]: string } = {};
|
||||||
for (const pair of (clone.headers as any).entries()) {
|
for (const pair of (clone.headers as any).entries()) {
|
||||||
headers[pair[0]] = pair[1];
|
headers[pair[0]] = pair[1];
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ export class BxNumberStepper extends HTMLInputElement implements BxHtmlSettingEl
|
|||||||
|
|
||||||
if (options.ticks || options.exactTicks) {
|
if (options.ticks || options.exactTicks) {
|
||||||
const markersId = `markers-${key}`;
|
const markersId = `markers-${key}`;
|
||||||
const $markers = CE('datalist', {id: markersId});
|
const $markers = CE('datalist', { id: markersId });
|
||||||
$range.setAttribute('list', markersId);
|
$range.setAttribute('list', markersId);
|
||||||
|
|
||||||
if (options.exactTicks) {
|
if (options.exactTicks) {
|
||||||
@ -140,7 +140,7 @@ export class BxNumberStepper extends HTMLInputElement implements BxHtmlSettingEl
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = self.uiMin + options.ticks!; i < self.uiMax; i += options.ticks!) {
|
for (let i = self.uiMin + options.ticks!; i < self.uiMax; i += options.ticks!) {
|
||||||
$markers.appendChild(CE<HTMLOptionElement>('option', {value: i}));
|
$markers.appendChild(CE<HTMLOptionElement>('option', { value: i }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.appendChild($markers);
|
self.appendChild($markers);
|
||||||
|
@ -260,6 +260,9 @@ export class BxSelectElement extends HTMLSelectElement {
|
|||||||
for (let i = 0; i < optionsList.length; i++) {
|
for (let i = 0; i < optionsList.length; i++) {
|
||||||
const $option = optionsList[i];
|
const $option = optionsList[i];
|
||||||
const $indicator = indicatorsList[i];
|
const $indicator = indicatorsList[i];
|
||||||
|
if (!$option || !$indicator) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
clearDataSet($indicator);
|
clearDataSet($indicator);
|
||||||
if ($option.selected) {
|
if ($option.selected) {
|
||||||
@ -288,7 +291,7 @@ export class BxSelectElement extends HTMLSelectElement {
|
|||||||
visibleIndex: currentIndex,
|
visibleIndex: currentIndex,
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
const goNext = (e.target as any).closest('button') === $btnNext;
|
const goNext = (e.target as HTMLElement).closest('button') === $btnNext;
|
||||||
|
|
||||||
let newIndex = goNext ? currentIndex + 1 : currentIndex - 1;
|
let newIndex = goNext ? currentIndex + 1 : currentIndex - 1;
|
||||||
if (newIndex > this.optionsList.length - 1) {
|
if (newIndex > this.optionsList.length - 1) {
|
||||||
|
Reference in New Issue
Block a user