mirror of
https://github.com/moonlight-stream/moonlight-chrome.git
synced 2025-08-17 08:36:42 +00:00
244 lines
8.4 KiB
JavaScript
244 lines
8.4 KiB
JavaScript
function getXMLString(xml, tagName) {
|
|
var xmlDoc = xml.responseXML;
|
|
return xmlDoc.getElementsByTagName(tagName)[0].childNodes[0].nodeValue;
|
|
}
|
|
|
|
function verifyResponseStatus(xml) {
|
|
var responseCode = parseInt(getXMLString(xml, "status_code"));
|
|
if (responseCode !== 200) {
|
|
throw "Error, expected status code 200, received status code: ".concat(responseCode.toString());
|
|
}
|
|
}
|
|
|
|
function getServerInfo() {
|
|
var connectionResp = openHttpConnectionToString(baseUrlHttps + "/serverinfo?"+buildUniqueIdUuidString(), true);
|
|
var serverResp = getServerVersion(connectionResp);
|
|
|
|
if(serverResp == 200) {
|
|
return connectionResp;
|
|
} else {
|
|
if(serverResp == 401) {
|
|
return openHttpConnectionToString(baseUrlHttp + "/serverinfo", true);
|
|
}
|
|
throw serverResp
|
|
}
|
|
}
|
|
|
|
function getComputerDetails() {
|
|
var serverInfo = getServerInfo();
|
|
var details = {
|
|
name: getXMLString(serverinfo, "hostname"),
|
|
uuid: UUID.fromString(getXmlString(serverInfo, "uniqueid").trim()),
|
|
macAddress: getXmlString(serverInfo, "mac").trim(),
|
|
localIPStr: getXmlString(serverInfo, "LocalIP"),
|
|
externalIpStr: getXmlString(serverInfo, "ExternalIP"),
|
|
pairState: parseInt(getXmlString(serverInfo, "PairStatus").trim()) == 1 ? PairState.PAIRED : PairState.NOT_PAIRED, // needs support for PiarState.FAILED
|
|
runningGameId: getCurrentGame(serverInfo), // force to 0 if an error happens
|
|
state: ONLINE
|
|
};
|
|
return details;
|
|
}
|
|
|
|
function openHTTPRequest(destinationURL, enableReadTimeout, callbackFunction) {
|
|
var xmlHttp = new XMLHttpRequest();
|
|
if(enableReadTimeout) {
|
|
xmlHttp.timeout = 5000;
|
|
}
|
|
xmlHttp.onreadystatechange = function() {
|
|
callbackFunction(xmlHttp);
|
|
// if (xmlHttp.readyState == 4 && xmlHttp.status == 200) callbackFunction(xmlHttp.responseText);
|
|
}
|
|
xmlHttp.open("GET", theUrl, true); // true for asynchronous
|
|
xmlHttp.send(null);
|
|
}
|
|
|
|
|
|
function getServerVersion(serverInfo) {
|
|
return getXmlString(serverInfo, "appversion");
|
|
}
|
|
|
|
function getPairState() {
|
|
return pm.getPairState(getServerInfo());
|
|
}
|
|
|
|
function getPairState(serverInfo) {
|
|
return pm.getPairState(serverInfo);
|
|
}
|
|
|
|
|
|
function getMaxLumaPixelsH264(serverInfo) {
|
|
var str = getXmlString(serverInfo, "MaxLumaPixelsH264");
|
|
if (str !== null) {
|
|
return parseInt(str);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function getMaxLumaPixelsHEVC(serverInfo) {
|
|
var str = getXmlString(serverInfo, "MaxLumaPixelsHEVC");
|
|
if (str !== null) {
|
|
return parseInt(str);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function getGpuType(String serverInfo) {
|
|
return getXmlString(serverInfo, "gputype");
|
|
}
|
|
|
|
public boolean supports4K(String serverInfo) throws XmlPullParserException, IOException {
|
|
// serverinfo returns supported resolutions in descending order, so getting the first
|
|
// height will give us whether we support 4K. If this is not present, we don't support
|
|
// 4K.
|
|
var heightStr = getXmlString(serverInfo, "Height");
|
|
if (heightStr == null) {
|
|
return false;
|
|
}
|
|
|
|
// GFE 2.8 released without 4K support, even though it claims to have such
|
|
// support using the SupportedDisplayMode element. We'll use the new ServerCodecModeSupport
|
|
// element to tell whether 4K is supported or not. For now, we just check the existence
|
|
// of this element. I'm hopeful that the production version that ships with 4K will
|
|
// also be the first production version that has this element.
|
|
if (getXmlString(serverInfo, "ServerCodecModeSupport") == null) {
|
|
return false;
|
|
}
|
|
|
|
if (parseInt(heightStr) >= 2160) {
|
|
// Found a 4K resolution in the list
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function getCurrentGame(String serverInfo) throws IOException, XmlPullParserException {
|
|
// GFE 2.8 started keeping currentgame set to the last game played. As a result, it no longer
|
|
// has the semantics that its name would indicate. To contain the effects of this change as much
|
|
// as possible, we'll force the current game to zero if the server isn't in a streaming session.
|
|
var serverState = getXmlString(serverInfo, "state").trim();
|
|
if (serverState != null && !serverState.match("_SERVER_AVAILABLE" + "$")) { // an endsWith implementation.
|
|
var game = getXmlString(serverInfo, "currentgame").trim();
|
|
return parseInt(game);
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function getAppById(appId) {
|
|
var appList = getAppList();
|
|
for (var i = 0, len = appList.length; i < len; i++) {
|
|
if (appList[i].getAppId() == appId) {
|
|
return appFromList;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/* NOTE: Only use this function if you know what you're doing.
|
|
* It's totally valid to have two apps named the same thing,
|
|
* or even nothing at all! Look apps up by ID if at all possible
|
|
* using the above function */
|
|
function getAppByName(appName) {
|
|
var appList = getAppList();
|
|
for (var i = 0, len = appList.length; i < len; i++) {
|
|
if (appList[i].getAppName().equalsIgnoreCase(appName)) {
|
|
return appList[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function pair(pin) {
|
|
return pm.pair(pin);
|
|
}
|
|
|
|
function getAppListByReader(xml) {
|
|
var rootElement = xml.getElementsByTagName("root")[0];
|
|
var appElements = rootElement.getElementsByTagName("App");
|
|
var returnVar;
|
|
for(var i = 0, len = appElements.length; i < len; i++) {
|
|
returnVar.push(
|
|
var app {
|
|
appTitle: appElements[i].getElementsByTagName("AppTitle")[0].nodeValue;
|
|
ID: appElements[i].getElementsByTagName("ID")[0].nodeValue;
|
|
isRunning: appElements[i].getElementsByTagName("IsRunning")[0].nodeValue;
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
function getAppListRaw() {
|
|
return openHttpConnectionToString(baseUrlHttps + "/applist?" + buildUniqueIdUuidString(), true);
|
|
}
|
|
|
|
function getAppList() {
|
|
if (verbose) {
|
|
// Use the raw function so the app list is printed
|
|
return getAppListByReader(new StringReader(getAppListRaw()));
|
|
}
|
|
else {
|
|
var resp = openHttpConnection(baseUrlHttps + "/applist?" + buildUniqueIdUuidString(), true);
|
|
var appList = getAppListByReader(new InputStreamReader(resp.byteStream()));
|
|
resp.close();
|
|
return appList;
|
|
}
|
|
}
|
|
|
|
function unpair() {
|
|
openHttpConnectionToString(baseUrlHttps + "/unpair?"+buildUniqueIdUuidString(), true);
|
|
}
|
|
|
|
|
|
function getBoxArt(app) {
|
|
var resp = openHttpConnection(baseUrlHttps + "/appasset?"+ buildUniqueIdUuidString() + "&appid=" + app.getAppId() + "&AssetType=2&AssetIdx=0", true);
|
|
return resp.byteStream();
|
|
}
|
|
|
|
|
|
var hexChar = ["0", "1", "2", "3", "4", "5", "6", "7","8", "9", "A", "B", "C", "D", "E", "F"];
|
|
function byteToHex(b) { // thanks, https://gist.github.com/amorri40/3430429
|
|
if (b.constructor === Array) { // if we were given an array, then return an array.
|
|
var returnVar;
|
|
for(var i = 0, len = b.length; i < len; i++) {
|
|
returnVar.push(hexChar[(b[i] >> 4) & 0x0f] + hexChar[b[i] & 0x0f]);
|
|
}
|
|
return returnVar;
|
|
}
|
|
return hexChar[(b >> 4) & 0x0f] + hexChar[b & 0x0f];
|
|
}
|
|
|
|
function launchApp(context, appId){
|
|
var xmlStr = openHttpConnectionToString(baseUrlHttps +
|
|
"/launch?" + buildUniqueIdUuidString() +
|
|
"&appid=" + appId +
|
|
"&mode=" + context.negotiatedWidth + "x" + context.negotiatedHeight + "x" + context.negotiatedFps +
|
|
"&additionalStates=1&sops=" + (context.streamConfig.getSops() ? 1 : 0) +
|
|
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
|
"&rikeyid="+context.riKeyId +
|
|
"&localAudioPlayMode=" + (context.streamConfig.getPlayLocalAudio() ? 1 : 0) +
|
|
"&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()),
|
|
false);
|
|
var gameSession = getXmlString(xmlStr, "gamesession");
|
|
return gameSession != null && !gameSession.equals("0");
|
|
}
|
|
|
|
function resumeApp(context) {
|
|
var xmlStr = openHttpConnectionToString(baseUrlHttps + "/resume?" + buildUniqueIdUuidString() +
|
|
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
|
"&rikeyid="+context.riKeyId, false);
|
|
var resume = getXmlString(xmlStr, "resume");
|
|
return parseInt(resume) != 0;
|
|
}
|
|
|
|
function quitApp() {
|
|
var xmlStr = openHttpConnectionToString(baseUrlHttps + "/cancel?" + buildUniqueIdUuidString(), false);
|
|
var cancel = getXmlString(xmlStr, "cancel");
|
|
return parseInt(cancel) != 0;
|
|
}
|
|
|
|
|