First separation commit.

Added LICENSE, README.md, CONTRIBUTORS.md
This commit is contained in:
Erwin de Haan 2019-01-09 12:36:54 +01:00
parent 09513af31b
commit 4678528d00
657 changed files with 422 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,231 @@
define(["apiclientcore", "localassetmanager"], function(ApiClient, localassetmanager) {
"use strict";
function isLocalId(str) {
return startsWith(str, localPrefix)
}
function isLocalViewId(str) {
return startsWith(str, localViewPrefix)
}
function isTopLevelLocalViewId(str) {
return "localview" === str
}
function stripLocalPrefix(str) {
var res = stripStart(str, localPrefix);
return res = stripStart(res, localViewPrefix)
}
function startsWith(str, find) {
return !!(str && find && str.length > find.length && 0 === str.indexOf(find))
}
function stripStart(str, find) {
return startsWith(str, find) ? str.substr(find.length) : str
}
function createEmptyList() {
return {
Items: [],
TotalRecordCount: 0
}
}
function convertGuidToLocal(guid) {
return guid ? isLocalId(guid) ? guid : "local:" + guid : null
}
function adjustGuidProperties(downloadedItem) {
downloadedItem.Id = convertGuidToLocal(downloadedItem.Id), downloadedItem.SeriesId = convertGuidToLocal(downloadedItem.SeriesId), downloadedItem.SeasonId = convertGuidToLocal(downloadedItem.SeasonId), downloadedItem.AlbumId = convertGuidToLocal(downloadedItem.AlbumId), downloadedItem.ParentId = convertGuidToLocal(downloadedItem.ParentId), downloadedItem.ParentThumbItemId = convertGuidToLocal(downloadedItem.ParentThumbItemId), downloadedItem.ParentPrimaryImageItemId = convertGuidToLocal(downloadedItem.ParentPrimaryImageItemId), downloadedItem.PrimaryImageItemId = convertGuidToLocal(downloadedItem.PrimaryImageItemId), downloadedItem.ParentLogoItemId = convertGuidToLocal(downloadedItem.ParentLogoItemId), downloadedItem.ParentBackdropItemId = convertGuidToLocal(downloadedItem.ParentBackdropItemId), downloadedItem.ParentBackdropImageTags = null
}
function getLocalView(instance, serverId, userId) {
return instance.getLocalFolders(serverId, userId).then(function(views) {
var localView = null;
return views.length > 0 && (localView = {
Name: instance.downloadsTitleText || "Downloads",
ServerId: serverId,
Id: "localview",
Type: "localview",
IsFolder: !0
}), Promise.resolve(localView)
})
}
function ApiClientEx(serverAddress, clientName, applicationVersion, deviceName, deviceId, devicePixelRatio) {
ApiClient.call(this, serverAddress, clientName, applicationVersion, deviceName, deviceId, devicePixelRatio)
}
var localPrefix = "local:",
localViewPrefix = "localview:";
return Object.assign(ApiClientEx.prototype, ApiClient.prototype), ApiClientEx.prototype.getPlaybackInfo = function(itemId, options, deviceProfile) {
var onFailure = function() {
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
};
if (isLocalId(itemId)) return localassetmanager.getLocalItem(this.serverId(), stripLocalPrefix(itemId)).then(function(item) {
return {
MediaSources: item.Item.MediaSources.map(function(m) {
return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m
})
}
}, onFailure);
var instance = this;
return localassetmanager.getLocalItem(this.serverId(), itemId).then(function(item) {
if (item) {
var mediaSources = item.Item.MediaSources.map(function(m) {
return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m
});
return localassetmanager.fileExists(item.LocalPath).then(function(exists) {
if (exists) {
var res = {
MediaSources: mediaSources
};
return Promise.resolve(res)
}
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
}, onFailure)
}
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
}, onFailure)
}, ApiClientEx.prototype.getItems = function(userId, options) {
var i, serverInfo = this.serverInfo();
if (serverInfo && "localview" === options.ParentId) return this.getLocalFolders(serverInfo.Id, userId).then(function(items) {
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
});
if (serverInfo && options && (isLocalId(options.ParentId) || isLocalId(options.SeriesId) || isLocalId(options.SeasonId) || isLocalViewId(options.ParentId) || isLocalId(options.AlbumIds))) return localassetmanager.getViewItems(serverInfo.Id, userId, options).then(function(items) {
items.forEach(function(item) {
adjustGuidProperties(item)
});
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
});
if (options && options.ExcludeItemIds && options.ExcludeItemIds.length) {
var exItems = options.ExcludeItemIds.split(",");
for (i = 0; i < exItems.length; i++)
if (isLocalId(exItems[i])) return Promise.resolve(createEmptyList())
} else if (options && options.Ids && options.Ids.length) {
var ids = options.Ids.split(","),
hasLocal = !1;
for (i = 0; i < ids.length; i++) isLocalId(ids[i]) && (hasLocal = !0);
if (hasLocal) return localassetmanager.getItemsFromIds(serverInfo.Id, ids).then(function(items) {
items.forEach(function(item) {
adjustGuidProperties(item)
});
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
})
}
return ApiClient.prototype.getItems.call(this, userId, options)
}, ApiClientEx.prototype.getUserViews = function(options, userId) {
var instance = this;
options = options || {};
var basePromise = ApiClient.prototype.getUserViews.call(instance, options, userId);
return options.enableLocalView ? basePromise.then(function(result) {
var serverInfo = instance.serverInfo();
return serverInfo ? getLocalView(instance, serverInfo.Id, userId).then(function(localView) {
return localView && (result.Items.push(localView), result.TotalRecordCount++), Promise.resolve(result)
}) : Promise.resolve(result)
}) : basePromise
}, ApiClientEx.prototype.getItem = function(userId, itemId) {
if (!itemId) throw new Error("null itemId");
itemId && (itemId = itemId.toString());
var serverInfo;
return isTopLevelLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? getLocalView(this, serverInfo.Id, userId) : isLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? this.getLocalFolders(serverInfo.Id, userId).then(function(items) {
var views = items.filter(function(item) {
return item.Id === itemId
});
return views.length > 0 ? Promise.resolve(views[0]) : Promise.reject()
}) : isLocalId(itemId) && (serverInfo = this.serverInfo()) ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) {
return adjustGuidProperties(item.Item), Promise.resolve(item.Item)
}) : ApiClient.prototype.getItem.call(this, userId, itemId)
}, ApiClientEx.prototype.getLocalFolders = function(userId) {
var serverInfo = this.serverInfo();
return userId = userId || serverInfo.UserId, localassetmanager.getViews(serverInfo.Id, userId)
}, ApiClientEx.prototype.getNextUpEpisodes = function(options) {
return options.SeriesId && isLocalId(options.SeriesId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getNextUpEpisodes.call(this, options)
}, ApiClientEx.prototype.getSeasons = function(itemId, options) {
return isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Season", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getSeasons.call(this, itemId, options)
}, ApiClientEx.prototype.getEpisodes = function(itemId, options) {
return isLocalId(options.SeasonId) || isLocalId(options.seasonId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getEpisodes.call(this, itemId, options)
}, ApiClientEx.prototype.getLatestOfflineItems = function(options) {
options.SortBy = "DateCreated", options.SortOrder = "Descending";
var serverInfo = this.serverInfo();
return serverInfo ? localassetmanager.getViewItems(serverInfo.Id, null, options).then(function(items) {
return items.forEach(function(item) {
adjustGuidProperties(item)
}), Promise.resolve(items)
}) : Promise.resolve([])
}, ApiClientEx.prototype.getThemeMedia = function(userId, itemId, inherit) {
return isLocalViewId(itemId) || isLocalId(itemId) || isTopLevelLocalViewId(itemId) ? Promise.reject() : ApiClient.prototype.getThemeMedia.call(this, userId, itemId, inherit)
}, ApiClientEx.prototype.getSpecialFeatures = function(userId, itemId) {
return isLocalId(itemId) ? Promise.resolve([]) : ApiClient.prototype.getSpecialFeatures.call(this, userId, itemId)
}, ApiClientEx.prototype.getSimilarItems = function(itemId, options) {
return isLocalId(itemId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getSimilarItems.call(this, itemId, options)
}, ApiClientEx.prototype.updateFavoriteStatus = function(userId, itemId, isFavorite) {
return isLocalId(itemId) ? Promise.resolve() : ApiClient.prototype.updateFavoriteStatus.call(this, userId, itemId, isFavorite)
}, ApiClientEx.prototype.getScaledImageUrl = function(itemId, options) {
if (isLocalId(itemId) || options && options.itemid && isLocalId(options.itemid)) {
var serverInfo = this.serverInfo(),
id = stripLocalPrefix(itemId);
return localassetmanager.getImageUrl(serverInfo.Id, id, options)
}
return ApiClient.prototype.getScaledImageUrl.call(this, itemId, options)
}, ApiClientEx.prototype.reportPlaybackStart = function(options) {
if (!options) throw new Error("null options");
return isLocalId(options.ItemId) ? Promise.resolve() : ApiClient.prototype.reportPlaybackStart.call(this, options)
}, ApiClientEx.prototype.reportPlaybackProgress = function(options) {
if (!options) throw new Error("null options");
if (isLocalId(options.ItemId)) {
var serverInfo = this.serverInfo();
return serverInfo ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(options.ItemId)).then(function(item) {
var libraryItem = item.Item;
return "Video" === libraryItem.MediaType || "AudioBook" === libraryItem.Type ? (libraryItem.UserData = libraryItem.UserData || {}, libraryItem.UserData.PlaybackPositionTicks = options.PositionTicks, libraryItem.UserData.PlayedPercentage = Math.min(libraryItem.RunTimeTicks ? (options.PositionTicks || 0) / libraryItem.RunTimeTicks * 100 : 0, 100), localassetmanager.addOrUpdateLocalItem(item)) : Promise.resolve()
}) : Promise.resolve()
}
return ApiClient.prototype.reportPlaybackProgress.call(this, options)
}, ApiClientEx.prototype.reportPlaybackStopped = function(options) {
if (!options) throw new Error("null options");
if (isLocalId(options.ItemId)) {
var serverInfo = this.serverInfo(),
action = {
Date: (new Date).getTime(),
ItemId: stripLocalPrefix(options.ItemId),
PositionTicks: options.PositionTicks,
ServerId: serverInfo.Id,
Type: 0,
UserId: this.getCurrentUserId()
};
return localassetmanager.recordUserAction(action)
}
return ApiClient.prototype.reportPlaybackStopped.call(this, options)
}, ApiClientEx.prototype.getIntros = function(itemId) {
return isLocalId(itemId) ? Promise.resolve({
Items: [],
TotalRecordCount: 0
}) : ApiClient.prototype.getIntros.call(this, itemId)
}, ApiClientEx.prototype.getInstantMixFromItem = function(itemId, options) {
return isLocalId(itemId) ? Promise.resolve({
Items: [],
TotalRecordCount: 0
}) : ApiClient.prototype.getInstantMixFromItem.call(this, itemId, options)
}, ApiClientEx.prototype.getItemDownloadUrl = function(itemId) {
if (isLocalId(itemId)) {
var serverInfo = this.serverInfo();
if (serverInfo) return localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) {
return Promise.resolve(item.LocalPath)
})
}
return ApiClient.prototype.getItemDownloadUrl.call(this, itemId)
}, ApiClientEx
});

View file

@ -0,0 +1,23 @@
define([], function() {
"use strict";
function MyStore() {}
function updateCache(instance) {
instance.cache.put("data", new Response(JSON.stringify(instance.localData)))
}
return MyStore.prototype.init = function() {
var instance = this;
return caches.open("embydata").then(function(result) {
instance.cache = result, instance.localData = {}
})
}, MyStore.prototype.setItem = function(name, value) {
if (this.localData) {
this.localData[name] !== value && (this.localData[name] = value, updateCache(this))
}
}, MyStore.prototype.getItem = function(name) {
if (this.localData) return this.localData[name]
}, MyStore.prototype.removeItem = function(name) {
this.localData && (this.localData[name] = null, delete this.localData[name], updateCache(this))
}, new MyStore
});

View file

@ -0,0 +1,37 @@
define([], function() {
"use strict";
function onCachePutFail(e) {
console.log(e)
}
function updateCache(instance) {
var cache = instance.cache;
cache && cache.put("data", new Response(JSON.stringify(instance.localData))).catch(onCachePutFail)
}
function onCacheOpened(result) {
this.cache = result, this.localData = {}
}
function MyStore() {
try {
self.caches && caches.open("embydata").then(onCacheOpened.bind(this))
} catch (err) {
console.log("Error opening cache: " + err)
}
}
return MyStore.prototype.setItem = function(name, value) {
localStorage.setItem(name, value);
var localData = this.localData;
if (localData) {
localData[name] !== value && (localData[name] = value, updateCache(this))
}
}, MyStore.prototype.getItem = function(name) {
return localStorage.getItem(name)
}, MyStore.prototype.removeItem = function(name) {
localStorage.removeItem(name);
var localData = this.localData;
localData && (localData[name] = null, delete localData[name], updateCache(this))
}, new MyStore
});

View file

@ -0,0 +1,14 @@
define([], function() {
"use strict";
function MyStore() {
this.localData = {}
}
return MyStore.prototype.setItem = function(name, value) {
this.localData[name] = value
}, MyStore.prototype.getItem = function(name) {
return this.localData[name]
}, MyStore.prototype.removeItem = function(name) {
this.localData[name] = null
}, new MyStore
});

View file

@ -0,0 +1,8 @@
define([], function() {
"use strict";
function CameraRoll() {}
return CameraRoll.prototype.getFiles = function() {
return Promise.resolve([])
}, new CameraRoll
});

View file

@ -0,0 +1,744 @@
define(["events", "apiclient", "appStorage"], function(events, apiClientFactory, appStorage) {
"use strict";
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 paramsToString(params) {
var values = [];
for (var key in params) {
var value = params[key];
null !== value && void 0 !== value && "" !== value && values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
}
return values.join("&")
}
function resolveFailure(instance, resolve) {
resolve({
State: "Unavailable",
ConnectUser: instance.connectUser()
})
}
function mergeServers(credentialProvider, list1, list2) {
for (var i = 0, length = list2.length; i < length; i++) credentialProvider.addOrUpdateServer(list1, list2[i]);
return list1
}
function updateServerInfo(server, systemInfo) {
server.Name = systemInfo.ServerName, systemInfo.Id && (server.Id = systemInfo.Id), systemInfo.LocalAddress && (server.LocalAddress = systemInfo.LocalAddress), systemInfo.WanAddress && (server.RemoteAddress = systemInfo.WanAddress)
}
function getEmbyServerUrl(baseUrl, handler) {
return baseUrl + "/emby/" + handler
}
function getFetchPromise(request) {
var headers = request.headers || {};
"json" === request.dataType && (headers.accept = "application/json");
var fetchRequest = {
headers: headers,
method: request.type,
credentials: "same-origin"
},
contentType = request.contentType;
return request.data && ("string" == typeof request.data ? fetchRequest.body = request.data : (fetchRequest.body = paramsToString(request.data), contentType = contentType || "application/x-www-form-urlencoded; charset=UTF-8")), contentType && (headers["Content-Type"] = contentType), request.timeout ? fetchWithTimeout(request.url, fetchRequest, request.timeout) : fetch(request.url, fetchRequest)
}
function fetchWithTimeout(url, options, timeoutMs) {
return console.log("fetchWithTimeout: timeoutMs: " + timeoutMs + ", url: " + url), new Promise(function(resolve, reject) {
var timeout = setTimeout(reject, timeoutMs);
options = options || {}, options.credentials = "same-origin", fetch(url, options).then(function(response) {
clearTimeout(timeout), console.log("fetchWithTimeout: succeeded connecting to url: " + url), resolve(response)
}, function(error) {
clearTimeout(timeout), console.log("fetchWithTimeout: timed out connecting to url: " + url), reject()
})
})
}
function ajax(request) {
if (!request) throw new Error("Request cannot be null");
return request.headers = request.headers || {}, console.log("ConnectionManager requesting url: " + request.url), getFetchPromise(request).then(function(response) {
return console.log("ConnectionManager response status: " + response.status + ", url: " + request.url), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : response : Promise.reject(response)
}, function(err) {
throw console.log("ConnectionManager request failed to url: " + request.url), err
})
}
function getConnectUrl(handler) {
return "https://connect.emby.media/service/" + handler
}
function replaceAll(originalString, strReplace, strWith) {
var reg = new RegExp(strReplace, "ig");
return originalString.replace(reg, strWith)
}
function normalizeAddress(address) {
return address = address.trim(), 0 !== address.toLowerCase().indexOf("http") && (address = "http://" + address), address = replaceAll(address, "Http:", "http:"), address = replaceAll(address, "Https:", "https:")
}
function stringEqualsIgnoreCase(str1, str2) {
return (str1 || "").toLowerCase() === (str2 || "").toLowerCase()
}
function compareVersions(a, b) {
a = a.split("."), b = b.split(".");
for (var 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 (aVal > bVal) return 1
}
return 0
}
var defaultTimeout = 2e4,
ConnectionMode = {
Local: 0,
Remote: 1,
Manual: 2
},
ConnectionManager = function(credentialProvider, appName, appVersion, deviceName, deviceId, capabilities, devicePixelRatio) {
function onConnectUserSignIn(user) {
connectUser = user, events.trigger(self, "connectusersignedin", [user])
}
function onAuthenticated(apiClient, result, options, saveCredentials) {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.filter(function(s) {
return s.Id === result.ServerId
}),
server = servers.length ? servers[0] : apiClient.serverInfo();
return !1 !== options.updateDateLastAccessed && (server.DateLastAccessed = (new Date).getTime()), server.Id = result.ServerId, saveCredentials ? (server.UserId = result.User.Id, server.AccessToken = result.AccessToken) : (server.UserId = null, server.AccessToken = null), credentialProvider.addOrUpdateServer(credentials.Servers, server), credentialProvider.credentials(credentials), apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, apiClient.serverInfo(server), afterConnected(apiClient, options), onLocalUserSignIn(server, apiClient.serverAddress(), result.User)
}
function afterConnected(apiClient, options) {
options = options || {}, !1 !== options.reportCapabilities && apiClient.reportCapabilities(capabilities), apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, !1 !== options.enableWebSocket && (console.log("calling apiClient.ensureWebSocket"), apiClient.ensureWebSocket())
}
function onLocalUserSignIn(server, serverUrl, user) {
return self._getOrAddApiClient(server, serverUrl), (self.onLocalUserSignedIn ? self.onLocalUserSignedIn.call(self, user) : Promise.resolve()).then(function() {
events.trigger(self, "localusersignedin", [user])
})
}
function ensureConnectUser(credentials) {
return connectUser && connectUser.Id === credentials.ConnectUserId ? Promise.resolve() : credentials.ConnectUserId && credentials.ConnectAccessToken ? (connectUser = null, getConnectUser(credentials.ConnectUserId, credentials.ConnectAccessToken).then(function(user) {
return onConnectUserSignIn(user), Promise.resolve()
}, function() {
return Promise.resolve()
})) : Promise.resolve()
}
function getConnectUser(userId, accessToken) {
if (!userId) throw new Error("null userId");
if (!accessToken) throw new Error("null accessToken");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/user?id=" + userId,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": accessToken
}
})
}
function addAuthenticationInfoFromConnect(server, serverUrl, credentials) {
if (!server.ExchangeToken) throw new Error("server.ExchangeToken cannot be null");
if (!credentials.ConnectUserId) throw new Error("credentials.ConnectUserId cannot be null");
var url = getEmbyServerUrl(serverUrl, "Connect/Exchange?format=json&ConnectUserId=" + credentials.ConnectUserId),
auth = 'MediaBrowser Client="' + appName + '", Device="' + deviceName + '", DeviceId="' + deviceId + '", Version="' + appVersion + '"';
return ajax({
type: "GET",
url: url,
dataType: "json",
headers: {
"X-MediaBrowser-Token": server.ExchangeToken,
"X-Emby-Authorization": auth
}
}).then(function(auth) {
return server.UserId = auth.LocalUserId, server.AccessToken = auth.AccessToken, auth
}, function() {
return server.UserId = null, server.AccessToken = null, Promise.reject()
})
}
function validateAuthentication(server, serverUrl) {
return ajax({
type: "GET",
url: getEmbyServerUrl(serverUrl, "System/Info"),
dataType: "json",
headers: {
"X-MediaBrowser-Token": server.AccessToken
}
}).then(function(systemInfo) {
return updateServerInfo(server, systemInfo), Promise.resolve()
}, function() {
return server.UserId = null, server.AccessToken = null, Promise.resolve()
})
}
function getImageUrl(localUser) {
if (connectUser && connectUser.ImageUrl) return {
url: connectUser.ImageUrl
};
if (localUser && localUser.PrimaryImageTag) {
return {
url: self.getApiClient(localUser).getUserImageUrl(localUser.Id, {
tag: localUser.PrimaryImageTag,
type: "Primary"
}),
supportsParams: !0
}
}
return {
url: null,
supportsParams: !1
}
}
function logoutOfServer(apiClient) {
var serverInfo = apiClient.serverInfo() || {},
logoutInfo = {
serverId: serverInfo.Id
};
return apiClient.logout().then(function() {
events.trigger(self, "localusersignedout", [logoutInfo])
}, function() {
events.trigger(self, "localusersignedout", [logoutInfo])
})
}
function getConnectServers(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": appName + "/" + appVersion,
"X-Connect-UserToken": credentials.ConnectAccessToken
}
}).then(function(servers) {
return servers.map(function(i) {
return {
ExchangeToken: i.AccessKey,
ConnectServerId: i.Id,
Id: i.SystemId,
Name: i.Name,
RemoteAddress: i.Url,
LocalAddress: i.LocalAddress,
UserLinkType: "guest" === (i.UserType || "").toLowerCase() ? "Guest" : "LinkedUser"
}
})
}, function() {
return credentials.Servers.slice(0).filter(function(s) {
return s.ExchangeToken
})
}) : Promise.resolve([])
}
function filterServers(servers, connectServers) {
return servers.filter(function(server) {
return !server.ExchangeToken || connectServers.filter(function(connectServer) {
return server.Id === connectServer.Id
}).length > 0
})
}
function findServers() {
return new Promise(function(resolve, reject) {
var onFinish = function(foundServers) {
var servers = foundServers.map(function(foundServer) {
var info = {
Id: foundServer.Id,
LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
Name: foundServer.Name
};
return info.LastConnectionMode = info.ManualAddress ? ConnectionMode.Manual : ConnectionMode.Local, info
});
resolve(servers)
};
require(["serverdiscovery"], function(serverDiscovery) {
serverDiscovery.findServers(1e3).then(onFinish, function() {
onFinish([])
})
})
})
}
function convertEndpointAddressToManualAddress(info) {
if (info.Address && info.EndpointAddress) {
var address = info.EndpointAddress.split(":")[0],
parts = info.Address.split(":");
if (parts.length > 1) {
var portString = parts[parts.length - 1];
isNaN(parseInt(portString)) || (address += ":" + portString)
}
return normalizeAddress(address)
}
return null
}
function getTryConnectPromise(url, connectionMode, state, resolve, reject) {
console.log("getTryConnectPromise " + url), ajax({
url: getEmbyServerUrl(url, "system/info/public"),
timeout: defaultTimeout,
type: "GET",
dataType: "json"
}).then(function(result) {
state.resolved || (state.resolved = !0, console.log("Reconnect succeeded to " + url), resolve({
url: url,
connectionMode: connectionMode,
data: result
}))
}, function() {
state.resolved || (console.log("Reconnect failed to " + url), ++state.rejects >= state.numAddresses && reject())
})
}
function tryReconnect(serverInfo) {
var addresses = [],
addressesStrings = [];
return !serverInfo.manualAddressOnly && serverInfo.LocalAddress && -1 === addressesStrings.indexOf(serverInfo.LocalAddress) && (addresses.push({
url: serverInfo.LocalAddress,
mode: ConnectionMode.Local,
timeout: 0
}), addressesStrings.push(addresses[addresses.length - 1].url)), serverInfo.ManualAddress && -1 === addressesStrings.indexOf(serverInfo.ManualAddress) && (addresses.push({
url: serverInfo.ManualAddress,
mode: ConnectionMode.Manual,
timeout: 100
}), addressesStrings.push(addresses[addresses.length - 1].url)), !serverInfo.manualAddressOnly && serverInfo.RemoteAddress && -1 === addressesStrings.indexOf(serverInfo.RemoteAddress) && (addresses.push({
url: serverInfo.RemoteAddress,
mode: ConnectionMode.Remote,
timeout: 200
}), addressesStrings.push(addresses[addresses.length - 1].url)), console.log("tryReconnect: " + addressesStrings.join("|")), new Promise(function(resolve, reject) {
var state = {};
state.numAddresses = addresses.length, state.rejects = 0, addresses.map(function(url) {
setTimeout(function() {
state.resolved || getTryConnectPromise(url.url, url.mode, state, resolve, reject)
}, url.timeout)
})
})
}
function onSuccessfulConnection(server, systemInfo, connectionMode, serverUrl, options, resolve) {
var credentials = credentialProvider.credentials();
options = options || {}, credentials.ConnectAccessToken && !1 !== options.enableAutoLogin ? ensureConnectUser(credentials).then(function() {
server.ExchangeToken ? addAuthenticationInfoFromConnect(server, serverUrl, credentials).then(function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}, function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}) : afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}) : afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}
function afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, verifyLocalAuthentication, options, resolve) {
if (options = options || {}, !1 === options.enableAutoLogin) server.UserId = null, server.AccessToken = null;
else if (verifyLocalAuthentication && server.AccessToken && !1 !== options.enableAutoLogin) return void validateAuthentication(server, serverUrl).then(function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !1, options, resolve)
});
updateServerInfo(server, systemInfo), server.LastConnectionMode = connectionMode, !1 !== options.updateDateLastAccessed && (server.DateLastAccessed = (new Date).getTime()), credentialProvider.addOrUpdateServer(credentials.Servers, server), credentialProvider.credentials(credentials);
var result = {
Servers: []
};
result.ApiClient = self._getOrAddApiClient(server, serverUrl), result.ApiClient.setSystemInfo(systemInfo), result.State = server.AccessToken && !1 !== options.enableAutoLogin ? "SignedIn" : "ServerSignIn", result.Servers.push(server), result.ApiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, result.ApiClient.updateServerInfo(server, serverUrl);
var resolveActions = function() {
resolve(result), events.trigger(self, "connected", [result])
};
"SignedIn" === result.State ? (afterConnected(result.ApiClient, options), result.ApiClient.getCurrentUser().then(function(user) {
onLocalUserSignIn(server, serverUrl, user).then(resolveActions, resolveActions)
}, resolveActions)) : resolveActions()
}
function getCacheKey(feature, apiClient, options) {
options = options || {};
var viewOnly = options.viewOnly,
cacheKey = "regInfo-" + apiClient.serverId();
return viewOnly && (cacheKey += "-viewonly"), cacheKey
}
function addAppInfoToConnectRequest(request) {
request.headers = request.headers || {}, request.headers["X-Application"] = appName + "/" + appVersion
}
function exchangePin(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
var request = {
type: "POST",
url: getConnectUrl("pin/authenticate"),
data: {
deviceId: pinInfo.DeviceId,
pin: pinInfo.Pin
},
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}
console.log("Begin ConnectionManager constructor");
var self = this;
this._apiClients = [];
var connectUser;
self.connectUser = function() {
return connectUser
}, self._minServerVersion = "3.2.33", self.appVersion = function() {
return appVersion
}, self.appName = function() {
return appName
}, self.capabilities = function() {
return capabilities
}, self.deviceId = function() {
return deviceId
}, self.credentialProvider = function() {
return credentialProvider
}, self.connectUserId = function() {
return credentialProvider.credentials().ConnectUserId
}, self.connectToken = function() {
return credentialProvider.credentials().ConnectAccessToken
}, self.getServerInfo = function(id) {
return credentialProvider.credentials().Servers.filter(function(s) {
return s.Id === id
})[0]
}, self.getLastUsedServer = function() {
var servers = credentialProvider.credentials().Servers;
return servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), servers.length ? servers[0] : null
}, self.addApiClient = function(apiClient) {
self._apiClients.push(apiClient);
var existingServers = credentialProvider.credentials().Servers.filter(function(s) {
return stringEqualsIgnoreCase(s.ManualAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.LocalAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.RemoteAddress, apiClient.serverAddress())
}),
existingServer = existingServers.length ? existingServers[0] : apiClient.serverInfo();
if (existingServer.DateLastAccessed = (new Date).getTime(), existingServer.LastConnectionMode = ConnectionMode.Manual, existingServer.ManualAddress = apiClient.serverAddress(), apiClient.manualAddressOnly && (existingServer.manualAddressOnly = !0), apiClient.serverInfo(existingServer), apiClient.onAuthenticated = function(instance, result) {
return onAuthenticated(instance, result, {}, !0)
}, !existingServers.length) {
var credentials = credentialProvider.credentials();
credentials.Servers = [existingServer], credentialProvider.credentials(credentials)
}
events.trigger(self, "apiclientcreated", [apiClient])
}, self.clearData = function() {
console.log("connection manager clearing data"), connectUser = null;
var credentials = credentialProvider.credentials();
credentials.ConnectAccessToken = null, credentials.ConnectUserId = null, credentials.Servers = [], credentialProvider.credentials(credentials)
}, self._getOrAddApiClient = function(server, serverUrl) {
var apiClient = self.getApiClient(server.Id);
return apiClient || (apiClient = new apiClientFactory(serverUrl, appName, appVersion, deviceName, deviceId, devicePixelRatio), self._apiClients.push(apiClient), apiClient.serverInfo(server), apiClient.onAuthenticated = function(instance, result) {
return onAuthenticated(instance, result, {}, !0)
}, events.trigger(self, "apiclientcreated", [apiClient])), console.log("returning instance from getOrAddApiClient"), apiClient
}, self.getOrCreateApiClient = function(serverId) {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.filter(function(s) {
return stringEqualsIgnoreCase(s.Id, serverId)
});
if (!servers.length) throw new Error("Server not found: " + serverId);
var server = servers[0];
return self._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode))
}, self.user = function(apiClient) {
return new Promise(function(resolve, reject) {
function onLocalUserDone(e) {
var image = getImageUrl(localUser);
resolve({
localUser: localUser,
name: connectUser ? connectUser.Name : localUser ? localUser.Name : null,
imageUrl: image.url,
supportsImageParams: image.supportsParams,
connectUser: connectUser
})
}
function onEnsureConnectUserDone() {
apiClient && apiClient.getCurrentUserId() ? apiClient.getCurrentUser().then(function(u) {
localUser = u, onLocalUserDone()
}, onLocalUserDone) : onLocalUserDone()
}
var localUser, credentials = credentialProvider.credentials();
!credentials.ConnectUserId || !credentials.ConnectAccessToken || apiClient && apiClient.getCurrentUserId() ? onEnsureConnectUserDone() : ensureConnectUser(credentials).then(onEnsureConnectUserDone, onEnsureConnectUserDone)
})
}, self.logout = function() {
console.log("begin connectionManager loguot");
for (var promises = [], i = 0, length = self._apiClients.length; i < length; i++) {
var apiClient = self._apiClients[i];
apiClient.accessToken() && promises.push(logoutOfServer(apiClient))
}
return Promise.all(promises).then(function() {
for (var credentials = credentialProvider.credentials(), servers = credentials.Servers.filter(function(u) {
return "Guest" !== u.UserLinkType
}), j = 0, numServers = servers.length; j < numServers; j++) {
var server = servers[j];
server.UserId = null, server.AccessToken = null, server.ExchangeToken = null
}
credentials.Servers = servers, credentials.ConnectAccessToken = null, credentials.ConnectUserId = null, credentialProvider.credentials(credentials), connectUser && (connectUser = null, events.trigger(self, "connectusersignedout"))
})
}, self.getSavedServers = function() {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.slice(0);
return servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), servers
}, self.getAvailableServers = function() {
console.log("Begin getAvailableServers");
var credentials = credentialProvider.credentials();
return Promise.all([getConnectServers(credentials), findServers()]).then(function(responses) {
var connectServers = responses[0],
foundServers = responses[1],
servers = credentials.Servers.slice(0);
return mergeServers(credentialProvider, servers, foundServers), mergeServers(credentialProvider, servers, connectServers), servers = filterServers(servers, connectServers), servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), credentials.Servers = servers, credentialProvider.credentials(credentials), servers
})
}, self.connectToServers = function(servers, options) {
console.log("Begin connectToServers, with " + servers.length + " servers");
var firstServer = servers.length ? servers[0] : null;
return firstServer ? self.connectToServer(firstServer, options).then(function(result) {
return "Unavailable" === result.State && (result.State = "ServerSelection"), console.log("resolving connectToServers with result.State: " + result.State), result
}) : Promise.resolve({
Servers: servers,
State: servers.length || self.connectUser() ? "ServerSelection" : "ConnectSignIn",
ConnectUser: self.connectUser()
})
}, self.connectToServer = function(server, options) {
return console.log("begin connectToServer"), new Promise(function(resolve, reject) {
options = options || {}, tryReconnect(server).then(function(result) {
var serverUrl = result.url,
connectionMode = result.connectionMode;
result = result.data, 1 === compareVersions(self.minServerVersion(), result.Version) ? (console.log("minServerVersion requirement not met. Server version: " + result.Version), resolve({
State: "ServerUpdateNeeded",
Servers: [server]
})) : server.Id && result.Id !== server.Id ? (console.log("http request succeeded, but found a different server Id than what was expected"), resolveFailure(self, resolve)) : onSuccessfulConnection(server, result, connectionMode, serverUrl, options, resolve)
}, function() {
resolveFailure(self, resolve)
})
})
}, self.connectToAddress = function(address, options) {
function onFail() {
return console.log("connectToAddress " + address + " failed"), Promise.resolve({
State: "Unavailable",
ConnectUser: instance.connectUser()
})
}
if (!address) return Promise.reject();
address = normalizeAddress(address);
var instance = this,
server = {
ManualAddress: address,
LastConnectionMode: ConnectionMode.Manual
};
return self.connectToServer(server, options).catch(onFail)
}, self.loginToConnect = function(username, password) {
return username && password ? 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": appName + "/" + appVersion
}
}).then(function(result) {
var credentials = credentialProvider.credentials();
return credentials.ConnectAccessToken = result.AccessToken, credentials.ConnectUserId = result.User.Id, credentialProvider.credentials(credentials), onConnectUserSignIn(result.User), result
}) : Promise.reject()
}, self.signupForConnect = function(options) {
var email = options.email,
username = options.username,
password = options.password,
passwordConfirm = options.passwordConfirm;
if (!email) return Promise.reject({
errorCode: "invalidinput"
});
if (!username) return Promise.reject({
errorCode: "invalidinput"
});
if (!password) return Promise.reject({
errorCode: "invalidinput"
});
if (!passwordConfirm) return Promise.reject({
errorCode: "passwordmatch"
});
if (password !== passwordConfirm) return Promise.reject({
errorCode: "passwordmatch"
});
var data = {
email: email,
userName: username,
rawpw: password
};
return options.grecaptcha && (data.grecaptcha = options.grecaptcha), ajax({
type: "POST",
url: "https://connect.emby.media/service/register",
data: data,
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: {
"X-Application": appName + "/" + appVersion,
"X-CONNECT-TOKEN": "CONNECT-REGISTER"
}
}).catch(function(response) {
try {
return response.json()
} catch (err) {
throw err
}
}).then(function(result) {
if (result && result.Status) return "SUCCESS" === result.Status ? Promise.resolve(result) : Promise.reject({
errorCode: result.Status
});
Promise.reject()
})
}, self.getUserInvitations = function() {
var connectToken = self.connectToken();
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/servers?userId=" + self.connectUserId() + "&status=Waiting",
dataType: "json",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.deleteServer = function(serverId) {
if (!serverId) throw new Error("null serverId");
var server = credentialProvider.credentials().Servers.filter(function(s) {
return s.Id === serverId
});
return server = server.length ? server[0] : null, new Promise(function(resolve, reject) {
function onDone() {
var credentials = credentialProvider.credentials();
credentials.Servers = credentials.Servers.filter(function(s) {
return s.Id !== serverId
}), credentialProvider.credentials(credentials), resolve()
}
if (!server.ConnectServerId) return void onDone();
var connectToken = self.connectToken(),
connectUserId = self.connectUserId();
if (!connectToken || !connectUserId) return void onDone();
ajax({
type: "DELETE",
url: "https://connect.emby.media/service/serverAuthorizations?serverId=" + server.ConnectServerId + "&userId=" + connectUserId,
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
}).then(onDone, onDone)
})
}, self.rejectServer = function(serverId) {
var connectToken = self.connectToken();
if (!serverId) throw new Error("null serverId");
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
var url = "https://connect.emby.media/service/serverAuthorizations?serverId=" + serverId + "&userId=" + self.connectUserId();
return fetch(url, {
method: "DELETE",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.acceptServer = function(serverId) {
var connectToken = self.connectToken();
if (!serverId) throw new Error("null serverId");
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/ServerAuthorizations/accept?serverId=" + serverId + "&userId=" + self.connectUserId(),
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.resetRegistrationInfo = function(apiClient) {
var cacheKey = getCacheKey("themes", apiClient, {
viewOnly: !0
});
appStorage.removeItem(cacheKey), cacheKey = getCacheKey("themes", apiClient, {
viewOnly: !1
}), appStorage.removeItem(cacheKey)
}, self.getRegistrationInfo = function(feature, apiClient, options) {
var cacheKey = getCacheKey(feature, apiClient, options);
appStorage.setItem(cacheKey, JSON.stringify({
lastValidDate: new Date().getTime(),
deviceId: self.deviceId()
}));
return Promise.resolve();
}, self.createPin = function() {
var request = {
type: "POST",
url: getConnectUrl("pin"),
data: {
deviceId: deviceId
},
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}, self.getPinStatus = function(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
var queryString = {
deviceId: pinInfo.DeviceId,
pin: pinInfo.Pin
},
request = {
type: "GET",
url: getConnectUrl("pin") + "?" + paramsToString(queryString),
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}, self.exchangePin = function(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
return exchangePin(pinInfo).then(function(result) {
var credentials = credentialProvider.credentials();
return credentials.ConnectAccessToken = result.AccessToken, credentials.ConnectUserId = result.UserId, credentialProvider.credentials(credentials), ensureConnectUser(credentials)
})
}
};
return ConnectionManager.prototype.connect = function(options) {
console.log("Begin connect");
var instance = this;
return instance.getAvailableServers().then(function(servers) {
return instance.connectToServers(servers, options)
})
}, ConnectionManager.prototype.isLoggedIntoConnect = function() {
return !(!this.connectToken() || !this.connectUserId())
}, ConnectionManager.prototype.getApiClients = function() {
for (var servers = this.getSavedServers(), i = 0, length = servers.length; i < length; i++) {
var server = servers[i];
server.Id && this._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode))
}
return this._apiClients
}, ConnectionManager.prototype.getApiClient = function(item) {
if (!item) throw new Error("item or serverId cannot be null");
return item.ServerId && (item = item.ServerId), this._apiClients.filter(function(a) {
var serverInfo = a.serverInfo();
return !serverInfo || serverInfo.Id === item
})[0]
}, ConnectionManager.prototype.minServerVersion = function(val) {
return val && (this._minServerVersion = val), this._minServerVersion
}, ConnectionManager.prototype.handleMessageReceived = function(msg) {
var serverId = msg.ServerId;
if (serverId) {
var apiClient = this.getApiClient(serverId);
if (apiClient) {
if ("string" == typeof msg.Data) try {
msg.Data = JSON.parse(msg.Data)
} catch (err) {}
apiClient.handleMessageReceived(msg)
}
}
}, ConnectionManager
});

View file

@ -0,0 +1,29 @@
define(["events", "appStorage"], function(events, appStorage) {
"use strict";
function ensure(instance, data) {
if (!instance._credentials) {
var json = appStorage.getItem(instance.key) || "{}";
console.log("credentials initialized with: " + json), instance._credentials = JSON.parse(json), instance._credentials.Servers = instance._credentials.Servers || []
}
}
function set(instance, data) {
data ? (instance._credentials = data, appStorage.setItem(instance.key, JSON.stringify(data))) : instance.clear(), events.trigger(instance, "credentialsupdated")
}
function Credentials(key) {
this.key = key || "servercredentials3"
}
return Credentials.prototype.clear = function() {
this._credentials = null, appStorage.removeItem(this.key)
}, Credentials.prototype.credentials = function(data) {
return data && set(this, data), ensure(this), this._credentials
}, Credentials.prototype.addOrUpdateServer = function(list, server) {
if (!server.Id) throw new Error("Server.Id cannot be null or empty");
var existing = list.filter(function(s) {
return s.Id === server.Id
})[0];
return existing ? (existing.DateLastAccessed = Math.max(existing.DateLastAccessed || 0, server.DateLastAccessed || 0), existing.UserLinkType = server.UserLinkType, server.AccessToken && (existing.AccessToken = server.AccessToken, existing.UserId = server.UserId), server.ExchangeToken && (existing.ExchangeToken = server.ExchangeToken), server.RemoteAddress && (existing.RemoteAddress = server.RemoteAddress), server.ManualAddress && (existing.ManualAddress = server.ManualAddress), server.LocalAddress && (existing.LocalAddress = server.LocalAddress), server.Name && (existing.Name = server.Name), server.WakeOnLanInfos && server.WakeOnLanInfos.length && (existing.WakeOnLanInfos = server.WakeOnLanInfos), null != server.LastConnectionMode && (existing.LastConnectionMode = server.LastConnectionMode), server.ConnectServerId && (existing.ConnectServerId = server.ConnectServerId), existing) : (list.push(server), server)
}, Credentials
});

View file

@ -0,0 +1,30 @@
define([], function() {
"use strict";
function getCallbacks(obj, name) {
if (!obj) throw new Error("obj cannot be null!");
obj._callbacks = obj._callbacks || {};
var list = obj._callbacks[name];
return list || (obj._callbacks[name] = [], list = obj._callbacks[name]), list
}
return {
on: function(obj, eventName, fn) {
getCallbacks(obj, eventName).push(fn)
},
off: function(obj, eventName, fn) {
var list = getCallbacks(obj, eventName),
i = list.indexOf(fn); - 1 !== i && list.splice(i, 1)
},
trigger: function(obj, eventName) {
var eventObject = {
type: eventName
},
eventArgs = [];
eventArgs.push(eventObject);
for (var additionalArgs = arguments[2] || [], i = 0, length = additionalArgs.length; i < length; i++) eventArgs.push(additionalArgs[i]);
getCallbacks(obj, eventName).slice(0).forEach(function(c) {
c.apply(obj, eventArgs)
})
}
}
});

View file

@ -0,0 +1,8 @@
define([], function() {
"use strict";
function FileUpload() {}
return FileUpload.prototype.upload = function(file, url) {
return Promise.reject()
}, FileUpload
});

View file

@ -0,0 +1,406 @@
define(["filerepository", "itemrepository", "useractionrepository", "transfermanager"], function(filerepository, itemrepository, useractionrepository, transfermanager) {
"use strict";
function getLocalItem(serverId, itemId) {
return console.log("[lcoalassetmanager] Begin getLocalItem"), itemrepository.get(serverId, itemId)
}
function recordUserAction(action) {
return action.Id = createGuid(), useractionrepository.set(action.Id, action)
}
function getUserActions(serverId) {
return useractionrepository.getByServerId(serverId)
}
function deleteUserAction(action) {
return useractionrepository.remove(action.Id)
}
function deleteUserActions(actions) {
var results = [];
return actions.forEach(function(action) {
results.push(deleteUserAction(action))
}), Promise.all(results)
}
function getServerItems(serverId) {
return console.log("[localassetmanager] Begin getServerItems"), itemrepository.getAll(serverId)
}
function getItemsFromIds(serverId, ids) {
var actions = ids.map(function(id) {
var strippedId = stripStart(id, "local:");
return getLocalItem(serverId, strippedId)
});
return Promise.all(actions).then(function(items) {
var libItems = items.map(function(locItem) {
return locItem.Item
});
return Promise.resolve(libItems)
})
}
function getViews(serverId, userId) {
return itemrepository.getServerItemTypes(serverId, userId).then(function(types) {
var item, list = [];
return types.indexOf("Audio") > -1 && (item = {
Name: "Music",
ServerId: serverId,
Id: "localview:MusicView",
Type: "MusicView",
CollectionType: "music",
IsFolder: !0
}, list.push(item)), types.indexOf("Photo") > -1 && (item = {
Name: "Photos",
ServerId: serverId,
Id: "localview:PhotosView",
Type: "PhotosView",
CollectionType: "photos",
IsFolder: !0
}, list.push(item)), types.indexOf("Episode") > -1 && (item = {
Name: "TV",
ServerId: serverId,
Id: "localview:TVView",
Type: "TVView",
CollectionType: "tvshows",
IsFolder: !0
}, list.push(item)), types.indexOf("Movie") > -1 && (item = {
Name: "Movies",
ServerId: serverId,
Id: "localview:MoviesView",
Type: "MoviesView",
CollectionType: "movies",
IsFolder: !0
}, list.push(item)), types.indexOf("Video") > -1 && (item = {
Name: "Videos",
ServerId: serverId,
Id: "localview:VideosView",
Type: "VideosView",
CollectionType: "videos",
IsFolder: !0
}, list.push(item)), types.indexOf("MusicVideo") > -1 && (item = {
Name: "Music Videos",
ServerId: serverId,
Id: "localview:MusicVideosView",
Type: "MusicVideosView",
CollectionType: "videos",
IsFolder: !0
}, list.push(item)), Promise.resolve(list)
})
}
function updateFiltersForTopLevelView(parentId, mediaTypes, includeItemTypes, query) {
switch (parentId) {
case "MusicView":
return query.Recursive ? includeItemTypes.push("Audio") : includeItemTypes.push("MusicAlbum"), !0;
case "PhotosView":
return query.Recursive ? includeItemTypes.push("Photo") : includeItemTypes.push("PhotoAlbum"), !0;
case "TVView":
return query.Recursive ? includeItemTypes.push("Episode") : includeItemTypes.push("Series"), !0;
case "VideosView":
return query.Recursive, includeItemTypes.push("Video"), !0;
case "MoviesView":
return query.Recursive, includeItemTypes.push("Movie"), !0;
case "MusicVideosView":
return query.Recursive, includeItemTypes.push("MusicVideo"), !0
}
return !1
}
function normalizeId(id) {
return id ? (id = stripStart(id, "localview:"), id = stripStart(id, "local:")) : null
}
function normalizeIdList(val) {
return val ? val.split(",").map(normalizeId) : []
}
function shuffle(array) {
for (var temporaryValue, randomIndex, currentIndex = array.length; 0 !== currentIndex;) randomIndex = Math.floor(Math.random() * currentIndex), currentIndex -= 1, temporaryValue = array[currentIndex], array[currentIndex] = array[randomIndex], array[randomIndex] = temporaryValue;
return array
}
function sortItems(items, query) {
if (!query.SortBy || 0 === query.SortBy.length) return items;
if ("Random" === query.SortBy) return shuffle(items);
var sortSpec = getSortSpec(query);
return items.sort(function(a, b) {
for (var i = 0; i < sortSpec.length; i++) {
var result = compareValues(a, b, sortSpec[i].Field, sortSpec[i].OrderDescending);
if (0 !== result) return result
}
return 0
}), items
}
function compareValues(a, b, field, orderDesc) {
if (!a.hasOwnProperty(field) || !b.hasOwnProperty(field)) return 0;
var valA = a[field],
valB = b[field],
result = 0;
return "string" == typeof valA || "string" == typeof valB ? (valA = valA || "", valB = valB || "", result = valA.toLowerCase().localeCompare(valB.toLowerCase())) : valA > valB ? result = 1 : valA < valB && (result = -1), orderDesc && (result *= -1), result
}
function getSortSpec(query) {
for (var sortFields = (query.SortBy || "").split(","), sortOrders = (query.SortOrder || "").split(","), sortSpec = [], i = 0; i < sortFields.length; i++) {
var orderDesc = !1;
i < sortOrders.length && -1 !== sortOrders[i].toLowerCase().indexOf("desc") && (orderDesc = !0), sortSpec.push({
Field: sortFields[i],
OrderDescending: orderDesc
})
}
return sortSpec
}
function getViewItems(serverId, userId, options) {
var searchParentId = options.ParentId;
searchParentId = normalizeId(searchParentId);
var seasonId = normalizeId(options.SeasonId || options.seasonId),
seriesId = normalizeId(options.SeriesId || options.seriesId),
albumIds = normalizeIdList(options.AlbumIds || options.albumIds),
includeItemTypes = options.IncludeItemTypes ? options.IncludeItemTypes.split(",") : [],
filters = options.Filters ? options.Filters.split(",") : [],
mediaTypes = options.MediaTypes ? options.MediaTypes.split(",") : [];
return updateFiltersForTopLevelView(searchParentId, mediaTypes, includeItemTypes, options) && (searchParentId = null), getServerItems(serverId).then(function(items) {
var itemsMap = new Map,
subtreeIdSet = new Set;
if (items.forEach(function(item) {
item.Item.LocalChildren = [], itemsMap.set(item.Item.Id, item.Item)
}), itemsMap.forEach(function(item, ignored, ignored2) {
if (item.ParentId && itemsMap.has(item.ParentId)) {
itemsMap.get(item.ParentId).LocalChildren.push(item)
}
}), options.Recursive && searchParentId && itemsMap.has(searchParentId)) {
var addSubtreeIds = function(recurseItem) {
subtreeIdSet.has(recurseItem.Id) || subtreeIdSet.add(recurseItem.Id), recurseItem.LocalChildren.forEach(function(childItem) {
addSubtreeIds(childItem)
})
},
searchParentItem = itemsMap.get(searchParentId);
addSubtreeIds(searchParentItem)
}
var resultItems = items.filter(function(item) {
return (!item.SyncStatus || "synced" === item.SyncStatus) && ((!mediaTypes.length || -1 !== mediaTypes.indexOf(item.Item.MediaType || "")) && ((!seriesId || item.Item.SeriesId === seriesId) && ((!seasonId || item.Item.SeasonId === seasonId) && ((!albumIds.length || -1 !== albumIds.indexOf(item.Item.AlbumId || "")) && ((!item.Item.IsFolder || -1 === filters.indexOf("IsNotFolder")) && (!(!item.Item.IsFolder && -1 !== filters.indexOf("IsFolder")) && ((!includeItemTypes.length || -1 !== includeItemTypes.indexOf(item.Item.Type || "")) && (!searchParentId || (options.Recursive ? subtreeIdSet.has(item.Item.Id) : item.Item.ParentId === searchParentId)))))))))
}).map(function(item2) {
return item2.Item
});
return resultItems = sortItems(resultItems, options), options.Limit && (resultItems = resultItems.slice(0, options.Limit)), Promise.resolve(resultItems)
})
}
function removeObsoleteContainerItems(serverId) {
return getServerItems(serverId).then(function(items) {
var seriesItems = items.filter(function(item) {
return "series" === (item.Item.Type || "").toLowerCase()
}),
seasonItems = items.filter(function(item) {
return "season" === (item.Item.Type || "").toLowerCase()
}),
albumItems = items.filter(function(item) {
var type = (item.Item.Type || "").toLowerCase();
return "musicalbum" === type || "photoalbum" === type
}),
requiredSeriesIds = items.filter(function(item) {
return "episode" === (item.Item.Type || "").toLowerCase()
}).map(function(item2) {
return item2.Item.SeriesId
}).filter(filterDistinct),
requiredSeasonIds = items.filter(function(item) {
return "episode" === (item.Item.Type || "").toLowerCase()
}).map(function(item2) {
return item2.Item.SeasonId
}).filter(filterDistinct),
requiredAlbumIds = items.filter(function(item) {
var type = (item.Item.Type || "").toLowerCase();
return "audio" === type || "photo" === type
}).map(function(item2) {
return item2.Item.AlbumId
}).filter(filterDistinct),
obsoleteItems = [];
seriesItems.forEach(function(item) {
requiredSeriesIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
}), seasonItems.forEach(function(item) {
requiredSeasonIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
}), albumItems.forEach(function(item) {
requiredAlbumIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
});
var p = Promise.resolve();
return obsoleteItems.forEach(function(item) {
p = p.then(function() {
return itemrepository.remove(item.ServerId, item.Id)
})
}), p
})
}
function removeLocalItem(localItem) {
return itemrepository.get(localItem.ServerId, localItem.Id).then(function(item) {
var onFileDeletedSuccessOrFail = function() {
return itemrepository.remove(localItem.ServerId, localItem.Id)
},
p = Promise.resolve();
return item.LocalPath && (p = p.then(function() {
return filerepository.deleteFile(item.LocalPath)
})), item && item.Item && item.Item.MediaSources && item.Item.MediaSources.forEach(function(mediaSource) {
mediaSource.MediaStreams && mediaSource.MediaStreams.length > 0 && mediaSource.MediaStreams.forEach(function(mediaStream) {
mediaStream.Path && (p = p.then(function() {
return filerepository.deleteFile(mediaStream.Path)
}))
})
}), p.then(onFileDeletedSuccessOrFail, onFileDeletedSuccessOrFail)
}, function(item) {
return Promise.resolve()
})
}
function addOrUpdateLocalItem(localItem) {
return itemrepository.set(localItem.ServerId, localItem.Id, localItem)
}
function getSubtitleSaveFileName(localItem, mediaPath, language, isForced, format) {
var name = getNameWithoutExtension(mediaPath);
name = filerepository.getValidFileName(name), language && (name += "." + language.toLowerCase()), isForced && (name += ".foreign"), name = name + "." + format.toLowerCase();
var mediaFolder = filerepository.getParentPath(localItem.LocalPath);
return filerepository.combinePath(mediaFolder, name)
}
function getItemFileSize(path) {
return filerepository.getItemFileSize(path)
}
function getNameWithoutExtension(path) {
var fileName = path,
pos = fileName.lastIndexOf(".");
return pos > 0 && (fileName = fileName.substring(0, pos)), fileName
}
function downloadFile(url, localItem) {
var imageUrl = getImageUrl(localItem.Item.ServerId, localItem.Item.Id, {
type: "Primary",
index: 0
});
return transfermanager.downloadFile(url, localItem, imageUrl)
}
function downloadSubtitles(url, fileName) {
return transfermanager.downloadSubtitles(url, fileName)
}
function getImageUrl(serverId, itemId, imageOptions) {
var imageType = imageOptions.type,
index = imageOptions.index,
pathArray = getImagePath(serverId, itemId, imageType, index);
return filerepository.getImageUrl(pathArray)
}
function hasImage(serverId, itemId, imageType, index) {
var pathArray = getImagePath(serverId, itemId, imageType, index),
localFilePath = filerepository.getFullMetadataPath(pathArray);
return filerepository.fileExists(localFilePath).then(function(exists) {
return Promise.resolve(exists)
}, function(err) {
return Promise.resolve(!1)
})
}
function fileExists(localFilePath) {
return filerepository.fileExists(localFilePath)
}
function downloadImage(localItem, url, serverId, itemId, imageType, index) {
var localPathParts = getImagePath(serverId, itemId, imageType, index);
return transfermanager.downloadImage(url, localPathParts)
}
function isDownloadFileInQueue(path) {
return transfermanager.isDownloadFileInQueue(path)
}
function getDownloadItemCount() {
return transfermanager.getDownloadItemCount()
}
function getDirectoryPath(item) {
var parts = [],
itemtype = item.Type.toLowerCase(),
mediaType = (item.MediaType || "").toLowerCase();
"episode" === itemtype || "series" === itemtype || "season" === itemtype ? parts.push("TV") : "video" === mediaType ? parts.push("Videos") : "audio" === itemtype || "musicalbum" === itemtype || "musicartist" === itemtype ? parts.push("Music") : "photo" === itemtype || "photoalbum" === itemtype ? parts.push("Photos") : "game" !== itemtype && "gamesystem" !== itemtype || parts.push("Games");
var albumArtist = item.AlbumArtist;
albumArtist && parts.push(albumArtist);
var seriesName = item.SeriesName;
seriesName && parts.push(seriesName);
var seasonName = item.SeasonName;
seasonName && parts.push(seasonName), item.Album && parts.push(item.Album), ("video" === mediaType && "episode" !== itemtype || "game" === itemtype || item.IsFolder) && parts.push(item.Name);
for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(filerepository.getValidFileName(parts[i]));
return finalParts
}
function getImagePath(serverId, itemId, imageType, index) {
var parts = [];
parts.push("images"), index = index || 0, parts.push(itemId + "_" + imageType + "_" + index.toString());
for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(parts[i]);
return finalParts
}
function getLocalFileName(item, originalFileName) {
var filename = originalFileName || item.Name;
return filerepository.getValidFileName(filename)
}
function resyncTransfers() {
return transfermanager.resyncTransfers()
}
function createGuid() {
var d = (new Date).getTime();
return window.performance && "function" == typeof window.performance.now && (d += performance.now()), "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (d + 16 * Math.random()) % 16 | 0;
return d = Math.floor(d / 16), ("x" === c ? r : 3 & r | 8).toString(16)
})
}
function startsWith(str, find) {
return !!(str && find && str.length > find.length && 0 === str.indexOf(find))
}
function stripStart(str, find) {
return startsWith(str, find) ? str.substr(find.length) : str
}
function filterDistinct(value, index, self) {
return self.indexOf(value) === index
}
function enableBackgroundCompletion() {
return transfermanager.enableBackgroundCompletion
}
return {
getLocalItem: getLocalItem,
getDirectoryPath: getDirectoryPath,
getLocalFileName: getLocalFileName,
recordUserAction: recordUserAction,
getUserActions: getUserActions,
deleteUserAction: deleteUserAction,
deleteUserActions: deleteUserActions,
removeLocalItem: removeLocalItem,
addOrUpdateLocalItem: addOrUpdateLocalItem,
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
hasImage: hasImage,
downloadImage: downloadImage,
getImageUrl: getImageUrl,
getSubtitleSaveFileName: getSubtitleSaveFileName,
getServerItems: getServerItems,
getItemFileSize: getItemFileSize,
isDownloadFileInQueue: isDownloadFileInQueue,
getDownloadItemCount: getDownloadItemCount,
getViews: getViews,
getViewItems: getViewItems,
resyncTransfers: resyncTransfers,
getItemsFromIds: getItemsFromIds,
removeObsoleteContainerItems: removeObsoleteContainerItems,
fileExists: fileExists,
enableBackgroundCompletion: enableBackgroundCompletion
}
});

View file

@ -0,0 +1,8 @@
define([], function() {
"use strict";
return {
findServers: function(timeoutMs) {
return Promise.resolve([])
}
}
});

View file

@ -0,0 +1,57 @@
define(["localassetmanager", "cameraRoll"], function(localAssetManager, cameraRoll) {
"use strict";
function getFilesToUpload(files, uploadHistory) {
return files.filter(function(file) {
if (!file) return !1;
var uploadId = getUploadId(file);
return 0 === uploadHistory.FilesUploaded.filter(function(u) {
return uploadId === u.Id
}).length
})
}
function getUploadId(file) {
return btoa(file.Id + "1")
}
function uploadNext(files, index, server, apiClient, resolve, reject) {
var length = files.length;
if (index >= length) return void resolve();
uploadFile(files[index], apiClient).then(function() {
uploadNext(files, index + 1, server, apiClient, resolve, reject)
}, function() {
uploadNext(files, index + 1, server, apiClient, resolve, reject)
})
}
function uploadFile(file, apiClient) {
return new Promise(function(resolve, reject) {
require(["fileupload"], function(FileUpload) {
var url = apiClient.getUrl("Devices/CameraUploads", {
DeviceId: apiClient.deviceId(),
Name: file.Name,
Album: "Camera Roll",
Id: getUploadId(file),
api_key: apiClient.accessToken()
});
console.log("Uploading file to " + url), (new FileUpload).upload(file, url).then(resolve, reject)
})
})
}
function ContentUploader() {}
return ContentUploader.prototype.uploadImages = function(connectionManager, server) {
return cameraRoll.getFiles().then(function(photos) {
if (!photos.length) return Promise.resolve();
var apiClient = connectionManager.getApiClient(server.Id);
return apiClient.getContentUploadHistory().then(function(uploadHistory) {
return photos = getFilesToUpload(photos, uploadHistory), console.log("Found " + photos.length + " files to upload"), new Promise(function(resolve, reject) {
uploadNext(photos, 0, server, apiClient, resolve, reject)
})
}, function() {
return Promise.resolve()
})
})
}, ContentUploader
});

View file

@ -0,0 +1,45 @@
define([], function() {
"use strict";
function getValidFileName(path) {
return path
}
function getFullLocalPath(pathArray) {
return pathArray.join("/")
}
function getPathFromArray(pathArray) {
return pathArray.join("/")
}
function deleteFile(path) {
return Promise.resolve()
}
function deleteDirectory(path) {
return Promise.resolve()
}
function fileExists(path) {
return Promise.resolve()
}
function getItemFileSize(path) {
return Promise.resolve(0)
}
function getImageUrl(pathParts) {
return pathParts.join("/")
}
return {
getValidFileName: getValidFileName,
getFullLocalPath: getFullLocalPath,
getPathFromArray: getPathFromArray,
deleteFile: deleteFile,
deleteDirectory: deleteDirectory,
fileExists: fileExists,
getItemFileSize: getItemFileSize,
getImageUrl: getImageUrl
}
});

View file

@ -0,0 +1,123 @@
define([], function() {
"use strict";
function ServerDatabase(dbName, readyCallback) {
var request = indexedDB.open(dbName, dbVersion);
request.onerror = function(event) {}, request.onupgradeneeded = function(event) {
var db = event.target.result;
db.createObjectStore(dbName).transaction.oncomplete = function(event) {
readyCallback(db)
}
}, request.onsuccess = function(event) {
var db = event.target.result;
readyCallback(db)
}
}
function getDbName(serverId) {
return "items_" + serverId
}
function getDb(serverId, callback) {
var dbName = getDbName(serverId),
db = databases[dbName];
if (db) return void callback(db);
new ServerDatabase(dbName, function(db) {
databases[dbName] = db, callback(db)
})
}
function getServerItemTypes(serverId, userId) {
return getAll(serverId, userId).then(function(all) {
return all.map(function(item2) {
return item2.Item.Type || ""
}).filter(filterDistinct)
})
}
function getAll(serverId, userId) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var request, storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName);
if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) {
resolve(event.target.result)
};
else {
var results = [];
request = objectStore.openCursor(), request.onsuccess = function(event) {
var cursor = event.target.result;
cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results)
}
}
request.onerror = reject
})
})
}
function get(serverId, key) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName),
request = objectStore.get(key);
request.onerror = reject, request.onsuccess = function(event) {
resolve(request.result)
}
})
})
}
function set(serverId, key, val) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.put(val, key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function remove(serverId, key) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.delete(key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function clear(serverId) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.clear();
request.onerror = reject, request.onsuccess = resolve
})
})
}
function filterDistinct(value, index, self) {
return self.indexOf(value) === index
}
var indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB,
dbVersion = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, 1),
databases = {};
return {
get: get,
set: set,
remove: remove,
clear: clear,
getAll: getAll,
getServerItemTypes: getServerItemTypes
}
});

View file

@ -0,0 +1,17 @@
define(["connectionManager"], function(connectionManager) {
"use strict";
var isSyncing;
return {
sync: function(options) {
return console.log("localSync.sync starting..."), isSyncing ? Promise.resolve() : (isSyncing = !0, new Promise(function(resolve, reject) {
require(["multiserversync", "appSettings"], function(MultiServerSync, appSettings) {
options = options || {}, options.cameraUploadServers = appSettings.cameraUploadServers(), (new MultiServerSync).sync(connectionManager, options).then(function() {
isSyncing = null, resolve()
}, function(err) {
isSyncing = null, reject(err)
})
})
}))
}
}
});

View file

@ -0,0 +1,368 @@
define(["localassetmanager"], function(localassetmanager) {
"use strict";
function processDownloadStatus(apiClient, serverInfo, options) {
return console.log("[mediasync] Begin processDownloadStatus"), localassetmanager.resyncTransfers().then(function() {
return localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
console.log("[mediasync] Begin processDownloadStatus getServerItems completed");
var p = Promise.resolve(),
cnt = 0;
return items.filter(function(item) {
return "transferring" === item.SyncStatus || "queued" === item.SyncStatus
}).forEach(function(item) {
p = p.then(function() {
return reportTransfer(apiClient, item)
}), cnt++
}), p.then(function() {
return console.log("[mediasync] Exit processDownloadStatus. Items reported: " + cnt.toString()), Promise.resolve()
})
})
})
}
function reportTransfer(apiClient, item) {
return localassetmanager.getItemFileSize(item.LocalPath).then(function(size) {
return size > 0 ? apiClient.reportSyncJobItemTransferred(item.SyncJobItemId).then(function() {
return item.SyncStatus = "synced", console.log("[mediasync] reportSyncJobItemTransferred called for " + item.LocalPath), localassetmanager.addOrUpdateLocalItem(item)
}, function(error) {
return console.error("[mediasync] Mediasync error on reportSyncJobItemTransferred", error), item.SyncStatus = "error", localassetmanager.addOrUpdateLocalItem(item)
}) : localassetmanager.isDownloadFileInQueue(item.LocalPath).then(function(result) {
return result ? Promise.resolve() : (console.log("[mediasync] reportTransfer: Size is 0 and download no longer in queue. Deleting item."), localassetmanager.removeLocalItem(item).then(function() {
return console.log("[mediasync] reportTransfer: Item deleted."), Promise.resolve()
}, function(err2) {
return console.log("[mediasync] reportTransfer: Failed to delete item.", err2), Promise.resolve()
}))
})
}, function(error) {
return console.error("[mediasync] reportTransfer: error on getItemFileSize. Deleting item.", error), localassetmanager.removeLocalItem(item).then(function() {
return console.log("[mediasync] reportTransfer: Item deleted."), Promise.resolve()
}, function(err2) {
return console.log("[mediasync] reportTransfer: Failed to delete item.", err2), Promise.resolve()
})
})
}
function reportOfflineActions(apiClient, serverInfo) {
return console.log("[mediasync] Begin reportOfflineActions"), localassetmanager.getUserActions(serverInfo.Id).then(function(actions) {
return actions.length ? apiClient.reportOfflineActions(actions).then(function() {
return localassetmanager.deleteUserActions(actions).then(function() {
return console.log("[mediasync] Exit reportOfflineActions (actions reported and deleted.)"), Promise.resolve()
})
}, function(err) {
return console.error("[mediasync] error on apiClient.reportOfflineActions: " + err.toString()), localassetmanager.deleteUserActions(actions)
}) : (console.log("[mediasync] Exit reportOfflineActions (no actions)"), Promise.resolve())
})
}
function syncData(apiClient, serverInfo) {
return console.log("[mediasync] Begin syncData"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
var completedItems = items.filter(function(item) {
return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus)
}),
request = {
TargetId: apiClient.deviceId(),
LocalItemIds: completedItems.map(function(xitem) {
return xitem.ItemId
})
};
return apiClient.syncData(request).then(function(result) {
return afterSyncData(apiClient, serverInfo, result).then(function() {
return console.log("[mediasync] Exit syncData"), Promise.resolve()
}, function(err) {
return console.error("[mediasync] Error in syncData: " + err.toString()), Promise.resolve()
})
})
})
}
function afterSyncData(apiClient, serverInfo, syncDataResult) {
console.log("[mediasync] Begin afterSyncData");
var p = Promise.resolve();
return syncDataResult.ItemIdsToRemove && syncDataResult.ItemIdsToRemove.length > 0 && syncDataResult.ItemIdsToRemove.forEach(function(itemId) {
p = p.then(function() {
return removeLocalItem(itemId, serverInfo.Id)
})
}), p = p.then(function() {
return removeObsoleteContainerItems(serverInfo.Id)
}), p.then(function() {
return console.log("[mediasync] Exit afterSyncData"), Promise.resolve()
})
}
function removeObsoleteContainerItems(serverId) {
return console.log("[mediasync] Begin removeObsoleteContainerItems"), localassetmanager.removeObsoleteContainerItems(serverId)
}
function removeLocalItem(itemId, serverId) {
return console.log("[mediasync] Begin removeLocalItem"), localassetmanager.getLocalItem(serverId, itemId).then(function(item) {
return item ? localassetmanager.removeLocalItem(item) : Promise.resolve()
}, function(err2) {
return console.error("[mediasync] removeLocalItem: Failed: ", err2), Promise.resolve()
})
}
function getNewMedia(apiClient, downloadCount) {
return console.log("[mediasync] Begin getNewMedia"), apiClient.getReadySyncItems(apiClient.deviceId()).then(function(jobItems) {
console.log("[mediasync] getReadySyncItems returned " + jobItems.length + " items");
var p = Promise.resolve(),
currentCount = downloadCount;
return jobItems.forEach(function(jobItem) {
currentCount++ <= 10 && (p = p.then(function() {
return getNewItem(jobItem, apiClient)
}))
}), p.then(function() {
return console.log("[mediasync] Exit getNewMedia"), Promise.resolve()
})
}, function(err) {
return console.error("[mediasync] getReadySyncItems: Failed: ", err), Promise.resolve()
})
}
function afterMediaDownloaded(apiClient, jobItem, localItem) {
return console.log("[mediasync] Begin afterMediaDownloaded"), getImages(apiClient, jobItem, localItem).then(function() {
var libraryItem = jobItem.Item;
return downloadParentItems(apiClient, jobItem, libraryItem).then(function() {
return getSubtitles(apiClient, jobItem, localItem)
})
})
}
function createLocalItem(libraryItem, jobItem) {
console.log("[localassetmanager] Begin createLocalItem");
var item = {
Item: libraryItem,
ItemId: libraryItem.Id,
ServerId: libraryItem.ServerId,
Id: libraryItem.Id
};
return jobItem && (item.SyncJobItemId = jobItem.SyncJobItemId), console.log("[localassetmanager] End createLocalItem"), item
}
function getNewItem(jobItem, apiClient) {
console.log("[mediasync] Begin getNewItem");
var libraryItem = jobItem.Item;
return localassetmanager.getLocalItem(libraryItem.ServerId, libraryItem.Id).then(function(existingItem) {
if (existingItem && ("queued" === existingItem.SyncStatus || "transferring" === existingItem.SyncStatus || "synced" === existingItem.SyncStatus) && (console.log("[mediasync] getNewItem: getLocalItem found existing item"), localassetmanager.enableBackgroundCompletion())) return Promise.resolve();
libraryItem.CanDelete = !1, libraryItem.CanDownload = !1, libraryItem.SupportsSync = !1, libraryItem.People = [], libraryItem.Chapters = [], libraryItem.Studios = [], libraryItem.SpecialFeatureCount = null, libraryItem.LocalTrailerCount = null, libraryItem.RemoteTrailers = [];
var localItem = createLocalItem(libraryItem, jobItem);
return localItem.SyncStatus = "queued", downloadMedia(apiClient, jobItem, localItem)
})
}
function downloadParentItems(apiClient, jobItem, libraryItem) {
var p = Promise.resolve();
return libraryItem.SeriesId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.SeriesId)
})), libraryItem.SeasonId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.SeasonId).then(function(seasonItem) {
return libraryItem.SeasonPrimaryImageTag = (seasonItem.Item.ImageTags || {}).Primary, Promise.resolve()
})
})), libraryItem.AlbumId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.AlbumId)
})), p
}
function downloadItem(apiClient, itemId) {
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(downloadedItem) {
downloadedItem.CanDelete = !1, downloadedItem.CanDownload = !1, downloadedItem.SupportsSync = !1, downloadedItem.People = [], downloadedItem.SpecialFeatureCount = null, downloadedItem.BackdropImageTags = null, downloadedItem.ParentBackdropImageTags = null, downloadedItem.ParentArtImageTag = null, downloadedItem.ParentLogoImageTag = null;
var localItem = createLocalItem(downloadedItem, null);
return localassetmanager.addOrUpdateLocalItem(localItem).then(function() {
return Promise.resolve(localItem)
}, function(err) {
return console.error("[mediasync] downloadItem failed: " + err.toString()), Promise.resolve(null)
})
})
}
function ensureLocalPathParts(localItem, jobItem) {
if (!localItem.LocalPathParts) {
var libraryItem = localItem.Item,
parts = localassetmanager.getDirectoryPath(libraryItem);
parts.push(localassetmanager.getLocalFileName(libraryItem, jobItem.OriginalFileName)), localItem.LocalPathParts = parts
}
}
function downloadMedia(apiClient, jobItem, localItem) {
console.log("[mediasync] downloadMedia: start.");
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/File", {
api_key: apiClient.accessToken()
});
return ensureLocalPathParts(localItem, jobItem), localassetmanager.downloadFile(url, localItem).then(function(result) {
console.log("[mediasync] downloadMedia-downloadFile returned path: " + result.path);
var localPath = result.path,
libraryItem = localItem.Item;
if (localPath && libraryItem.MediaSources)
for (var i = 0; i < libraryItem.MediaSources.length; i++) {
var mediaSource = libraryItem.MediaSources[i];
mediaSource.Path = localPath, mediaSource.Protocol = "File"
}
return localItem.LocalPath = localPath, localItem.SyncStatus = "transferring", localassetmanager.addOrUpdateLocalItem(localItem).then(function() {
return afterMediaDownloaded(apiClient, jobItem, localItem).then(function() {
return result.isComplete ? (localItem.SyncStatus = "synced", reportTransfer(apiClient, localItem)) : Promise.resolve()
}, function(err) {
return console.log("[mediasync] downloadMedia: afterMediaDownloaded failed: " + err), Promise.reject(err)
})
}, function(err) {
return console.log("[mediasync] downloadMedia: addOrUpdateLocalItem failed: " + err), Promise.reject(err)
})
}, function(err) {
return console.log("[mediasync] downloadMedia: localassetmanager.downloadFile failed: " + err), Promise.reject(err)
})
}
function getImages(apiClient, jobItem, localItem) {
console.log("[mediasync] Begin getImages");
var p = Promise.resolve(),
libraryItem = localItem.Item,
serverId = libraryItem.ServerId,
mainImageTag = (libraryItem.ImageTags || {}).Primary;
libraryItem.Id && mainImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, mainImageTag, "Primary")
}));
var logoImageTag = (libraryItem.ImageTags || {}).Logo;
libraryItem.Id && logoImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, logoImageTag, "Logo")
}));
var artImageTag = (libraryItem.ImageTags || {}).Art;
libraryItem.Id && artImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, artImageTag, "Art")
}));
var bannerImageTag = (libraryItem.ImageTags || {}).Banner;
libraryItem.Id && bannerImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, bannerImageTag, "Banner")
}));
var thumbImageTag = (libraryItem.ImageTags || {}).Thumb;
if (libraryItem.Id && thumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, thumbImageTag, "Thumb")
})), libraryItem.Id && libraryItem.BackdropImageTags)
for (var i = 0; i < libraryItem.BackdropImageTags.length; i++);
return libraryItem.SeriesId && libraryItem.SeriesPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesPrimaryImageTag, "Primary")
})), libraryItem.SeriesId && libraryItem.SeriesThumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesThumbImageTag, "Thumb")
})), libraryItem.SeasonId && libraryItem.SeasonPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeasonId, libraryItem.SeasonPrimaryImageTag, "Primary")
})), libraryItem.AlbumId && libraryItem.AlbumPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.AlbumId, libraryItem.AlbumPrimaryImageTag, "Primary")
})), libraryItem.ParentThumbItemId && libraryItem.ParentThumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.ParentThumbItemId, libraryItem.ParentThumbImageTag, "Thumb")
})), libraryItem.ParentPrimaryImageItemId && libraryItem.ParentPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.ParentPrimaryImageItemId, libraryItem.ParentPrimaryImageTag, "Primary")
})), p.then(function() {
return console.log("[mediasync] Finished getImages"), localassetmanager.addOrUpdateLocalItem(localItem)
}, function(err) {
return console.log("[mediasync] Error getImages: " + err.toString()), Promise.resolve()
})
}
function downloadImage(localItem, apiClient, serverId, itemId, imageTag, imageType, index) {
return index = index || 0, localassetmanager.hasImage(serverId, itemId, imageType, index).then(function(hasImage) {
if (hasImage) return console.log("[mediasync] downloadImage - skip existing: " + itemId + " " + imageType + "_" + index.toString()), Promise.resolve();
var maxWidth = 400;
"backdrop" === imageType && (maxWidth = null);
var imageUrl = apiClient.getScaledImageUrl(itemId, {
tag: imageTag,
type: imageType,
maxWidth: maxWidth,
api_key: apiClient.accessToken()
});
return console.log("[mediasync] downloadImage " + itemId + " " + imageType + "_" + index.toString()), localassetmanager.downloadImage(localItem, imageUrl, serverId, itemId, imageType, index).then(function(result) {
return Promise.resolve(result)
}, function(err) {
return console.log("[mediasync] Error downloadImage: " + err.toString()), Promise.resolve()
})
}, function(err) {
return console.log("[mediasync] Error downloadImage: " + err.toString()), Promise.resolve()
})
}
function getSubtitles(apiClient, jobItem, localItem) {
if (console.log("[mediasync] Begin getSubtitles"), !jobItem.Item.MediaSources.length) return console.log("[mediasync] Cannot download subtitles because video has no media source info."), Promise.resolve();
var files = jobItem.AdditionalFiles.filter(function(f) {
return "Subtitles" === f.Type
}),
mediaSource = jobItem.Item.MediaSources[0],
p = Promise.resolve();
return files.forEach(function(file) {
p = p.then(function() {
return getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource)
})
}), p.then(function() {
return console.log("[mediasync] Exit getSubtitles"), Promise.resolve()
})
}
function getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource) {
console.log("[mediasync] Begin getItemSubtitle");
var subtitleStream = mediaSource.MediaStreams.filter(function(m) {
return "Subtitle" === m.Type && m.Index === file.Index
})[0];
if (!subtitleStream) return console.log("[mediasync] Cannot download subtitles because matching stream info was not found."), Promise.resolve();
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/AdditionalFiles", {
Name: file.Name,
api_key: apiClient.accessToken()
}),
fileName = localassetmanager.getSubtitleSaveFileName(localItem, jobItem.OriginalFileName, subtitleStream.Language, subtitleStream.IsForced, subtitleStream.Codec);
return localassetmanager.downloadSubtitles(url, fileName).then(function(subtitleResult) {
return localItem.AdditionalFiles && localItem.AdditionalFiles.forEach(function(item) {
item.Name === file.Name && (item.Path = subtitleResult.path)
}), subtitleStream.Path = subtitleResult.path, subtitleStream.DeliveryMethod = "External", localassetmanager.addOrUpdateLocalItem(localItem)
})
}
function checkLocalFileExistence(apiClient, serverInfo, options) {
return options.checkFileExistence ? (console.log("[mediasync] Begin checkLocalFileExistence"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
var completedItems = items.filter(function(item) {
return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus)
}),
p = Promise.resolve();
return completedItems.forEach(function(completedItem) {
p = p.then(function() {
return localassetmanager.fileExists(completedItem.LocalPath).then(function(exists) {
return exists ? Promise.resolve() : localassetmanager.removeLocalItem(completedItem).then(function() {
return Promise.resolve()
}, function() {
return Promise.resolve()
})
})
})
}), p
})) : Promise.resolve()
}
return function() {
var self = this;
"string" == typeof webWorkerBaseUrl && -1 !== webWorkerBaseUrl.indexOf("ms-appx://") ? self.sync = function(apiClient, serverInfo, options) {
return console.log("[mediasync]************************************* Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() {
return processDownloadStatus(apiClient, serverInfo, options).then(function() {
return localassetmanager.getDownloadItemCount().then(function(downloadCount) {
return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() {
return getNewMedia(apiClient, downloadCount).then(function() {
return syncData(apiClient, serverInfo).then(function() {
return console.log("[mediasync]************************************* Exit sync"), Promise.resolve()
})
})
})
})
})
}, function(err) {
console.error(err.toString())
})
} : self.sync = function(apiClient, serverInfo, options) {
return console.log("[mediasync]************************************* Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() {
return syncData(apiClient, serverInfo).then(function() {
return processDownloadStatus(apiClient, serverInfo, options).then(function() {
return localassetmanager.getDownloadItemCount().then(function(downloadCount) {
return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() {
return getNewMedia(apiClient, downloadCount).then(function() {
return syncData(apiClient, serverInfo)
})
})
})
})
})
}, function(err) {
console.error(err.toString())
})
}
}
});

View file

@ -0,0 +1,22 @@
define(["serversync"], function(ServerSync) {
"use strict";
function syncNext(connectionManager, servers, index, options, resolve, reject) {
var length = servers.length;
if (index >= length) return console.log("MultiServerSync.sync complete"), void resolve();
var server = servers[index];
console.log("Creating ServerSync to server: " + server.Id), (new ServerSync).sync(connectionManager, server, options).then(function() {
console.log("ServerSync succeeded to server: " + server.Id), syncNext(connectionManager, servers, index + 1, options, resolve, reject)
}, function(err) {
console.log("ServerSync failed to server: " + server.Id + ". " + err), syncNext(connectionManager, servers, index + 1, options, resolve, reject)
})
}
function MultiServerSync() {}
return MultiServerSync.prototype.sync = function(connectionManager, options) {
return console.log("MultiServerSync.sync starting..."), new Promise(function(resolve, reject) {
var servers = connectionManager.getSavedServers();
syncNext(connectionManager, servers, 0, options, resolve, reject)
})
}, MultiServerSync
});

View file

@ -0,0 +1,46 @@
define([], function() {
"use strict";
function performSync(connectionManager, server, options) {
console.log("ServerSync.performSync to server: " + server.Id), options = options || {};
var cameraUploadServers = options.cameraUploadServers || [];
console.log("ServerSync cameraUploadServers: " + JSON.stringify(cameraUploadServers));
var uploadPhotos = -1 !== cameraUploadServers.indexOf(server.Id);
return console.log("ServerSync uploadPhotos: " + uploadPhotos), (uploadPhotos ? uploadContent(connectionManager, server, options) : Promise.resolve()).then(function() {
return syncMedia(connectionManager, server, options)
})
}
function uploadContent(connectionManager, server, options) {
return new Promise(function(resolve, reject) {
require(["contentuploader"], function(ContentUploader) {
(new ContentUploader).uploadImages(connectionManager, server).then(resolve, reject)
})
})
}
function syncMedia(connectionManager, server, options) {
return new Promise(function(resolve, reject) {
require(["mediasync"], function(MediaSync) {
var apiClient = connectionManager.getApiClient(server.Id);
(new MediaSync).sync(apiClient, server, options).then(resolve, reject)
})
})
}
function ServerSync() {}
return ServerSync.prototype.sync = function(connectionManager, server, options) {
if (!server.AccessToken && !server.ExchangeToken) return console.log("Skipping sync to server " + server.Id + " because there is no saved authentication information."), Promise.resolve();
var connectionOptions = {
updateDateLastAccessed: !1,
enableWebSocket: !1,
reportCapabilities: !1,
enableAutomaticBitrateDetection: !1
};
return connectionManager.connectToServer(server, connectionOptions).then(function(result) {
return "SignedIn" === result.State ? performSync(connectionManager, server, options) : (console.log("Unable to connect to server id: " + server.Id), Promise.reject())
}, function(err) {
throw console.log("Unable to connect to server id: " + server.Id), err
})
}, ServerSync
});

View file

@ -0,0 +1,30 @@
define([], function() {
"use strict";
function downloadFile(url, folder, localItem, imageUrl) {
return Promise.reject()
}
function downloadSubtitles(url, folder, fileName) {
return Promise.reject()
}
function downloadImage(url, folder, fileName) {
return Promise.reject()
}
function resyncTransfers() {
return Promise.resolve()
}
function getDownloadItemCount() {
return Promise.resolve(0)
}
return {
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
downloadImage: downloadImage,
resyncTransfers: resyncTransfers,
getDownloadItemCount: getDownloadItemCount
}
});

View file

@ -0,0 +1,108 @@
define([], function() {
"use strict";
function getDb(callback) {
var db = databaseInstance;
if (db) return void callback(db);
var request = indexedDB.open(dbName, dbVersion);
request.onerror = function(event) {}, request.onupgradeneeded = function(event) {
var db = event.target.result;
db.createObjectStore(dbName).transaction.oncomplete = function(event) {
callback(db)
}
}, request.onsuccess = function(event) {
var db = event.target.result;
callback(db)
}
}
function getByServerId(serverId) {
return getAll().then(function(items) {
return items.filter(function(item) {
return item.ServerId === serverId
})
})
}
function getAll() {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var request, storeName = dbName,
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName);
if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) {
resolve(event.target.result)
};
else {
var results = [];
request = objectStore.openCursor(), request.onsuccess = function(event) {
var cursor = event.target.result;
cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results)
}
}
request.onerror = reject
})
})
}
function get(key) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName),
request = objectStore.get(key);
request.onerror = reject, request.onsuccess = function(event) {
resolve(request.result)
}
})
})
}
function set(key, val) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.put(val, key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function remove(key) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.delete(key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function clear() {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.clear();
request.onerror = reject, request.onsuccess = resolve
})
})
}
var databaseInstance, indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB,
dbName = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, "useractions"),
dbVersion = 1;
return {
get: get,
set: set,
remove: remove,
clear: clear,
getAll: getAll,
getByServerId: getByServerId
}
});

View file

@ -0,0 +1,15 @@
define([], function() {
"use strict";
function send(info) {
return Promise.reject()
}
function isSupported() {
return !1
}
return {
send: send,
isSupported: isSupported
}
});