EmbyCrackedClient/web/modules/emby-apiclient/connectionmanager.js
2025-06-25 11:46:04 +08:00

1594 lines
51 KiB
JavaScript

import events from "./events.js";
import ApiClient from "./apiclient.js";
import credentialProvider from "./credentials.js";
import {
appStorage,
serverDiscovery,
appHost,
apiClientFactory,
} from "./../common/servicelocator.js";
import queryString from "./../common/querystring.js";
import userSettings from "./../common/usersettings/usersettings.js";
import appSettings from "./../common/appsettings.js";
let defaultTimeout = 2e4,
currentApiClient;
function setCurrentApiClient(instance, apiClient) {
instance.globalScopeApiClient && (globalThis.ApiClient = apiClient),
(currentApiClient = apiClient);
}
let ConnectionMode = { Local: 0, Remote: 1, Manual: 2 };
function getServerAddress(server, mode) {
switch (mode) {
case ConnectionMode.Local:
return server.LocalAddress;
case ConnectionMode.Manual:
return server.ManualAddress;
case ConnectionMode.Remote:
return server.RemoteAddress;
default:
return (
server.ManualAddress || server.LocalAddress || server.RemoteAddress
);
}
}
function mergeServers(credentialProvider, list1, list2) {
let changed = !1;
for (let i = 0, length = list2.length; i < length; i++)
credentialProvider.addOrUpdateServer(list1, list2[i]) && (changed = !0);
return changed;
}
function updateServerInfo(server, systemInfo) {
systemInfo.ServerName && (server.Name = systemInfo.ServerName),
systemInfo.Id && (server.Id = systemInfo.Id),
systemInfo.LocalAddress && (server.LocalAddress = systemInfo.LocalAddress),
systemInfo.WanAddress && (server.RemoteAddress = systemInfo.WanAddress);
}
function getCapabilities() {
let supportsSync = appHost.supports("sync");
return (
supportsSync && appHost.getSyncProfile
? appHost.getSyncProfile()
: Promise.resolve(null)
).then(function (deviceProfile) {
let caps = {
PlayableMediaTypes: ["Audio", "Video"],
SupportedCommands: [
"MoveUp",
"MoveDown",
"MoveLeft",
"MoveRight",
"PageUp",
"PageDown",
"PreviousLetter",
"NextLetter",
"ToggleOsd",
"ToggleContextMenu",
"Select",
"Back",
"SendKey",
"SendString",
"GoHome",
"GoToSettings",
"VolumeUp",
"VolumeDown",
"Mute",
"Unmute",
"ToggleMute",
"SetVolume",
"SetAudioStreamIndex",
"SetSubtitleStreamIndex",
"RefreshMediaSource",
"DisplayContent",
"GoToSearch",
"DisplayMessage",
"SetRepeatMode",
"SetShuffle",
"SetSubtitleOffset",
"SetPlaybackRate",
"ChannelUp",
"ChannelDown",
"PlayMediaSource",
"PlayTrailers",
],
SupportsMediaControl: !0,
};
return (
(caps.DeviceProfile = deviceProfile),
(caps.IconUrl = appHost.deviceIconUrl ? appHost.deviceIconUrl() : null),
(caps.SupportsSync = supportsSync),
(caps.SupportsContentUploading = appHost.supports("cameraupload")),
(caps = appHost.getPushTokenInfo
? Object.assign(caps, appHost.getPushTokenInfo())
: caps)
);
});
}
function getAbortError() {
var err = new Error("AbortError");
return (err.name = "AbortError"), err;
}
function getFetchPromise(request, signal) {
if (signal && signal.aborted) return Promise.reject(getAbortError());
var abortController,
boundAbort,
headers = request.headers || {},
fetchRequest =
("json" === request.dataType && (headers.accept = "application/json"),
{ headers: headers, method: request.type, credentials: "same-origin" });
request.timeout &&
((boundAbort = (abortController = new AbortController()).abort.bind(
abortController
)),
signal && signal.addEventListener("abort", boundAbort),
setTimeout(boundAbort, request.timeout),
(signal = abortController.signal)),
signal && (fetchRequest.signal = signal);
let contentType = request.contentType;
return (
request.data &&
("string" == typeof request.data
? (fetchRequest.body = request.data)
: ((fetchRequest.body = queryString.paramsToString(request.data)),
(contentType =
contentType ||
"application/x-www-form-urlencoded; charset=UTF-8"))),
contentType && (headers["Content-Type"] = contentType),
fetch(request.url, fetchRequest)
);
}
function sortServers(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0);
}
function setServerProperties(server) {
server.Type = "Server";
}
function ajax(request, signal) {
// Crack Start
if (request.url.includes("service/registration/validateDevice")) {
console.log("[Crack <3] Access URL: " + request.url);
const fakeResponse = {
status: 200,
json: () =>
Promise.resolve({
cacheExpirationDays: 8964,
resultCode: "GOOD",
message: "Device Valid",
isPremiere: "true",
}),
headers: {
get: (name) => (name.toLowerCase() === "content-type" ? "application/json" : null),
},
};
console.log("[Crack <3] return god gift");
return Promise.resolve(fakeResponse.json());
}
// Crack End
if (request)
return (
(request.headers = request.headers || {}),
console.log("ConnectionManager requesting url: " + request.url),
getFetchPromise(request, signal).then((response) =>
response.status < 400
? request.dataType === "json"
? response.json()
: request.dataType === "text"
? response.text()
: request.headers.accept === "application/json"
? response.json()
: response.status === 204
? response.text()
: response
: Promise.reject(response)
)
);
throw new Error("Request cannot be null");
}
function getConnectUrl(handler) {
return "https://connect.emby.media/service/" + handler;
}
function replaceAll(originalString, strReplace, strWith) {
strReplace = new RegExp(strReplace, "ig");
return originalString.replace(strReplace, strWith);
}
function normalizeAddress(address) {
return (
(address = replaceAll(
(address = (address = address.trim()).toLowerCase().startsWith("http")
? address
: address.includes(":443") || address.includes(":8920")
? "https://" + address
: "http://" + address),
"Http:",
"http:"
)),
(address = replaceAll(address, "Https:", "https:"))
);
}
function convertEndpointAddressToAddress(info) {
if (info.Address && info.EndpointAddress) {
let address = info.EndpointAddress.split(":")[0];
var info = info.Address.split(":");
return (
1 < info.length &&
((info = info[info.length - 1]),
isNaN(parseInt(info)) || (address += ":" + info)),
normalizeAddress(address)
);
}
return null;
}
function filterServers(servers, connectServers) {
return servers.filter(
(server) =>
!server.ExchangeToken ||
0 <
connectServers.filter((connectServer) => server.Id === connectServer.Id)
.length
);
}
function compareVersions(a, b) {
(a = a.split(".")), (b = b.split("."));
for (let i = 0, length = Math.max(a.length, b.length); i < length; i++) {
var aVal = parseInt(a[i] || "0"),
bVal = parseInt(b[i] || "0");
if (aVal < bVal) return -1;
if (bVal < aVal) return 1;
}
return 0;
}
function onCredentialsSaved(e, data) {
events.trigger(this, "credentialsupdated", [data]);
}
function setTimeoutPromise(timeout) {
return new Promise(function (resolve) {
setTimeout(resolve, timeout);
});
}
function addAppInfoToConnectRequest(instance, request) {
(request.headers = request.headers || {}),
(request.headers["X-Application"] =
instance.appName() + "/" + instance.appVersion());
}
function exchangePinInternal(instance, pinInfo) {
if (pinInfo)
return (
addAppInfoToConnectRequest(
instance,
(instance = {
type: "POST",
url: getConnectUrl("pin/authenticate"),
data: { deviceId: pinInfo.DeviceId, pin: pinInfo.Pin },
dataType: "json",
})
),
ajax(instance)
);
throw new Error("pinInfo cannot be null");
}
function getCacheKey(feature, apiClient, options = {}) {
options = options.viewOnly;
let cacheKey = "regInfo-" + apiClient.serverId();
return options && (cacheKey += "-viewonly"), cacheKey;
}
function getConnectUser(instance, userId, accessToken) {
if (!userId) throw new Error("null userId");
if (accessToken)
return ajax({
type: "GET",
url: "https://connect.emby.media/service/user?id=" + userId,
dataType: "json",
headers: {
"X-Application": instance.appName() + "/" + instance.appVersion(),
"X-Connect-UserToken": accessToken,
},
});
throw new Error("null accessToken");
}
function onConnectUserSignIn(instance, user) {
(instance._connectUser = user),
events.trigger(instance, "connectusersignedin", [user]);
}
function ensureConnectUser(instance, credentials) {
var connectUser = instance.connectUser();
return (!connectUser || connectUser.Id !== credentials.ConnectUserId) &&
credentials.ConnectUserId &&
credentials.ConnectAccessToken
? ((instance._connectUser = null),
getConnectUser(
instance,
credentials.ConnectUserId,
credentials.ConnectAccessToken
).then(
(user) => (onConnectUserSignIn(instance, user), Promise.resolve()),
() => Promise.resolve()
))
: Promise.resolve();
}
function updateUserAuthenticationInfoOnServer(server, userId, accessToken) {
if (accessToken) {
(server.UserId = userId),
(server.AccessToken = null),
delete server.AccessToken;
var users = (server.Users || []).slice(0);
for (let i = 0, length = users.length; i < length; i++) {
var user = users[i];
if (user.UserId === userId) return void (user.AccessToken = accessToken);
}
users.push({ UserId: userId, AccessToken: accessToken }),
(server.Users = users);
} else removeUserFromServer(server, userId);
}
function removeUserFromServer(server, userId) {
if (
(server.UserId === userId && (server.UserId = null),
(server.AccessToken = null),
delete server.AccessToken,
server.Users)
) {
var users = (server.Users || []).slice(0),
list = [];
for (let i = 0, length = users.length; i < length; i++) {
var user = users[i];
user.UserId !== userId && list.push(user);
}
server.Users = list;
}
}
function clearUsersFromServer(server) {
(server.UserId = null),
(server.AccessToken = null),
delete server.AccessToken,
server.Users && (server.Users = []);
}
function getUserAuthInfoFromServer(server, userId) {
if (server.Users) {
var users = (server.Users || []).slice(0);
for (let i = 0, length = users.length; i < length; i++) {
var user = users[i];
if (user.UserId === userId) return user;
}
return null;
}
return server.UserId === userId && server.AccessToken && !globalThis.appMode
? { UserId: userId, AccessToken: server.AccessToken }
: null;
}
function getLastUserAuthInfoFromServer(server) {
return server.UserId
? getUserAuthInfoFromServer(server, server.UserId)
: null;
}
function validateAuthentication(instance, server, userAuthInfo, serverUrl) {
console.log("connectionManager.validateAuthentication: " + serverUrl);
let userId = userAuthInfo.UserId;
return ajax({
type: "GET",
url: instance.getEmbyServerUrl(serverUrl, "System/Info", {
api_key: userAuthInfo.AccessToken,
}),
dataType: "json",
}).then(
(systemInfo) => (updateServerInfo(server, systemInfo), systemInfo),
() => (removeUserFromServer(server, userId), Promise.resolve())
);
}
function findServers() {
function onFinish(foundServers) {
return foundServers.map(function (foundServer) {
return {
Id: foundServer.Id,
LocalAddress:
convertEndpointAddressToAddress(foundServer) || foundServer.Address,
Name: foundServer.Name,
LastConnectionMode: ConnectionMode.Local,
};
});
}
return serverDiscovery.findServers(1e3).then(onFinish, () => onFinish([]));
}
function validateServerAddressWithEndpoint(connectionManager, url, endpoint) {
return ajax({
url: connectionManager.getEmbyServerUrl(url, endpoint),
timeout: defaultTimeout,
type: "GET",
dataType: "text",
}).then(function (result) {
var srch =
String.fromCharCode(106) +
String.fromCharCode(101) +
String.fromCharCode(108) +
String.fromCharCode(108) +
String.fromCharCode(121) +
String.fromCharCode(102);
return (result || "").toLowerCase().includes(srch)
? Promise.reject("serverversion")
: Promise.resolve();
});
}
function validateServerAddress(instance, url) {
return !1 === instance.enableServerAddressValidation
? Promise.resolve()
: Promise.all([
validateServerAddressWithEndpoint(instance, url, "web/manifest.json"),
validateServerAddressWithEndpoint(instance, url, "web/index.html"),
validateServerAddressWithEndpoint(
instance,
url,
"web/strings/en-US.json"
),
]);
}
function onAuthenticated(apiClient, result) {
var options = {};
let instance = this,
credentials = credentialProvider.credentials();
var servers = credentials.Servers.filter((s) => s.Id === result.ServerId);
let server = servers.length ? servers[0] : apiClient.serverInfo();
return (
!1 !== options.updateDateLastAccessed &&
(server.DateLastAccessed = Date.now()),
(server.Id = result.ServerId),
updateUserAuthenticationInfoOnServer(
server,
result.User.Id,
result.AccessToken
),
credentialProvider.addOrUpdateServer(credentials.Servers, server) &&
credentialProvider.credentials(credentials),
(apiClient.enableAutomaticBitrateDetection =
options.enableAutomaticBitrateDetection),
apiClient.serverInfo(server),
apiClient.setAuthenticationInfo(
getUserAuthInfoFromServer(server, result.User.Id),
(server.Users || []).slice(0)
),
(options.reportCapabilities = !0),
afterConnected(instance, apiClient, server, options),
apiClient.getPublicSystemInfo().then(function (systemInfo) {
return (
updateServerInfo(server, systemInfo),
credentialProvider.addOrUpdateServer(credentials.Servers, server) &&
credentialProvider.credentials(credentials),
instance._getOrAddApiClient(server, apiClient.serverAddress()),
onLocalUserSignIn(
instance,
server,
apiClient,
result.User.Id,
apiClient.serverAddress()
)
);
})
);
}
function reportCapabilities(instance, apiClient) {
return instance.reportCapabilities(apiClient);
}
function afterConnected(instance, apiClient, server, options = {}) {
(!0 !== options.reportCapabilities && !1 === options.reportCapabilities) ||
reportCapabilities(instance, apiClient),
(apiClient.enableAutomaticBitrateDetection =
options.enableAutomaticBitrateDetection),
(apiClient.enableWebSocketAutoConnect = !1 !== options.enableWebSocket),
apiClient.enableWebSocketAutoConnect &&
(console.log("calling apiClient.ensureWebSocket"),
(apiClient.connected = !0),
apiClient.ensureWebSocket());
}
function onLocalUserSignIn(instance, server, apiClient, userId, serverUrl) {
return (
setCurrentApiClient(instance, apiClient),
userSettings.setUserInfo(userId, apiClient).then(() => {
events.trigger(instance, "localusersignedin", [
server.Id,
userId,
apiClient,
]);
})
);
}
function addAuthenticationInfoFromConnect(
instance,
server,
systemInfo,
serverUrl,
credentials
) {
if (!server.ExchangeToken)
throw new Error("server.ExchangeToken cannot be null");
var appName, appVersion, deviceName, deviceId;
if (credentials.ConnectUserId)
return (
(credentials = {
format: "json",
ConnectUserId: credentials.ConnectUserId,
}),
(appName = instance.appName()),
(appVersion = instance.appVersion()),
(deviceName = instance.deviceName()),
(deviceId = instance.deviceId()),
appName && (credentials["X-Emby-Client"] = appName),
deviceId && (credentials["X-Emby-Device-Id"] = deviceId),
appVersion && (credentials["X-Emby-Client-Version"] = appVersion),
deviceName && (credentials["X-Emby-Device-Name"] = deviceName),
(credentials["X-Emby-Token"] = server.ExchangeToken),
ajax({
type: "GET",
url: instance.getEmbyServerUrl(
serverUrl,
"Connect/Exchange",
credentials
),
dataType: "json",
}).then(
(auth) => (
updateUserAuthenticationInfoOnServer(
server,
auth.LocalUserId,
auth.AccessToken
),
auth
),
() => (clearUsersFromServer(server), Promise.reject())
)
);
throw new Error("credentials.ConnectUserId cannot be null");
}
function logoutOfServer(instance, apiClient) {
let logoutInfo = { serverId: apiClient.serverId() };
return apiClient.logout().then(
() => {
userSettings.setUserInfo(null, null),
events.trigger(instance, "localusersignedout", [logoutInfo]);
},
() => {
userSettings.setUserInfo(null, null),
events.trigger(instance, "localusersignedout", [logoutInfo]);
}
);
}
function getConnectServers(instance, credentials) {
return (
console.log("Begin getConnectServers"),
credentials.ConnectAccessToken && credentials.ConnectUserId
? ajax({
type: "GET",
url:
"https://connect.emby.media/service/servers?userId=" +
credentials.ConnectUserId,
dataType: "json",
headers: {
"X-Application": instance.appName() + "/" + instance.appVersion(),
"X-Connect-UserToken": credentials.ConnectAccessToken,
},
}).then(
(servers) =>
servers.map((i) => ({
ExchangeToken: i.AccessKey,
ConnectServerId: i.Id,
Id: i.SystemId,
Name: i.Name,
RemoteAddress: i.Url,
LocalAddress: i.LocalAddress,
})),
() => credentials.Servers.slice(0).filter((s) => s.ExchangeToken)
)
: Promise.resolve([])
);
}
function tryReconnectToUrl(instance, url, connectionMode, delay, signal) {
return (
console.log("tryReconnectToUrl: " + url),
setTimeoutPromise(delay).then(() =>
ajax(
{
url: instance.getEmbyServerUrl(url, "system/info/public"),
timeout: defaultTimeout,
type: "GET",
dataType: "json",
},
signal
).then((result) => ({
url: url,
connectionMode: connectionMode,
data: result,
}))
)
);
}
function isLocalHostAddress(address) {
return (
!!address.includes("://127.0.0.1") ||
!!address.toLowerCase().includes("://localhost")
);
}
function tryReconnect(instance, serverInfo, signal) {
var addresses = [],
addressesStrings = [];
if (
(serverInfo.ManualAddress &&
isLocalHostAddress(serverInfo.ManualAddress) &&
!addressesStrings.includes(serverInfo.ManualAddress.toLowerCase()) &&
(addresses.push({
url: serverInfo.ManualAddress,
mode: ConnectionMode.Manual,
}),
addressesStrings.push(addresses[addresses.length - 1].url.toLowerCase())),
serverInfo.ManualAddressOnly ||
!serverInfo.LocalAddress ||
addressesStrings.includes(serverInfo.LocalAddress.toLowerCase()) ||
(addresses.push({
url: serverInfo.LocalAddress,
mode: ConnectionMode.Local,
}),
addressesStrings.push(addresses[addresses.length - 1].url.toLowerCase())),
serverInfo.ManualAddress &&
!addressesStrings.includes(serverInfo.ManualAddress.toLowerCase()) &&
(addresses.push({
url: serverInfo.ManualAddress,
mode: ConnectionMode.Manual,
}),
addressesStrings.push(addresses[addresses.length - 1].url.toLowerCase())),
serverInfo.ManualAddressOnly ||
!serverInfo.RemoteAddress ||
addressesStrings.includes(serverInfo.RemoteAddress.toLowerCase()) ||
(addresses.push({
url: serverInfo.RemoteAddress,
mode: ConnectionMode.Remote,
}),
addressesStrings.push(addresses[addresses.length - 1].url.toLowerCase())),
console.log("tryReconnect: " + addressesStrings.join("|")),
!addressesStrings.length)
)
return Promise.reject();
let abortController = new AbortController();
var serverInfo = abortController.abort.bind(abortController),
promises =
(signal && signal.addEventListener("abort", serverInfo),
(signal = abortController.signal),
[]);
for (let i = 0, length = addresses.length; i < length; i++)
promises.push(
tryReconnectToUrl(
instance,
addresses[i].url,
addresses[i].mode,
200 * i,
signal
)
);
return Promise.any(promises).then(
(result) => (abortController.abort(), result)
);
}
function afterConnectValidated(
instance,
server,
credentials,
systemInfo,
connectionMode,
serverUrl,
verifyLocalAuthentication,
options
) {
console.log("connectionManager.afterConnectValidated: " + serverUrl);
var userAuthInfo =
((options = options || {}).userId
? getUserAuthInfoFromServer(server, options.userId)
: getLastUserAuthInfoFromServer(server)) || {};
if (
verifyLocalAuthentication &&
userAuthInfo.UserId &&
userAuthInfo.AccessToken
)
return validateAuthentication(
instance,
server,
userAuthInfo,
serverUrl
).then((fullSystemInfo) =>
afterConnectValidated(
instance,
server,
credentials,
fullSystemInfo || systemInfo,
connectionMode,
serverUrl,
!1,
options
)
);
updateServerInfo(server, systemInfo),
(server.LastConnectionMode = connectionMode),
!1 !== options.updateDateLastAccessed &&
(server.DateLastAccessed = Date.now()),
credentialProvider.addOrUpdateServer(credentials.Servers, server) &&
credentialProvider.credentials(credentials);
let result = { Servers: [] },
enableAutoLogin =
((result.ApiClient = instance._getOrAddApiClient(server, serverUrl)),
result.ApiClient.setSystemInfo(systemInfo),
options.enableAutoLogin);
null == enableAutoLogin && (enableAutoLogin = appSettings.enableAutoLogin()),
(result.State =
userAuthInfo.UserId && userAuthInfo.AccessToken && !1 !== enableAutoLogin
? "SignedIn"
: "ServerSignIn"),
result.Servers.push(server),
(result.ApiClient.enableAutomaticBitrateDetection =
options.enableAutomaticBitrateDetection),
result.ApiClient.updateServerInfo(server, serverUrl),
instance.resetRegistrationInfo(result.ApiClient, !0);
function resolveActions() {
return (
events.trigger(instance, "connected", [result]), Promise.resolve(result)
);
}
return (
console.log(
"connectionManager.afterConnectValidated result.State: " +
(result.State || "")
),
"SignedIn" === result.State
? (afterConnected(instance, result.ApiClient, server, options),
onLocalUserSignIn(
instance,
server,
result.ApiClient,
userAuthInfo.UserId,
serverUrl
).then(resolveActions, resolveActions))
: resolveActions()
);
}
function onSuccessfulConnection(
instance,
server,
systemInfo,
connectionMode,
serverUrl,
options
) {
console.log("connectionManager.onSuccessfulConnection: " + serverUrl);
let credentials = credentialProvider.credentials(),
enableAutoLogin = (options = options || {}).enableAutoLogin;
return (
null == enableAutoLogin &&
(enableAutoLogin = appSettings.enableAutoLogin()),
credentials.ConnectAccessToken && !1 !== enableAutoLogin
? ensureConnectUser(instance, credentials).then(() =>
server.ExchangeToken
? addAuthenticationInfoFromConnect(
instance,
server,
systemInfo,
serverUrl,
credentials
).then(
() =>
afterConnectValidated(
instance,
server,
credentials,
systemInfo,
connectionMode,
serverUrl,
!0,
options
),
() =>
afterConnectValidated(
instance,
server,
credentials,
systemInfo,
connectionMode,
serverUrl,
!0,
options
)
)
: afterConnectValidated(
instance,
server,
credentials,
systemInfo,
connectionMode,
serverUrl,
!0,
options
)
)
: afterConnectValidated(
instance,
server,
credentials,
systemInfo,
connectionMode,
serverUrl,
!0,
options
)
);
}
function resolveIfAvailable(
instance,
url,
server,
result,
connectionMode,
options
) {
return (
console.log("connectionManager.resolveIfAvailable: " + url),
validateServerAddress(instance, url).then(
() =>
onSuccessfulConnection(
instance,
server,
result,
connectionMode,
url,
options
),
(err) =>
"serverversion" === err
? (console.log(
"minServerVersion requirement not met. Server version: " +
result.Version
),
{ State: "ServerUpdateNeeded", Servers: [server] })
: {
State: "Unavailable",
Server: server,
ConnectUser: instance.connectUser(),
}
)
);
}
function onGetUserRecordFromAuthenticationError(err) {
return (
console.log("Error in getUserRecordFromAuthentication: " + err),
Promise.resolve(null)
);
}
function getUserRecordFromAuthentication(user, apiClient) {
return (
user.UserId === apiClient.getCurrentUserId()
? apiClient.getCurrentUser()
: apiClient.getUser(user.UserId)
).catch(onGetUserRecordFromAuthenticationError);
}
function onServerAddressChanged(e, data) {
events.trigger(this, "serveraddresschanged", [data]);
}
class ConnectionManager {
constructor() {
(this._apiClients = []),
(this._apiClientsMap = {}),
console.log("Begin ConnectionManager constructor"),
(this._appName = appHost.appName()),
(this._appVersion = appHost.appVersion()),
(this._deviceName = appHost.deviceName()),
(this._deviceId = appHost.deviceId()),
(this._minServerVersion = "4.7.14"),
events.on(
credentialProvider,
"credentialsupdated",
onCredentialsSaved.bind(this)
);
}
appName() {
return this._appName;
}
appVersion() {
return this._appVersion;
}
deviceName() {
return this._deviceName;
}
deviceId() {
return this._deviceId;
}
minServerVersion(val) {
return val && (this._minServerVersion = val), this._minServerVersion;
}
connectUser() {
return this._connectUser;
}
connectUserId() {
return credentialProvider.credentials().ConnectUserId;
}
connectToken() {
return credentialProvider.credentials().ConnectAccessToken;
}
getServerInfo(id) {
return credentialProvider
.credentials()
.Servers.filter((s) => s.Id === id)[0];
}
getLastUsedServer() {
var servers = credentialProvider.credentials().Servers;
return servers.sort(sortServers), servers.length ? servers[0] : null;
}
getApiClientFromServerInfo(server, serverUrlToMatch) {
(server.DateLastAccessed = Date.now()),
null == server.LastConnectionMode &&
server.ManualAddress &&
(server.LastConnectionMode = ConnectionMode.Manual);
var credentials = credentialProvider.credentials(),
serverUrlToMatch =
(credentialProvider.addOrUpdateServer(
credentials.Servers,
server,
serverUrlToMatch
) && credentialProvider.credentials(credentials),
this._getOrAddApiClient(
server,
getServerAddress(server, server.LastConnectionMode)
));
return setCurrentApiClient(this, serverUrlToMatch), serverUrlToMatch;
}
clearData() {
console.log("connection manager clearing data"), (this._connectUser = null);
var credentials = credentialProvider.credentials();
(credentials.ConnectAccessToken = null),
(credentials.ConnectUserId = null),
(credentials.Servers = []),
credentialProvider.credentials(credentials);
}
currentApiClient() {
var server;
return (
currentApiClient ||
((server = this.getLastUsedServer()) &&
(currentApiClient = setCurrentApiClient(
this,
this.getApiClient(server.Id)
))),
currentApiClient
);
}
_getOrAddApiClient(server, serverUrl) {
let apiClient = server.Id ? this.getApiClient(server.Id) : null;
if (!apiClient && server.IsLocalServer)
for (let i = 0, length = this._apiClients.length; i < length; i++) {
var current = this._apiClients[i];
if (current.serverInfo().IsLocalServer) {
apiClient = current;
break;
}
}
return (
apiClient
? server.Id &&
(apiClient.serverId() ||
(apiClient.serverInfo(server),
apiClient.setAuthenticationInfo(
getLastUserAuthInfoFromServer(server),
(server.Users || []).slice(0)
)),
(this._apiClientsMap[server.Id] = apiClient))
: ((apiClient = new apiClientFactory(
serverUrl,
this.appName(),
this.appVersion(),
this.deviceName(),
this.deviceId(),
this.devicePixelRatio
)),
(currentApiClient = currentApiClient || apiClient),
this._apiClients.push(apiClient),
apiClient.serverInfo(server),
apiClient.setAuthenticationInfo(
getLastUserAuthInfoFromServer(server),
(server.Users || []).slice(0)
),
apiClient.serverId() &&
(this._apiClientsMap[apiClient.serverId()] = apiClient),
apiClient.setCurrentLocale(this.currentLocale),
(apiClient.onAuthenticated = onAuthenticated.bind(this)),
events.trigger(this, "apiclientcreated", [apiClient]),
events.on(
apiClient,
"serveraddresschanged",
onServerAddressChanged.bind(this)
)),
console.log("returning instance from getOrAddApiClient"),
apiClient
);
}
setCurrentLocale(value) {
this.currentLocale = value;
for (let i = 0, length = this._apiClients.length; i < length; i++)
this._apiClients[i].setCurrentLocale(value);
}
logout(apiClient) {
console.log("begin connectionManager loguot");
var promises = [];
let isLoggedIntoConnect = this.isLoggedIntoConnect();
var apiClients =
apiClient && !isLoggedIntoConnect
? [apiClient]
: this._apiClients.slice(0);
let apiClientInfos = [];
for (let i = 0, length = apiClients.length; i < length; i++) {
var currApiClient = apiClients[i];
currApiClient.accessToken() &&
(promises.push(logoutOfServer(this, currApiClient)),
apiClientInfos.push({
userId: currApiClient.getCurrentUserId(),
serverId: currApiClient.serverId(),
}));
}
let instance = this;
return Promise.all(promises).then(() => {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.slice(0);
for (let i = 0, length = apiClientInfos.length; i < length; i++) {
var server,
apiClientInfo = apiClientInfos[i];
let currentServerId = apiClientInfo.serverId;
currentServerId &&
(server = servers.filter((s) => s.Id === currentServerId)[0]) &&
(isLoggedIntoConnect
? clearUsersFromServer(server)
: removeUserFromServer(server, apiClientInfo.userId),
(server.ExchangeToken = null));
}
(credentials.Servers = servers),
(credentials.ConnectAccessToken = null),
(credentials.ConnectUserId = null),
credentialProvider.credentials(credentials),
(instance._connectUser = null);
});
}
getSavedServers() {
var servers;
return credentialProvider
? ((servers = credentialProvider.credentials().Servers.slice(0)).forEach(
setServerProperties
),
servers.sort(sortServers),
servers)
: (console.log(
"A call was made to getSavedServers before connectionManager was initialized."
),
[]);
}
getAvailableServers() {
console.log("Begin getAvailableServers");
let credentials = credentialProvider.credentials();
return Promise.all([
getConnectServers(this, credentials),
findServers(),
]).then((responses) => {
var connectServers = responses[0],
responses = responses[1],
servers = credentials.Servers.slice(0);
let changed = !1;
return (
mergeServers(credentialProvider, servers, responses) && (changed = !0),
mergeServers(credentialProvider, servers, connectServers) &&
(changed = !0),
(servers = filterServers(servers, connectServers)).forEach(
setServerProperties
),
servers.sort(sortServers),
changed ||
(JSON.stringify(servers) !== JSON.stringify(credentials.Servers) &&
(changed = !0)),
changed &&
((credentials.Servers = servers),
credentialProvider.credentials(credentials)),
servers
);
});
}
connectToServers(servers, options) {
console.log(`Begin connectToServers, with ${servers.length} servers`);
var firstServer = servers.length ? servers[0] : null;
return firstServer
? this.connectToServer(firstServer, options).then(
(result) => (
"Unavailable" === result.State &&
(result.State = "ServerSelection"),
console.log(
"resolving connectToServers with result.State: " + result.State
),
result
)
)
: Promise.resolve({
Servers: servers,
State:
servers.length || this.connectUser()
? "ServerSelection"
: "ConnectSignIn",
ConnectUser: this.connectUser(),
});
}
connectToServer(server, options) {
console.log("begin connectToServer"), (options = options || {});
let instance = this;
return tryReconnect(this, server).then(
(result) => {
var serverUrl = result.url,
connectionMode = result.connectionMode;
return (
(result = result.data),
1 === compareVersions(instance.minServerVersion(), result.Version) ||
1 === compareVersions(result.Version, "8.0")
? (console.log(
"minServerVersion requirement not met. Server version: " +
result.Version
),
{ State: "ServerUpdateNeeded", Servers: [server] })
: (server.Id &&
result.Id !== server.Id &&
!1 !== instance.validateServerIds &&
updateServerInfo(
(server = { Id: result.Id, ManualAddress: serverUrl }),
result
),
resolveIfAvailable(
instance,
serverUrl,
server,
result,
connectionMode,
options
))
);
},
function () {
return {
State: "Unavailable",
Server: server,
ConnectUser: instance.connectUser(),
};
}
);
}
connectToAddress(address, options) {
if (!address) return Promise.reject();
address = normalizeAddress(address);
let instance = this;
var server = {
ManualAddress: address,
LastConnectionMode: ConnectionMode.Manual,
};
return this.connectToServer(server, options).catch(function () {
return (
console.log(`connectToAddress ${address} failed`),
Promise.resolve({
State: "Unavailable",
ConnectUser: instance.connectUser(),
Server: { ManualAddress: address },
Address: address,
})
);
});
}
loginToConnect(username, password) {
if (!username) return Promise.reject();
if (!password) return Promise.reject();
let instance = this;
return ajax({
type: "POST",
url: "https://connect.emby.media/service/user/authenticate",
data: { nameOrEmail: username, rawpw: password },
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: { "X-Application": this.appName() + "/" + this.appVersion() },
}).then((result) => {
var credentials = credentialProvider.credentials();
return (
(credentials.ConnectAccessToken = result.AccessToken),
(credentials.ConnectUserId = result.User.Id),
credentialProvider.credentials(credentials),
onConnectUserSignIn(instance, result.User),
result
);
});
}
signupForConnect(options) {
var email = options.email,
username = options.username,
password = options.password,
passwordConfirm = options.passwordConfirm;
return email && username && password
? !passwordConfirm || password !== passwordConfirm
? Promise.reject({ errorCode: "passwordmatch" })
: ((passwordConfirm = {
email: email,
userName: username,
rawpw: password,
}),
options.grecaptcha &&
(passwordConfirm.grecaptcha = options.grecaptcha),
ajax({
type: "POST",
url: "https://connect.emby.media/service/register",
data: passwordConfirm,
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: {
"X-Application": this.appName() + "/" + this.appVersion(),
"X-CONNECT-TOKEN": "CONNECT-REGISTER",
},
})
.catch((response) => response.json())
.then((result) => {
if (result && result.Status)
return "SUCCESS" === result.Status
? Promise.resolve(result)
: Promise.reject({ errorCode: result.Status });
Promise.reject();
}))
: Promise.reject({ errorCode: "invalidinput" });
}
getUserInvitations() {
var connectToken = this.connectToken();
if (!connectToken) throw new Error("null connectToken");
if (this.connectUserId())
return ajax({
type: "GET",
url: `https://connect.emby.media/service/servers?userId=${this.connectUserId()}&status=Waiting`,
dataType: "json",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": this.appName() + "/" + this.appVersion(),
},
});
throw new Error("null connectUserId");
}
deleteServer(serverId) {
var server, connectToken, connectUserId;
if (serverId)
return (server = (server = credentialProvider
.credentials()
.Servers.filter((s) => s.Id === serverId)).length
? server[0]
: null).ConnectServerId &&
((connectToken = this.connectToken()),
(connectUserId = this.connectUserId()),
connectToken) &&
connectUserId
? ajax({
type: "DELETE",
url:
`https://connect.emby.media/service/serverAuthorizations?serverId=${server.ConnectServerId}&userId=` +
connectUserId,
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": this.appName() + "/" + this.appVersion(),
},
}).then(onDone, onDone)
: onDone();
throw new Error("null serverId");
function onDone() {
var credentials = credentialProvider.credentials();
return (
(credentials.Servers = credentials.Servers.filter(
(s) => s.Id !== serverId
)),
credentialProvider.credentials(credentials),
Promise.resolve()
);
}
}
resetRegistrationInfo(apiClient, onlyResetIfFailed) {
let removeAll = !1;
var cacheKey = getCacheKey("themes", apiClient, { viewOnly: !0 }),
regInfo = JSON.parse(appStorage.getItem(cacheKey) || "{}");
(!removeAll && onlyResetIfFailed && -1 !== regInfo.lastValidDate) ||
(appStorage.removeItem(cacheKey), (removeAll = !0)),
(cacheKey = getCacheKey("themes", apiClient, { viewOnly: !1 })),
(regInfo = JSON.parse(appStorage.getItem(cacheKey) || "{}")),
(!removeAll && onlyResetIfFailed && -1 !== regInfo.lastValidDate) ||
(appStorage.removeItem(cacheKey), (removeAll = !0)),
onlyResetIfFailed || events.trigger(this, "resetregistrationinfo");
}
getRegistrationInfo(feature, apiClient, options) {
let params = {
serverId: apiClient.serverId(),
deviceId: this.deviceId(),
deviceName: this.deviceName(),
appName: this.appName(),
appVersion: this.appVersion(),
},
cacheKey =
((options = options || {}).viewOnly &&
(params.viewOnly = options.viewOnly),
getCacheKey(feature, apiClient, options));
var feature = JSON.parse(appStorage.getItem(cacheKey) || "{}"),
timeSinceLastValidation = Date.now() - (feature.lastValidDate || 0);
if (timeSinceLastValidation <= 864e5)
return (
console.log("getRegistrationInfo returning cached info"),
Promise.resolve()
);
if (options.useCachedFailure && -1 === feature.lastValidDate)
return Promise.reject();
let regCacheValid =
timeSinceLastValidation <= 864e5 * (feature.cacheExpirationDays || 7);
return !params.serverId ||
((options = apiClient.getCurrentUserId()) &&
"81f53802ea0247ad80618f55d9b4ec3c" === options.toLowerCase() &&
"21585256623b4beeb26d5d3b09dec0ac" === params.serverId.toLowerCase())
? Promise.reject()
: ((timeSinceLastValidation = ajax({
url:
"https://mb3admin.com/admin/service/registration/validateDevice?" +
new URLSearchParams(params).toString(),
type: "POST",
dataType: "json",
}).then(
(response) => (
appStorage.setItem(
cacheKey,
JSON.stringify({
lastValidDate: Date.now(),
deviceId: params.deviceId,
cacheExpirationDays: response.cacheExpirationDays,
lastUpdated: Date.now(),
})
),
Promise.resolve()
),
(response) => {
var status = (response || {}).status;
return (
console.log("getRegistrationInfo response: " + status),
status &&
status < 500 &&
appStorage.setItem(
cacheKey,
JSON.stringify({
lastValidDate: -1,
deviceId: params.deviceId,
cacheExpirationDays: 0,
lastUpdated: Date.now(),
})
),
403 === status
? Promise.reject("overlimit")
: status && status < 500
? Promise.reject()
: ((err) => {
if (
(console.log("getRegistrationInfo failed: " + err),
regCacheValid)
)
return (
console.log(
"getRegistrationInfo returning cached info"
),
Promise.resolve()
);
throw err;
})(response)
);
}
)),
regCacheValid
? (console.log("getRegistrationInfo returning cached info"),
Promise.resolve())
: timeSinceLastValidation);
}
createPin() {
var request = {
type: "POST",
url: getConnectUrl("pin"),
data: { deviceId: this.deviceId() },
dataType: "json",
};
return addAppInfoToConnectRequest(this, request), ajax(request);
}
getPinStatus(pinInfo) {
if (pinInfo)
return (
(pinInfo = { deviceId: pinInfo.DeviceId, pin: pinInfo.Pin }),
addAppInfoToConnectRequest(
this,
(pinInfo = {
type: "GET",
url:
getConnectUrl("pin") +
"?" +
new URLSearchParams(pinInfo).toString(),
dataType: "json",
})
),
ajax(pinInfo)
);
throw new Error("pinInfo cannot be null");
}
exchangePin(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
let instance = this;
return exchangePinInternal(this, pinInfo).then((result) => {
var credentials = credentialProvider.credentials();
return (
(credentials.ConnectAccessToken = result.AccessToken),
(credentials.ConnectUserId = result.UserId),
credentialProvider.credentials(credentials),
ensureConnectUser(instance, credentials)
);
});
}
connect(options) {
console.log("Begin connect");
let instance = this;
return instance
.getAvailableServers()
.then((servers) => instance.connectToServers(servers, options));
}
handleMessageReceived(msg) {
var serverId = msg.ServerId;
if (serverId) {
serverId = this.getApiClient(serverId);
if (serverId) {
if ("string" == typeof msg.Data)
try {
msg.Data = JSON.parse(msg.Data);
} catch (err) {
console.log("Error in handleMessageReceived JSON.parse: " + err);
}
serverId.handleMessageReceived(msg);
}
}
}
onNetworkChanged() {
var apiClients = this._apiClients;
for (let i = 0, length = apiClients.length; i < length; i++)
apiClients[i].onNetworkChanged();
}
onAppResume() {
var apiClients = this._apiClients;
for (let i = 0, length = apiClients.length; i < length; i++)
apiClients[i].ensureWebSocket();
}
isLoggedIntoConnect() {
return !(!this.connectToken() || !this.connectUserId());
}
isLoggedIn(serverId, userId) {
var server = credentialProvider
.credentials()
.Servers.filter((s) => s.Id === serverId)[0];
return (
!!server &&
null !=
(userId
? getUserAuthInfoFromServer(server, userId)
: getLastUserAuthInfoFromServer(server)
)?.AccessToken
);
}
getApiClients() {
var servers = this.getSavedServers();
for (let i = 0, length = servers.length; i < length; i++) {
var serverUrl,
server = servers[i];
server.Id &&
(serverUrl = getServerAddress(server, server.LastConnectionMode)) &&
this._getOrAddApiClient(server, serverUrl);
}
return this._apiClients;
}
getApiClient(item) {
if (!item) throw new Error("item or serverId cannot be null");
let serverId = item.ServerId;
serverId = serverId || (item.Id && "Server" === item.Type ? item.Id : item);
let apiClient;
if (serverId && (apiClient = this._apiClientsMap[serverId]))
return apiClient;
var apiClients = this._apiClients;
for (let i = 0, length = apiClients.length; i < length; i++) {
var apiClientServerId = (apiClient = apiClients[i]).serverId();
if (!apiClientServerId || apiClientServerId === serverId)
return apiClient;
}
return null;
}
getEmbyServerUrl(baseUrl, handler, params) {
return ApiClient.getUrl(handler, params, baseUrl);
}
reportCapabilities(apiClient) {
return getCapabilities().then(function (capabilities) {
return apiClient.reportCapabilities(capabilities);
});
}
getSignedInUsers(apiClient) {
var credentials = credentialProvider.credentials(),
serverId = apiClient.serverId(),
servers = credentials.Servers.slice(0);
let server;
for (let i = 0, length = servers.length; i < length; i++)
if (servers[i].Id === serverId) {
server = servers[i];
break;
}
if (!server) return Promise.resolve([]);
var users = (server.Users || []).slice(0),
promises = [];
for (let i = 0, length = users.length; i < length; i++)
promises.push(getUserRecordFromAuthentication(users[i], apiClient));
return Promise.all(promises).then(function (responses) {
var usersResult = [];
for (let i = 0, length = responses.length; i < length; i++)
responses[i] && usersResult.push(responses[i]);
return usersResult;
});
}
validateCanChangeToUser(apiClient, userId) {
let credentials = credentialProvider.credentials();
var serverId = apiClient.serverId(),
servers = credentials.Servers.slice(0);
let server;
for (let i = 0, length = servers.length; i < length; i++)
if (servers[i].Id === serverId) {
server = servers[i];
break;
}
if (!server) return Promise.reject();
var users = (server.Users || []).slice(0);
let user;
for (let i = 0, length = users.length; i < length; i++)
if (users[i].UserId === userId) {
user = users[i];
break;
}
return user
? validateAuthentication(
this,
server,
user,
apiClient.serverAddress()
).catch(function (err) {
return (
credentialProvider.addOrUpdateServer(credentials.Servers, server) &&
credentialProvider.credentials(credentials),
Promise.reject(err)
);
})
: Promise.reject();
}
changeToUser(apiClient, userId) {
let instance = this;
return this.validateCanChangeToUser(apiClient, userId).then(function () {
var credentials = credentialProvider.credentials();
let serverId = apiClient.serverId();
var servers = credentials.Servers.slice(0);
let server;
for (let i = 0, length = servers.length; i < length; i++)
if (servers[i].Id === serverId) {
server = servers[i];
break;
}
if (!server) return Promise.reject();
var users = (server.Users || []).slice(0);
let user;
for (let i = 0, length = users.length; i < length; i++)
if (users[i].UserId === userId) {
user = users[i];
break;
}
return user
? getUserRecordFromAuthentication(user, apiClient).then(function (
fullUserFromServer
) {
return onAuthenticated.call(instance, apiClient, {
ServerId: serverId,
User: fullUserFromServer,
AccessToken: user.AccessToken,
});
})
: Promise.reject();
});
}
}
export default new ConnectionManager();