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();