mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-04 22:57:19 +02:00
248 lines
8.2 KiB
TypeScript
Executable File
248 lines
8.2 KiB
TypeScript
Executable File
import { TouchController } from "@/modules/touch-controller";
|
|
import { BxEvent } from "./bx-event";
|
|
import { SupportedInputType } from "./bx-exposed";
|
|
import { NATIVE_FETCH } from "./bx-flags";
|
|
import { STATES } from "./global";
|
|
import { patchIceCandidates } from "./network";
|
|
import { PrefKey } from "@/enums/pref-keys";
|
|
import { getPref } from "./settings-storages/global-settings-storage";
|
|
import type { RemotePlayConsoleAddresses } from "@/types/network";
|
|
import { RemotePlayManager } from "@/modules/remote-play-manager";
|
|
import { StreamResolution, TouchControllerMode } from "@/enums/pref-values";
|
|
|
|
export class XhomeInterceptor {
|
|
private static consoleAddrs: RemotePlayConsoleAddresses = {};
|
|
|
|
private static readonly BASE_DEVICE_INFO = {
|
|
appInfo: {
|
|
env: {
|
|
clientAppId: window.location.host,
|
|
clientAppType: 'browser',
|
|
clientAppVersion: '24.17.36',
|
|
clientSdkVersion: '10.1.14',
|
|
httpEnvironment: 'prod',
|
|
sdkInstallId: '',
|
|
},
|
|
},
|
|
|
|
dev: {
|
|
displayInfo: {
|
|
dimensions: {
|
|
widthInPixels: 1920,
|
|
heightInPixels: 1080,
|
|
},
|
|
pixelDensity: {
|
|
dpiX: 1,
|
|
dpiY: 1,
|
|
},
|
|
},
|
|
hw: {
|
|
make: 'Microsoft',
|
|
model: 'unknown',
|
|
sdktype: 'web',
|
|
},
|
|
os: {
|
|
name: 'windows',
|
|
ver: '22631.2715',
|
|
platform: 'desktop',
|
|
},
|
|
browser: {
|
|
browserName: 'chrome',
|
|
browserVersion: '125.0',
|
|
},
|
|
},
|
|
};
|
|
|
|
private static async handleLogin(request: Request) {
|
|
try {
|
|
const clone = request.clone();
|
|
const obj = await clone.json();
|
|
obj.offeringId = 'xhome';
|
|
|
|
request = new Request('https://xhome.gssv-play-prod.xboxlive.com/v2/login/user', {
|
|
method: 'POST',
|
|
body: JSON.stringify(obj),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
} catch (e) {
|
|
alert(e);
|
|
console.log(e);
|
|
}
|
|
|
|
return NATIVE_FETCH(request);
|
|
}
|
|
|
|
private static async handleConfiguration(request: Request | URL) {
|
|
BxEvent.dispatch(window, BxEvent.STREAM_STARTING);
|
|
|
|
const response = await NATIVE_FETCH(request);
|
|
const obj = await response.clone().json();
|
|
|
|
const serverDetails = obj.serverDetails;
|
|
const pairs = [
|
|
['ipAddress', 'port'],
|
|
['ipV4Address', 'ipV4Port'],
|
|
['ipV6Address', 'ipV6Port'],
|
|
];
|
|
|
|
XhomeInterceptor.consoleAddrs = {};
|
|
for (const pair of pairs) {
|
|
const [keyAddr, keyPort] = pair;
|
|
if (keyAddr && keyPort && serverDetails[keyAddr]) {
|
|
const port = serverDetails[keyPort];
|
|
// Add port 9002 to the list of ports
|
|
const ports = new Set<number>();
|
|
port && ports.add(port);
|
|
ports.add(9002);
|
|
// Save it
|
|
XhomeInterceptor.consoleAddrs[serverDetails[keyAddr]] = Array.from(ports);
|
|
}
|
|
}
|
|
|
|
response.json = () => Promise.resolve(obj);
|
|
response.text = () => Promise.resolve(JSON.stringify(obj));
|
|
|
|
return response;
|
|
}
|
|
|
|
private static async handleInputConfigs(request: Request | URL, opts: { [index: string]: any }) {
|
|
const response = await NATIVE_FETCH(request);
|
|
|
|
if (getPref<TouchControllerMode>(PrefKey.TOUCH_CONTROLLER_MODE) !== TouchControllerMode.ALL) {
|
|
return response;
|
|
}
|
|
|
|
const obj = await response.clone().json() as any;
|
|
|
|
const xboxTitleId = JSON.parse(opts.body).titleIds[0];
|
|
TouchController.setXboxTitleId(xboxTitleId);
|
|
|
|
const inputConfigs = obj[0];
|
|
|
|
let hasTouchSupport = inputConfigs.supportedTabs.length > 0;
|
|
if (!hasTouchSupport) {
|
|
const supportedInputTypes = inputConfigs.supportedInputTypes;
|
|
hasTouchSupport = supportedInputTypes.includes(SupportedInputType.NATIVE_TOUCH) || supportedInputTypes.includes(SupportedInputType.CUSTOM_TOUCH_OVERLAY);
|
|
}
|
|
|
|
if (hasTouchSupport) {
|
|
TouchController.disable();
|
|
|
|
BxEvent.dispatch(window, BxEvent.CUSTOM_TOUCH_LAYOUTS_LOADED, {
|
|
data: null,
|
|
});
|
|
} else {
|
|
TouchController.enable();
|
|
TouchController.requestCustomLayouts(xboxTitleId);
|
|
}
|
|
|
|
response.json = () => Promise.resolve(obj);
|
|
response.text = () => Promise.resolve(JSON.stringify(obj));
|
|
|
|
return response;
|
|
}
|
|
|
|
private static async handleTitles(request: Request) {
|
|
const clone = request.clone();
|
|
|
|
const headers: { [index: string]: any } = {};
|
|
for (const pair of (clone.headers as any).entries()) {
|
|
headers[pair[0]] = pair[1];
|
|
}
|
|
headers.authorization = `Bearer ${RemotePlayManager.getInstance()!.getXcloudToken()}`;
|
|
|
|
const index = request.url.indexOf('.xboxlive.com');
|
|
request = new Request('https://wus.core.gssv-play-prod' + request.url.substring(index), {
|
|
method: clone.method,
|
|
body: await clone.text(),
|
|
headers: headers,
|
|
});
|
|
|
|
return NATIVE_FETCH(request);
|
|
}
|
|
|
|
private static async handlePlay(request: RequestInfo | URL) {
|
|
BxEvent.dispatch(window, BxEvent.STREAM_LOADING);
|
|
|
|
const clone = (request as Request).clone();
|
|
const body = await clone.json();
|
|
|
|
// body.settings.useIceConnection = true;
|
|
|
|
const newRequest = new Request(request, {
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
return NATIVE_FETCH(newRequest);
|
|
}
|
|
|
|
static async handle(request: Request) {
|
|
TouchController.disable();
|
|
|
|
const clone = request.clone();
|
|
|
|
const headers: { [index: string]: string } = {};
|
|
for (const pair of (clone.headers as any).entries()) {
|
|
headers[pair[0]] = pair[1];
|
|
}
|
|
// Add xHome token to headers
|
|
headers.authorization = `Bearer ${RemotePlayManager.getInstance()!.getXhomeToken()}`;
|
|
|
|
// Patch resolution
|
|
const deviceInfo = XhomeInterceptor.BASE_DEVICE_INFO;
|
|
const resolution = getPref<StreamResolution>(PrefKey.REMOTE_PLAY_STREAM_RESOLUTION);
|
|
switch (resolution) {
|
|
case StreamResolution.DIM_1080P_HQ:
|
|
deviceInfo.dev.os.name = 'tizen';
|
|
break;
|
|
case StreamResolution.DIM_720P:
|
|
deviceInfo.dev.os.name = 'android';
|
|
break;
|
|
default:
|
|
deviceInfo.dev.os.name = 'windows';
|
|
break;
|
|
}
|
|
|
|
headers['x-ms-device-info'] = JSON.stringify(deviceInfo);
|
|
|
|
const opts: Record<string, any> = {
|
|
method: clone.method,
|
|
headers: headers,
|
|
};
|
|
|
|
// Copy body
|
|
if (clone.method === 'POST') {
|
|
opts.body = await clone.text();
|
|
}
|
|
|
|
// Replace xCloud domain with xHome domain
|
|
let url = request.url;
|
|
if (!url.includes('/servers/home')) {
|
|
const parsed = new URL(url);
|
|
url = STATES.remotePlay.server + parsed.pathname;
|
|
}
|
|
|
|
// Create new Request instance
|
|
request = new Request(url, opts);
|
|
|
|
// Get console IP
|
|
if (url.includes('/configuration')) {
|
|
return XhomeInterceptor.handleConfiguration(request);
|
|
} else if (url.endsWith('/sessions/home/play')) {
|
|
return XhomeInterceptor.handlePlay(request);
|
|
} else if (url.includes('inputconfigs')) {
|
|
return XhomeInterceptor.handleInputConfigs(request, opts);
|
|
} else if (url.includes('/login/user')) {
|
|
return XhomeInterceptor.handleLogin(request);
|
|
} else if (url.endsWith('/titles')) {
|
|
return XhomeInterceptor.handleTitles(request);
|
|
} else if (url && url.endsWith('/ice') && url.includes('/sessions/') && request.method === 'GET') {
|
|
return patchIceCandidates(request, XhomeInterceptor.consoleAddrs);
|
|
}
|
|
|
|
return await NATIVE_FETCH(request);
|
|
}
|
|
}
|