mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-18 03:18:19 -07:00
536 lines
16 KiB
JavaScript
536 lines
16 KiB
JavaScript
(function (globalScope) {
|
|
|
|
function mediaSync() {
|
|
|
|
var self = this;
|
|
|
|
self.sync = function (apiClient, serverInfo, options) {
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
reportOfflineActions(apiClient, serverInfo).done(function () {
|
|
|
|
// Do the first data sync
|
|
syncData(apiClient, serverInfo, false).done(function () {
|
|
|
|
// Download new content
|
|
getNewMedia(apiClient, serverInfo, options).done(function () {
|
|
|
|
// Do the second data sync
|
|
syncData(apiClient, serverInfo, false).done(function () {
|
|
|
|
deferred.resolve();
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
return deferred.promise();
|
|
};
|
|
|
|
function reportOfflineActions(apiClient, serverInfo) {
|
|
|
|
Logger.log('Begin reportOfflineActions');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
LocalAssetManager.getOfflineActions(serverInfo.Id).done(function (actions) {
|
|
|
|
if (!actions.length) {
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
apiClient.reportOfflineActions(actions).done(function () {
|
|
|
|
LocalAssetManager.deleteOfflineActions(actions).done(function () {
|
|
|
|
deferred.resolve();
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function syncData(apiClient, serverInfo, syncUserItemAccess) {
|
|
|
|
Logger.log('Begin syncData');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
LocalAssetManager.getServerItemIds(serverInfo.Id).done(function (localIds) {
|
|
|
|
var request = {
|
|
TargetId: apiClient.deviceId(),
|
|
LocalItemIds: localIds,
|
|
OfflineUserIds: (serverInfo.Users || []).map(function (u) { return u.Id; })
|
|
};
|
|
|
|
apiClient.syncData(request).done(function (result) {
|
|
|
|
afterSyncData(apiClient, serverInfo, syncUserItemAccess, result, deferred);
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function afterSyncData(apiClient, serverInfo, enableSyncUserItemAccess, syncDataResult, deferred) {
|
|
|
|
Logger.log('Begin afterSyncData');
|
|
|
|
removeLocalItems(syncDataResult, serverInfo.Id).done(function (result) {
|
|
|
|
if (enableSyncUserItemAccess) {
|
|
syncUserItemAccess(syncDataResult, serverInfo.Id).done(function () {
|
|
|
|
deferred.resolve();
|
|
|
|
}).fail(getOnFail(deferred));
|
|
}
|
|
else {
|
|
deferred.resolve();
|
|
}
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
deferred.resolve();
|
|
}
|
|
|
|
function removeLocalItems(syncDataResult, serverId) {
|
|
|
|
Logger.log('Begin removeLocalItems');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
removeNextLocalItem(syncDataResult.ItemIdsToRemove, 0, serverId, deferred);
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function removeNextLocalItem(itemIdsToRemove, index, serverId, deferred) {
|
|
|
|
var length = itemIdsToRemove.length;
|
|
|
|
if (index >= length) {
|
|
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
removeLocalItem(itemIdsToRemove[index], serverId).done(function () {
|
|
|
|
removeNextLocalItem(itemIdsToRemove, index + 1, serverId, deferred);
|
|
}).fail(function () {
|
|
removeNextLocalItem(itemIdsToRemove, index + 1, serverId, deferred);
|
|
});
|
|
}
|
|
|
|
function removeLocalItem(itemId, serverId) {
|
|
|
|
Logger.log('Begin removeLocalItem');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
LocalAssetManager.removeLocalItem(itemId, serverId).done(function (localIds) {
|
|
|
|
deferred.resolve();
|
|
|
|
}).fail(getOnFail(deferred));
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getNewMedia(apiClient, serverInfo, options) {
|
|
|
|
Logger.log('Begin getNewMedia');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
apiClient.getReadySyncItems(apiClient.deviceId()).done(function (jobItems) {
|
|
|
|
getNextNewItem(jobItems, 0, apiClient, serverInfo, options, deferred);
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getNextNewItem(jobItems, index, apiClient, serverInfo, options, deferred) {
|
|
|
|
var length = jobItems.length;
|
|
|
|
if (index >= length) {
|
|
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
var hasGoneNext = false;
|
|
var goNext = function () {
|
|
|
|
if (!hasGoneNext) {
|
|
hasGoneNext = true;
|
|
getNextNewItem(jobItems, index + 1, apiClient, serverInfo, options, deferred);
|
|
}
|
|
};
|
|
|
|
getNewItem(jobItems[index], apiClient, serverInfo, options).done(goNext).fail(goNext);
|
|
}
|
|
|
|
function getNewItem(jobItem, apiClient, serverInfo, options) {
|
|
|
|
Logger.log('Begin getNewItem');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
var libraryItem = jobItem.Item;
|
|
LocalAssetManager.createLocalItem(libraryItem, serverInfo, jobItem.OriginalFileName).done(function (localItem) {
|
|
|
|
downloadMedia(apiClient, jobItem, localItem, options).done(function (isQueued) {
|
|
|
|
if (isQueued) {
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
getImages(apiClient, jobItem, localItem).done(function () {
|
|
|
|
getSubtitles(apiClient, jobItem, localItem).done(function () {
|
|
|
|
apiClient.reportSyncJobItemTransferred(jobItem.SyncJobItemId).done(function () {
|
|
|
|
deferred.resolve();
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function downloadMedia(apiClient, jobItem, localItem, options) {
|
|
|
|
Logger.log('Begin downloadMedia');
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/File", {
|
|
api_key: apiClient.accessToken()
|
|
});
|
|
|
|
var localPath = localItem.LocalPath;
|
|
|
|
Logger.log('Downloading media. Url: ' + url + '. Local path: ' + localPath);
|
|
|
|
options = options || {};
|
|
|
|
LocalAssetManager.downloadFile(url, localPath, options.enableBackgroundTransfer, options.enableNewDownloads).done(function (path, isQueued) {
|
|
|
|
if (isQueued) {
|
|
deferred.resolveWith(null, [true]);
|
|
return;
|
|
}
|
|
LocalAssetManager.addOrUpdateLocalItem(localItem).done(function () {
|
|
|
|
deferred.resolveWith(null, [false]);
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getImages(apiClient, jobItem, localItem) {
|
|
|
|
Logger.log('Begin getImages');
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
getNextImage(0, apiClient, localItem, deferred);
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getNextImage(index, apiClient, localItem, deferred) {
|
|
|
|
Logger.log('Begin getNextImage');
|
|
if (index >= 4) {
|
|
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
// Just for now while media syncing gets worked out
|
|
deferred.resolve();
|
|
return;
|
|
|
|
var libraryItem = localItem.Item;
|
|
|
|
var serverId = libraryItem.ServerId;
|
|
var itemId = null;
|
|
var imageTag = null;
|
|
var imageType = "Primary";
|
|
|
|
switch (index) {
|
|
|
|
case 0:
|
|
itemId = libraryItem.Id;
|
|
imageType = "Primary";
|
|
imageTag = (libraryItem.ImageTags || {})["Primary"];
|
|
break;
|
|
case 1:
|
|
itemId = libraryItem.SeriesId;
|
|
imageType = "Primary";
|
|
imageTag = libraryItem.SeriesPrimaryImageTag;
|
|
break;
|
|
case 2:
|
|
itemId = libraryItem.SeriesId;
|
|
imageType = "Thumb";
|
|
imageTag = libraryItem.SeriesPrimaryImageTag;
|
|
break;
|
|
case 3:
|
|
itemId = libraryItem.AlbumId;
|
|
imageType = "Primary";
|
|
imageTag = libraryItem.AlbumPrimaryImageTag;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!itemId || !imageTag) {
|
|
getNextImage(index + 1, apiClient, localItem, deferred);
|
|
return;
|
|
}
|
|
|
|
downloadImage(apiClient, serverId, itemId, imageTag, imageType).done(function () {
|
|
|
|
// For the sake of simplicity, limit to one image
|
|
deferred.resolve();
|
|
return;
|
|
|
|
getNextImage(index + 1, apiClient, localItem, deferred);
|
|
|
|
}).fail(getOnFail(deferred));
|
|
}
|
|
|
|
function downloadImage(apiClient, serverId, itemId, imageTag, imageType) {
|
|
|
|
Logger.log('Begin downloadImage');
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
LocalAssetManager.hasImage(serverId, itemId, imageTag).done(function (hasImage) {
|
|
|
|
if (hasImage) {
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
var imageUrl = apiClient.getImageUrl(itemId, {
|
|
tag: imageTag,
|
|
type: imageType,
|
|
api_key: apiClient.accessToken()
|
|
});
|
|
|
|
LocalAssetManager.downloadImage(imageUrl, serverId, itemId, imageTag).done(function () {
|
|
|
|
deferred.resolve();
|
|
|
|
}).fail(getOnFail(deferred));
|
|
|
|
});
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getSubtitles(apiClient, jobItem, localItem) {
|
|
|
|
Logger.log('Begin getSubtitles');
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
if (!jobItem.Item.MediaSources.length) {
|
|
logger.Error("Cannot download subtitles because video has no media source info.");
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
var files = jobItem.AdditionalFiles.filter(function (f) {
|
|
return f.Type == 'Subtitles';
|
|
});
|
|
|
|
var mediaSource = jobItem.Item.MediaSources[0];
|
|
|
|
getNextSubtitle(files, 0, apiClient, jobItem, localItem, mediaSource, deferred);
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getNextSubtitle(files, index, apiClient, jobItem, localItem, mediaSource, deferred) {
|
|
|
|
var length = files.length;
|
|
|
|
if (index >= length) {
|
|
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource).done(function () {
|
|
|
|
getNextSubtitle(files, index + 1, apiClient, jobItem, localItem, mediaSource, deferred);
|
|
|
|
}).fail(function () {
|
|
getNextSubtitle(files, index + 1, apiClient, jobItem, localItem, mediaSource, deferred);
|
|
});
|
|
}
|
|
|
|
function getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource) {
|
|
|
|
Logger.log('Begin getItemSubtitle');
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
var subtitleStream = mediaSource.MediaStreams.filter(function (m) {
|
|
return m.Type == 'Subtitle' && m.Index == file.Index;
|
|
})[0];
|
|
|
|
if (!subtitleStream) {
|
|
|
|
// We shouldn't get in here, but let's just be safe anyway
|
|
Logger.log("Cannot download subtitles because matching stream info wasn't found.");
|
|
deferred.reject();
|
|
return;
|
|
}
|
|
|
|
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/AdditionalFiles", {
|
|
Name: file.Name,
|
|
api_key: apiClient.accessToken()
|
|
});
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
LocalAssetManager.downloadSubtitles(url, localItem, subtitleStream).done(function (subtitlePath) {
|
|
|
|
subtitleStream.Path = subtitlePath;
|
|
LocalAssetManager.addOrUpdateLocalItem(localItem).done(function () {
|
|
deferred.resolve();
|
|
}).fail(getOnFail(deferred));
|
|
|
|
}).fail(getOnFail(deferred));
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function syncUserItemAccess(syncDataResult, serverId) {
|
|
Logger.log('Begin syncUserItemAccess');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
var itemIds = [];
|
|
for (var id in syncDataResult.ItemUserAccess) {
|
|
itemIds.push(id);
|
|
}
|
|
|
|
syncNextUserAccessForItem(itemIds, 0, syncDataResult, serverId, deferred);
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function syncNextUserAccessForItem(itemIds, index, syncDataResult, serverId, deferred) {
|
|
|
|
var length = itemIds.length;
|
|
|
|
if (index >= length) {
|
|
|
|
deferred.resolve();
|
|
return;
|
|
}
|
|
|
|
syncUserAccessForItem(itemIds[index], syncDataResult).done(function () {
|
|
|
|
syncNextUserAccessForItem(itemIds, index + 1, syncDataResult, serverId, deferred);
|
|
}).fail(function () {
|
|
syncNextUserAccessForItem(itemIds, index + 1, syncDataResult, serverId, deferred);
|
|
});
|
|
}
|
|
|
|
function syncUserAccessForItem(itemId, syncDataResult) {
|
|
Logger.log('Begin syncUserAccessForItem');
|
|
|
|
var deferred = DeferredBuilder.Deferred();
|
|
|
|
require(['localassetmanager'], function () {
|
|
|
|
LocalAssetManager.getUserIdsWithAccess(itemId, serverId).done(function (savedUserIdsWithAccess) {
|
|
|
|
var userIdsWithAccess = syncDataResult.ItemUserAccess[itemId];
|
|
|
|
if (userIdsWithAccess.join(',') == savedUserIdsWithAccess.join(',')) {
|
|
// Hasn't changed, nothing to do
|
|
deferred.resolve();
|
|
}
|
|
else {
|
|
|
|
LocalAssetManager.saveUserIdsWithAccess(itemId, serverId, userIdsWithAccess).done(function () {
|
|
deferred.resolve();
|
|
}).fail(getOnFail(deferred));
|
|
}
|
|
|
|
}).fail(getOnFail(deferred));
|
|
});
|
|
|
|
return deferred.promise();
|
|
}
|
|
|
|
function getOnFail(deferred) {
|
|
return function () {
|
|
|
|
deferred.reject();
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!globalScope.MediaBrowser) {
|
|
globalScope.MediaBrowser = {};
|
|
}
|
|
|
|
globalScope.MediaBrowser.MediaSync = mediaSync;
|
|
|
|
})(this); |