mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-08-10 07:07:46 +02:00
Game-specific settings (#623)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { PrefKey, StorageKey } from "@/enums/pref-keys";
|
||||
import type { GlobalPref, StorageKey, StreamPref } from "@/enums/pref-keys";
|
||||
import { BX_FLAGS } from "./bx-flags";
|
||||
import { BxLogger } from "./bx-logger";
|
||||
import { AppInterface } from "./global";
|
||||
@@ -11,19 +11,16 @@ type ScriptEvents = {
|
||||
'xcloud.server.ready': {};
|
||||
'xcloud.server.unavailable': {};
|
||||
|
||||
'dialog.shown': {},
|
||||
'dialog.dismissed': {},
|
||||
'dialog.shown': {};
|
||||
'dialog.dismissed': {};
|
||||
|
||||
'titleInfo.ready': {};
|
||||
'setting.changed': {
|
||||
storageKey: StorageKey;
|
||||
settingKey: PrefKey;
|
||||
settingValue: any;
|
||||
};
|
||||
|
||||
'mkb.setting.updated': {};
|
||||
'keyboardShortcuts.updated': {};
|
||||
'deviceVibration.updated': {};
|
||||
'setting.changed': {
|
||||
storageKey: Omit<StorageKey, StorageKey.STREAM>;
|
||||
settingKey: GlobalPref;
|
||||
// settingValue: any;
|
||||
};
|
||||
|
||||
// GH pages
|
||||
'list.forcedNativeMkb.updated': {
|
||||
@@ -33,7 +30,7 @@ type ScriptEvents = {
|
||||
};
|
||||
|
||||
'list.localCoOp.updated': {
|
||||
ids: Set<string>,
|
||||
ids: Set<string>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -44,11 +41,27 @@ type StreamEvents = {
|
||||
'state.stopped': {};
|
||||
'state.error': {};
|
||||
|
||||
'gameBar.activated': {},
|
||||
'speaker.state.changed': { state: SpeakerState },
|
||||
'video.visibility.changed': { isVisible: boolean },
|
||||
'xboxTitleId.changed': {
|
||||
id: number;
|
||||
};
|
||||
'gameSettings.switched': {
|
||||
id: number;
|
||||
};
|
||||
'setting.changed': {
|
||||
storageKey: StorageKey.STREAM | `${StorageKey.STREAM}.${number}`;
|
||||
settingKey: StreamPref;
|
||||
// settingValue: any;
|
||||
};
|
||||
|
||||
'mkb.setting.updated': {};
|
||||
'keyboardShortcuts.updated': {};
|
||||
'deviceVibration.updated': {};
|
||||
|
||||
'gameBar.activated': {};
|
||||
'speaker.state.changed': { state: SpeakerState };
|
||||
'video.visibility.changed': { isVisible: boolean };
|
||||
// Inside patch
|
||||
'microphone.state.changed': { state: MicrophoneState },
|
||||
'microphone.state.changed': { state: MicrophoneState };
|
||||
|
||||
dataChannelCreated: { dataChannel: RTCDataChannel };
|
||||
};
|
||||
@@ -136,7 +149,7 @@ export class BxEventBus<TEvents extends Record<string, any>> {
|
||||
}
|
||||
}
|
||||
|
||||
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'emit', event, payload);
|
||||
BX_FLAGS.Debug && BxLogger.warning('EventBus', 'emit', `${this.group}.${event as string}`, payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,14 +5,14 @@ import { deepClone, STATES } from "@utils/global";
|
||||
import { BxLogger } from "./bx-logger";
|
||||
import { BX_FLAGS } from "./bx-flags";
|
||||
import { NavigationDialogManager } from "@/modules/ui/dialog/navigation-dialog";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { GamePassCloudGallery } from "@/enums/game-pass-gallery";
|
||||
import { TouchController } from "@/modules/touch-controller";
|
||||
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
|
||||
import { Patcher, type PatchPage } from "@/modules/patcher/patcher";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
import { FeatureGates } from "./feature-gates";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
import { LocalCoOpManager } from "./local-co-op-manager";
|
||||
|
||||
export enum SupportedInputType {
|
||||
@@ -107,17 +107,17 @@ export const BxExposed = {
|
||||
}
|
||||
|
||||
// Remove native MKB support on mobile browsers or by user's choice
|
||||
if (getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.OFF) {
|
||||
if (getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.OFF) {
|
||||
supportedInputTypes = supportedInputTypes.filter(i => i !== SupportedInputType.MKB);
|
||||
}
|
||||
|
||||
titleInfo.details.hasMkbSupport = supportedInputTypes.includes(SupportedInputType.MKB);
|
||||
|
||||
if (STATES.userAgent.capabilities.touch) {
|
||||
let touchControllerAvailability = getPref(PrefKey.TOUCH_CONTROLLER_MODE);
|
||||
let touchControllerAvailability = getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE);
|
||||
|
||||
// Disable touch control when gamepad found
|
||||
if (touchControllerAvailability !== TouchControllerMode.OFF && getPref(PrefKey.TOUCH_CONTROLLER_AUTO_OFF)) {
|
||||
if (touchControllerAvailability !== TouchControllerMode.OFF && getGlobalPref(GlobalPref.TOUCH_CONTROLLER_AUTO_OFF)) {
|
||||
const gamepads = window.navigator.getGamepads();
|
||||
let gamepadFound = false;
|
||||
|
||||
|
@@ -1,28 +1,5 @@
|
||||
import { BxLogger } from "./bx-logger";
|
||||
|
||||
export type BxFlags = {
|
||||
Debug: boolean;
|
||||
|
||||
CheckForUpdate: boolean;
|
||||
EnableXcloudLogging: boolean;
|
||||
SafariWorkaround: boolean;
|
||||
|
||||
ForceNativeMkbTitles: string[];
|
||||
FeatureGates: { [key: string]: boolean } | null,
|
||||
|
||||
DeviceInfo: {
|
||||
deviceType: 'android' | 'android-tv' | 'android-handheld' | 'webos' | 'unknown',
|
||||
userAgent?: string,
|
||||
|
||||
androidInfo?: {
|
||||
manufacturer: string,
|
||||
brand: string,
|
||||
board: string,
|
||||
model: string,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Setup flags
|
||||
const DEFAULT_FLAGS: BxFlags = {
|
||||
Debug: false,
|
||||
|
@@ -9,6 +9,7 @@ import iconCursorText from "@assets/svg/cursor-text.svg" with { type: "text" };
|
||||
import iconDisplay from "@assets/svg/display.svg" with { type: "text" };
|
||||
import iconEye from "@assets/svg/eye.svg" with { type: "text" };
|
||||
import iconEyeSlash from "@assets/svg/eye-slash.svg" with { type: "text" };
|
||||
// import iconGlobalRestore from "@assets/svg/global-restore.svg" with { type: "text" };
|
||||
import iconHome from "@assets/svg/home.svg" with { type: "text" };
|
||||
import iconLocalCoOp from "@assets/svg/local-co-op.svg" with { type: "text" };
|
||||
import iconNativeMkb from "@assets/svg/native-mkb.svg" with { type: "text" };
|
||||
@@ -52,6 +53,7 @@ export const BxIcon = {
|
||||
DISPLAY: iconDisplay,
|
||||
EYE: iconEye,
|
||||
EYE_SLASH: iconEyeSlash,
|
||||
// GLOBAL_RESTORE: iconGlobalRestore,
|
||||
HOME: iconHome,
|
||||
LOCAL_CO_OP: iconLocalCoOp,
|
||||
NATIVE_MKB: iconNativeMkb,
|
||||
|
@@ -1,15 +1,15 @@
|
||||
import { CE } from "@utils/html";
|
||||
import { compressCss, isLiteVersion, renderStylus } from "@macros/build" with { type: "macro" };
|
||||
import { BlockFeature, UiSection } from "@/enums/pref-values";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
|
||||
|
||||
export function addCss() {
|
||||
const STYLUS_CSS = renderStylus() as unknown as string;
|
||||
let css = STYLUS_CSS;
|
||||
|
||||
const PREF_HIDE_SECTIONS = getPref(PrefKey.UI_HIDE_SECTIONS);
|
||||
const PREF_HIDE_SECTIONS = getGlobalPref(GlobalPref.UI_HIDE_SECTIONS);
|
||||
const selectorToHide = [];
|
||||
|
||||
if (isLiteVersion()) {
|
||||
@@ -24,7 +24,7 @@ export function addCss() {
|
||||
}
|
||||
|
||||
// Hide BYOG section
|
||||
if (getPref(PrefKey.BLOCK_FEATURES).includes(BlockFeature.BYOG)) {
|
||||
if (getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.BYOG)) {
|
||||
selectorToHide.push('#BodyContent > div[class*=ByogRow-module__container___]');
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export function addCss() {
|
||||
}
|
||||
|
||||
// Hide "Start a party" button in the Guide menu
|
||||
if (getPref(PrefKey.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) {
|
||||
if (getGlobalPref(GlobalPref.BLOCK_FEATURES).includes(BlockFeature.FRIENDS)) {
|
||||
selectorToHide.push('#gamepass-dialog-root div[class^=AchievementsPreview-module__container] + button[class*=HomeLandingPage-module__button]');
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function addCss() {
|
||||
}
|
||||
|
||||
// Reduce animations
|
||||
if (getPref(PrefKey.UI_REDUCE_ANIMATIONS)) {
|
||||
if (getGlobalPref(GlobalPref.UI_REDUCE_ANIMATIONS)) {
|
||||
css += compressCss(`
|
||||
div[class*=GameCard-module__gameTitleInnerWrapper],
|
||||
div[class*=GameCard-module__card],
|
||||
@@ -65,7 +65,7 @@ div[class*=ScrollArrows-module] {
|
||||
}
|
||||
|
||||
// Hide the top-left dots icon while playing
|
||||
if (getPref(PrefKey.UI_HIDE_SYSTEM_MENU_ICON)) {
|
||||
if (getGlobalPref(GlobalPref.UI_HIDE_SYSTEM_MENU_ICON)) {
|
||||
css += compressCss(`
|
||||
div[class*=Grip-module__container] {
|
||||
visibility: hidden;
|
||||
@@ -98,7 +98,7 @@ div[class*=StreamMenu-module__menu] {
|
||||
`);
|
||||
|
||||
// Simplify Stream's menu
|
||||
if (getPref(PrefKey.UI_SIMPLIFY_STREAM_MENU)) {
|
||||
if (getGlobalPref(GlobalPref.UI_SIMPLIFY_STREAM_MENU)) {
|
||||
css += compressCss(`
|
||||
div[class*=Menu-module__scrollable] {
|
||||
--bxStreamMenuItemSize: 80px;
|
||||
@@ -158,7 +158,7 @@ body:not([data-media-type=tv]) div[class*=MenuItem-module__label] {
|
||||
}
|
||||
|
||||
// Hide scrollbar
|
||||
if (getPref(PrefKey.UI_SCROLLBAR_HIDE)) {
|
||||
if (getGlobalPref(GlobalPref.UI_SCROLLBAR_HIDE)) {
|
||||
css += compressCss(`
|
||||
html {
|
||||
scrollbar-width: none;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { BX_FLAGS } from "./bx-flags";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { BlockFeature, NativeMkbMode } from "@/enums/pref-values";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
|
||||
export let FeatureGates: { [key: string]: boolean } = {
|
||||
PwaPrompt: false,
|
||||
@@ -12,13 +12,13 @@ export let FeatureGates: { [key: string]: boolean } = {
|
||||
};
|
||||
|
||||
// Enable Native Mouse & Keyboard
|
||||
const nativeMkbMode = getPref(PrefKey.NATIVE_MKB_MODE);
|
||||
const nativeMkbMode = getGlobalPref(GlobalPref.NATIVE_MKB_MODE);
|
||||
if (nativeMkbMode !== NativeMkbMode.DEFAULT) {
|
||||
FeatureGates.EnableMouseAndKeyboard = nativeMkbMode === NativeMkbMode.ON;
|
||||
}
|
||||
|
||||
// Disable chat feature
|
||||
const blockFeatures = getPref(PrefKey.BLOCK_FEATURES);
|
||||
const blockFeatures = getGlobalPref(GlobalPref.BLOCK_FEATURES);
|
||||
if (blockFeatures.includes(BlockFeature.CHAT)) {
|
||||
FeatureGates.EnableGuideChatTab = false;
|
||||
}
|
||||
|
@@ -2,35 +2,27 @@ import { VIRTUAL_GAMEPAD_ID } from "@modules/mkb/mkb-handler";
|
||||
import { t } from "@utils/translation";
|
||||
import { Toast } from "@utils/toast";
|
||||
import { BxLogger } from "@utils/bx-logger";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { GamepadKey, GamepadKeyName } from "@/enums/gamepad";
|
||||
import { getStreamPref } from "@/utils/pref-utils";
|
||||
import { StreamPref } from "@/enums/pref-keys";
|
||||
|
||||
export type NativeMouseData = {
|
||||
X: number,
|
||||
Y: number,
|
||||
Buttons: number,
|
||||
WheelX: number,
|
||||
WheelY: number,
|
||||
Type?: 0, // 0: Relative, 1: Absolute
|
||||
}
|
||||
|
||||
export type XcloudInputChannel = {
|
||||
sendGamepadInput: (timestamp: number, gamepads: XcloudGamepad[]) => void;
|
||||
queueMouseInput: (data: NativeMouseData) => void;
|
||||
}
|
||||
|
||||
// Show a toast when connecting/disconecting controller
|
||||
export function showGamepadToast(gamepad: Gamepad) {
|
||||
// Don't show Toast for virtual controller
|
||||
// Don't show toast for virtual controller
|
||||
if (gamepad.id === VIRTUAL_GAMEPAD_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show toast when toggling local co-op feature
|
||||
if ((gamepad as any)._noToast) {
|
||||
return;
|
||||
}
|
||||
|
||||
BxLogger.info('Gamepad', gamepad);
|
||||
let text = '🎮';
|
||||
|
||||
if (getPref(PrefKey.LOCAL_CO_OP_ENABLED)) {
|
||||
if (getStreamPref(StreamPref.LOCAL_CO_OP_ENABLED)) {
|
||||
text += ` #${gamepad.index + 1}`;
|
||||
}
|
||||
|
||||
@@ -49,6 +41,10 @@ export function showGamepadToast(gamepad: Gamepad) {
|
||||
Toast.show(text, status, { instant: false });
|
||||
}
|
||||
|
||||
export function simplifyGamepadName(name: string) {
|
||||
return name.replace(/\s+\(.*Vendor: ([0-9a-f]{4}) Product: ([0-9a-f]{4})\)$/, ' ($1-$2)');
|
||||
}
|
||||
|
||||
export function getUniqueGamepadNames() {
|
||||
const gamepads = window.navigator.getGamepads();
|
||||
const names: string[] = [];
|
||||
|
@@ -4,11 +4,6 @@ import { BxLogger } from "./bx-logger";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
|
||||
|
||||
export type ForceNativeMkbResponse = {
|
||||
$schemaVersion: number;
|
||||
data: { [key: string]: string };
|
||||
}
|
||||
|
||||
export class GhPagesUtils {
|
||||
static fetchLatestCommit() {
|
||||
const url = 'https://api.github.com/repos/redphx/better-xcloud/branches/gh-pages';
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import type { BaseSettingsStore } from "./settings-storages/base-settings-storage";
|
||||
import { UserAgent } from "./user-agent";
|
||||
|
||||
export const SCRIPT_VERSION = Bun.env.SCRIPT_VERSION!;
|
||||
@@ -47,8 +46,6 @@ export const STATES: BxStates = {
|
||||
pointerServerPort: 9269,
|
||||
};
|
||||
|
||||
export const STORAGE: { [key: string]: BaseSettingsStore } = {};
|
||||
|
||||
export function deepClone(obj: any): typeof obj | {} {
|
||||
if (!obj) {
|
||||
return {};
|
||||
|
@@ -4,6 +4,7 @@ import type { NavigationNearbyElements } from "@/modules/ui/dialog/navigation-di
|
||||
import type { PresetRecord, AllPresets } from "@/types/presets";
|
||||
import { t } from "./translation";
|
||||
import type { BxSelectElement } from "@/web-components/bx-select";
|
||||
import type { AnyPref } from "@/enums/pref-keys";
|
||||
|
||||
export enum ButtonStyle {
|
||||
PRIMARY = 1,
|
||||
@@ -57,6 +58,8 @@ export type SettingsRowOptions = Partial<{
|
||||
icon: BxIconRaw,
|
||||
multiLines: boolean;
|
||||
$note: HTMLElement;
|
||||
onContextMenu: (e?: Event) => {};
|
||||
pref: AnyPref,
|
||||
}>;
|
||||
|
||||
// Quickly create a tree of elements without having to use innerHTML
|
||||
@@ -206,10 +209,12 @@ export function createButton<T=HTMLButtonElement>(options: BxButtonOptions): T {
|
||||
return $btn as T;
|
||||
}
|
||||
|
||||
export function createSettingRow(label: string, $control: HTMLElement | false | undefined, options: SettingsRowOptions={}) {
|
||||
export function createSettingRow(label: string, $control: HTMLElement | false | null | undefined, options: SettingsRowOptions={}) {
|
||||
let $label: HTMLElement;
|
||||
|
||||
const $row = CE('label', { class: 'bx-settings-row' },
|
||||
const $row = CE('label', {
|
||||
class: 'bx-settings-row',
|
||||
},
|
||||
$label = CE('span', { class: 'bx-settings-label' },
|
||||
options.icon && createSvgIcon(options.icon),
|
||||
label,
|
||||
@@ -218,6 +223,14 @@ export function createSettingRow(label: string, $control: HTMLElement | false |
|
||||
$control,
|
||||
);
|
||||
|
||||
if (options.pref) {
|
||||
($row as any).prefKey = options.pref;
|
||||
}
|
||||
|
||||
if (options.onContextMenu) {
|
||||
$row.addEventListener('contextmenu', options.onContextMenu);
|
||||
}
|
||||
|
||||
// Make link inside <label> focusable
|
||||
const $link = $label.querySelector('a');
|
||||
if ($link) {
|
||||
|
@@ -1,40 +0,0 @@
|
||||
import { BaseLocalTable } from "./base-table";
|
||||
import { LocalDb } from "./local-db";
|
||||
import { ControllerShortcutDefaultId } from "./controller-shortcuts-table";
|
||||
import { deepClone } from "../global";
|
||||
import { ControllerCustomizationDefaultPresetId } from "./controller-customizations-table";
|
||||
|
||||
export class ControllerSettingsTable extends BaseLocalTable<ControllerSettingsRecord> {
|
||||
private static instance: ControllerSettingsTable;
|
||||
public static getInstance = () => ControllerSettingsTable.instance ?? (ControllerSettingsTable.instance = new ControllerSettingsTable(LocalDb.TABLE_CONTROLLER_SETTINGS));
|
||||
|
||||
static readonly DEFAULT_DATA: ControllerSettingsRecord['data'] = {
|
||||
shortcutPresetId: ControllerShortcutDefaultId.DEFAULT,
|
||||
customizationPresetId: ControllerCustomizationDefaultPresetId.DEFAULT,
|
||||
};
|
||||
|
||||
async getControllerData(id: string): Promise<ControllerSettingsRecord['data']> {
|
||||
const setting = await this.get(id);
|
||||
if (!setting) {
|
||||
return deepClone(ControllerSettingsTable.DEFAULT_DATA);
|
||||
}
|
||||
|
||||
return setting.data;
|
||||
}
|
||||
|
||||
async getControllersData() {
|
||||
const all = await this.getAll();
|
||||
const results: { [key: string]: ControllerSettingsRecord['data'] } = {};
|
||||
|
||||
for (const key in all) {
|
||||
if (!all[key]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const settings = Object.assign(all[key].data, ControllerSettingsTable.DEFAULT_DATA);
|
||||
results[key] = settings;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
@@ -2,15 +2,15 @@ import { BxEvent } from "@utils/bx-event";
|
||||
import { STATES } from "@utils/global";
|
||||
import { BxLogger } from "@utils/bx-logger";
|
||||
import { patchSdpBitrate, setCodecPreferences } from "./sdp";
|
||||
import { StreamPlayer, type StreamPlayerOptions } from "@/modules/stream-player";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref, getPrefDefinition } from "./settings-storages/global-settings-storage";
|
||||
import { StreamPlayer } from "@/modules/stream-player";
|
||||
import { GlobalPref, StreamPref } from "@/enums/pref-keys";
|
||||
import { CodecProfile } from "@/enums/pref-values";
|
||||
import type { SettingDefinition } from "@/types/setting-definition";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
import { getGlobalPref, getGlobalPrefDefinition, getStreamPref } from "@/utils/pref-utils";
|
||||
|
||||
export function patchVideoApi() {
|
||||
const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.UI_SKIP_SPLASH_VIDEO);
|
||||
const PREF_SKIP_SPLASH_VIDEO = getGlobalPref(GlobalPref.UI_SKIP_SPLASH_VIDEO);
|
||||
|
||||
// Show video player when it's ready
|
||||
const showFunc = function(this: HTMLVideoElement) {
|
||||
@@ -20,13 +20,13 @@ export function patchVideoApi() {
|
||||
}
|
||||
|
||||
const playerOptions = {
|
||||
processing: getPref(PrefKey.VIDEO_PROCESSING),
|
||||
sharpness: getPref(PrefKey.VIDEO_SHARPNESS),
|
||||
saturation: getPref(PrefKey.VIDEO_SATURATION),
|
||||
contrast: getPref(PrefKey.VIDEO_CONTRAST),
|
||||
brightness: getPref(PrefKey.VIDEO_BRIGHTNESS),
|
||||
processing: getStreamPref(StreamPref.VIDEO_PROCESSING),
|
||||
sharpness: getStreamPref(StreamPref.VIDEO_SHARPNESS),
|
||||
saturation: getStreamPref(StreamPref.VIDEO_SATURATION),
|
||||
contrast: getStreamPref(StreamPref.VIDEO_CONTRAST),
|
||||
brightness: getStreamPref(StreamPref.VIDEO_BRIGHTNESS),
|
||||
} satisfies StreamPlayerOptions;
|
||||
STATES.currentStream.streamPlayer = new StreamPlayer(this, getPref(PrefKey.VIDEO_PLAYER_TYPE), playerOptions);
|
||||
STATES.currentStream.streamPlayer = new StreamPlayer(this, getStreamPref(StreamPref.VIDEO_PLAYER_TYPE), playerOptions);
|
||||
|
||||
BxEventBus.Stream.emit('state.playing', {
|
||||
$video: this,
|
||||
@@ -60,7 +60,7 @@ export function patchVideoApi() {
|
||||
|
||||
|
||||
export function patchRtcCodecs() {
|
||||
const codecProfile = getPref(PrefKey.STREAM_CODEC_PROFILE);
|
||||
const codecProfile = getGlobalPref(GlobalPref.STREAM_CODEC_PROFILE);
|
||||
if (codecProfile === 'default') {
|
||||
return;
|
||||
}
|
||||
@@ -80,9 +80,9 @@ export function patchRtcPeerConnection() {
|
||||
return dataChannel;
|
||||
}
|
||||
|
||||
const maxVideoBitrateDef = getPrefDefinition(PrefKey.STREAM_MAX_VIDEO_BITRATE) as Extract<SettingDefinition, { min: number }>;
|
||||
const maxVideoBitrate = getPref(PrefKey.STREAM_MAX_VIDEO_BITRATE);
|
||||
const codec = getPref(PrefKey.STREAM_CODEC_PROFILE);
|
||||
const maxVideoBitrateDef = getGlobalPrefDefinition(GlobalPref.STREAM_MAX_VIDEO_BITRATE) as Extract<SettingDefinition, { min: number }>;
|
||||
const maxVideoBitrate = getGlobalPref(GlobalPref.STREAM_MAX_VIDEO_BITRATE);
|
||||
const codec = getGlobalPref(GlobalPref.STREAM_CODEC_PROFILE);
|
||||
|
||||
if (codec !== CodecProfile.DEFAULT || maxVideoBitrate < maxVideoBitrateDef.max) {
|
||||
const nativeSetLocalDescription = RTCPeerConnection.prototype.setLocalDescription;
|
||||
@@ -113,8 +113,8 @@ export function patchRtcPeerConnection() {
|
||||
STATES.currentStream.peerConnection = conn;
|
||||
|
||||
conn.addEventListener('connectionstatechange', e => {
|
||||
BxLogger.info('connectionstatechange', conn.connectionState);
|
||||
});
|
||||
BxLogger.info('connectionstatechange', conn.connectionState);
|
||||
});
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ export function patchAudioContext() {
|
||||
|
||||
ctx.createGain = function() {
|
||||
const gainNode = nativeCreateGain.apply(this);
|
||||
gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100;
|
||||
gainNode.gain.value = getStreamPref(StreamPref.AUDIO_VOLUME) / 100;
|
||||
|
||||
STATES.currentStream.audioGainNode = gainNode;
|
||||
return gainNode;
|
||||
|
@@ -8,11 +8,10 @@ import { FeatureGates } from "./feature-gates";
|
||||
import { BxLogger } from "./bx-logger";
|
||||
import { XhomeInterceptor } from "./xhome-interceptor";
|
||||
import { XcloudInterceptor } from "./xcloud-interceptor";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import type { RemotePlayConsoleAddresses } from "@/types/network";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { BlockFeature, StreamResolution } from "@/enums/pref-values";
|
||||
import { blockAllNotifications } from "./utils";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
|
||||
type RequestType = 'xcloud' | 'xhome';
|
||||
|
||||
@@ -107,7 +106,7 @@ export async function patchIceCandidates(request: Request, consoleAddrs?: Remote
|
||||
}
|
||||
|
||||
const options = {
|
||||
preferIpv6Server: getPref(PrefKey.SERVER_PREFER_IPV6),
|
||||
preferIpv6Server: getGlobalPref(GlobalPref.SERVER_PREFER_IPV6),
|
||||
consoleAddrs: consoleAddrs,
|
||||
};
|
||||
|
||||
@@ -125,7 +124,7 @@ export async function patchIceCandidates(request: Request, consoleAddrs?: Remote
|
||||
|
||||
export function interceptHttpRequests() {
|
||||
let BLOCKED_URLS: string[] = [];
|
||||
if (getPref(PrefKey.BLOCK_TRACKING)) {
|
||||
if (getGlobalPref(GlobalPref.BLOCK_TRACKING)) {
|
||||
// Clear Applications Insight buffers
|
||||
clearAllLogs();
|
||||
|
||||
@@ -141,7 +140,7 @@ export function interceptHttpRequests() {
|
||||
|
||||
// 'https://notificationinbox.xboxlive.com',
|
||||
// 'https://accounts.xboxlive.com/family/memberXuid',
|
||||
const blockFeatures = getPref(PrefKey.BLOCK_FEATURES);
|
||||
const blockFeatures = getGlobalPref(GlobalPref.BLOCK_FEATURES);
|
||||
if (blockFeatures.includes(BlockFeature.CHAT)) {
|
||||
BLOCKED_URLS.push(
|
||||
'https://xblmessaging.xboxlive.com/network/xbox/users/me/inbox',
|
||||
|
60
src/utils/pref-utils.ts
Normal file
60
src/utils/pref-utils.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ALL_PREFS, GlobalPref, StreamPref, type AnyPref } from "@/enums/pref-keys";
|
||||
import type { PrefInfo, SettingActionOrigin } from "@/types/setting-definition";
|
||||
import { GlobalSettingsStorage } from "./settings-storages/global-settings-storage";
|
||||
import { StreamSettingsStorage } from "./settings-storages/stream-settings-storage";
|
||||
|
||||
export const STORAGE = {
|
||||
Global: new GlobalSettingsStorage(),
|
||||
Stream: new StreamSettingsStorage(),
|
||||
};
|
||||
|
||||
const streamSettingsStorage = STORAGE.Stream;
|
||||
export const getStreamPrefDefinition = streamSettingsStorage.getDefinition.bind(streamSettingsStorage);
|
||||
export const getStreamPref = streamSettingsStorage.getSetting.bind(streamSettingsStorage);
|
||||
export const setStreamPref = streamSettingsStorage.setSetting.bind(streamSettingsStorage);
|
||||
export const getGamePref = streamSettingsStorage.getSettingByGame.bind(streamSettingsStorage);
|
||||
export const setGamePref = streamSettingsStorage.setSettingByGame.bind(streamSettingsStorage);
|
||||
export const setGameIdPref = streamSettingsStorage.setGameId.bind(streamSettingsStorage);
|
||||
export const hasGamePref = streamSettingsStorage.hasGameSetting.bind(streamSettingsStorage);
|
||||
STORAGE.Stream = streamSettingsStorage;
|
||||
|
||||
const globalSettingsStorage = STORAGE.Global;
|
||||
export const getGlobalPrefDefinition = globalSettingsStorage.getDefinition.bind(globalSettingsStorage);
|
||||
export const getGlobalPref = globalSettingsStorage.getSetting.bind(globalSettingsStorage);
|
||||
export const setGlobalPref = globalSettingsStorage.setSetting.bind(globalSettingsStorage);
|
||||
|
||||
|
||||
export function isGlobalPref(prefKey: AnyPref): prefKey is GlobalPref {
|
||||
return ALL_PREFS.global.includes(prefKey as GlobalPref);
|
||||
}
|
||||
|
||||
export function isStreamPref(prefKey: AnyPref): prefKey is StreamPref {
|
||||
return ALL_PREFS.stream.includes(prefKey as StreamPref);
|
||||
}
|
||||
|
||||
export function getPrefInfo(prefKey: AnyPref): PrefInfo {
|
||||
if (isGlobalPref(prefKey)) {
|
||||
return {
|
||||
storage: STORAGE.Global,
|
||||
definition: getGlobalPrefDefinition(prefKey as GlobalPref),
|
||||
// value: getGlobalPref(prefKey as GlobalPref),
|
||||
}
|
||||
} else if (isStreamPref(prefKey)) {
|
||||
return {
|
||||
storage: STORAGE.Stream,
|
||||
definition: getStreamPrefDefinition(prefKey as StreamPref),
|
||||
// value: getStreamPref(prefKey as StreamPref),
|
||||
}
|
||||
}
|
||||
|
||||
alert('Missing pref definition: ' + prefKey);
|
||||
return {} as PrefInfo;
|
||||
}
|
||||
|
||||
export function setPref(prefKey: AnyPref, value: any, origin: SettingActionOrigin) {
|
||||
if (isGlobalPref(prefKey)) {
|
||||
setGlobalPref(prefKey as GlobalPref, value, origin);
|
||||
} else if (isStreamPref(prefKey)) {
|
||||
setStreamPref(prefKey as StreamPref, value, origin);
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
import { STATES } from "@utils/global";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
|
||||
|
||||
export function getPreferredServerRegion(shortName = false): string | null {
|
||||
let preferredRegion = getPref(PrefKey.SERVER_REGION);
|
||||
let preferredRegion = getGlobalPref(GlobalPref.SERVER_REGION);
|
||||
const serverRegions = STATES.serverRegions;
|
||||
|
||||
// Return preferred region
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { AppInterface, STATES } from "./global";
|
||||
import { CE } from "./html";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { BxLogger } from "./bx-logger";
|
||||
import { StreamPlayerType } from "@/enums/pref-values";
|
||||
import { getGlobalPref } from "@/utils/pref-utils";
|
||||
|
||||
|
||||
export class ScreenshotManager {
|
||||
@@ -49,7 +49,7 @@ export class ScreenshotManager {
|
||||
}
|
||||
|
||||
let $player;
|
||||
if (getPref(PrefKey.SCREENSHOT_APPLY_FILTERS)) {
|
||||
if (getGlobalPref(GlobalPref.SCREENSHOT_APPLY_FILTERS)) {
|
||||
$player = streamPlayer.getPlayerElement();
|
||||
} else {
|
||||
$player = streamPlayer.getPlayerElement(StreamPlayerType.VIDEO);
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import type { PreferenceSetting } from "@/types/preferences";
|
||||
import { CE, escapeCssSelector } from "@utils/html";
|
||||
import type { PrefKey } from "@/enums/pref-keys";
|
||||
import type { BaseSettingsStore } from "./settings-storages/base-settings-storage";
|
||||
import type { AnyPref } from "@/enums/pref-keys";
|
||||
import { type BaseSettingDefinition, type MultipleOptionsParams, type NumberStepperParams } from "@/types/setting-definition";
|
||||
import { BxEvent } from "./bx-event";
|
||||
import { BxNumberStepper } from "@/web-components/bx-number-stepper";
|
||||
import { getPrefInfo, isGlobalPref, setGlobalPref, setGamePref } from "./pref-utils";
|
||||
import { SettingsManager } from "@/modules/settings-manager";
|
||||
|
||||
export enum SettingElementType {
|
||||
OPTIONS = 'options',
|
||||
@@ -107,6 +107,20 @@ export class SettingElement {
|
||||
!(e as any).ignoreOnChange && onChange(e, values);
|
||||
});
|
||||
|
||||
Object.defineProperty($control, 'value', {
|
||||
get() {
|
||||
return Array.from($control.options)
|
||||
.filter(option => option.selected)
|
||||
.map(option => option.value);
|
||||
},
|
||||
set(value) {
|
||||
const values = value.split(',');
|
||||
Array.from($control.options).forEach(option => {
|
||||
option.selected = values.includes(option.value);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return $control;
|
||||
}
|
||||
|
||||
@@ -154,10 +168,14 @@ export class SettingElement {
|
||||
return $control;
|
||||
}
|
||||
|
||||
static fromPref(key: PrefKey, storage: BaseSettingsStore, onChange: any, overrideParams={}) {
|
||||
const definition = storage.getDefinition(key);
|
||||
static fromPref(key: AnyPref, onChange?: ((e: Event, value: any) => void) | null | undefined, overrideParams={}) {
|
||||
const { definition, storage } = getPrefInfo(key);
|
||||
if (!definition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
let currentValue = storage.getSetting(key);
|
||||
let currentValue = storage.getSetting(key) as any;
|
||||
|
||||
let type;
|
||||
if ('options' in definition) {
|
||||
@@ -179,8 +197,14 @@ export class SettingElement {
|
||||
currentValue = definition.default;
|
||||
}
|
||||
|
||||
const $control = SettingElement.render(type!, key as string, definition, currentValue, (e: any, value: any) => {
|
||||
storage.setSetting(key, value);
|
||||
const $control = SettingElement.render(type!, key as string, definition, currentValue, (e: Event, value: any) => {
|
||||
if (isGlobalPref(key)) {
|
||||
setGlobalPref(key, value, 'ui');
|
||||
} else {
|
||||
const id = SettingsManager.getInstance().getTargetGameId();
|
||||
setGamePref(id, key, value, 'ui');
|
||||
}
|
||||
|
||||
onChange && onChange(e, value);
|
||||
}, params);
|
||||
|
||||
|
@@ -1,23 +1,22 @@
|
||||
import type { PrefKey, PrefTypeMap, StorageKey } from "@/enums/pref-keys";
|
||||
import type { NumberStepperParams, SettingAction, SettingDefinitions } from "@/types/setting-definition";
|
||||
import type { AnyPref, PrefTypeMap, StorageKey } from "@/enums/pref-keys";
|
||||
import type { NumberStepperParams, SettingAction, SettingActionOrigin, SettingDefinition, SettingDefinitions } from "@/types/setting-definition";
|
||||
import { t } from "../translation";
|
||||
import { SCRIPT_VARIANT } from "../global";
|
||||
import { deepClone, SCRIPT_VARIANT } from "../global";
|
||||
import { BxEventBus } from "../bx-event-bus";
|
||||
import { isStreamPref } from "../pref-utils";
|
||||
import { isPlainObject } from "../utils";
|
||||
|
||||
export class BaseSettingsStore {
|
||||
export class BaseSettingsStorage<T extends AnyPref> {
|
||||
private storage: Storage;
|
||||
private storageKey: StorageKey;
|
||||
private storageKey: StorageKey | StorageKey.STREAM | `${StorageKey.STREAM}.${number}`;
|
||||
private _settings: object | null;
|
||||
private definitions: SettingDefinitions;
|
||||
private definitions: SettingDefinitions<T>;
|
||||
|
||||
constructor(storageKey: StorageKey, definitions: SettingDefinitions) {
|
||||
constructor(storageKey: typeof this.storageKey, definitions:SettingDefinitions<T>) {
|
||||
this.storage = window.localStorage;
|
||||
this.storageKey = storageKey;
|
||||
|
||||
let settingId: keyof typeof definitions
|
||||
for (settingId in definitions) {
|
||||
const setting = definitions[settingId];
|
||||
|
||||
for (const [_, setting] of Object.entries(definitions) as [T, SettingDefinition][]) {
|
||||
// Convert requiredVariants to array
|
||||
if (typeof setting.requiredVariants === 'string') {
|
||||
setting.requiredVariants = [setting.requiredVariants];
|
||||
@@ -45,59 +44,69 @@ export class BaseSettingsStore {
|
||||
|
||||
// Validate setting values
|
||||
for (const key in settings) {
|
||||
settings[key] = this.validateValue('get', key as PrefKey, settings[key]);
|
||||
settings[key] = this.validateValue('get', key as T, settings[key]);
|
||||
}
|
||||
|
||||
this._settings = settings;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
getDefinition(key: PrefKey) {
|
||||
getDefinition(key: T) {
|
||||
if (!this.definitions[key]) {
|
||||
const error = 'Request invalid definition: ' + key;
|
||||
alert(error);
|
||||
throw Error(error);
|
||||
alert('Request invalid definition: ' + key);
|
||||
return {} as SettingDefinition;
|
||||
}
|
||||
|
||||
return this.definitions[key];
|
||||
}
|
||||
|
||||
getSetting<T extends keyof PrefTypeMap>(key: T, checkUnsupported = true): PrefTypeMap[T] {
|
||||
const definition = this.definitions[key];
|
||||
hasSetting<K extends keyof PrefTypeMap<K>>(key: K): boolean {
|
||||
return key in this.settings;
|
||||
}
|
||||
|
||||
getSetting<K extends keyof PrefTypeMap<K>>(key: K, checkUnsupported = true): PrefTypeMap<K>[K] {
|
||||
const definition = this.definitions[key] as SettingDefinition;
|
||||
|
||||
// Return default value if build variant is different
|
||||
if (definition.requiredVariants && !definition.requiredVariants.includes(SCRIPT_VARIANT)) {
|
||||
return definition.default as PrefTypeMap[T];
|
||||
return (isPlainObject(definition.default) ? deepClone(definition.default) : definition.default) as PrefTypeMap<K>[K];
|
||||
}
|
||||
|
||||
// Return default value if the feature is not supported
|
||||
if (checkUnsupported && definition.unsupported) {
|
||||
if ('unsupportedValue' in definition) {
|
||||
return definition.unsupportedValue as PrefTypeMap[T];
|
||||
return definition.unsupportedValue as PrefTypeMap<K>[K];
|
||||
} else {
|
||||
return definition.default as PrefTypeMap[T];
|
||||
return (isPlainObject(definition.default) ? deepClone(definition.default) : definition.default) as PrefTypeMap<K>[K];
|
||||
}
|
||||
}
|
||||
|
||||
if (!(key in this.settings)) {
|
||||
this.settings[key] = this.validateValue('get', key, null);
|
||||
this.settings[key] = this.validateValue('get', key as any, null);
|
||||
}
|
||||
|
||||
return this.settings[key] as PrefTypeMap[T];
|
||||
return (isPlainObject(this.settings[key]) ? deepClone(this.settings[key]) : this.settings[key]) as PrefTypeMap<K>[K];
|
||||
}
|
||||
|
||||
setSetting<T=any>(key: PrefKey, value: T, emitEvent = false) {
|
||||
setSetting<V=any>(key: T, value: V, origin: SettingActionOrigin) {
|
||||
value = this.validateValue('set', key, value);
|
||||
|
||||
this.settings[key] = this.validateValue('get', key, value);
|
||||
this.saveSettings();
|
||||
|
||||
emitEvent && BxEventBus.Script.emit('setting.changed', {
|
||||
storageKey: this.storageKey,
|
||||
settingKey: key,
|
||||
settingValue: value,
|
||||
});
|
||||
if (origin === 'ui') {
|
||||
if (isStreamPref(key)) {
|
||||
BxEventBus.Stream.emit('setting.changed', {
|
||||
storageKey: this.storageKey as any,
|
||||
settingKey: key,
|
||||
});
|
||||
} else {
|
||||
BxEventBus.Script.emit('setting.changed', {
|
||||
storageKey: this.storageKey,
|
||||
settingKey: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -106,8 +115,8 @@ export class BaseSettingsStore {
|
||||
this.storage.setItem(this.storageKey, JSON.stringify(this.settings));
|
||||
}
|
||||
|
||||
private validateValue(action: SettingAction, key: PrefKey, value: any) {
|
||||
const def = this.definitions[key];
|
||||
private validateValue(action: SettingAction, key: T, value: any) {
|
||||
const def = this.definitions[key] as SettingDefinition;
|
||||
if (!def) {
|
||||
return value;
|
||||
}
|
||||
@@ -154,12 +163,12 @@ export class BaseSettingsStore {
|
||||
return value;
|
||||
}
|
||||
|
||||
getLabel(key: PrefKey): string {
|
||||
return this.definitions[key].label || key;
|
||||
getLabel(key: T): string {
|
||||
return (this.definitions[key] as SettingDefinition).label || key;
|
||||
}
|
||||
|
||||
getValueText(key: PrefKey, value: any): string {
|
||||
const definition = this.definitions[key];
|
||||
getValueText(key: T, value: any): string {
|
||||
const definition = this.definitions[key] as SettingDefinition;
|
||||
if ('min' in definition) {
|
||||
const params = (definition as any).params as NumberStepperParams;
|
||||
if (params.customTextValue) {
|
||||
|
20
src/utils/settings-storages/game-settings-storage.ts
Normal file
20
src/utils/settings-storages/game-settings-storage.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { StorageKey, type StreamPref } from "@/enums/pref-keys";
|
||||
import { BaseSettingsStorage } from "./base-settings-storage";
|
||||
import { StreamSettingsStorage } from "./stream-settings-storage";
|
||||
|
||||
export class GameSettingsStorage extends BaseSettingsStorage<StreamPref> {
|
||||
constructor(id: number) {
|
||||
super(`${StorageKey.STREAM}.${id}`, StreamSettingsStorage.DEFINITIONS);
|
||||
}
|
||||
|
||||
deleteSetting(pref: StreamPref) {
|
||||
if (this.hasSetting(pref)) {
|
||||
delete this.settings[pref];
|
||||
this.saveSettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -1,16 +1,14 @@
|
||||
import { BypassServers } from "@/enums/bypass-servers";
|
||||
import { PrefKey, StorageKey } from "@/enums/pref-keys";
|
||||
import { GlobalPref, StorageKey, type GlobalPrefTypeMap } from "@/enums/pref-keys";
|
||||
import { UserAgentProfile } from "@/enums/user-agent";
|
||||
import { type SettingDefinition, type SettingDefinitions } from "@/types/setting-definition";
|
||||
import { type SettingDefinition } from "@/types/setting-definition";
|
||||
import { BX_FLAGS } from "../bx-flags";
|
||||
import { STATES, AppInterface, STORAGE } from "../global";
|
||||
import { STATES, AppInterface } from "../global";
|
||||
import { CE } from "../html";
|
||||
import { t, SUPPORTED_LANGUAGES } from "../translation";
|
||||
import { UserAgent } from "../user-agent";
|
||||
import { BaseSettingsStore as BaseSettingsStorage } from "./base-settings-storage";
|
||||
import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerStyleStandard, TouchControllerStyleCustom, GameBarPosition, DeviceVibrationMode, NativeMkbMode, UiLayout, UiSection, StreamPlayerType, StreamVideoProcessing, VideoRatio, StreamStat, VideoPosition, BlockFeature, StreamStatPosition, VideoPowerPreference } from "@/enums/pref-values";
|
||||
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
|
||||
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
|
||||
import { BaseSettingsStorage } from "./base-settings-storage";
|
||||
import { CodecProfile, StreamResolution, TouchControllerMode, TouchControllerStyleStandard, TouchControllerStyleCustom, GameBarPosition, NativeMkbMode, UiLayout, UiSection, BlockFeature } from "@/enums/pref-values";
|
||||
import { GhPagesUtils } from "../gh-pages";
|
||||
import { BxEventBus } from "../bx-event-bus";
|
||||
import { BxIcon } from "../bx-icon";
|
||||
@@ -72,28 +70,28 @@ function getSupportedCodecProfiles() {
|
||||
return options;
|
||||
}
|
||||
|
||||
export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
private static readonly DEFINITIONS = {
|
||||
[PrefKey.VERSION_LAST_CHECK]: {
|
||||
export class GlobalSettingsStorage extends BaseSettingsStorage<GlobalPref> {
|
||||
private static readonly DEFINITIONS: Record<keyof GlobalPrefTypeMap, SettingDefinition> = {
|
||||
[GlobalPref.VERSION_LAST_CHECK]: {
|
||||
default: 0,
|
||||
},
|
||||
[PrefKey.VERSION_LATEST]: {
|
||||
[GlobalPref.VERSION_LATEST]: {
|
||||
default: '',
|
||||
},
|
||||
[PrefKey.VERSION_CURRENT]: {
|
||||
[GlobalPref.VERSION_CURRENT]: {
|
||||
default: '',
|
||||
},
|
||||
[PrefKey.SCRIPT_LOCALE]: {
|
||||
[GlobalPref.SCRIPT_LOCALE]: {
|
||||
label: t('language'),
|
||||
default: localStorage.getItem(StorageKey.LOCALE) || 'en-US',
|
||||
options: SUPPORTED_LANGUAGES,
|
||||
},
|
||||
[PrefKey.SERVER_REGION]: {
|
||||
[GlobalPref.SERVER_REGION]: {
|
||||
label: t('region'),
|
||||
note: CE('a', { target: '_blank', href: 'https://umap.openstreetmap.fr/en/map/xbox-cloud-gaming-servers_1135022' }, t('server-locations')),
|
||||
default: 'default',
|
||||
},
|
||||
[PrefKey.SERVER_BYPASS_RESTRICTION]: {
|
||||
[GlobalPref.SERVER_BYPASS_RESTRICTION]: {
|
||||
label: t('bypass-region-restriction'),
|
||||
note: '⚠️ ' + t('use-this-at-your-own-risk'),
|
||||
default: 'off',
|
||||
@@ -103,7 +101,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
}, BypassServers),
|
||||
},
|
||||
|
||||
[PrefKey.STREAM_PREFERRED_LOCALE]: {
|
||||
[GlobalPref.STREAM_PREFERRED_LOCALE]: {
|
||||
label: t('preferred-game-language'),
|
||||
default: 'default',
|
||||
options: {
|
||||
@@ -140,7 +138,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
'zh-TW': '中文 (繁體)',
|
||||
},
|
||||
},
|
||||
[PrefKey.STREAM_RESOLUTION]: {
|
||||
[GlobalPref.STREAM_RESOLUTION]: {
|
||||
label: t('target-resolution'),
|
||||
default: 'auto',
|
||||
options: {
|
||||
@@ -155,7 +153,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.STREAM_CODEC_PROFILE]: {
|
||||
[GlobalPref.STREAM_CODEC_PROFILE]: {
|
||||
label: t('visual-quality'),
|
||||
default: CodecProfile.DEFAULT,
|
||||
options: getSupportedCodecProfiles(),
|
||||
@@ -174,26 +172,26 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
};
|
||||
},
|
||||
},
|
||||
[PrefKey.SERVER_PREFER_IPV6]: {
|
||||
[GlobalPref.SERVER_PREFER_IPV6]: {
|
||||
label: t('prefer-ipv6-server'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.SCREENSHOT_APPLY_FILTERS]: {
|
||||
[GlobalPref.SCREENSHOT_APPLY_FILTERS]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('screenshot-apply-filters'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.UI_SKIP_SPLASH_VIDEO]: {
|
||||
[GlobalPref.UI_SKIP_SPLASH_VIDEO]: {
|
||||
label: t('skip-splash-video'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.UI_HIDE_SYSTEM_MENU_ICON]: {
|
||||
label: t('hide-system-menu-icon'),
|
||||
[GlobalPref.UI_HIDE_SYSTEM_MENU_ICON]: {
|
||||
label: '⣿ ' + t('hide-system-menu-icon'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.UI_IMAGE_QUALITY]: {
|
||||
[GlobalPref.UI_IMAGE_QUALITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('image-quality'),
|
||||
default: 90,
|
||||
@@ -213,7 +211,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.STREAM_COMBINE_SOURCES]: {
|
||||
[GlobalPref.STREAM_COMBINE_SOURCES]: {
|
||||
requiredVariants: 'full',
|
||||
|
||||
label: t('combine-audio-video-streams'),
|
||||
@@ -222,28 +220,28 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
note: t('combine-audio-video-streams-summary'),
|
||||
},
|
||||
|
||||
[PrefKey.TOUCH_CONTROLLER_MODE]: {
|
||||
[GlobalPref.TOUCH_CONTROLLER_MODE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('tc-availability'),
|
||||
label: t('availability'),
|
||||
default: TouchControllerMode.ALL,
|
||||
options: {
|
||||
[TouchControllerMode.DEFAULT]: t('default'),
|
||||
[TouchControllerMode.OFF]: t('off'),
|
||||
[TouchControllerMode.ALL]: t('tc-all-games'),
|
||||
[TouchControllerMode.ALL]: t('all-games'),
|
||||
},
|
||||
|
||||
unsupported: !STATES.userAgent.capabilities.touch,
|
||||
unsupportedValue: TouchControllerMode.DEFAULT,
|
||||
},
|
||||
[PrefKey.TOUCH_CONTROLLER_AUTO_OFF]: {
|
||||
[GlobalPref.TOUCH_CONTROLLER_AUTO_OFF]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('tc-auto-off'),
|
||||
default: false,
|
||||
unsupported: !STATES.userAgent.capabilities.touch,
|
||||
},
|
||||
[PrefKey.TOUCH_CONTROLLER_DEFAULT_OPACITY]: {
|
||||
[GlobalPref.TOUCH_CONTROLLER_DEFAULT_OPACITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('tc-default-opacity'),
|
||||
label: t('default-opacity'),
|
||||
default: 100,
|
||||
min: 10,
|
||||
max: 100,
|
||||
@@ -255,7 +253,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
unsupported: !STATES.userAgent.capabilities.touch,
|
||||
},
|
||||
[PrefKey.TOUCH_CONTROLLER_STYLE_STANDARD]: {
|
||||
[GlobalPref.TOUCH_CONTROLLER_STYLE_STANDARD]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('tc-standard-layout-style'),
|
||||
default: TouchControllerStyleStandard.DEFAULT,
|
||||
@@ -266,7 +264,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
unsupported: !STATES.userAgent.capabilities.touch,
|
||||
},
|
||||
[PrefKey.TOUCH_CONTROLLER_STYLE_CUSTOM]: {
|
||||
[GlobalPref.TOUCH_CONTROLLER_STYLE_CUSTOM]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('tc-custom-layout-style'),
|
||||
default: TouchControllerStyleCustom.DEFAULT,
|
||||
@@ -277,22 +275,22 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
unsupported: !STATES.userAgent.capabilities.touch,
|
||||
},
|
||||
|
||||
[PrefKey.UI_SIMPLIFY_STREAM_MENU]: {
|
||||
[GlobalPref.UI_SIMPLIFY_STREAM_MENU]: {
|
||||
label: t('simplify-stream-menu'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.MKB_HIDE_IDLE_CURSOR]: {
|
||||
[GlobalPref.MKB_HIDE_IDLE_CURSOR]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('hide-idle-cursor'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.UI_DISABLE_FEEDBACK_DIALOG]: {
|
||||
[GlobalPref.UI_DISABLE_FEEDBACK_DIALOG]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('disable-post-stream-feedback-dialog'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.STREAM_MAX_VIDEO_BITRATE]: {
|
||||
[GlobalPref.STREAM_MAX_VIDEO_BITRATE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('bitrate-video-maximum'),
|
||||
note: '⚠️ ' + t('unexpected-behavior'),
|
||||
@@ -326,7 +324,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.GAME_BAR_POSITION]: {
|
||||
[GlobalPref.GAME_BAR_POSITION]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('position'),
|
||||
default: GameBarPosition.BOTTOM_LEFT,
|
||||
@@ -337,74 +335,12 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.LOCAL_CO_OP_ENABLED]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('enable-local-co-op-support'),
|
||||
labelIcon: BxIcon.LOCAL_CO_OP,
|
||||
default: false,
|
||||
note: () => CE('div', false,
|
||||
CE('a', {
|
||||
href: 'https://github.com/redphx/better-xcloud/discussions/275',
|
||||
target: '_blank',
|
||||
}, t('enable-local-co-op-support-note')),
|
||||
CE('br'),
|
||||
'⚠️ ' + t('unexpected-behavior'),
|
||||
),
|
||||
},
|
||||
|
||||
[PrefKey.UI_CONTROLLER_SHOW_STATUS]: {
|
||||
[GlobalPref.UI_CONTROLLER_SHOW_STATUS]: {
|
||||
label: t('show-controller-connection-status'),
|
||||
default: true,
|
||||
},
|
||||
|
||||
[PrefKey.DEVICE_VIBRATION_MODE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('device-vibration'),
|
||||
default: DeviceVibrationMode.OFF,
|
||||
options: {
|
||||
[DeviceVibrationMode.OFF]: t('off'),
|
||||
[DeviceVibrationMode.ON]: t('on'),
|
||||
[DeviceVibrationMode.AUTO]: t('device-vibration-not-using-gamepad'),
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.DEVICE_VIBRATION_INTENSITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('vibration-intensity'),
|
||||
default: 50,
|
||||
min: 10,
|
||||
max: 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
exactTicks: 20,
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.CONTROLLER_POLLING_RATE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('polling-rate'),
|
||||
default: 4,
|
||||
min: 4,
|
||||
max: 60,
|
||||
params: {
|
||||
steps: 4,
|
||||
exactTicks: 20,
|
||||
reverse: true,
|
||||
customTextValue(value: any) {
|
||||
value = parseInt(value);
|
||||
|
||||
let text = +(1000 / value).toFixed(2) + ' Hz';
|
||||
if (value === 4) {
|
||||
text = `${text} (${t('default')})`;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.MKB_ENABLED]: {
|
||||
[GlobalPref.MKB_ENABLED]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('enable-mkb'),
|
||||
default: false,
|
||||
@@ -427,7 +363,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.NATIVE_MKB_MODE]: {
|
||||
[GlobalPref.NATIVE_MKB_MODE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('native-mkb'),
|
||||
default: NativeMkbMode.DEFAULT,
|
||||
@@ -449,7 +385,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.NATIVE_MKB_FORCED_GAMES]: {
|
||||
[GlobalPref.NATIVE_MKB_FORCED_GAMES]: {
|
||||
label: t('force-native-mkb-games'),
|
||||
default: [],
|
||||
unsupported: !AppInterface && UserAgent.isMobile(),
|
||||
@@ -467,98 +403,21 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('horizontal-scroll-sensitivity'),
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 100 * 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
exactTicks: 20 * 100,
|
||||
customTextValue: (value: any) => {
|
||||
if (!value) {
|
||||
return t('default');
|
||||
}
|
||||
|
||||
return (value / 100).toFixed(1) + 'x';
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('vertical-scroll-sensitivity'),
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 100 * 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
exactTicks: 20 * 100,
|
||||
customTextValue: (value: any) => {
|
||||
if (!value) {
|
||||
return t('default');
|
||||
}
|
||||
|
||||
return (value / 100).toFixed(1) + 'x';
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.MKB_P1_MAPPING_PRESET_ID]: {
|
||||
requiredVariants: 'full',
|
||||
default: MkbMappingDefaultPresetId.DEFAULT,
|
||||
},
|
||||
|
||||
[PrefKey.MKB_P1_SLOT]: {
|
||||
requiredVariants: 'full',
|
||||
default: 1,
|
||||
min: 1,
|
||||
max: 4,
|
||||
params: {
|
||||
hideSlider: true,
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.MKB_P2_MAPPING_PRESET_ID]: {
|
||||
requiredVariants: 'full',
|
||||
default: MkbMappingDefaultPresetId.OFF,
|
||||
},
|
||||
|
||||
[PrefKey.MKB_P2_SLOT]: {
|
||||
requiredVariants: 'full',
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 4,
|
||||
params: {
|
||||
hideSlider: true,
|
||||
customTextValue(value) {
|
||||
value = parseInt(value);
|
||||
return (value === 0) ? t('off') : value.toString();
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID]: {
|
||||
requiredVariants: 'full',
|
||||
default: KeyboardShortcutDefaultId.DEFAULT,
|
||||
},
|
||||
|
||||
[PrefKey.UI_REDUCE_ANIMATIONS]: {
|
||||
[GlobalPref.UI_REDUCE_ANIMATIONS]: {
|
||||
label: t('reduce-animations'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.LOADING_SCREEN_GAME_ART]: {
|
||||
[GlobalPref.LOADING_SCREEN_GAME_ART]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('show-game-art'),
|
||||
default: true,
|
||||
},
|
||||
[PrefKey.LOADING_SCREEN_SHOW_WAIT_TIME]: {
|
||||
[GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME]: {
|
||||
label: t('show-wait-time'),
|
||||
default: true,
|
||||
},
|
||||
[PrefKey.LOADING_SCREEN_ROCKET]: {
|
||||
[GlobalPref.LOADING_SCREEN_ROCKET]: {
|
||||
label: t('rocket-animation'),
|
||||
default: 'show',
|
||||
options: {
|
||||
@@ -568,12 +427,12 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.UI_CONTROLLER_FRIENDLY]: {
|
||||
[GlobalPref.UI_CONTROLLER_FRIENDLY]: {
|
||||
label: t('controller-friendly-ui'),
|
||||
default: BX_FLAGS.DeviceInfo.deviceType !== 'unknown',
|
||||
},
|
||||
|
||||
[PrefKey.UI_LAYOUT]: {
|
||||
[GlobalPref.UI_LAYOUT]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('layout'),
|
||||
default: UiLayout.DEFAULT,
|
||||
@@ -584,12 +443,12 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.UI_SCROLLBAR_HIDE]: {
|
||||
[GlobalPref.UI_SCROLLBAR_HIDE]: {
|
||||
label: t('hide-scrollbar'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.UI_HIDE_SECTIONS]: {
|
||||
[GlobalPref.UI_HIDE_SECTIONS]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('hide-sections'),
|
||||
default: [],
|
||||
@@ -607,17 +466,17 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.UI_GAME_CARD_SHOW_WAIT_TIME]: {
|
||||
[GlobalPref.UI_GAME_CARD_SHOW_WAIT_TIME]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('show-wait-time-in-game-card'),
|
||||
default: true,
|
||||
},
|
||||
|
||||
[PrefKey.BLOCK_TRACKING]: {
|
||||
[GlobalPref.BLOCK_TRACKING]: {
|
||||
label: t('disable-xcloud-analytics'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.BLOCK_FEATURES]: {
|
||||
[GlobalPref.BLOCK_FEATURES]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('disable-features'),
|
||||
default: [],
|
||||
@@ -631,7 +490,7 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
|
||||
|
||||
[PrefKey.USER_AGENT_PROFILE]: {
|
||||
[GlobalPref.USER_AGENT_PROFILE]: {
|
||||
label: t('user-agent-profile'),
|
||||
note: '⚠️ ' + t('unexpected-behavior'),
|
||||
default: (BX_FLAGS.DeviceInfo.deviceType === 'android-tv' || BX_FLAGS.DeviceInfo.deviceType === 'webos') ? UserAgentProfile.VR_OCULUS : 'default',
|
||||
@@ -645,246 +504,24 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
[UserAgentProfile.CUSTOM]: t('custom'),
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_PLAYER_TYPE]: {
|
||||
label: t('renderer'),
|
||||
default: StreamPlayerType.VIDEO,
|
||||
options: {
|
||||
[StreamPlayerType.VIDEO]: t('default'),
|
||||
[StreamPlayerType.WEBGL2]: t('webgl2'),
|
||||
},
|
||||
suggest: {
|
||||
lowest: StreamPlayerType.VIDEO,
|
||||
highest: StreamPlayerType.WEBGL2,
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_PROCESSING]: {
|
||||
label: t('clarity-boost'),
|
||||
default: StreamVideoProcessing.USM,
|
||||
options: {
|
||||
[StreamVideoProcessing.USM]: t('unsharp-masking'),
|
||||
[StreamVideoProcessing.CAS]: t('amd-fidelity-cas'),
|
||||
},
|
||||
suggest: {
|
||||
lowest: StreamVideoProcessing.USM,
|
||||
highest: StreamVideoProcessing.CAS,
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_POWER_PREFERENCE]: {
|
||||
label: t('renderer-configuration'),
|
||||
default: VideoPowerPreference.DEFAULT,
|
||||
options: {
|
||||
[VideoPowerPreference.DEFAULT]: t('default'),
|
||||
[VideoPowerPreference.LOW_POWER]: t('battery-saving'),
|
||||
[VideoPowerPreference.HIGH_PERFORMANCE]: t('high-performance'),
|
||||
},
|
||||
suggest: {
|
||||
highest: 'low-power',
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_MAX_FPS]: {
|
||||
label: t('limit-fps'),
|
||||
default: 60,
|
||||
min: 10,
|
||||
max: 60,
|
||||
params: {
|
||||
steps: 10,
|
||||
exactTicks: 10,
|
||||
customTextValue: (value: any) => {
|
||||
value = parseInt(value);
|
||||
return value === 60 ? t('unlimited') : value + 'fps';
|
||||
},
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_SHARPNESS]: {
|
||||
label: t('sharpness'),
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 10,
|
||||
params: {
|
||||
exactTicks: 2,
|
||||
customTextValue: (value: any) => {
|
||||
value = parseInt(value);
|
||||
return value === 0 ? t('off') : value.toString();
|
||||
},
|
||||
},
|
||||
suggest: {
|
||||
lowest: 0,
|
||||
highest: 2,
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_RATIO]: {
|
||||
label: t('aspect-ratio'),
|
||||
note: STATES.browser.capabilities.touch ? t('aspect-ratio-note') : undefined,
|
||||
default: VideoRatio['16:9'],
|
||||
options: {
|
||||
[VideoRatio['16:9']]: `16:9 (${t('default')})`,
|
||||
[VideoRatio['18:9']]: '18:9',
|
||||
[VideoRatio['21:9']]: '21:9',
|
||||
[VideoRatio['16:10']]: '16:10',
|
||||
[VideoRatio['4:3']]: '4:3',
|
||||
|
||||
[VideoRatio.FILL]: t('stretch'),
|
||||
//'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]: {
|
||||
label: t('saturation'),
|
||||
default: 100,
|
||||
min: 50,
|
||||
max: 150,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_CONTRAST]: {
|
||||
label: t('contrast'),
|
||||
default: 100,
|
||||
min: 50,
|
||||
max: 150,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
[PrefKey.VIDEO_BRIGHTNESS]: {
|
||||
label: t('brightness'),
|
||||
default: 100,
|
||||
min: 50,
|
||||
max: 150,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.AUDIO_MIC_ON_PLAYING]: {
|
||||
[GlobalPref.AUDIO_MIC_ON_PLAYING]: {
|
||||
label: t('enable-mic-on-startup'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.AUDIO_VOLUME_CONTROL_ENABLED]: {
|
||||
[GlobalPref.AUDIO_VOLUME_CONTROL_ENABLED]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('enable-volume-control'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.AUDIO_VOLUME]: {
|
||||
label: t('volume'),
|
||||
default: 100,
|
||||
min: 0,
|
||||
max: 600,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
ticks: 100,
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.STATS_ITEMS]: {
|
||||
label: t('stats'),
|
||||
default: [StreamStat.PING, StreamStat.FPS, StreamStat.BITRATE, StreamStat.DECODE_TIME, StreamStat.PACKETS_LOST, StreamStat.FRAMES_LOST],
|
||||
multipleOptions: {
|
||||
[StreamStat.CLOCK]: t('clock'),
|
||||
[StreamStat.PLAYTIME]: t('playtime'),
|
||||
[StreamStat.BATTERY]: t('battery'),
|
||||
[StreamStat.PING]: t('stat-ping'),
|
||||
[StreamStat.JITTER]: t('jitter'),
|
||||
[StreamStat.FPS]: t('stat-fps'),
|
||||
[StreamStat.BITRATE]: t('stat-bitrate'),
|
||||
[StreamStat.DECODE_TIME]: t('stat-decode-time'),
|
||||
[StreamStat.PACKETS_LOST]: t('stat-packets-lost'),
|
||||
[StreamStat.FRAMES_LOST]: t('stat-frames-lost'),
|
||||
[StreamStat.DOWNLOAD]: t('downloaded'),
|
||||
[StreamStat.UPLOAD]: t('uploaded'),
|
||||
},
|
||||
params: {
|
||||
size: 0,
|
||||
},
|
||||
ready: setting => {
|
||||
// Remove Battery option in unsupported browser
|
||||
const multipleOptions = (setting as any).multipleOptions;
|
||||
if (!STATES.browser.capabilities.batteryApi) {
|
||||
delete multipleOptions[StreamStat.BATTERY];
|
||||
}
|
||||
|
||||
// Update texts
|
||||
for (const key in multipleOptions) {
|
||||
multipleOptions[key] = (key as string).toUpperCase() + ': ' + multipleOptions[key];
|
||||
}
|
||||
},
|
||||
},
|
||||
[PrefKey.STATS_SHOW_WHEN_PLAYING]: {
|
||||
label: t('show-stats-on-startup'),
|
||||
default: false,
|
||||
},
|
||||
[PrefKey.STATS_QUICK_GLANCE_ENABLED]: {
|
||||
label: '👀 ' + t('enable-quick-glance-mode'),
|
||||
default: true,
|
||||
},
|
||||
[PrefKey.STATS_POSITION]: {
|
||||
label: t('position'),
|
||||
default: StreamStatPosition.TOP_RIGHT,
|
||||
options: {
|
||||
[StreamStatPosition.TOP_LEFT]: t('top-left'),
|
||||
[StreamStatPosition.TOP_CENTER]: t('top-center'),
|
||||
[StreamStatPosition.TOP_RIGHT]: t('top-right'),
|
||||
},
|
||||
},
|
||||
[PrefKey.STATS_TEXT_SIZE]: {
|
||||
label: t('text-size'),
|
||||
default: '0.9rem',
|
||||
options: {
|
||||
'0.9rem': t('small'),
|
||||
'1.0rem': t('normal'),
|
||||
'1.1rem': t('large'),
|
||||
},
|
||||
},
|
||||
[PrefKey.STATS_OPACITY_ALL]: {
|
||||
label: t('opacity'),
|
||||
default: 80,
|
||||
min: 50,
|
||||
max: 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
ticks: 10,
|
||||
},
|
||||
},
|
||||
[PrefKey.STATS_OPACITY_BACKGROUND]: {
|
||||
label: t('background-opacity'),
|
||||
default: 100,
|
||||
min: 0,
|
||||
max: 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
ticks: 10,
|
||||
},
|
||||
},
|
||||
[PrefKey.STATS_CONDITIONAL_FORMATTING]: {
|
||||
label: t('conditional-formatting'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.REMOTE_PLAY_ENABLED]: {
|
||||
[GlobalPref.REMOTE_PLAY_ENABLED]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('enable-remote-play-feature'),
|
||||
labelIcon: BxIcon.REMOTE_PLAY,
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.REMOTE_PLAY_STREAM_RESOLUTION]: {
|
||||
[GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION]: {
|
||||
requiredVariants: 'full',
|
||||
default: StreamResolution.DIM_1080P,
|
||||
options: {
|
||||
@@ -894,22 +531,15 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.GAME_FORTNITE_FORCE_CONSOLE]: {
|
||||
[GlobalPref.GAME_FORTNITE_FORCE_CONSOLE]: {
|
||||
requiredVariants: 'full',
|
||||
label: '🎮 ' + t('fortnite-force-console-version'),
|
||||
default: false,
|
||||
note: t('fortnite-allow-stw-mode'),
|
||||
},
|
||||
} satisfies SettingDefinitions;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super(StorageKey.GLOBAL, GlobalSettingsStorage.DEFINITIONS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const globalSettings = new GlobalSettingsStorage();
|
||||
export const getPrefDefinition = globalSettings.getDefinition.bind(globalSettings);
|
||||
export const getPref = globalSettings.getSetting.bind(globalSettings);
|
||||
export const setPref = globalSettings.setSetting.bind(globalSettings);
|
||||
STORAGE.Global = globalSettings;
|
||||
|
465
src/utils/settings-storages/stream-settings-storage.ts
Normal file
465
src/utils/settings-storages/stream-settings-storage.ts
Normal file
@@ -0,0 +1,465 @@
|
||||
import { StreamPref, StorageKey, type StreamPrefTypeMap, type PrefTypeMap } from "@/enums/pref-keys";
|
||||
import { DeviceVibrationMode, StreamPlayerType, StreamVideoProcessing, VideoPowerPreference, VideoRatio, VideoPosition, StreamStat, StreamStatPosition } from "@/enums/pref-values";
|
||||
import { STATES } from "../global";
|
||||
import { KeyboardShortcutDefaultId } from "../local-db/keyboard-shortcuts-table";
|
||||
import { MkbMappingDefaultPresetId } from "../local-db/mkb-mapping-presets-table";
|
||||
import { t } from "../translation";
|
||||
import { BaseSettingsStorage } from "./base-settings-storage";
|
||||
import { CE } from "../html";
|
||||
import type { SettingActionOrigin, SettingDefinition } from "@/types/setting-definition";
|
||||
import { BxIcon } from "../bx-icon";
|
||||
import { GameSettingsStorage } from "./game-settings-storage";
|
||||
import { BxLogger } from "../bx-logger";
|
||||
import { ControllerCustomizationDefaultPresetId } from "../local-db/controller-customizations-table";
|
||||
import { ControllerShortcutDefaultId } from "../local-db/controller-shortcuts-table";
|
||||
|
||||
|
||||
export class StreamSettingsStorage extends BaseSettingsStorage<StreamPref> {
|
||||
static readonly DEFINITIONS: Record<keyof StreamPrefTypeMap, SettingDefinition> = {
|
||||
[StreamPref.DEVICE_VIBRATION_MODE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('device-vibration'),
|
||||
default: DeviceVibrationMode.OFF,
|
||||
options: {
|
||||
[DeviceVibrationMode.OFF]: t('off'),
|
||||
[DeviceVibrationMode.ON]: t('on'),
|
||||
[DeviceVibrationMode.AUTO]: t('device-vibration-not-using-gamepad'),
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.DEVICE_VIBRATION_INTENSITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('vibration-intensity'),
|
||||
default: 50,
|
||||
min: 10,
|
||||
max: 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
exactTicks: 20,
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.CONTROLLER_POLLING_RATE]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('polling-rate'),
|
||||
default: 4,
|
||||
min: 4,
|
||||
max: 60,
|
||||
params: {
|
||||
steps: 4,
|
||||
exactTicks: 20,
|
||||
reverse: true,
|
||||
customTextValue(value: any) {
|
||||
value = parseInt(value);
|
||||
|
||||
let text = +(1000 / value).toFixed(2) + ' Hz';
|
||||
if (value === 4) {
|
||||
text = `${text} (${t('default')})`;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.CONTROLLER_SETTINGS]: {
|
||||
default: {},
|
||||
},
|
||||
|
||||
[StreamPref.NATIVE_MKB_SCROLL_HORIZONTAL_SENSITIVITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('horizontal-scroll-sensitivity'),
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 100 * 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
exactTicks: 20 * 100,
|
||||
customTextValue: (value: any) => {
|
||||
if (!value) {
|
||||
return t('default');
|
||||
}
|
||||
|
||||
return (value / 100).toFixed(1) + 'x';
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.NATIVE_MKB_SCROLL_VERTICAL_SENSITIVITY]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('vertical-scroll-sensitivity'),
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 100 * 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
exactTicks: 20 * 100,
|
||||
customTextValue: (value: any) => {
|
||||
if (!value) {
|
||||
return t('default');
|
||||
}
|
||||
|
||||
return (value / 100).toFixed(1) + 'x';
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.MKB_P1_MAPPING_PRESET_ID]: {
|
||||
requiredVariants: 'full',
|
||||
default: MkbMappingDefaultPresetId.DEFAULT,
|
||||
},
|
||||
|
||||
[StreamPref.MKB_P1_SLOT]: {
|
||||
requiredVariants: 'full',
|
||||
default: 1,
|
||||
min: 1,
|
||||
max: 4,
|
||||
params: {
|
||||
hideSlider: true,
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.MKB_P2_MAPPING_PRESET_ID]: {
|
||||
requiredVariants: 'full',
|
||||
default: MkbMappingDefaultPresetId.OFF,
|
||||
},
|
||||
|
||||
[StreamPref.MKB_P2_SLOT]: {
|
||||
requiredVariants: 'full',
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 4,
|
||||
params: {
|
||||
hideSlider: true,
|
||||
customTextValue(value) {
|
||||
value = parseInt(value);
|
||||
return (value === 0) ? t('off') : value.toString();
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID]: {
|
||||
requiredVariants: 'full',
|
||||
default: KeyboardShortcutDefaultId.DEFAULT,
|
||||
},
|
||||
|
||||
[StreamPref.VIDEO_PLAYER_TYPE]: {
|
||||
label: t('renderer'),
|
||||
default: StreamPlayerType.VIDEO,
|
||||
options: {
|
||||
[StreamPlayerType.VIDEO]: t('default'),
|
||||
[StreamPlayerType.WEBGL2]: t('webgl2'),
|
||||
},
|
||||
suggest: {
|
||||
lowest: StreamPlayerType.VIDEO,
|
||||
highest: StreamPlayerType.WEBGL2,
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_PROCESSING]: {
|
||||
label: t('clarity-boost'),
|
||||
default: StreamVideoProcessing.USM,
|
||||
options: {
|
||||
[StreamVideoProcessing.USM]: t('unsharp-masking'),
|
||||
[StreamVideoProcessing.CAS]: t('amd-fidelity-cas'),
|
||||
},
|
||||
suggest: {
|
||||
lowest: StreamVideoProcessing.USM,
|
||||
highest: StreamVideoProcessing.CAS,
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_POWER_PREFERENCE]: {
|
||||
label: t('renderer-configuration'),
|
||||
default: VideoPowerPreference.DEFAULT,
|
||||
options: {
|
||||
[VideoPowerPreference.DEFAULT]: t('default'),
|
||||
[VideoPowerPreference.LOW_POWER]: t('battery-saving'),
|
||||
[VideoPowerPreference.HIGH_PERFORMANCE]: t('high-performance'),
|
||||
},
|
||||
suggest: {
|
||||
highest: 'low-power',
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_MAX_FPS]: {
|
||||
label: t('limit-fps'),
|
||||
default: 60,
|
||||
min: 10,
|
||||
max: 60,
|
||||
params: {
|
||||
steps: 10,
|
||||
exactTicks: 10,
|
||||
customTextValue: (value: any) => {
|
||||
value = parseInt(value);
|
||||
return value === 60 ? t('unlimited') : value + 'fps';
|
||||
},
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_SHARPNESS]: {
|
||||
label: t('sharpness'),
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 10,
|
||||
params: {
|
||||
exactTicks: 2,
|
||||
customTextValue: (value: any) => {
|
||||
value = parseInt(value);
|
||||
return value === 0 ? t('off') : value.toString();
|
||||
},
|
||||
},
|
||||
suggest: {
|
||||
lowest: 0,
|
||||
highest: 2,
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_RATIO]: {
|
||||
label: t('aspect-ratio'),
|
||||
note: STATES.browser.capabilities.touch ? t('aspect-ratio-note') : undefined,
|
||||
default: VideoRatio['16:9'],
|
||||
options: {
|
||||
[VideoRatio['16:9']]: `16:9 (${t('default')})`,
|
||||
[VideoRatio['18:9']]: '18:9',
|
||||
[VideoRatio['21:9']]: '21:9',
|
||||
[VideoRatio['16:10']]: '16:10',
|
||||
[VideoRatio['4:3']]: '4:3',
|
||||
|
||||
[VideoRatio.FILL]: t('stretch'),
|
||||
//'cover': 'Cover',
|
||||
},
|
||||
},
|
||||
[StreamPref.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'),
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.VIDEO_SATURATION]: {
|
||||
label: t('saturation'),
|
||||
default: 100,
|
||||
min: 50,
|
||||
max: 150,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_CONTRAST]: {
|
||||
label: t('contrast'),
|
||||
default: 100,
|
||||
min: 50,
|
||||
max: 150,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
[StreamPref.VIDEO_BRIGHTNESS]: {
|
||||
label: t('brightness'),
|
||||
default: 100,
|
||||
min: 50,
|
||||
max: 150,
|
||||
params: {
|
||||
suffix: '%',
|
||||
ticks: 25,
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.AUDIO_VOLUME]: {
|
||||
label: t('volume'),
|
||||
default: 100,
|
||||
min: 0,
|
||||
max: 600,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
ticks: 100,
|
||||
},
|
||||
},
|
||||
|
||||
[StreamPref.STATS_ITEMS]: {
|
||||
label: t('stats'),
|
||||
default: [StreamStat.PING, StreamStat.FPS, StreamStat.BITRATE, StreamStat.DECODE_TIME, StreamStat.PACKETS_LOST, StreamStat.FRAMES_LOST],
|
||||
multipleOptions: {
|
||||
[StreamStat.CLOCK]: t('clock'),
|
||||
[StreamStat.PLAYTIME]: t('playtime'),
|
||||
[StreamStat.BATTERY]: t('battery'),
|
||||
[StreamStat.PING]: t('stat-ping'),
|
||||
[StreamStat.JITTER]: t('jitter'),
|
||||
[StreamStat.FPS]: t('stat-fps'),
|
||||
[StreamStat.BITRATE]: t('stat-bitrate'),
|
||||
[StreamStat.DECODE_TIME]: t('stat-decode-time'),
|
||||
[StreamStat.PACKETS_LOST]: t('stat-packets-lost'),
|
||||
[StreamStat.FRAMES_LOST]: t('stat-frames-lost'),
|
||||
[StreamStat.DOWNLOAD]: t('downloaded'),
|
||||
[StreamStat.UPLOAD]: t('uploaded'),
|
||||
},
|
||||
params: {
|
||||
size: 0,
|
||||
},
|
||||
ready: setting => {
|
||||
// Remove Battery option in unsupported browser
|
||||
const multipleOptions = (setting as any).multipleOptions;
|
||||
if (!STATES.browser.capabilities.batteryApi) {
|
||||
delete multipleOptions[StreamStat.BATTERY];
|
||||
}
|
||||
|
||||
// Update texts
|
||||
for (const key in multipleOptions) {
|
||||
multipleOptions[key] = (key as string).toUpperCase() + ': ' + multipleOptions[key];
|
||||
}
|
||||
},
|
||||
},
|
||||
[StreamPref.STATS_SHOW_WHEN_PLAYING]: {
|
||||
label: t('show-stats-on-startup'),
|
||||
default: false,
|
||||
},
|
||||
[StreamPref.STATS_QUICK_GLANCE_ENABLED]: {
|
||||
label: '👀 ' + t('enable-quick-glance-mode'),
|
||||
default: true,
|
||||
},
|
||||
[StreamPref.STATS_POSITION]: {
|
||||
label: t('position'),
|
||||
default: StreamStatPosition.TOP_RIGHT,
|
||||
options: {
|
||||
[StreamStatPosition.TOP_LEFT]: t('top-left'),
|
||||
[StreamStatPosition.TOP_CENTER]: t('top-center'),
|
||||
[StreamStatPosition.TOP_RIGHT]: t('top-right'),
|
||||
},
|
||||
},
|
||||
[StreamPref.STATS_TEXT_SIZE]: {
|
||||
label: t('text-size'),
|
||||
default: '0.9rem',
|
||||
options: {
|
||||
'0.9rem': t('small'),
|
||||
'1.0rem': t('normal'),
|
||||
'1.1rem': t('large'),
|
||||
},
|
||||
},
|
||||
[StreamPref.STATS_OPACITY_ALL]: {
|
||||
label: t('opacity'),
|
||||
default: 80,
|
||||
min: 50,
|
||||
max: 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
ticks: 10,
|
||||
},
|
||||
},
|
||||
[StreamPref.STATS_OPACITY_BACKGROUND]: {
|
||||
label: t('background-opacity'),
|
||||
default: 100,
|
||||
min: 0,
|
||||
max: 100,
|
||||
params: {
|
||||
steps: 10,
|
||||
suffix: '%',
|
||||
ticks: 10,
|
||||
},
|
||||
},
|
||||
[StreamPref.STATS_CONDITIONAL_FORMATTING]: {
|
||||
label: t('conditional-formatting'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[StreamPref.LOCAL_CO_OP_ENABLED]: {
|
||||
requiredVariants: 'full',
|
||||
label: t('enable-local-co-op-support'),
|
||||
labelIcon: BxIcon.LOCAL_CO_OP,
|
||||
default: false,
|
||||
note: () => CE('div', false,
|
||||
CE('a', {
|
||||
href: 'https://github.com/redphx/better-xcloud/discussions/275',
|
||||
target: '_blank',
|
||||
}, t('enable-local-co-op-support-note')),
|
||||
CE('br'),
|
||||
'⚠️ ' + t('unexpected-behavior'),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
private gameSettings: {[key: number]: GameSettingsStorage} = {};
|
||||
private xboxTitleId: number = -1;
|
||||
|
||||
constructor() {
|
||||
super(StorageKey.STREAM, StreamSettingsStorage.DEFINITIONS);
|
||||
}
|
||||
|
||||
setGameId(id: number) {
|
||||
this.xboxTitleId = id;
|
||||
}
|
||||
|
||||
getGameSettings(id: number) {
|
||||
if (id > -1) {
|
||||
if (!this.gameSettings[id]) {
|
||||
this.gameSettings[id] = new GameSettingsStorage(id);
|
||||
}
|
||||
|
||||
return this.gameSettings[id];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getSetting<K extends keyof PrefTypeMap<K>>(key: K, checkUnsupported?: boolean): PrefTypeMap<K>[K] {
|
||||
return this.getSettingByGame(this.xboxTitleId, key, true, checkUnsupported)!;
|
||||
}
|
||||
|
||||
getSettingByGame<K extends keyof PrefTypeMap<K>>(id: number, key: K, returnBaseValue: boolean=true, checkUnsupported?: boolean): PrefTypeMap<K>[K] | undefined {
|
||||
const gameSettings = this.getGameSettings(id);
|
||||
if (gameSettings?.hasSetting(key)) {
|
||||
return gameSettings.getSetting(key, checkUnsupported);
|
||||
}
|
||||
|
||||
if (returnBaseValue) {
|
||||
return super.getSetting(key, checkUnsupported);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setSetting<V = any>(key: StreamPref, value: V, origin: SettingActionOrigin): V {
|
||||
return this.setSettingByGame(this.xboxTitleId, key, value, origin);
|
||||
}
|
||||
|
||||
setSettingByGame<V = any>(id: number, key: StreamPref, value: V, origin: SettingActionOrigin): V {
|
||||
const gameSettings = this.getGameSettings(id);
|
||||
if (gameSettings) {
|
||||
BxLogger.info('setSettingByGame', id, key, value);
|
||||
return gameSettings.setSetting(key, value, origin);
|
||||
}
|
||||
|
||||
BxLogger.info('setSettingByGame', id, key, value);
|
||||
return super.setSetting(key, value, origin);
|
||||
}
|
||||
|
||||
hasGameSetting(id: number, key: StreamPref): boolean {
|
||||
const gameSettings = this.getGameSettings(id);
|
||||
return !!(gameSettings && gameSettings.hasSetting(key));
|
||||
}
|
||||
|
||||
getControllerSetting(gamepadId: string): ControllerSetting {
|
||||
const controllerSettings = this.getSetting(StreamPref.CONTROLLER_SETTINGS);
|
||||
let controllerSetting = controllerSettings[gamepadId];
|
||||
if (!controllerSetting) {
|
||||
controllerSetting = {} as ControllerSetting;
|
||||
}
|
||||
|
||||
// Set missing settings
|
||||
if (!controllerSetting.hasOwnProperty('shortcutPresetId')) {
|
||||
controllerSetting.shortcutPresetId = ControllerShortcutDefaultId.DEFAULT;
|
||||
}
|
||||
|
||||
if (!controllerSetting.hasOwnProperty('customizationPresetId')) {
|
||||
controllerSetting.customizationPresetId = ControllerCustomizationDefaultPresetId.DEFAULT;
|
||||
}
|
||||
|
||||
return controllerSetting;
|
||||
}
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
import { PrefKey, type PrefTypeMap } from "@/enums/pref-keys";
|
||||
import { ControllerSettingsTable } from "./local-db/controller-settings-table";
|
||||
import { GlobalPref, StreamPref } from "@/enums/pref-keys";
|
||||
import { ControllerShortcutsTable } from "./local-db/controller-shortcuts-table";
|
||||
import { getPref, setPref } from "./settings-storages/global-settings-storage";
|
||||
import type { ControllerCustomizationConvertedPresetData, ControllerCustomizationPresetData, ControllerShortcutPresetRecord, KeyboardShortcutConvertedPresetData, MkbConvertedPresetData } from "@/types/presets";
|
||||
import { STATES } from "./global";
|
||||
import { DeviceVibrationMode } from "@/enums/pref-values";
|
||||
@@ -15,10 +13,11 @@ import { ShortcutAction } from "@/enums/shortcut-actions";
|
||||
import { KeyHelper } from "@/modules/mkb/key-helper";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
import { ControllerCustomizationsTable } from "./local-db/controller-customizations-table";
|
||||
import { getStreamPref, setStreamPref, STORAGE } from "@/utils/pref-utils";
|
||||
|
||||
|
||||
export type StreamSettingsData = {
|
||||
settings: PartialRecord<PrefKey, any>;
|
||||
settings: PartialRecord<GlobalPref, any>;
|
||||
xCloudPollingMode: 'none' | 'callbacks' | 'navigation' | 'all';
|
||||
|
||||
deviceVibrationIntensity: number;
|
||||
@@ -51,15 +50,10 @@ export class StreamSettings {
|
||||
keyboardShortcuts: {},
|
||||
};
|
||||
|
||||
static getPref<T extends keyof PrefTypeMap>(key: T) {
|
||||
return getPref<T>(key);
|
||||
}
|
||||
|
||||
static async refreshControllerSettings() {
|
||||
const settings = StreamSettings.settings;
|
||||
const controllers: StreamSettingsData['controllers'] = {};
|
||||
|
||||
const settingsTable = ControllerSettingsTable.getInstance();
|
||||
const shortcutsTable = ControllerShortcutsTable.getInstance();
|
||||
const mappingTable = ControllerCustomizationsTable.getInstance();
|
||||
|
||||
@@ -74,14 +68,14 @@ export class StreamSettings {
|
||||
continue;
|
||||
}
|
||||
|
||||
const settingsData = await settingsTable.getControllerData(gamepad.id);
|
||||
const controllerSetting = STORAGE.Stream.getControllerSetting(gamepad.id);
|
||||
|
||||
// Shortcuts
|
||||
const shortcutsPreset = await shortcutsTable.getPreset(settingsData.shortcutPresetId);
|
||||
const shortcutsPreset = await shortcutsTable.getPreset(controllerSetting.shortcutPresetId);
|
||||
const shortcutsMapping = !shortcutsPreset ? null : shortcutsPreset.data.mapping;
|
||||
|
||||
// Mapping
|
||||
const customizationPreset = await mappingTable.getPreset(settingsData.customizationPresetId);
|
||||
const customizationPreset = await mappingTable.getPreset(controllerSetting.customizationPresetId);
|
||||
const customizationData = StreamSettings.convertControllerCustomization(customizationPreset?.data);
|
||||
|
||||
controllers[gamepad.id] = {
|
||||
@@ -92,7 +86,7 @@ export class StreamSettings {
|
||||
settings.controllers = controllers;
|
||||
|
||||
// Controller polling rate
|
||||
settings.controllerPollingRate = StreamSettings.getPref(PrefKey.CONTROLLER_POLLING_RATE);
|
||||
settings.controllerPollingRate = getStreamPref(StreamPref.CONTROLLER_POLLING_RATE);
|
||||
// Device vibration
|
||||
await StreamSettings.refreshDeviceVibration();
|
||||
}
|
||||
@@ -150,23 +144,23 @@ export class StreamSettings {
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = StreamSettings.getPref(PrefKey.DEVICE_VIBRATION_MODE);
|
||||
const mode = getStreamPref(StreamPref.DEVICE_VIBRATION_MODE);
|
||||
let intensity = 0; // Disable
|
||||
|
||||
// Enable when no controllers are detected in Auto mode
|
||||
if (mode === DeviceVibrationMode.ON || (mode === DeviceVibrationMode.AUTO && !hasGamepad())) {
|
||||
// Set intensity
|
||||
intensity = StreamSettings.getPref(PrefKey.DEVICE_VIBRATION_INTENSITY) / 100;
|
||||
intensity = getStreamPref(StreamPref.DEVICE_VIBRATION_INTENSITY) / 100;
|
||||
}
|
||||
|
||||
StreamSettings.settings.deviceVibrationIntensity = intensity;
|
||||
BxEventBus.Script.emit('deviceVibration.updated', {});
|
||||
BxEventBus.Stream.emit('deviceVibration.updated', {});
|
||||
}
|
||||
|
||||
static async refreshMkbSettings() {
|
||||
const settings = StreamSettings.settings;
|
||||
|
||||
let presetId = StreamSettings.getPref(PrefKey.MKB_P1_MAPPING_PRESET_ID);
|
||||
let presetId = getStreamPref(StreamPref.MKB_P1_MAPPING_PRESET_ID);
|
||||
const orgPreset = (await MkbMappingPresetsTable.getInstance().getPreset(presetId))!;
|
||||
const orgPresetData = orgPreset.data;
|
||||
|
||||
@@ -197,19 +191,19 @@ export class StreamSettings {
|
||||
|
||||
settings.mkbPreset = converted;
|
||||
|
||||
setPref(PrefKey.MKB_P1_MAPPING_PRESET_ID, orgPreset.id);
|
||||
BxEventBus.Script.emit('mkb.setting.updated', {});
|
||||
setStreamPref(StreamPref.MKB_P1_MAPPING_PRESET_ID, orgPreset.id, 'direct');
|
||||
BxEventBus.Stream.emit('mkb.setting.updated', {});
|
||||
}
|
||||
|
||||
static async refreshKeyboardShortcuts() {
|
||||
const settings = StreamSettings.settings;
|
||||
|
||||
let presetId = StreamSettings.getPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID);
|
||||
let presetId = getStreamPref(StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID);
|
||||
if (presetId === KeyboardShortcutDefaultId.OFF) {
|
||||
settings.keyboardShortcuts = null;
|
||||
|
||||
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId);
|
||||
BxEventBus.Script.emit('keyboardShortcuts.updated', {});
|
||||
setStreamPref(StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId, 'direct');
|
||||
BxEventBus.Stream.emit('keyboardShortcuts.updated', {});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,8 +222,8 @@ export class StreamSettings {
|
||||
|
||||
settings.keyboardShortcuts = converted;
|
||||
|
||||
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, orgPreset.id);
|
||||
BxEventBus.Script.emit('keyboardShortcuts.updated', {});
|
||||
setStreamPref(StreamPref.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, orgPreset.id, 'direct');
|
||||
BxEventBus.Stream.emit('keyboardShortcuts.updated', {});
|
||||
}
|
||||
|
||||
static async refreshAllSettings() {
|
||||
|
@@ -1,12 +1,11 @@
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { StreamPref } from "@/enums/pref-keys";
|
||||
import { STATES } from "./global";
|
||||
import { humanFileSize, secondsToHm } from "./html";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { BxLogger } from "./bx-logger";
|
||||
import { StreamStat } from "@/enums/pref-values";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
import { getStreamPref } from "@/utils/pref-utils";
|
||||
|
||||
export type StreamStatGrade = '' | 'bad' | 'ok' | 'good';
|
||||
|
||||
type CurrentStats = {
|
||||
[StreamStat.PING]: {
|
||||
@@ -111,7 +110,7 @@ export class StreamStatsCollector {
|
||||
[StreamStat.FPS]: {
|
||||
current: 0,
|
||||
toString() {
|
||||
const maxFps = getPref(PrefKey.VIDEO_MAX_FPS);
|
||||
const maxFps = getStreamPref(StreamPref.VIDEO_MAX_FPS);
|
||||
return maxFps < 60 ? `${maxFps}/${this.current}`.padStart(5) : this.current.toString();
|
||||
},
|
||||
},
|
||||
|
@@ -32,6 +32,7 @@ const Texts = {
|
||||
"activated": "Activated",
|
||||
"active": "Active",
|
||||
"advanced": "Advanced",
|
||||
"all-games": "All games",
|
||||
"always-off": "Always off",
|
||||
"always-on": "Always on",
|
||||
"amd-fidelity-cas": "AMD FidelityFX CAS",
|
||||
@@ -41,6 +42,7 @@ const Texts = {
|
||||
"aspect-ratio-note": "Don't use with native touch games",
|
||||
"audio": "Audio",
|
||||
"auto": "Auto",
|
||||
"availability": "Availability",
|
||||
"back-to-home": "Back to home",
|
||||
"back-to-home-confirm": "Do you want to go back to the home page (without disconnecting)?",
|
||||
"background-opacity": "Background opacity",
|
||||
@@ -99,6 +101,7 @@ const Texts = {
|
||||
"deadzone-counterweight": "Deadzone counterweight",
|
||||
"decrease": "Decrease",
|
||||
"default": "Default",
|
||||
"default-opacity": "Default opacity",
|
||||
"default-preset-note": "You can't modify default presets. Create a new one to customize it.",
|
||||
"delete": "Delete",
|
||||
"detect-controller-button": "Detect controller button",
|
||||
@@ -288,6 +291,7 @@ const Texts = {
|
||||
"rename": "Rename",
|
||||
"renderer": "Renderer",
|
||||
"renderer-configuration": "Renderer configuration",
|
||||
"reset-highlighted-setting": "Reset highlighted setting",
|
||||
"right-click-to-unbind": "Right-click on a key to unbind it",
|
||||
"right-stick": "Right stick",
|
||||
"right-stick-deadzone": "Right stick deadzone",
|
||||
@@ -311,6 +315,7 @@ const Texts = {
|
||||
"server": "Server",
|
||||
"server-locations": "Server locations",
|
||||
"settings": "Settings",
|
||||
"settings-for": "Settings for",
|
||||
"settings-reload": "Reload page to reflect changes",
|
||||
"settings-reload-note": "Settings in this tab only go into effect on the next page load",
|
||||
"settings-reloading": "Reloading...",
|
||||
@@ -352,12 +357,9 @@ const Texts = {
|
||||
"swap-buttons": "Swap buttons",
|
||||
"take-screenshot": "Take screenshot",
|
||||
"target-resolution": "Target resolution",
|
||||
"tc-all-games": "All games",
|
||||
"tc-all-white": "All white",
|
||||
"tc-auto-off": "Off when controller found",
|
||||
"tc-availability": "Availability",
|
||||
"tc-custom-layout-style": "Custom layout's button style",
|
||||
"tc-default-opacity": "Default opacity",
|
||||
"tc-muted-colors": "Muted colors",
|
||||
"tc-standard-layout-style": "Standard layout's button style",
|
||||
"text-size": "Text size",
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { AppInterface, SCRIPT_VERSION } from "@utils/global";
|
||||
import { AppInterface, SCRIPT_VERSION, } from "@utils/global";
|
||||
import { UserAgent } from "@utils/user-agent";
|
||||
import { t, Translations } from "./translation";
|
||||
import { Toast } from "./toast";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref, setPref } from "./settings-storages/global-settings-storage";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { LocalDb } from "./local-db/local-db";
|
||||
import { BlockFeature } from "@/enums/pref-values";
|
||||
import { getGlobalPref, setGlobalPref } from "@/utils/pref-utils";
|
||||
|
||||
/**
|
||||
* Check for update
|
||||
@@ -18,8 +18,8 @@ export function checkForUpdate() {
|
||||
|
||||
const CHECK_INTERVAL_SECONDS = 2 * 3600; // check every 2 hours
|
||||
|
||||
const currentVersion = getPref(PrefKey.VERSION_CURRENT);
|
||||
const lastCheck = getPref(PrefKey.VERSION_LAST_CHECK);
|
||||
const currentVersion = getGlobalPref(GlobalPref.VERSION_CURRENT);
|
||||
const lastCheck = getGlobalPref(GlobalPref.VERSION_LAST_CHECK);
|
||||
const now = Math.round((+new Date) / 1000);
|
||||
|
||||
if (currentVersion === SCRIPT_VERSION && now - lastCheck < CHECK_INTERVAL_SECONDS) {
|
||||
@@ -27,13 +27,13 @@ export function checkForUpdate() {
|
||||
}
|
||||
|
||||
// Start checking
|
||||
setPref(PrefKey.VERSION_LAST_CHECK, now);
|
||||
setGlobalPref(GlobalPref.VERSION_LAST_CHECK, now, 'direct');
|
||||
fetch('https://api.github.com/repos/redphx/better-xcloud/releases/latest')
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
// Store the latest version
|
||||
setPref(PrefKey.VERSION_LATEST, json.tag_name.substring(1));
|
||||
setPref(PrefKey.VERSION_CURRENT, SCRIPT_VERSION);
|
||||
setGlobalPref(GlobalPref.VERSION_LATEST, json.tag_name.substring(1), 'direct');
|
||||
setGlobalPref(GlobalPref.VERSION_CURRENT, SCRIPT_VERSION, 'direct');
|
||||
});
|
||||
|
||||
// Update translations
|
||||
@@ -155,13 +155,13 @@ export function clearAllData() {
|
||||
}
|
||||
|
||||
export function blockAllNotifications() {
|
||||
const blockFeatures = getPref(PrefKey.BLOCK_FEATURES);
|
||||
const blockFeatures = getGlobalPref(GlobalPref.BLOCK_FEATURES);
|
||||
const blockAll = [BlockFeature.FRIENDS, BlockFeature.NOTIFICATIONS_ACHIEVEMENTS, BlockFeature.NOTIFICATIONS_INVITES].every(value => blockFeatures.includes(value));
|
||||
return blockAll;
|
||||
}
|
||||
|
||||
export function blockSomeNotifications() {
|
||||
const blockFeatures = getPref(PrefKey.BLOCK_FEATURES);
|
||||
const blockFeatures = getGlobalPref(GlobalPref.BLOCK_FEATURES);
|
||||
if (blockAllNotifications()) {
|
||||
return false;
|
||||
}
|
||||
@@ -169,3 +169,11 @@ export function blockSomeNotifications() {
|
||||
const blockSome = [BlockFeature.FRIENDS, BlockFeature.NOTIFICATIONS_ACHIEVEMENTS, BlockFeature.NOTIFICATIONS_INVITES].some(value => blockFeatures.includes(value));
|
||||
return blockSome;
|
||||
}
|
||||
|
||||
export function isPlainObject(input: any) {
|
||||
return (
|
||||
typeof input === 'object' &&
|
||||
input !== null &&
|
||||
input.constructor === Object
|
||||
);
|
||||
}
|
||||
|
@@ -9,10 +9,10 @@ import { STATES } from "./global";
|
||||
import { generateMsDeviceInfo, getOsNameFromResolution, patchIceCandidates } from "./network";
|
||||
import { getPreferredServerRegion } from "./region";
|
||||
import { BypassServerIps } from "@/enums/bypass-servers";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { NativeMkbMode, TouchControllerMode } from "@/enums/pref-values";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
|
||||
export class XcloudInterceptor {
|
||||
private static readonly SERVER_EXTRA_INFO: Record<string, [string, ServerContinent]> = {
|
||||
@@ -43,7 +43,7 @@ export class XcloudInterceptor {
|
||||
};
|
||||
|
||||
private static async handleLogin(request: RequestInfo | URL, init?: RequestInit) {
|
||||
const bypassServer = getPref(PrefKey.SERVER_BYPASS_RESTRICTION);
|
||||
const bypassServer = getGlobalPref(GlobalPref.SERVER_BYPASS_RESTRICTION);
|
||||
if (bypassServer !== 'off') {
|
||||
const ip = BypassServerIps[bypassServer as keyof typeof BypassServerIps];
|
||||
ip && (request as Request).headers.set('X-Forwarded-For', ip);
|
||||
@@ -112,8 +112,8 @@ export class XcloudInterceptor {
|
||||
private static async handlePlay(request: RequestInfo | URL, init?: RequestInit) {
|
||||
BxEventBus.Stream.emit('state.loading', {});
|
||||
|
||||
const PREF_STREAM_TARGET_RESOLUTION = getPref(PrefKey.STREAM_RESOLUTION);
|
||||
const PREF_STREAM_PREFERRED_LOCALE = getPref(PrefKey.STREAM_PREFERRED_LOCALE);
|
||||
const PREF_STREAM_TARGET_RESOLUTION = getGlobalPref(GlobalPref.STREAM_RESOLUTION);
|
||||
const PREF_STREAM_PREFERRED_LOCALE = getGlobalPref(GlobalPref.STREAM_PREFERRED_LOCALE);
|
||||
|
||||
const url = (typeof request === 'string') ? request : (request as Request).url;
|
||||
const parsedUrl = new URL(url);
|
||||
@@ -159,7 +159,7 @@ export class XcloudInterceptor {
|
||||
private static async handleWaitTime(request: RequestInfo | URL, init?: RequestInit) {
|
||||
const response = await NATIVE_FETCH(request, init);
|
||||
|
||||
if (getPref(PrefKey.LOADING_SCREEN_SHOW_WAIT_TIME)) {
|
||||
if (getGlobalPref(GlobalPref.LOADING_SCREEN_SHOW_WAIT_TIME)) {
|
||||
const json = await response.clone().json();
|
||||
if (json.estimatedAllocationTimeInSeconds > 0) {
|
||||
// Setup wait time overlay
|
||||
@@ -176,7 +176,7 @@ export class XcloudInterceptor {
|
||||
}
|
||||
|
||||
// Touch controller for all games
|
||||
if (isFullVersion() && getPref(PrefKey.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL) {
|
||||
if (isFullVersion() && getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) === TouchControllerMode.ALL) {
|
||||
const titleInfo = STATES.currentStream.titleInfo;
|
||||
if (titleInfo?.details.hasTouchSupport) {
|
||||
TouchController.disable();
|
||||
@@ -202,11 +202,11 @@ export class XcloudInterceptor {
|
||||
|
||||
let overrideMkb: boolean | null = null;
|
||||
|
||||
if (getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.ON || (STATES.currentStream.titleInfo && BX_FLAGS.ForceNativeMkbTitles?.includes(STATES.currentStream.titleInfo.details.productId))) {
|
||||
if (getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.ON || (STATES.currentStream.titleInfo && BX_FLAGS.ForceNativeMkbTitles?.includes(STATES.currentStream.titleInfo.details.productId))) {
|
||||
overrideMkb = true;
|
||||
}
|
||||
|
||||
if (getPref(PrefKey.NATIVE_MKB_MODE) === NativeMkbMode.OFF) {
|
||||
if (getGlobalPref(GlobalPref.NATIVE_MKB_MODE) === NativeMkbMode.OFF) {
|
||||
overrideMkb = false;
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ export class XcloudInterceptor {
|
||||
}
|
||||
|
||||
// Enable mic
|
||||
if (getPref(PrefKey.AUDIO_MIC_ON_PLAYING)) {
|
||||
if (getGlobalPref(GlobalPref.AUDIO_MIC_ON_PLAYING)) {
|
||||
overrides.audioConfiguration = overrides.audioConfiguration || {};
|
||||
overrides.audioConfiguration.enableMicrophone = true;
|
||||
}
|
||||
|
@@ -4,12 +4,11 @@ import { SupportedInputType } from "./bx-exposed";
|
||||
import { NATIVE_FETCH } from "./bx-flags";
|
||||
import { STATES } from "./global";
|
||||
import { generateMsDeviceInfo, getOsNameFromResolution, patchIceCandidates } from "./network";
|
||||
import { PrefKey } from "@/enums/pref-keys";
|
||||
import { getPref } from "./settings-storages/global-settings-storage";
|
||||
import type { RemotePlayConsoleAddresses } from "@/types/network";
|
||||
import { GlobalPref } from "@/enums/pref-keys";
|
||||
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
||||
import { TouchControllerMode } from "@/enums/pref-values";
|
||||
import { BxEventBus } from "./bx-event-bus";
|
||||
import { getGlobalPref } from "./pref-utils";
|
||||
|
||||
export class XhomeInterceptor {
|
||||
private static consoleAddrs: RemotePlayConsoleAddresses = {};
|
||||
@@ -71,7 +70,7 @@ export class XhomeInterceptor {
|
||||
private static async handleInputConfigs(request: Request | URL, opts: { [index: string]: any }) {
|
||||
const response = await NATIVE_FETCH(request);
|
||||
|
||||
if (getPref(PrefKey.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.ALL) {
|
||||
if (getGlobalPref(GlobalPref.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.ALL) {
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -152,7 +151,7 @@ export class XhomeInterceptor {
|
||||
headers.authorization = `Bearer ${RemotePlayManager.getInstance()!.getXhomeToken()}`;
|
||||
|
||||
// Patch resolution
|
||||
const osName = getOsNameFromResolution(getPref(PrefKey.REMOTE_PLAY_STREAM_RESOLUTION));
|
||||
const osName = getOsNameFromResolution(getGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION));
|
||||
headers['x-ms-device-info'] = JSON.stringify(generateMsDeviceInfo(osName));
|
||||
|
||||
const opts: Record<string, any> = {
|
||||
|
Reference in New Issue
Block a user