295 lines
No EOL
20 KiB
JavaScript
295 lines
No EOL
20 KiB
JavaScript
define(["apphost", "globalize", "connectionManager", "layoutManager", "focusManager", "scrollHelper", "appSettings", "registrationServices", "dialogHelper", "paper-icon-button-light", "formDialogStyle"], function(appHost, globalize, connectionManager, layoutManager, focusManager, scrollHelper, appSettings, registrationServices, dialogHelper) {
|
|
"use strict";
|
|
|
|
function submitJob(dlg, apiClient, userId, syncOptions, form) {
|
|
if (!userId) throw new Error("userId cannot be null");
|
|
if (!syncOptions) throw new Error("syncOptions cannot be null");
|
|
if (!form) throw new Error("form cannot be null");
|
|
var selectSyncTarget = form.querySelector("#selectSyncTarget"),
|
|
target = selectSyncTarget ? selectSyncTarget.value : null;
|
|
if (!target) return require(["toast"], function(toast) {
|
|
toast(globalize.translate("sharedcomponents#PleaseSelectDeviceToSyncTo"))
|
|
}), !1;
|
|
var options = {
|
|
userId: userId,
|
|
TargetId: target,
|
|
ParentId: syncOptions.ParentId,
|
|
Category: syncOptions.Category
|
|
};
|
|
return setJobValues(options, form), syncOptions.items && syncOptions.items.length && (options.ItemIds = (syncOptions.items || []).map(function(i) {
|
|
return i.Id || i
|
|
}).join(",")), apiClient.ajax({
|
|
type: "POST",
|
|
url: apiClient.getUrl("Sync/Jobs"),
|
|
data: JSON.stringify(options),
|
|
contentType: "application/json",
|
|
dataType: "json"
|
|
}).then(function() {
|
|
dialogHelper.close(dlg), require(["toast"], function(toast) {
|
|
showSubmissionToast(target, apiClient), "download" === syncOptions.mode && syncNow()
|
|
})
|
|
}), !0
|
|
}
|
|
|
|
function showSubmissionToast(targetId, apiClient) {
|
|
require(["toast"], function(toast) {
|
|
toast(targetId === apiClient.deviceId() ? globalize.translate("sharedcomponents#DownloadingDots") : globalize.translate("sharedcomponents#SyncingDots"))
|
|
})
|
|
}
|
|
|
|
function syncNow() {
|
|
require(["localsync"], function(localSync) {
|
|
localSync.sync()
|
|
})
|
|
}
|
|
|
|
function submitQuickSyncJob(apiClient, userId, targetId, syncOptions) {
|
|
if (!userId) throw new Error("userId cannot be null");
|
|
if (!syncOptions) throw new Error("syncOptions cannot be null");
|
|
if (!targetId) throw new Error("targetId cannot be null");
|
|
var options = {
|
|
userId: userId,
|
|
TargetId: targetId,
|
|
ParentId: syncOptions.ParentId,
|
|
Category: syncOptions.Category,
|
|
Quality: syncOptions.Quality,
|
|
Bitrate: syncOptions.Bitrate
|
|
};
|
|
return syncOptions.items && syncOptions.items.length && (options.ItemIds = (syncOptions.items || []).map(function(i) {
|
|
return i.Id || i
|
|
}).join(",")), apiClient.ajax({
|
|
type: "POST",
|
|
url: apiClient.getUrl("Sync/Jobs"),
|
|
data: JSON.stringify(options),
|
|
contentType: "application/json",
|
|
dataType: "json"
|
|
}).then(function() {
|
|
require(["toast"], function(toast) {
|
|
showSubmissionToast(targetId, apiClient), "download" === syncOptions.mode && syncNow()
|
|
})
|
|
})
|
|
}
|
|
|
|
function setJobValues(job, form) {
|
|
var txtBitrate = form.querySelector("#txtBitrate"),
|
|
bitrate = txtBitrate ? txtBitrate.value : null;
|
|
bitrate && (bitrate = 1e6 * parseFloat(bitrate)), job.Bitrate = bitrate;
|
|
var selectQuality = form.querySelector("#selectQuality");
|
|
selectQuality && (job.Quality = selectQuality.value, appSettings.set("sync-lastquality", job.Quality || ""));
|
|
var selectProfile = form.querySelector("#selectProfile");
|
|
selectProfile && (job.Profile = selectProfile.value);
|
|
var txtItemLimit = form.querySelector("#txtItemLimit");
|
|
txtItemLimit && (job.ItemLimit = txtItemLimit.value || null);
|
|
var chkSyncNewContent = form.querySelector("#chkSyncNewContent");
|
|
chkSyncNewContent && (job.SyncNewContent = chkSyncNewContent.checked);
|
|
var chkUnwatchedOnly = form.querySelector("#chkUnwatchedOnly");
|
|
chkUnwatchedOnly && (job.UnwatchedOnly = chkUnwatchedOnly.checked)
|
|
}
|
|
|
|
function renderForm(options) {
|
|
return new Promise(function(resolve, reject) {
|
|
require(["emby-checkbox", "emby-input", "emby-select"], function() {
|
|
renderFormInternal(options, connectionManager.deviceId(), resolve)
|
|
})
|
|
})
|
|
}
|
|
|
|
function renderFormInternal(options, defaultTargetId, resolve) {
|
|
var elem = options.elem,
|
|
dialogOptions = options.dialogOptions,
|
|
targets = dialogOptions.Targets,
|
|
html = "",
|
|
mode = options.mode,
|
|
targetContainerClass = "download" === mode ? " hide" : "",
|
|
syncTargetLabel = "convert" === mode ? globalize.translate("sharedcomponents#LabelConvertTo") : globalize.translate("sharedcomponents#LabelSyncTo");
|
|
options.readOnlySyncTarget ? (html += '<div class="inputContainer' + targetContainerClass + '">', html += '<input is="emby-input" type="text" id="selectSyncTarget" readonly label="' + syncTargetLabel + '"/>', html += "</div>") : (html += '<div class="selectContainer' + targetContainerClass + '">', html += '<select is="emby-select" id="selectSyncTarget" required="required" label="' + syncTargetLabel + '">', html += targets.map(function(t) {
|
|
return "<option" + (defaultTargetId === t.Id ? ' selected="selected"' : "") + ' value="' + t.Id + '">' + t.Name + "</option>"
|
|
}).join(""), html += "</select>", targets.length || (html += '<div class="fieldDescription">' + globalize.translate("sharedcomponents#LabelSyncNoTargetsHelp") + "</div>"), appHost.supports("externallinks") && (html += '<div class="fieldDescription"><a is="emby-linkbutton" class="button-link lnkLearnMore" href="https://github.com/MediaBrowser/Wiki/wiki/Sync" target="_blank">' + globalize.translate("sharedcomponents#LearnMore") + "</a></div>"), html += "</div>"), html += '<div class="fldProfile selectContainer hide">', html += '<select is="emby-select" id="selectProfile" label="' + globalize.translate("sharedcomponents#LabelProfile") + '">', html += "</select>", html += '<div class="fieldDescription profileDescription"></div>', html += "</div>", html += '<div class="fldQuality selectContainer hide">', html += '<select is="emby-select" id="selectQuality" required="required" label="' + globalize.translate("sharedcomponents#LabelQuality") + '">', html += "</select>", html += '<div class="fieldDescription qualityDescription"></div>', html += "</div>", html += '<div class="fldBitrate inputContainer hide">', html += '<input is="emby-input" type="number" step=".1" min=".1" id="txtBitrate" label="' + globalize.translate("sharedcomponents#LabelBitrateMbps") + '"/>', html += "</div>", -1 !== dialogOptions.Options.indexOf("UnwatchedOnly") && (html += '<div class="checkboxContainer checkboxContainer-withDescription">', html += "<label>", html += '<input is="emby-checkbox" type="checkbox" id="chkUnwatchedOnly"/>', html += "convert" === mode ? "<span>" + globalize.translate("sharedcomponents#ConvertUnwatchedVideosOnly") + "</span>" : "<span>" + globalize.translate("sharedcomponents#SyncUnwatchedVideosOnly") + "</span>", html += "</label>", html += "convert" === mode ? '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate("sharedcomponents#ConvertUnwatchedVideosOnlyHelp") + "</div>" : '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate("sharedcomponents#SyncUnwatchedVideosOnlyHelp") + "</div>", html += "</div>"), -1 !== dialogOptions.Options.indexOf("SyncNewContent") && (html += '<div class="checkboxContainer checkboxContainer-withDescription">', html += "<label>", html += '<input is="emby-checkbox" type="checkbox" id="chkSyncNewContent"/>', html += "convert" === mode ? "<span>" + globalize.translate("sharedcomponents#AutomaticallyConvertNewContent") + "</span>" : "<span>" + globalize.translate("sharedcomponents#AutomaticallySyncNewContent") + "</span>", html += "</label>", html += "convert" === mode ? '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate("sharedcomponents#AutomaticallyConvertNewContentHelp") + "</div>" : '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate("sharedcomponents#AutomaticallySyncNewContentHelp") + "</div>", html += "</div>"), -1 !== dialogOptions.Options.indexOf("ItemLimit") && (html += '<div class="inputContainer">', html += '<input is="emby-input" type="number" step="1" min="1" id="txtItemLimit" label="' + globalize.translate("sharedcomponents#LabelItemLimit") + '"/>', html += "convert" === mode ? '<div class="fieldDescription">' + globalize.translate("sharedcomponents#ConvertItemLimitHelp") + "</div>" : '<div class="fieldDescription">' + globalize.translate("sharedcomponents#DownloadItemLimitHelp") + "</div>", html += "</div>"), elem.innerHTML = html;
|
|
var selectSyncTarget = elem.querySelector("#selectSyncTarget");
|
|
selectSyncTarget && (selectSyncTarget.addEventListener("change", function() {
|
|
loadQualityOptions(elem, this.value, options.dialogOptionsFn).then(resolve)
|
|
}), selectSyncTarget.dispatchEvent(new CustomEvent("change", {
|
|
bubbles: !0
|
|
})));
|
|
var selectProfile = elem.querySelector("#selectProfile");
|
|
selectProfile && (selectProfile.addEventListener("change", function() {
|
|
onProfileChange(elem, this.value)
|
|
}), dialogOptions.ProfileOptions.length && selectProfile.dispatchEvent(new CustomEvent("change", {
|
|
bubbles: !0
|
|
})));
|
|
var selectQuality = elem.querySelector("#selectQuality");
|
|
selectQuality && (selectQuality.addEventListener("change", function() {
|
|
onQualityChange(elem, this.value)
|
|
}), selectQuality.dispatchEvent(new CustomEvent("change", {
|
|
bubbles: !0
|
|
}))), setTimeout(function() {
|
|
focusManager.autoFocus(elem)
|
|
}, 100)
|
|
}
|
|
|
|
function showWifiMessage() {
|
|
require(["dialog", "appRouter"], function(dialog, appRouter) {
|
|
var options = {
|
|
title: globalize.translate("sharedcomponents#HeaderWaitingForWifi"),
|
|
text: globalize.translate("sharedcomponents#WifiRequiredToDownload")
|
|
},
|
|
items = [];
|
|
items.push({
|
|
name: options.confirmText || globalize.translate("sharedcomponents#ButtonOk"),
|
|
id: "ok",
|
|
type: "submit"
|
|
}), items.push({
|
|
name: options.cancelText || globalize.translate("sharedcomponents#HeaderDownloadSettings"),
|
|
id: "downloadsettings",
|
|
type: "cancel"
|
|
}), options.buttons = items, dialog(options).then(function(result) {
|
|
return "ok" === result ? Promise.resolve() : "downloadsettings" === result ? (appRouter.show(appRouter.getRouteUrl("downloadsettings")), Promise.resolve()) : Promise.reject()
|
|
})
|
|
})
|
|
}
|
|
|
|
function validateNetwork() {
|
|
switch (navigator.connection ? navigator.connection.type : null) {
|
|
case "cellular":
|
|
case "bluetooth":
|
|
return showWifiMessage(), !1;
|
|
default:
|
|
return !0
|
|
}
|
|
}
|
|
|
|
function showSyncMenu(options) {
|
|
return "download" === options.mode && appSettings.syncOnlyOnWifi() && !validateNetwork() ? Promise.reject() : registrationServices.validateFeature("sync").then(function() {
|
|
return showSyncMenuInternal(options)
|
|
})
|
|
}
|
|
|
|
function enableAutoSync(options) {
|
|
if ("download" !== options.mode) return !1;
|
|
var firstItem = (options.items || [])[0] || {};
|
|
return "Audio" === firstItem.Type || ("MusicAlbum" === firstItem.Type || ("MusicArtist" === firstItem.Type || ("MusicGenre" === firstItem.Type || "Playlist" === firstItem.Type && "Audio" === firstItem.MediaType)))
|
|
}
|
|
|
|
function showSyncMenuInternal(options) {
|
|
var apiClient = connectionManager.getApiClient(options.serverId),
|
|
userId = apiClient.getCurrentUserId();
|
|
if (enableAutoSync(options)) return submitQuickSyncJob(apiClient, userId, apiClient.deviceId(), {
|
|
items: options.items,
|
|
Quality: "custom",
|
|
Bitrate: appSettings.maxStaticMusicBitrate()
|
|
});
|
|
var dialogOptionsFn = getTargetDialogOptionsFn(apiClient, {
|
|
UserId: userId,
|
|
ItemIds: (options.items || []).map(function(i) {
|
|
return i.Id || i
|
|
}).join(","),
|
|
ParentId: options.ParentId,
|
|
Category: options.Category,
|
|
IncludeProviders: "convert" === options.mode ? "ConvertSyncProvider" : null,
|
|
ExcludeProviders: "convert" === options.mode ? null : "ConvertSyncProvider"
|
|
});
|
|
return dialogOptionsFn().then(function(dialogOptions) {
|
|
currentDialogOptions = dialogOptions;
|
|
var dlgElementOptions = {
|
|
removeOnClose: !0,
|
|
scrollY: !1,
|
|
autoFocus: !1
|
|
};
|
|
layoutManager.tv ? dlgElementOptions.size = "fullscreen" : dlgElementOptions.size = "small";
|
|
var dlg = dialogHelper.createDialog(dlgElementOptions);
|
|
dlg.classList.add("formDialog");
|
|
var html = "";
|
|
html += '<div class="formDialogHeader">', html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon"></i></button>', html += '<h3 class="formDialogHeaderTitle">';
|
|
var syncButtonLabel = "download" === options.mode ? globalize.translate("sharedcomponents#Download") : "convert" === options.mode ? globalize.translate("sharedcomponents#Convert") : globalize.translate("sharedcomponents#Sync");
|
|
html += syncButtonLabel, html += "</h3>", appHost.supports("externallinks") && (html += '<a is="emby-linkbutton" href="https://github.com/MediaBrowser/Wiki/wiki/Sync" target="_blank" class="button-link lnkHelp" style="margin-top:0;display:inline-block;vertical-align:middle;margin-left:auto;"><i class="md-icon">info</i><span>' + globalize.translate("sharedcomponents#Help") + "</span></a>"), html += "</div>", html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">', html += '<div class="dialogContentInner dialog-content-centered">', html += '<form class="formSubmitSyncRequest" style="margin: auto;">', html += '<div class="formFields"></div>', html += '<div class="formDialogFooter">', html += '<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem"><span>' + syncButtonLabel + "</span></button>", html += "</div>", html += "</form>", html += "</div>", html += "</div>", dlg.innerHTML = html;
|
|
var submitted = !1;
|
|
dlg.querySelector("form").addEventListener("submit", function(e) {
|
|
return submitted = submitJob(dlg, apiClient, userId, options, this), e.preventDefault(), !1
|
|
}), dlg.querySelector(".btnCancel").addEventListener("click", function() {
|
|
dialogHelper.close(dlg)
|
|
}), layoutManager.tv && scrollHelper.centerFocus.on(dlg.querySelector(".formDialogContent"), !1);
|
|
var promise = dialogHelper.open(dlg);
|
|
return renderForm({
|
|
elem: dlg.querySelector(".formFields"),
|
|
dialogOptions: dialogOptions,
|
|
dialogOptionsFn: dialogOptionsFn,
|
|
mode: options.mode
|
|
}), promise.then(function() {
|
|
return layoutManager.tv && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), submitted ? Promise.resolve() : Promise.reject()
|
|
})
|
|
})
|
|
}
|
|
|
|
function getTargetDialogOptionsFn(apiClient, query) {
|
|
return function(targetId) {
|
|
return query.TargetId = targetId, apiClient.getJSON(apiClient.getUrl("Sync/Options", query))
|
|
}
|
|
}
|
|
|
|
function setQualityFieldVisible(form, visible) {
|
|
var fldQuality = form.querySelector(".fldQuality"),
|
|
selectQuality = form.querySelector("#selectQuality");
|
|
visible ? (fldQuality && fldQuality.classList.remove("hide"), selectQuality && selectQuality.removeAttribute("required")) : (fldQuality && fldQuality.classList.add("hide"), selectQuality && selectQuality.removeAttribute("required"))
|
|
}
|
|
|
|
function onProfileChange(form, profileId) {
|
|
var options = currentDialogOptions || {},
|
|
profileOptions = options.ProfileOptions || [];
|
|
if (profileOptions.length) {
|
|
var option = profileOptions.filter(function(o) {
|
|
return o.Id === profileId
|
|
})[0],
|
|
qualityOptions = options.QualityOptions || [];
|
|
option ? (form.querySelector(".profileDescription").innerHTML = option.Description || "", setQualityFieldVisible(form, qualityOptions.length > 0 && option.EnableQualityOptions && -1 !== options.Options.indexOf("Quality"))) : (form.querySelector(".profileDescription").innerHTML = "", setQualityFieldVisible(form, qualityOptions.length > 0 && -1 !== options.Options.indexOf("Quality")))
|
|
}
|
|
}
|
|
|
|
function onQualityChange(form, qualityId) {
|
|
var options = currentDialogOptions || {},
|
|
option = (options.QualityOptions || []).filter(function(o) {
|
|
return o.Id === qualityId
|
|
})[0],
|
|
qualityDescription = form.querySelector(".qualityDescription");
|
|
qualityDescription.innerHTML = option ? option.Description || "" : "";
|
|
var fldBitrate = form.querySelector(".fldBitrate"),
|
|
txtBitrate = form.querySelector("#txtBitrate");
|
|
"custom" === qualityId ? (fldBitrate && fldBitrate.classList.remove("hide"), txtBitrate && txtBitrate.setAttribute("required", "required")) : (fldBitrate && fldBitrate.classList.add("hide"), txtBitrate && txtBitrate.removeAttribute("required"))
|
|
}
|
|
|
|
function renderTargetDialogOptions(form, options) {
|
|
currentDialogOptions = options;
|
|
var fldProfile = form.querySelector(".fldProfile"),
|
|
selectProfile = form.querySelector("#selectProfile");
|
|
options.ProfileOptions.length && -1 !== options.Options.indexOf("Profile") ? (fldProfile && fldProfile.classList.remove("hide"), selectProfile && selectProfile.setAttribute("required", "required")) : (fldProfile && fldProfile.classList.add("hide"), selectProfile && selectProfile.removeAttribute("required")), setQualityFieldVisible(form, options.QualityOptions.length > 0), selectProfile && (selectProfile.innerHTML = options.ProfileOptions.map(function(o) {
|
|
var selectedAttribute = o.IsDefault ? ' selected="selected"' : "";
|
|
return '<option value="' + o.Id + '"' + selectedAttribute + ">" + o.Name + "</option>"
|
|
}).join(""), selectProfile.dispatchEvent(new CustomEvent("change", {
|
|
bubbles: !0
|
|
})));
|
|
var selectQuality = form.querySelector("#selectQuality");
|
|
if (selectQuality) {
|
|
selectQuality.innerHTML = options.QualityOptions.map(function(o) {
|
|
var selectedAttribute = o.IsDefault ? ' selected="selected"' : "";
|
|
return '<option value="' + o.Id + '"' + selectedAttribute + ">" + o.Name + "</option>"
|
|
}).join("");
|
|
var lastQuality = appSettings.get("sync-lastquality");
|
|
lastQuality && options.QualityOptions.filter(function(i) {
|
|
return i.Id === lastQuality
|
|
}).length && (selectQuality.value = lastQuality), selectQuality.dispatchEvent(new CustomEvent("change", {
|
|
bubbles: !0
|
|
}))
|
|
}
|
|
}
|
|
|
|
function loadQualityOptions(form, targetId, dialogOptionsFn) {
|
|
return dialogOptionsFn(targetId).then(function(options) {
|
|
return renderTargetDialogOptions(form, options)
|
|
})
|
|
}
|
|
var currentDialogOptions;
|
|
return {
|
|
showMenu: showSyncMenu,
|
|
renderForm: renderForm,
|
|
setJobValues: setJobValues
|
|
}
|
|
}); |