diff --git a/dashboard-ui/autoorganizelog.html b/dashboard-ui/autoorganizelog.html index 419774564e..1c4f5872cd 100644 --- a/dashboard-ui/autoorganizelog.html +++ b/dashboard-ui/autoorganizelog.html @@ -1,11 +1,11 @@  + ${TitleAutoOrganize} -
- +
@@ -52,48 +52,6 @@
- -
diff --git a/dashboard-ui/bower_components/emby-apiclient/apiclient.js b/dashboard-ui/bower_components/emby-apiclient/apiclient.js index 4b112ab836..1cef9a3711 100644 --- a/dashboard-ui/bower_components/emby-apiclient/apiclient.js +++ b/dashboard-ui/bower_components/emby-apiclient/apiclient.js @@ -1127,6 +1127,39 @@ }); }; + self.performMovieOrganization = function (id, options) { + + var url = self.getUrl("Library/FileOrganizations/" + id + "/Movie/Organize", options || {}); + + return self.ajax({ + type: "POST", + url: url + }); + }; + + self.getSmartMatchInfos = function (options) { + + options = options || {}; + + var url = self.getUrl("Library/FileOrganizationSmartMatch", options); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + + self.deleteSmartMatchEntry = function (id, options) { + + var url = self.getUrl("Library/FileOrganizationSmartMatch/" + id + "/Delete", options || {}); + + return self.ajax({ + type: "POST", + url: url + }); + }; + self.getLiveTvSeriesTimer = function (id) { if (!id) { diff --git a/dashboard-ui/components/fileorganizer/fileorganizer.js b/dashboard-ui/components/fileorganizer/fileorganizer.js new file mode 100644 index 0000000000..12d5a60a1b --- /dev/null +++ b/dashboard-ui/components/fileorganizer/fileorganizer.js @@ -0,0 +1,501 @@ +define(['paperdialoghelper', 'paper-tabs', 'paper-item', 'paper-input', 'paper-fab', 'paper-item-body'], function (paperDialogHelper) { + + var currentItemId; + var currentFile; + var currentDeferred; + var hasChanges = false; + var reloadItems; + + function submitEpisodeForm(form) { + + Dashboard.showLoadingMsg(); + + var resultId = $('#hfResultId', form).val(); + + var targetFolder = $('#spanTargetFolder', form).text(); + + var elemString = $('#hfNewSeriesProviderIds', form).val(); + var newSeriesName = $('#hfNewSeriesName', form).val(); + var newSeriesYear = $('#hfNewSeriesYear', form).val(); + + var options = { + + SeriesId: $('#selectSeries', form).val(), + SeasonNumber: $('#txtSeason', form).val(), + EpisodeNumber: $('#txtEpisode', form).val(), + EndingEpisodeNumber: $('#txtEndingEpisode', form).val(), + RememberCorrection: $('#chkRememberCorrection', form).checked(), + NewSeriesProviderIds: elemString, + NewSeriesName: newSeriesName, + NewSeriesYear: newSeriesYear, + TargetFolder: targetFolder + }; + + ApiClient.performEpisodeOrganization(resultId, options).then(function () { + + Dashboard.hideLoadingMsg(); + + document.querySelector('.organizerDialog').close(); + + reloadItems(); + + }, onApiFailure); + } + + function submitMovieForm(form) { + + Dashboard.showLoadingMsg(); + + var resultId = $('#hfResultIdMovie', form).val(); + + var targetFolder = $('#selectMovieFolder', form).val(); + + var options = { + MovieName: $('#txtMovieName', form).val(), + MovieYear: $('#txtMovieYear', form).val(), + TargetFolder: targetFolder + }; + + ApiClient.performMovieOrganization(resultId, options).then(function () { + + Dashboard.hideLoadingMsg(); + + document.querySelector('.organizerDialog').close(); + + reloadItems(); + + }, onApiFailure); + } + + function searchForIdentificationResults(popup, itemtype) { + + var lookupInfo = { + Name: $('#txtMovieName', popup).val(), + Year: $('#txtMovieYear', popup).val(), + }; + + var url = ApiClient.getUrl("Items/RemoteSearch/Movie"); + + if (itemtype == 'tvshows') { + lookupInfo.Name = $('#txtNewSeriesName', popup).val(); + lookupInfo.Year = $('#txtNewSeriesYear', popup).val(); + url = ApiClient.getUrl("Items/RemoteSearch/Series"); + } + + if (!lookupInfo.Name) { + Dashboard.alert(Globalize.translate('MessagePleaseEnterNameOrId')); + return; + } + + lookupInfo = { + SearchInfo: lookupInfo, + IncludeDisabledProviders: true + }; + + Dashboard.showLoadingMsg(); + + ApiClient.ajax({ + type: "POST", + url: url, + data: JSON.stringify(lookupInfo), + dataType: "json", + contentType: "application/json" + + }).then(function (results) { + + Dashboard.hideLoadingMsg(); + showIdentificationSearchResults(popup, results, itemtype); + + $('#btnBack', popup).off('click').on('click', function () { + $('.identificationResultForm', popup).hide(); + $('.organizeMovieForm', popup).show(); + $('.createSeriesForm', popup).show(); + + if (itemtype == 'tvshows') { + $('#btnBack', popup).off('click').on('click', function () { + $('.createSeriesForm', popup).hide(); + $('.episodeCorrectionForm', popup).show(); + + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + }); + } + else { + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + } + }); + + }, onApiFailure); + } + + function showIdentificationSearchResults(popup, results, itemtype) { + + $('.organizeMovieForm', popup).hide(); + $('.createSeriesForm', popup).hide(); + $('.identificationResultForm', popup).show(); + + var html = ''; + + for (var i = 0, length = results.length; i < length; i++) { + + var result = results[i]; + + html += getIdentifyResultHtml(result, i); + } + + var elem = $('.identificationSearchResultList', popup).html(html).trigger('create'); + + $('.searchImage', elem).on('click', function () { + + var index = parseInt(this.getAttribute('data-index')); + + var currentResult = results[index]; + + $('.identificationResultForm', popup).hide(); + + var targetName = currentResult.Name; + if (currentResult.ProductionYear) { + targetName = targetName + ' (' + currentResult.ProductionYear + ')'; + } + + if (itemtype == 'tvshows') { + $('#txtNewSeriesName', popup).val(currentResult.Name); + $('#txtNewSeriesYear', popup).val(currentResult.ProductionYear); + $('#txtSelectedNewSeries', popup).val(targetName); + $('#txtSelectedNewSeries2', popup).val(targetName); + $('#hfNewSeriesName', popup).val(currentResult.Name); + $('#hfNewSeriesYear', popup).val(currentResult.ProductionYear); + var elems = currentResult.ProviderIds; + $('#hfNewSeriesProviderIds', popup).val(JSON.stringify(elems)); + $('.createSeriesForm', popup).show(); + $('#btnBack', popup).off('click').on('click', function () { + $('.createSeriesForm', popup).hide(); + $('.episodeCorrectionForm', popup).show(); + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + }); + } + else { + $('#txtMovieName', popup).val(currentResult.Name); + $('#txtMovieYear', popup).val(currentResult.ProductionYear); + $('#txtSelectedMovie', popup).val(targetName); + $('.organizeMovieForm', popup).show(); + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + } + }); + } + + function getSearchImageDisplayUrl(url, provider) { + return ApiClient.getUrl("Items/RemoteSearch/Image", { imageUrl: url, ProviderName: provider }); + } + + function getIdentifyResultHtml(result, index) { + + var html = ''; + var cssClass = "searchImageContainer remoteImageContainer"; + + cssClass += " searchPosterImageContainer"; + + html += '
'; + + if (result.ImageUrl) { + var displayUrl = getSearchImageDisplayUrl(result.ImageUrl, result.SearchProviderName); + + html += ''; + } else { + + html += ''; + } + html += ''; + + html += '
'; + html += result.Name; + html += '
'; + + html += '
'; + html += result.ProductionYear || ' '; + html += '
'; + + html += '
'; + return html; + } + + function onEpisodeCorrectionFormSubmit() { + submitEpisodeForm(this); + return false; + } + + function onOrganizeMovieFormFormSubmit() { + submitMovieForm(this); + return false; + } + + function showTab(popup, index) { + $('.organizeMovieForm', popup).show(); + $('.episodeCorrectionForm', popup).show(); + $('.identificationResultForm', popup).hide(); + $('.createSeriesForm', popup).hide(); + $('.popupTabPage', popup).addClass('hide')[index].classList.remove('hide'); + + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + } + + function initEditor(popup, item, allSeries, movieLocations, seriesLocations) { + + $('#divNewSeries', popup).hide(); + + showTab(popup, 0); + + $('.inputFile', popup).html(item.OriginalFileName); + + $('#txtSeason', popup).val(item.ExtractedSeasonNumber); + $('#txtEpisode', popup).val(item.ExtractedEpisodeNumber); + $('#txtEndingEpisode', popup).val(item.ExtractedEndingEpisodeNumber); + + $('#chkRememberCorrection', popup).val(false); + $('.extractedName', popup).html(item.ExtractedName); + + if (!item.ExtractedName || item.ExtractedName.length < 4) { + $('#divRememberCorrection', popup).hide(); + } + + $('#txtNewSeriesName', popup).val(item.ExtractedName); + $('#txtNewSeriesYear', popup).val(item.ExtractedYear); + + $('#hfResultId', popup).val(item.Id); + $('#hfResultIdMovie', popup).val(item.Id); + $('#hfNewSeriesProviderIds', popup).val(null); + $('#hfNewSeriesName', popup).val(null); + $('#hfNewSeriesYear', popup).val(null); + + $('#txtSelectedNewSeries', popup).val(null); + $('#txtSelectedNewSeries2', popup).val(null); + + $('#txtMovieName', popup).val(item.ExtractedMovieName); + $('#txtMovieYear', popup).val(item.ExtractedMovieYear); + $('#txtSelectedMovie', popup).val(null); + + var seriesHtml = allSeries.map(function (s) { + + return ''; + + }).join(''); + + seriesHtml = '' + seriesHtml; + + $('#selectSeries', popup).html(seriesHtml); + + + var movieFolderHtml = movieLocations.map(function (s) { + return ''; + }).join(''); + + if (movieLocations.length > 1) { + movieFolderHtml = '' + movieFolderHtml; + } + + var seriesFolderHtml = seriesLocations.map(function (s) { + return ''; + }).join(''); + + if (seriesLocations.length > 1) { + seriesFolderHtml = '' + seriesFolderHtml; + } + + $('#selectMovieFolder', popup).html(movieFolderHtml); + $('#selectSeriesFolder', popup).html(seriesFolderHtml); + + $('.episodeCorrectionForm').off('submit', onEpisodeCorrectionFormSubmit).on('submit', onEpisodeCorrectionFormSubmit); + $('.organizeMovieForm').off('submit', onOrganizeMovieFormFormSubmit).on('submit', onOrganizeMovieFormFormSubmit); + + $('#btnIdentifyMovie', popup).on('click', function () { + searchForIdentificationResults(popup, 'movies'); + }); + + $('#btnIdentifySeries', popup).on('click', function () { + searchForIdentificationResults(popup, 'tvshows'); + }); + + $('.txt-readonly', popup).keydown(function (e) { + e.preventDefault(); + }); + + $('#btnNewSeries', popup).on('click', function () { + $('.episodeCorrectionForm', popup).hide(); + $('.createSeriesForm', popup).show(); + + $('#btnBack', popup).off('click').on('click', function () { + + $('.createSeriesForm', popup).hide(); + $('.episodeCorrectionForm', popup).show(); + + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + }); + }); + + $('.createSeriesForm').off('submit').on('submit', function () { + var folder = $('#selectSeriesFolder', popup).val(); + $('#spanTargetFolder', popup).text(folder); + + $('#divSelectSeries', popup).hide(); + $('#divNewSeries', popup).show(); + $('.episodeCorrectionForm', popup).show(); + $('.createSeriesForm', popup).hide(); + + $('#btnBack', popup).off('click').on('click', function () { + popup.close(); + }); + + return false; + }); + } + + function showEditor(page, item, allSeries, movieLocations, seriesLocations, reloadDelegate) { + + reloadItems = reloadDelegate; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'components/fileorganizer/fileorganizer.template.html', true); + + xhr.onload = function (e) { + + var template = this.response; + + var dlg = createDialog(); + dlg.setAttribute('id', 'with-backdrop'); + + var html = ''; + //html += '
'; + html += '

'; + html += ''; + html += '
' + Globalize.translate('FileOrganizeManually') + '
'; + html += '

'; + //html += '
'; + + html += '
'; + html += 'TV Episode'; + html += 'Movie'; + html += '
' + + html += '
'; + html += Globalize.translateDocument(template); + html += '
'; + + dlg.innerHTML = html; + document.body.appendChild(dlg); + + initEditor(dlg, item, allSeries, movieLocations, seriesLocations); + + // Has to be assigned a z-index after the call to .open() + $(dlg).on('iron-overlay-closed', onDialogClosed); + + var tabs = dlg.querySelector('paper-tabs'); + + $(tabs).on('iron-select', function () { + + var self = this; + + var selected = this.selected; + showTab(dlg, selected); + + //setTimeout(function () { + // Events.trigger(self, 'tabchange'); + //}, 400); + + }); + + //.on('tabchange', function () { + // var selected = this.selected; + + // showTab(dlg, selected); + //}); + + dlg.classList.add('organizerDialog'); + + paperDialogHelper.open(dlg); + //PaperDialogHelper.openWithHash(dlg, 'fileorganizer'); + //dlg.open(); + + $('#btnBack', dlg).on('click', function () { + paperDialogHelper.close(dlg); + }); + }; + + xhr.send(); + } + + function createDialog() { + //var dlg = document.createElement('paper-dialog'); + + var dlg = paperDialogHelper.createDialog({ + removeOnClose: true + }); + + dlg.setAttribute('with-backdrop', 'with-backdrop'); + dlg.setAttribute('role', 'alertdialog'); + + // without this safari will scroll the background instead of the dialog contents + // but not needed here since this is already on top of an existing dialog + dlg.setAttribute('modal', 'modal'); + + dlg.setAttribute('noAutoFocus', 'noAutoFocus'); + dlg.entryAnimation = 'scale-up-animation'; + dlg.exitAnimation = 'fade-out-animation'; + dlg.classList.add('ui-body-a'); + dlg.classList.add('background-theme-a'); + dlg.classList.add('smoothScrollY'); + + return dlg; + } + + function onApiFailure(e) { + + Dashboard.hideLoadingMsg(); + + document.querySelector('.organizerDialog').close(); + + if (e.status == 0) { + Dashboard.alert({ + title: 'Auto-Organize', + message: 'The operation is going to take a little longer. The view will be updated on completion.' + }); + } + else { + Dashboard.alert({ + title: Globalize.translate('AutoOrganizeError'), + message: Globalize.translate('ErrorOrganizingFileWithErrorCode', e.getResponseHeader("X-Application-Error-Code")) + }); + } + } + + function onDialogClosed() { + + $(this).remove(); + Dashboard.hideLoadingMsg(); + currentDeferred.resolveWith(null, [hasChanges]); + } + + window.FileOrganizer = { + show: function (page, item, allSeries, movieLocations, seriesLocations, reloadDelegate) { + + var deferred = DeferredBuilder.Deferred(); + + currentDeferred = deferred; + hasChanges = false; + + showEditor(page, item, allSeries, movieLocations, seriesLocations, reloadDelegate); + + return deferred.promise(); + } + }; +}); diff --git a/dashboard-ui/components/fileorganizer/fileorganizer.template.html b/dashboard-ui/components/fileorganizer/fileorganizer.template.html new file mode 100644 index 0000000000..cedd5cd843 --- /dev/null +++ b/dashboard-ui/components/fileorganizer/fileorganizer.template.html @@ -0,0 +1,120 @@ +
+
+

+
+
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ + +
${LabelEndingEpisodeNumberHelp}
+
+
+ + + +
+

+ +

+ + + + +
+ +
+

+
+ + +
+
+ + +
+
+ +
+
+ + + +
+
+ + +
+

+ +

+
+ +
+
+
+

+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+

+ +

+ +
+
+ +
+
+ +
+
+
+
+
diff --git a/dashboard-ui/scripts/autoorganizelog.js b/dashboard-ui/scripts/autoorganizelog.js index bbc243b0c0..d31960e7ce 100644 --- a/dashboard-ui/scripts/autoorganizelog.js +++ b/dashboard-ui/scripts/autoorganizelog.js @@ -54,41 +54,57 @@ Dashboard.showLoadingMsg(); + var seriesItems; + ApiClient.getItems(null, { recursive: true, includeItemTypes: 'Series', sortBy: 'SortName' }).then(function (result) { - Dashboard.hideLoadingMsg(); - showEpisodeCorrectionPopup(page, item, result.Items); - }, onApiFailure); + seriesItems = result.Items; + + ApiClient.getVirtualFolders().then(function (result) { + + Dashboard.hideLoadingMsg(); + + var movieLocations = []; + var seriesLocations = []; + + for (var n = 0; n < result.length; n++) { + + var virtualFolder = result[n]; + + for (var i = 0, length = virtualFolder.Locations.length; i < length; i++) { + var location = { + value: virtualFolder.Locations[i], + display: virtualFolder.Name + ': ' + virtualFolder.Locations[i] + }; + + if (virtualFolder.CollectionType == 'movies') { + movieLocations.push(location); + } + if (virtualFolder.CollectionType == 'tvshows') { + seriesLocations.push(location); + } + } + } + + showEpisodeCorrectionPopup(page, item, seriesItems, movieLocations, seriesLocations); + }, onApiFailure); + + }, onApiFailure); } - function showEpisodeCorrectionPopup(page, item, allSeries) { + function showEpisodeCorrectionPopup(page, item, allSeries, movieLocations, seriesLocations) { - var popup = $('.episodeCorrectionPopup', page).popup("open"); + require(['components/fileorganizer/fileorganizer'], function () { - $('.inputFile', popup).html(item.OriginalFileName); - - $('#txtSeason', popup).val(item.ExtractedSeasonNumber); - $('#txtEpisode', popup).val(item.ExtractedEpisodeNumber); - $('#txtEndingEpisode', popup).val(item.ExtractedEndingEpisodeNumber); - - $('#chkRememberCorrection', popup).val(false); - - $('#hfResultId', popup).val(item.Id); - - var seriesHtml = allSeries.map(function (s) { - - return ''; - - }).join(''); - - seriesHtml = '' + seriesHtml; - - $('#selectSeries', popup).html(seriesHtml); + FileOrganizer.show(page, item, allSeries, movieLocations, seriesLocations, function () { + reloadItems(page); + }); + }); } function organizeFile(page, id) { @@ -131,38 +147,9 @@ }, onApiFailure); } - }); } - function submitEpisodeForm(form) { - - Dashboard.showLoadingMsg(); - - var page = $(form).parents('.page'); - - var resultId = $('#hfResultId', form).val(); - - var options = { - - SeriesId: $('#selectSeries', form).val(), - SeasonNumber: $('#txtSeason', form).val(), - EpisodeNumber: $('#txtEpisode', form).val(), - EndingEpisodeNumber: $('#txtEndingEpisode', form).val(), - RememberCorrection: $('#chkRememberCorrection', form).checked() - }; - - ApiClient.performEpisodeOrganization(resultId, options).then(function () { - - Dashboard.hideLoadingMsg(); - - $('.episodeCorrectionPopup', page).popup("close"); - - reloadItems(page); - - }, onApiFailure); - } - function reloadItems(page) { Dashboard.showLoadingMsg(); @@ -173,7 +160,6 @@ renderResults(page, result); Dashboard.hideLoadingMsg(); - }, onApiFailure); } @@ -229,9 +215,9 @@ var status = item.Status; if (status == 'SkippedExisting') { - html += '
'; + html += ''; html += item.OriginalFileName; - html += '
'; + html += ''; } else if (status == 'Failure') { html += ''; @@ -323,14 +309,9 @@ var page = $.mobile.activePage; - if (msg.MessageType == "ScheduledTaskEnded") { + if ((msg.MessageType == 'ScheduledTaskEnded' && msg.Data.Key == 'AutoOrganize') || msg.MessageType == 'AutoOrganizeUpdate') { - var result = msg.Data; - - if (result.Key == 'AutoOrganize') { - - reloadItems(page); - } + reloadItems(page); } } @@ -338,15 +319,21 @@ Dashboard.hideLoadingMsg(); - Dashboard.alert({ - title: Globalize.translate('AutoOrganizeError'), - message: Globalize.translate('ErrorOrganizingFileWithErrorCode', e.getResponseHeader("X-Application-Error-Code")) - }); - } + var page = $.mobile.activePage; + $('.episodeCorrectionPopup', page).popup("close"); - function onEpisodeCorrectionFormSubmit() { - submitEpisodeForm(this); - return false; + if (e.status == 0) { + Dashboard.alert({ + title: 'Auto-Organize', + message: 'The operation is going to take a little longer. The view will be updated on completion.' + }); + } + else { + Dashboard.alert({ + title: Globalize.translate('AutoOrganizeError'), + message: Globalize.translate('ErrorOrganizingFileWithErrorCode', e.getResponseHeader("X-Application-Error-Code")) + }); + } } $(document).on('pageinit', "#libraryFileOrganizerLogPage", function () { @@ -361,8 +348,6 @@ }); - $('.episodeCorrectionForm').off('submit', onEpisodeCorrectionFormSubmit).on('submit', onEpisodeCorrectionFormSubmit); - }).on('pageshow', "#libraryFileOrganizerLogPage", function () { var page = this; @@ -377,7 +362,7 @@ taskKey: 'AutoOrganize' }); - $(ApiClient).on("websocketmessage.autoorganizelog", onWebSocketMessage); + Events.on(ApiClient, "websocketmessage", onWebSocketMessage); }).on('pagebeforehide', "#libraryFileOrganizerLogPage", function () { @@ -390,7 +375,7 @@ mode: 'off' }); - $(ApiClient).off("websocketmessage.autoorganizelog", onWebSocketMessage); + Events.off(ApiClient, "websocketmessage", onWebSocketMessage); }); })(jQuery, document, window); diff --git a/dashboard-ui/strings/javascript/en-US.json b/dashboard-ui/strings/javascript/en-US.json index cb8282476c..b51ab422e1 100644 --- a/dashboard-ui/strings/javascript/en-US.json +++ b/dashboard-ui/strings/javascript/en-US.json @@ -263,6 +263,7 @@ "HeaderSelectWatchFolderHelp": "Browse or enter the path to your watch folder. The folder must be writeable.", "OrganizePatternResult": "Result: {0}", "AutoOrganizeError": "Error Organizing File", + "FileOrganizeManually": "Organize File", "ErrorOrganizingFileWithErrorCode": "There was an error organizing the file. Error code: {0}.", "HeaderRestart": "Restart", "HeaderShutdown": "Shutdown", diff --git a/dashboard-ui/strings/javascript/javascript.json b/dashboard-ui/strings/javascript/javascript.json index f2eb4559fb..af6faec3ce 100644 --- a/dashboard-ui/strings/javascript/javascript.json +++ b/dashboard-ui/strings/javascript/javascript.json @@ -264,6 +264,7 @@ "HeaderSelectWatchFolderHelp": "Browse or enter the path to your watch folder. The folder must be writeable.", "OrganizePatternResult": "Result: {0}", "AutoOrganizeError": "Error Organizing File", + "FileOrganizeManually": "Organize File", "ErrorOrganizingFileWithErrorCode": "There was an error organizing the file. Error code: {0}.", "HeaderRestart": "Restart", "HeaderShutdown": "Shutdown",