diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index e9bcc0bfca..4513d1d9d9 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -347,6 +347,34 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', } } + function onOpenUploadMenu(e) { + + var context = dom.parentWithClass(e.target, 'subtitleEditorDialog'); + var selectLanguage = context.querySelector('#selectLanguage'); + var apiClient = connectionManager.getApiClient(currentItem.ServerId); + + require(['subtitleUploader'], function (subtitleUploader) { + + subtitleUploader.show({ + + languages: { + list: selectLanguage.innerHTML, + value: selectLanguage.value + }, + itemId: currentItem.Id, + serverId: currentItem.ServerId + + }).then(function (hasChanged) { + + if (hasChanged) { + hasChanges = true; + reload(context, apiClient, currentItem.Id); + } + }); + }); + + } + function onSearchSubmit(e) { var form = this; @@ -454,6 +482,8 @@ define(['dialogHelper', 'require', 'layoutManager', 'globalize', 'userSettings', dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit); + dlg.querySelector('.btnOpenUploadMenu').addEventListener('click', onOpenUploadMenu); + var btnSubmit = dlg.querySelector('.btnSubmit'); if (layoutManager.tv) { diff --git a/src/components/subtitleeditor/subtitleeditor.template.html b/src/components/subtitleeditor/subtitleeditor.template.html index 7471972f59..b01942c995 100644 --- a/src/components/subtitleeditor/subtitleeditor.template.html +++ b/src/components/subtitleeditor/subtitleeditor.template.html @@ -18,6 +18,7 @@ + diff --git a/src/components/subtitleuploader/style.css b/src/components/subtitleuploader/style.css new file mode 100644 index 0000000000..c9d5eb980d --- /dev/null +++ b/src/components/subtitleuploader/style.css @@ -0,0 +1,11 @@ +.subtitleEditor-dropZone { + border: 0.2em dashed currentcolor; + border-radius: 0.25em; + + text-align: center; + position: relative; + height: 12em; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/components/subtitleuploader/subtitleuploader.js b/src/components/subtitleuploader/subtitleuploader.js new file mode 100644 index 0000000000..bf3a8ca4b3 --- /dev/null +++ b/src/components/subtitleuploader/subtitleuploader.js @@ -0,0 +1,194 @@ +define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', 'layoutManager', 'globalize', 'require', 'emby-button', 'emby-select', 'formDialogStyle', 'css!./style'], function (dialogHelper, connectionManager, dom, loading, scrollHelper, layoutManager, globalize, require) { + 'use strict'; + + var currentItemId; + var currentServerId; + var currentFile; + var hasChanges = false; + + function onFileReaderError(evt) { + + loading.hide(); + + switch (evt.target.error.code) { + case evt.target.error.NOT_FOUND_ERR: + require(['toast'], function (toast) { + toast(globalize.translate('MessageFileReadError')); + }); + break; + case evt.target.error.ABORT_ERR: + break; // noop + default: + require(['toast'], function (toast) { + toast(globalize.translate('MessageFileReadError')); + }); + break; + } + } + + function isValidSubtitleFile(file) { + return file && ['.sub', '.srt', '.vtt', '.ass', '.ssa'] + .some(function(ext) { + return file.name.endsWith(ext); + }); + } + + function setFiles(page, files) { + + var file = files[0]; + + if (!isValidSubtitleFile(file)) { + page.querySelector('#subtitleOutput').innerHTML = ''; + page.querySelector('#fldUpload').classList.add('hide'); + page.querySelector('#labelDropSubtitle').classList.remove('hide'); + currentFile = null; + return; + } + + currentFile = file; + + var reader = new FileReader(); + + reader.onerror = onFileReaderError; + reader.onloadstart = function () { + page.querySelector('#fldUpload').classList.add('hide'); + }; + reader.onabort = function () { + loading.hide(); + console.debug('File read cancelled'); + }; + + // Closure to capture the file information. + reader.onload = (function (theFile) { + return function () { + + // Render thumbnail. + var html = 'subtitles' + escape(theFile.name) + ''; + + page.querySelector('#subtitleOutput').innerHTML = html; + page.querySelector('#fldUpload').classList.remove('hide'); + page.querySelector('#labelDropSubtitle').classList.add('hide'); + + }; + })(file); + + // Read in the subtitle file as a data URL. + reader.readAsDataURL(file); + } + + function onSubmit(e) { + + var file = currentFile; + + if (!isValidSubtitleFile(file)) { + require(['toast'], function (toast) { + toast(globalize.translate('MessageSubtitleFileTypeAllowed')); + }); + e.preventDefault(); + return false; + } + + loading.show(); + + var dlg = dom.parentWithClass(this, 'dialog'); + var language = dlg.querySelector('#selectLanguage').value; + var isForced = dlg.querySelector('#chkIsForced').checked; + + connectionManager.getApiClient(currentServerId).uploadItemSubtitle(currentItemId, language, isForced, file).then(function () { + + dlg.querySelector('#uploadSubtitle').value = ''; + loading.hide(); + hasChanges = true; + dialogHelper.close(dlg); + }); + + e.preventDefault(); + return false; + } + + function initEditor(page) { + + page.querySelector('.uploadSubtitleForm').addEventListener('submit', onSubmit); + + page.querySelector('#uploadSubtitle').addEventListener('change', function () { + setFiles(page, this.files); + }); + + page.querySelector('.btnBrowse').addEventListener('click', function () { + page.querySelector('#uploadSubtitle').click(); + }); + } + + function showEditor(options, resolve, reject) { + + options = options || {}; + + require(['text!./subtitleuploader.template.html'], function (template) { + + currentItemId = options.itemId; + currentServerId = options.serverId; + + var dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + + var dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('formDialog'); + dlg.classList.add('subtitleUploaderDialog'); + + dlg.innerHTML = globalize.translateDocument(template, 'core'); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg, false); + } + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', function () { + + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg, false); + } + + loading.hide(); + resolve(hasChanges); + }); + + dialogHelper.open(dlg); + + initEditor(dlg); + + var selectLanguage = dlg.querySelector('#selectLanguage'); + + if (options.languages) { + + selectLanguage.innerHTML = options.languages.list || null; + selectLanguage.value = options.languages.value || null; + } + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + + dialogHelper.close(dlg); + }); + }); + } + + return { + show: function (options) { + + return new Promise(function (resolve, reject) { + + hasChanges = false; + + showEditor(options, resolve, reject); + }); + } + }; +}); diff --git a/src/components/subtitleuploader/subtitleuploader.template.html b/src/components/subtitleuploader/subtitleuploader.template.html new file mode 100644 index 0000000000..ef09775cdd --- /dev/null +++ b/src/components/subtitleuploader/subtitleuploader.template.html @@ -0,0 +1,45 @@ +
+ +

+ ${HeaderUploadSubtitle} +

+
+ +
+
+ +
+ +
+

${HeaderAddUpdateSubtitle}

+ + +
+
+
+
${LabelDropSubtitleHere}
+ + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
diff --git a/src/scripts/site.js b/src/scripts/site.js index c00169d224..d3a47031bb 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -799,6 +799,7 @@ var AppInfo = {}; define('recordingButton', [componentsPath + '/recordingcreator/recordingbutton'], returnFirstDependency); define('recordingHelper', [componentsPath + '/recordingcreator/recordinghelper'], returnFirstDependency); define('subtitleEditor', [componentsPath + '/subtitleeditor/subtitleeditor'], returnFirstDependency); + define('subtitleUploader', [componentsPath + '/subtitleuploader/subtitleuploader'], returnFirstDependency); define('subtitleSync', [componentsPath + '/subtitlesync/subtitlesync'], returnFirstDependency); define('itemIdentifier', [componentsPath + '/itemidentifier/itemidentifier'], returnFirstDependency); define('itemMediaInfo', [componentsPath + '/itemMediaInfo/itemMediaInfo'], returnFirstDependency);