mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-04 22:57:19 +02:00
243 lines
6.8 KiB
TypeScript
Executable File
243 lines
6.8 KiB
TypeScript
Executable File
import { STATES } from "@utils/global";
|
|
import { Toast } from "@utils/toast";
|
|
import { BxEvent } from "@utils/bx-event";
|
|
import { t } from "@utils/translation";
|
|
import { localRedirect } from "@modules/ui/ui";
|
|
import { BxLogger } from "@utils/bx-logger";
|
|
import { HeaderSection } from "./ui/header";
|
|
import { GlobalPref } from "@/enums/pref-keys";
|
|
import { getGlobalPref, setGlobalPref } from "@/utils/pref-utils";
|
|
import { RemotePlayDialog } from "./ui/dialog/remote-play-dialog";
|
|
|
|
export const enum RemotePlayConsoleState {
|
|
ON = 'On',
|
|
OFF = 'Off',
|
|
STANDBY = 'ConnectedStandby',
|
|
UNKNOWN = 'Unknown',
|
|
}
|
|
|
|
type RemotePlayRegion = {
|
|
name: string;
|
|
baseUri: string;
|
|
isDefault: boolean;
|
|
};
|
|
|
|
type RemotePlayConsole = {
|
|
deviceName: string;
|
|
serverId: string;
|
|
powerState: RemotePlayConsoleState;
|
|
consoleType: string;
|
|
// playPath: string;
|
|
// outOfHomeWarning: string;
|
|
// wirelessWarning: string;
|
|
// isDevKit: string;
|
|
};
|
|
|
|
export class RemotePlayManager {
|
|
private static instance: RemotePlayManager | null | undefined;
|
|
public static getInstance(): typeof RemotePlayManager['instance'] {
|
|
if (typeof RemotePlayManager.instance === 'undefined') {
|
|
if (getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) {
|
|
RemotePlayManager.instance = new RemotePlayManager();
|
|
} else {
|
|
RemotePlayManager.instance = null;
|
|
}
|
|
}
|
|
|
|
return RemotePlayManager.instance;
|
|
}
|
|
private readonly LOG_TAG = 'RemotePlayManager';
|
|
|
|
private isInitialized = false;
|
|
|
|
private XCLOUD_TOKEN!: string;
|
|
private XHOME_TOKEN!: string;
|
|
|
|
private consoles!: Array<RemotePlayConsole>;
|
|
private regions: Array<RemotePlayRegion> = [];
|
|
|
|
private constructor() {
|
|
BxLogger.info(this.LOG_TAG, 'constructor()');
|
|
}
|
|
|
|
initialize() {
|
|
if (this.isInitialized) {
|
|
return;
|
|
}
|
|
|
|
this.isInitialized = true;
|
|
|
|
this.requestXhomeToken(() => {
|
|
this.getConsolesList(() => {
|
|
BxLogger.info(this.LOG_TAG, 'Consoles', this.consoles);
|
|
|
|
STATES.supportedRegion && HeaderSection.getInstance().showRemotePlayButton();
|
|
BxEvent.dispatch(window, BxEvent.REMOTE_PLAY_READY);
|
|
});
|
|
});
|
|
}
|
|
|
|
getXcloudToken() {
|
|
return this.XCLOUD_TOKEN;
|
|
}
|
|
|
|
setXcloudToken(token: string) {
|
|
this.XCLOUD_TOKEN = token;
|
|
}
|
|
|
|
getXhomeToken() {
|
|
return this.XHOME_TOKEN;
|
|
}
|
|
|
|
getConsoles() {
|
|
return this.consoles;
|
|
}
|
|
|
|
|
|
private requestXhomeToken(callback: any) {
|
|
if (this.XHOME_TOKEN) {
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
let GSSV_TOKEN;
|
|
try {
|
|
GSSV_TOKEN = JSON.parse(localStorage.getItem('xboxcom_xbl_user_info')!).tokens['http://gssv.xboxlive.com/'].token;
|
|
} catch (e) {
|
|
for (let i = 0; i < localStorage.length; i++){
|
|
const key = localStorage.key(i)!;
|
|
if (!key.startsWith('Auth.User.')) {
|
|
continue;
|
|
}
|
|
|
|
const json = JSON.parse(localStorage.getItem(key)!);
|
|
for (const token of json.tokens) {
|
|
if (!token.relyingParty.includes('gssv.xboxlive.com')) {
|
|
continue;
|
|
}
|
|
|
|
GSSV_TOKEN = token.tokenData.token;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
const request = new Request('https://xhome.gssv-play-prod.xboxlive.com/v2/login/user', {
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
offeringId: 'xhome',
|
|
token: GSSV_TOKEN,
|
|
}),
|
|
headers: {
|
|
'Content-Type': 'application/json; charset=utf-8',
|
|
},
|
|
});
|
|
|
|
fetch(request).then(resp => resp.json())
|
|
.then(json => {
|
|
this.regions = json.offeringSettings.regions;
|
|
this.XHOME_TOKEN = json.gsToken;
|
|
callback();
|
|
});
|
|
}
|
|
|
|
private async getConsolesList(callback: any) {
|
|
if (this.consoles) {
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
const options = {
|
|
method: 'GET',
|
|
headers: {
|
|
Authorization: `Bearer ${this.XHOME_TOKEN}`,
|
|
},
|
|
};
|
|
|
|
// Test servers one by one
|
|
for (const region of this.regions) {
|
|
try {
|
|
const request = new Request(`${region.baseUri}/v6/servers/home?mr=50`, options);
|
|
const resp = await fetch(request);
|
|
|
|
const json = await resp.json();
|
|
if (json.results.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
this.consoles = json.results;
|
|
|
|
// Store working server
|
|
STATES.remotePlay.server = region.baseUri;
|
|
|
|
break;
|
|
} catch (e) {}
|
|
}
|
|
|
|
// None of the servers worked
|
|
if (!STATES.remotePlay.server) {
|
|
this.consoles = [];
|
|
}
|
|
|
|
callback();
|
|
}
|
|
|
|
play(serverId: string, resolution?: string) {
|
|
if (resolution) {
|
|
setGlobalPref(GlobalPref.REMOTE_PLAY_STREAM_RESOLUTION, resolution, 'ui');
|
|
}
|
|
|
|
STATES.remotePlay.config = {
|
|
serverId: serverId,
|
|
};
|
|
window.BX_REMOTE_PLAY_CONFIG = STATES.remotePlay.config;
|
|
|
|
localRedirect('/launch/fortnite/BT5P2X999VH2#remote-play');
|
|
setTimeout(() => localRedirect('/consoles/launch/' + serverId), 100);
|
|
}
|
|
|
|
togglePopup(force = null) {
|
|
if (!this.isReady()) {
|
|
Toast.show(t('getting-consoles-list'));
|
|
return;
|
|
}
|
|
|
|
if (this.consoles.length === 0) {
|
|
Toast.show(t('no-consoles-found'), '', { instant: true });
|
|
return;
|
|
}
|
|
|
|
/*
|
|
// Show native dialog in Android app
|
|
if (AppInterface && AppInterface.showRemotePlayDialog) {
|
|
AppInterface.showRemotePlayDialog(JSON.stringify(this.consoles));
|
|
(document.activeElement as HTMLElement).blur();
|
|
return;
|
|
}
|
|
*/
|
|
|
|
RemotePlayDialog.getInstance().show();
|
|
}
|
|
|
|
static detect() {
|
|
if (!getGlobalPref(GlobalPref.REMOTE_PLAY_ENABLED)) {
|
|
return;
|
|
}
|
|
|
|
STATES.remotePlay.isPlaying = window.location.pathname.includes('/launch/') && window.location.hash.startsWith('#remote-play');
|
|
if (STATES.remotePlay?.isPlaying) {
|
|
window.BX_REMOTE_PLAY_CONFIG = STATES.remotePlay.config;
|
|
// Remove /launch/... from URL
|
|
window.history.replaceState({origin: 'better-xcloud'}, '', 'https://www.xbox.com/' + location.pathname.substring(1, 6) + '/play');
|
|
} else {
|
|
window.BX_REMOTE_PLAY_CONFIG = null;
|
|
}
|
|
}
|
|
|
|
isReady() {
|
|
return this.consoles !== null;
|
|
}
|
|
}
|