Cleanup utility function in connection manager
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import { ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import events from 'utils/events';
|
||||
import { ajax } from 'utils/fetch';
|
||||
import { equalsIgnoreCase } from 'utils/string';
|
||||
|
||||
import { ConnectionMode } from './connectionMode';
|
||||
import { ConnectionState } from './connectionState';
|
||||
import { compareVersions } from './utils/compareVersions';
|
||||
|
||||
const defaultTimeout = 20000;
|
||||
const DEFAULT_CONNECTION_TIMEOUT = 20000;
|
||||
|
||||
function getServerAddress(server, mode) {
|
||||
switch (mode) {
|
||||
@@ -20,185 +23,29 @@ function getServerAddress(server, mode) {
|
||||
}
|
||||
}
|
||||
|
||||
function paramsToString(params) {
|
||||
const values = [];
|
||||
|
||||
for (const key in params) {
|
||||
const value = params[key];
|
||||
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
values.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
||||
}
|
||||
}
|
||||
return values.join('&');
|
||||
}
|
||||
|
||||
function resolveFailure(instance, resolve) {
|
||||
resolve({
|
||||
State: ConnectionState.Unavailable
|
||||
});
|
||||
}
|
||||
|
||||
function mergeServers(credentialProvider, list1, list2) {
|
||||
for (let i = 0, length = list2.length; i < length; i++) {
|
||||
credentialProvider.addOrUpdateServer(list1, list2[i]);
|
||||
}
|
||||
|
||||
return list1;
|
||||
}
|
||||
|
||||
function updateServerInfo(server, systemInfo) {
|
||||
server.Name = systemInfo.ServerName;
|
||||
|
||||
if (systemInfo.Id) {
|
||||
server.Id = systemInfo.Id;
|
||||
}
|
||||
|
||||
if (systemInfo.LocalAddress) {
|
||||
server.LocalAddress = systemInfo.LocalAddress;
|
||||
}
|
||||
}
|
||||
|
||||
function getEmbyServerUrl(baseUrl, handler) {
|
||||
return `${baseUrl}/${handler}`;
|
||||
}
|
||||
|
||||
function getFetchPromise(request) {
|
||||
const headers = request.headers || {};
|
||||
|
||||
if (request.dataType === 'json') {
|
||||
headers.accept = 'application/json';
|
||||
}
|
||||
|
||||
const fetchRequest = {
|
||||
headers,
|
||||
method: request.type,
|
||||
credentials: 'same-origin'
|
||||
};
|
||||
|
||||
let contentType = request.contentType;
|
||||
|
||||
if (request.data) {
|
||||
if (typeof request.data === 'string') {
|
||||
fetchRequest.body = request.data;
|
||||
} else {
|
||||
fetchRequest.body = paramsToString(request.data);
|
||||
|
||||
contentType = contentType || 'application/x-www-form-urlencoded; charset=UTF-8';
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType) {
|
||||
headers['Content-Type'] = contentType;
|
||||
}
|
||||
|
||||
if (!request.timeout) {
|
||||
return fetch(request.url, fetchRequest);
|
||||
}
|
||||
|
||||
return fetchWithTimeout(request.url, fetchRequest, request.timeout);
|
||||
}
|
||||
|
||||
function fetchWithTimeout(url, options, timeoutMs) {
|
||||
console.log(`fetchWithTimeout: timeoutMs: ${timeoutMs}, url: ${url}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(reject, timeoutMs);
|
||||
|
||||
options = options || {};
|
||||
options.credentials = 'same-origin';
|
||||
|
||||
fetch(url, options).then(
|
||||
(response) => {
|
||||
clearTimeout(timeout);
|
||||
|
||||
console.log(`fetchWithTimeout: succeeded connecting to url: ${url}`);
|
||||
|
||||
resolve(response);
|
||||
},
|
||||
(error) => {
|
||||
clearTimeout(timeout);
|
||||
|
||||
console.log(`fetchWithTimeout: timed out connecting to url: ${url}`, error);
|
||||
|
||||
reject();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function ajax(request) {
|
||||
if (!request) {
|
||||
throw new Error('Request cannot be null');
|
||||
}
|
||||
|
||||
request.headers = request.headers || {};
|
||||
|
||||
console.log(`ConnectionManager requesting url: ${request.url}`);
|
||||
|
||||
return getFetchPromise(request).then(
|
||||
(response) => {
|
||||
console.log(`ConnectionManager response status: ${response.status}, url: ${request.url}`);
|
||||
|
||||
if (response.status < 400) {
|
||||
if (request.dataType === 'json' || request.headers.accept === 'application/json') {
|
||||
return response.json();
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(response);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
console.log(`ConnectionManager request failed to url: ${request.url}`);
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function replaceAll(originalString, strReplace, strWith) {
|
||||
const reg = new RegExp(strReplace, 'ig');
|
||||
return originalString.replace(reg, strWith);
|
||||
}
|
||||
|
||||
function normalizeAddress(address) {
|
||||
// Attempt to correct bad input
|
||||
address = address.trim();
|
||||
|
||||
// Seeing failures in iOS when protocol isn't lowercase
|
||||
address = replaceAll(address, 'Http:', 'http:');
|
||||
address = replaceAll(address, 'Https:', 'https:');
|
||||
address = address.replace('Http:', 'http:');
|
||||
address = address.replace('Https:', 'https:');
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
function stringEqualsIgnoreCase(str1, str2) {
|
||||
return (str1 || '').toLowerCase() === (str2 || '').toLowerCase();
|
||||
}
|
||||
|
||||
function compareVersions(a, b) {
|
||||
// -1 a is smaller
|
||||
// 1 a is larger
|
||||
// 0 equal
|
||||
a = a.split('.');
|
||||
b = b.split('.');
|
||||
|
||||
for (let i = 0, length = Math.max(a.length, b.length); i < length; i++) {
|
||||
const aVal = parseInt(a[i] || '0', 10);
|
||||
const bVal = parseInt(b[i] || '0', 10);
|
||||
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (aVal > bVal) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export default class ConnectionManager {
|
||||
constructor(credentialProvider, appName, appVersion, deviceName, deviceId, capabilities) {
|
||||
console.log('Begin ConnectionManager constructor');
|
||||
@@ -243,9 +90,9 @@ export default class ConnectionManager {
|
||||
.credentials()
|
||||
.Servers.filter(
|
||||
(s) =>
|
||||
stringEqualsIgnoreCase(s.ManualAddress, apiClient.serverAddress())
|
||||
|| stringEqualsIgnoreCase(s.LocalAddress, apiClient.serverAddress())
|
||||
|| stringEqualsIgnoreCase(s.RemoteAddress, apiClient.serverAddress())
|
||||
equalsIgnoreCase(s.ManualAddress, apiClient.serverAddress())
|
||||
|| equalsIgnoreCase(s.LocalAddress, apiClient.serverAddress())
|
||||
|| equalsIgnoreCase(s.RemoteAddress, apiClient.serverAddress())
|
||||
);
|
||||
|
||||
const existingServer = existingServers.length ? existingServers[0] : apiClient.serverInfo();
|
||||
@@ -301,7 +148,7 @@ export default class ConnectionManager {
|
||||
|
||||
self.getOrCreateApiClient = (serverId) => {
|
||||
const credentials = credentialProvider.credentials();
|
||||
const servers = credentials.Servers.filter((s) => stringEqualsIgnoreCase(s.Id, serverId));
|
||||
const servers = credentials.Servers.filter((s) => equalsIgnoreCase(s.Id, serverId));
|
||||
|
||||
if (!servers.length) {
|
||||
throw new Error(`Server not found: ${serverId}`);
|
||||
@@ -372,7 +219,7 @@ export default class ConnectionManager {
|
||||
function validateAuthentication(server, serverUrl) {
|
||||
return ajax({
|
||||
type: 'GET',
|
||||
url: getEmbyServerUrl(serverUrl, 'System/Info'),
|
||||
url: `${serverUrl}/System/Info`,
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
'X-MediaBrowser-Token': server.AccessToken
|
||||
@@ -498,7 +345,9 @@ export default class ConnectionManager {
|
||||
return Promise.all([findServers()]).then((responses) => {
|
||||
const foundServers = responses[0];
|
||||
const servers = credentials.Servers.slice(0);
|
||||
mergeServers(credentialProvider, servers, foundServers);
|
||||
foundServers.forEach(server => {
|
||||
credentialProvider.addOrUpdateServer(servers, server);
|
||||
});
|
||||
|
||||
servers.sort((a, b) => (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0));
|
||||
credentials.Servers = servers;
|
||||
@@ -579,8 +428,8 @@ export default class ConnectionManager {
|
||||
console.log('getTryConnectPromise ' + url);
|
||||
|
||||
ajax({
|
||||
url: getEmbyServerUrl(url, 'system/info/public'),
|
||||
timeout: defaultTimeout,
|
||||
url: `${url}/System/Info/Public`,
|
||||
timeout: DEFAULT_CONNECTION_TIMEOUT,
|
||||
type: 'GET',
|
||||
dataType: 'json'
|
||||
}).then(
|
||||
@@ -688,13 +537,17 @@ export default class ConnectionManager {
|
||||
console.log(
|
||||
'http request succeeded, but found a different server Id than what was expected'
|
||||
);
|
||||
resolveFailure(self, resolve);
|
||||
resolve({
|
||||
State: ConnectionState.Unavailable
|
||||
});
|
||||
} else {
|
||||
onSuccessfulConnection(server, result, connectionMode, serverUrl, true, resolve, options);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
resolveFailure(self, resolve);
|
||||
resolve({
|
||||
State: ConnectionState.Unavailable
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** The mode used to connect to a server. */
|
||||
export const enum ConnectionMode {
|
||||
Local = 0,
|
||||
Remote = 1,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export function compareVersions(a: string = '', b: string = '') {
|
||||
// -1 a is smaller
|
||||
// 1 a is larger
|
||||
// 0 equal
|
||||
const aParts = a.split('.');
|
||||
const bParts = b.split('.');
|
||||
|
||||
for (let i = 0, length = Math.max(aParts.length, bParts.length); i < length; i++) {
|
||||
const aVal = parseInt(aParts[i] || '0', 10);
|
||||
const bVal = parseInt(bParts[i] || '0', 10);
|
||||
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (aVal > bVal) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ function enableHlsPlayer(url, item, mediaSource, mediaType) {
|
||||
|
||||
// issue head request to get content type
|
||||
return new Promise(function (resolve, reject) {
|
||||
import('../../components/fetchhelper').then((fetchHelper) => {
|
||||
import('../../utils/fetch').then((fetchHelper) => {
|
||||
fetchHelper.ajax({
|
||||
url: url,
|
||||
type: 'HEAD'
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Checks if two string are equal ignoring case.
|
||||
* @param str1 The first string.
|
||||
* @param str2 The second string.
|
||||
* @returns True if the strings are equal ignoring case.
|
||||
*/
|
||||
export const equalsIgnoreCase = (str1 = '', str2 = '') => str1.toLowerCase() === str2.toLowerCase();
|
||||
|
||||
/**
|
||||
* Checks if a string is empty or contains only whitespace.
|
||||
* @param {string} value The string to test.
|
||||
|
||||
Reference in New Issue
Block a user