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'; var currentDialogOptions; 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'); var target = selectSyncTarget ? selectSyncTarget.value : null; if (!target) { require(['toast'], function (toast) { toast(globalize.translate('PleaseSelectDeviceToSyncTo')); }); return false; } var options = { userId: userId, TargetId: target, ParentId: syncOptions.ParentId, Category: syncOptions.Category }; setJobValues(options, form); if (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); if (syncOptions.mode === 'download') { syncNow(); } }); }); return true; } function showSubmissionToast(targetId, apiClient) { require(['toast'], function (toast) { var msg = targetId === apiClient.deviceId() ? globalize.translate('DownloadingDots') : globalize.translate('SyncingDots'); toast(msg); }); } 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 }; if (syncOptions.items && syncOptions.items.length) { options.ItemIds = (syncOptions.items || []).map(function (i) { return i.Id || i; }).join(','); } return 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); if (syncOptions.mode === 'download') { syncNow(); } }); }); } function setJobValues(job, form) { var txtBitrate = form.querySelector('#txtBitrate'); var bitrate = txtBitrate ? txtBitrate.value : null; if (bitrate) { bitrate = parseFloat(bitrate) * 1000000; } job.Bitrate = bitrate; var selectQuality = form.querySelector('#selectQuality'); if (selectQuality) { job.Quality = selectQuality.value; appSettings.set('sync-lastquality', job.Quality || ''); } var selectProfile = form.querySelector('#selectProfile'); if (selectProfile) { job.Profile = selectProfile.value; } var txtItemLimit = form.querySelector('#txtItemLimit'); if (txtItemLimit) { job.ItemLimit = txtItemLimit.value || null; } var chkSyncNewContent = form.querySelector('#chkSyncNewContent'); if (chkSyncNewContent) { job.SyncNewContent = chkSyncNewContent.checked; } var chkUnwatchedOnly = form.querySelector('#chkUnwatchedOnly'); if (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; var dialogOptions = options.dialogOptions; var targets = dialogOptions.Targets; var html = ''; var mode = options.mode; var targetContainerClass = mode === 'download' ? ' hide' : ''; var syncTargetLabel = mode === 'convert' ? globalize.translate('LabelConvertTo') : globalize.translate('LabelSyncTo'); if (options.readOnlySyncTarget) { html += '
'; html += ''; html += '
'; } else { html += '
'; html += ''; if (!targets.length) { html += '
' + globalize.translate('LabelSyncNoTargetsHelp') + '
'; } if (appHost.supports('externallinks')) { html += '
' + globalize.translate('LearnMore') + '
'; } html += '
'; } html += '
'; html += ''; html += '
'; html += '
'; html += '
'; html += ''; html += '
'; html += '
'; html += '
'; html += ''; html += '
'; if (dialogOptions.Options.indexOf('UnwatchedOnly') !== -1) { html += '
'; html += ''; if (mode === 'convert') { html += '
' + globalize.translate('ConvertUnwatchedVideosOnlyHelp') + '
'; } else { html += '
' + globalize.translate('SyncUnwatchedVideosOnlyHelp') + '
'; } html += '
'; } if (dialogOptions.Options.indexOf('SyncNewContent') !== -1) { html += '
'; html += ''; if (mode === 'convert') { html += '
' + globalize.translate('AutomaticallyConvertNewContentHelp') + '
'; } else { html += '
' + globalize.translate('AutomaticallySyncNewContentHelp') + '
'; } html += '
'; } if (dialogOptions.Options.indexOf('ItemLimit') !== -1) { html += '
'; html += ''; if (mode === 'convert') { html += '
' + globalize.translate('ConvertItemLimitHelp') + '
'; } else { html += '
' + globalize.translate('DownloadItemLimitHelp') + '
'; } html += '
'; } //html += ''; //html += ''; elem.innerHTML = html; var selectSyncTarget = elem.querySelector('#selectSyncTarget'); if (selectSyncTarget) { selectSyncTarget.addEventListener('change', function () { loadQualityOptions(elem, this.value, options.dialogOptionsFn).then(resolve); }); selectSyncTarget.dispatchEvent(new CustomEvent('change', { bubbles: true })); } var selectProfile = elem.querySelector('#selectProfile'); if (selectProfile) { selectProfile.addEventListener('change', function () { onProfileChange(elem, this.value); }); if (dialogOptions.ProfileOptions.length) { selectProfile.dispatchEvent(new CustomEvent('change', { bubbles: true })); } } var selectQuality = elem.querySelector('#selectQuality'); if (selectQuality) { selectQuality.addEventListener('change', function () { onQualityChange(elem, this.value); }); selectQuality.dispatchEvent(new CustomEvent('change', { bubbles: true })); } // This isn't ideal, but allow time for the change handlers above to run setTimeout(function () { focusManager.autoFocus(elem); }, 100); } function showWifiMessage() { require(['dialog', 'appRouter'], function (dialog, appRouter) { var options = { title: globalize.translate('HeaderWaitingForWifi'), text: globalize.translate('WifiRequiredToDownload') }; var items = []; items.push({ name: options.confirmText || globalize.translate('ButtonOk'), id: 'ok', type: 'submit' }); items.push({ name: options.cancelText || globalize.translate('HeaderDownloadSettings'), id: 'downloadsettings', type: 'cancel' }); options.buttons = items; dialog(options).then(function (result) { if (result === 'ok') { return Promise.resolve(); } if (result === 'downloadsettings') { appRouter.show(appRouter.getRouteUrl('downloadsettings')); return Promise.resolve(); } return Promise.reject(); }); }); } function validateNetwork() { var network = navigator.connection ? navigator.connection.type : null; switch (network) { case 'cellular': case 'bluetooth': showWifiMessage(); return false; default: return true; } } function showSyncMenu(options) { if (options.mode === 'download' && appSettings.syncOnlyOnWifi() && !validateNetwork()) { return Promise.reject(); } return registrationServices.validateFeature('sync').then(function () { return showSyncMenuInternal(options); }); } function enableAutoSync(options) { if (options.mode !== 'download') { return false; } var firstItem = (options.items || [])[0] || {}; if (firstItem.Type === 'Audio') { return true; } if (firstItem.Type === 'MusicAlbum') { return true; } if (firstItem.Type === 'MusicArtist') { return true; } if (firstItem.Type === 'MusicGenre') { return true; } if (firstItem.Type === 'Playlist' && firstItem.MediaType === 'Audio') { return true; } return false; } function showSyncMenuInternal(options) { var apiClient = connectionManager.getApiClient(options.serverId); var 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: options.mode === 'convert' ? 'ConvertSyncProvider' : null, ExcludeProviders: options.mode === 'convert' ? null : 'ConvertSyncProvider' }); return dialogOptionsFn().then(function (dialogOptions) { currentDialogOptions = dialogOptions; var dlgElementOptions = { removeOnClose: true, scrollY: false, autoFocus: false }; if (layoutManager.tv) { dlgElementOptions.size = 'fullscreen'; } else { dlgElementOptions.size = 'small'; } var dlg = dialogHelper.createDialog(dlgElementOptions); dlg.classList.add('formDialog'); var html = ''; html += '
'; html += ''; html += '

'; var syncButtonLabel = options.mode === 'download' ? globalize.translate('Download') : (options.mode === 'convert' ? globalize.translate('Convert') : globalize.translate('Sync')); html += syncButtonLabel; html += '

'; if (appHost.supports('externallinks')) { html += 'info' + globalize.translate('Help') + ''; } html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += '
'; html += ''; html += '
'; html += '
'; html += '
'; html += '
'; dlg.innerHTML = html; var submitted = false; dlg.querySelector('form').addEventListener('submit', function (e) { submitted = submitJob(dlg, apiClient, userId, options, this); e.preventDefault(); return false; }); dlg.querySelector('.btnCancel').addEventListener('click', function () { dialogHelper.close(dlg); }); if (layoutManager.tv) { scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); } var promise = dialogHelper.open(dlg); renderForm({ elem: dlg.querySelector('.formFields'), dialogOptions: dialogOptions, dialogOptionsFn: dialogOptionsFn, mode: options.mode }); return promise.then(function () { if (layoutManager.tv) { scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false); } if (submitted) { return Promise.resolve(); } return Promise.reject(); }); }); } function getTargetDialogOptionsFn(apiClient, query) { return function (targetId) { query.TargetId = targetId; return apiClient.getJSON(apiClient.getUrl('Sync/Options', query)); }; } function setQualityFieldVisible(form, visible) { var fldQuality = form.querySelector('.fldQuality'); var selectQuality = form.querySelector('#selectQuality'); if (visible) { if (fldQuality) { fldQuality.classList.remove('hide'); } if (selectQuality) { //selectQuality.setAttribute('required', 'required'); // This is a hack due to what appears to be a edge bug but it shoudln't matter as the list always has selectable items selectQuality.removeAttribute('required'); } } else { if (fldQuality) { fldQuality.classList.add('hide'); } if (selectQuality) { selectQuality.removeAttribute('required'); } } } function onProfileChange(form, profileId) { var options = currentDialogOptions || {}; var profileOptions = options.ProfileOptions || []; if (!profileOptions.length) { return; } var option = profileOptions.filter(function (o) { return o.Id === profileId; })[0]; var qualityOptions = options.QualityOptions || []; if (option) { form.querySelector('.profileDescription').innerHTML = option.Description || ''; setQualityFieldVisible(form, qualityOptions.length > 0 && option.EnableQualityOptions && options.Options.indexOf('Quality') !== -1); } else { form.querySelector('.profileDescription').innerHTML = ''; setQualityFieldVisible(form, qualityOptions.length > 0 && options.Options.indexOf('Quality') !== -1); } } function onQualityChange(form, qualityId) { var options = currentDialogOptions || {}; var option = (options.QualityOptions || []).filter(function (o) { return o.Id === qualityId; })[0]; var qualityDescription = form.querySelector('.qualityDescription'); if (option) { qualityDescription.innerHTML = option.Description || ''; } else { qualityDescription.innerHTML = ''; } var fldBitrate = form.querySelector('.fldBitrate'); var txtBitrate = form.querySelector('#txtBitrate'); if (qualityId === 'custom') { if (fldBitrate) { fldBitrate.classList.remove('hide'); } if (txtBitrate) { txtBitrate.setAttribute('required', 'required'); } } else { if (fldBitrate) { fldBitrate.classList.add('hide'); } if (txtBitrate) { txtBitrate.removeAttribute('required'); } } } function renderTargetDialogOptions(form, options) { currentDialogOptions = options; var fldProfile = form.querySelector('.fldProfile'); var selectProfile = form.querySelector('#selectProfile'); if (options.ProfileOptions.length && options.Options.indexOf('Profile') !== -1) { if (fldProfile) { fldProfile.classList.remove('hide'); } if (selectProfile) { selectProfile.setAttribute('required', 'required'); } } else { if (fldProfile) { fldProfile.classList.add('hide'); } if (selectProfile) { selectProfile.removeAttribute('required'); } } setQualityFieldVisible(form, options.QualityOptions.length > 0); if (selectProfile) { selectProfile.innerHTML = options.ProfileOptions.map(function (o) { var selectedAttribute = o.IsDefault ? ' selected="selected"' : ''; return ''; }).join(''); selectProfile.dispatchEvent(new CustomEvent('change', { bubbles: true })); } var selectQuality = form.querySelector('#selectQuality'); if (selectQuality) { selectQuality.innerHTML = options.QualityOptions.map(function (o) { var selectedAttribute = o.IsDefault ? ' selected="selected"' : ''; return ''; }).join(''); var lastQuality = appSettings.get('sync-lastquality'); if (lastQuality && options.QualityOptions.filter(function (i) { return i.Id === lastQuality; }).length) { selectQuality.value = lastQuality; } selectQuality.dispatchEvent(new CustomEvent('change', { bubbles: true })); } } function loadQualityOptions(form, targetId, dialogOptionsFn) { return dialogOptionsFn(targetId).then(function (options) { return renderTargetDialogOptions(form, options); }); } return { showMenu: showSyncMenu, renderForm: renderForm, setJobValues: setJobValues }; });