diff --git a/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs b/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs new file mode 100644 index 000000000..ffe9a37e0 --- /dev/null +++ b/src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using NzbDrone.Api.REST; +using NzbDrone.Core.MediaFiles; + +namespace NzbDrone.Api.Episodes +{ + public class RenameEpisodeModule : NzbDroneRestModule + { + private readonly IRenameEpisodeFileService _renameEpisodeFileService; + + public RenameEpisodeModule(IRenameEpisodeFileService renameEpisodeFileService) + : base("rename") + { + _renameEpisodeFileService = renameEpisodeFileService; + + GetResourceAll = GetEpisodes; + } + + private List GetEpisodes() + { + int seriesId; + + if (Request.Query.SeriesId.HasValue) + { + seriesId = (int)Request.Query.SeriesId; + } + + else + { + throw new BadRequestException("seriesId is missing"); + } + + if (Request.Query.SeasonNumber.HasValue) + { + var seasonNumber = (int)Request.Query.SeasonNumber; + return ToListResource(() => _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber)); + } + + return ToListResource(() => _renameEpisodeFileService.GetRenamePreviews(seriesId)); + } + } +} diff --git a/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs b/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs new file mode 100644 index 000000000..9598de0ce --- /dev/null +++ b/src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Api.REST; + +namespace NzbDrone.Api.Episodes +{ + public class RenameEpisodeResource : RestResource + { + public Int32 SeriesId { get; set; } + public Int32 SeasonNumber { get; set; } + public List EpisodeNumbers { get; set; } + public Int32 EpisodeFileId { get; set; } + public String ExistingPath { get; set; } + public String NewPath { get; set; } + } +} diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index f5bf73cf7..939dcd9ef 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -95,6 +95,8 @@ + + diff --git a/src/NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs index 662727575..5757641dc 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs @@ -38,22 +38,14 @@ namespace NzbDrone.Core.Test.MediaFiles private void GivenNoEpisodeFiles() { Mocker.GetMock() - .Setup(s => s.GetFilesBySeries(_series.Id)) - .Returns(new List()); - - Mocker.GetMock() - .Setup(s => s.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber)) + .Setup(s => s.Get(It.IsAny>())) .Returns(new List()); } private void GivenEpisodeFiles() { Mocker.GetMock() - .Setup(s => s.GetFilesBySeries(_series.Id)) - .Returns(_episodeFiles); - - Mocker.GetMock() - .Setup(s => s.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber)) + .Setup(s => s.Get(It.IsAny>())) .Returns(_episodeFiles); } @@ -68,7 +60,7 @@ namespace NzbDrone.Core.Test.MediaFiles { GivenNoEpisodeFiles(); - Subject.Execute(new RenameSeriesCommand(_series.Id)); + Subject.Execute(new RenameFilesCommand(_series.Id, new List{1})); Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Never()); @@ -83,7 +75,7 @@ namespace NzbDrone.Core.Test.MediaFiles .Setup(s => s.MoveEpisodeFile(It.IsAny(), It.IsAny())) .Throws(new SameFilenameException("Same file name", "Filename")); - Subject.Execute(new RenameSeriesCommand(_series.Id)); + Subject.Execute(new RenameFilesCommand(_series.Id, new List { 1 })); Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Never()); @@ -95,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles GivenEpisodeFiles(); GivenMovedFiles(); - Subject.Execute(new RenameSeriesCommand(_series.Id)); + Subject.Execute(new RenameFilesCommand(_series.Id, new List { 1 })); Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Once()); @@ -107,40 +99,24 @@ namespace NzbDrone.Core.Test.MediaFiles GivenEpisodeFiles(); GivenMovedFiles(); - Subject.Execute(new RenameSeriesCommand(_series.Id)); + Subject.Execute(new RenameFilesCommand(_series.Id, new List { 1 })); Mocker.GetMock() .Verify(v => v.Update(It.IsAny()), Times.Exactly(2)); } [Test] - public void rename_season_should_get_episodefiles_for_season() + public void should_get_episodefiles_by_ids_only() { GivenEpisodeFiles(); GivenMovedFiles(); - Subject.Execute(new RenameSeasonCommand(_series.Id, _episodeFiles.First().SeasonNumber)); + var files = new List { 1 }; + + Subject.Execute(new RenameFilesCommand(_series.Id, files)); Mocker.GetMock() - .Verify(v => v.GetFilesBySeries(_series.Id), Times.Never()); - - Mocker.GetMock() - .Verify(v => v.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber), Times.Once()); - } - - [Test] - public void rename_series_should_get_episodefiles_for_series() - { - GivenEpisodeFiles(); - GivenMovedFiles(); - - Subject.Execute(new RenameSeriesCommand(_series.Id)); - - Mocker.GetMock() - .Verify(v => v.GetFilesBySeries(_series.Id), Times.Once()); - - Mocker.GetMock() - .Verify(v => v.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber), Times.Never()); + .Verify(v => v.Get(files), Times.Once()); } } } diff --git a/src/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs b/src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs similarity index 58% rename from src/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs rename to src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs index eb7e578d4..2003a3e9f 100644 --- a/src/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs +++ b/src/NzbDrone.Core/MediaFiles/Commands/RenameFilesCommand.cs @@ -1,10 +1,12 @@ +using System.Collections.Generic; using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands { - public class RenameSeriesCommand : Command + public class RenameFilesCommand : Command { public int SeriesId { get; set; } + public List Files { get; set; } public override bool SendUpdatesToClient { @@ -14,13 +16,14 @@ namespace NzbDrone.Core.MediaFiles.Commands } } - public RenameSeriesCommand() + public RenameFilesCommand() { } - public RenameSeriesCommand(int seriesId) + public RenameFilesCommand(int seriesId, List files) { SeriesId = seriesId; + Files = files; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs b/src/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs deleted file mode 100644 index b9a917ca3..000000000 --- a/src/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs +++ /dev/null @@ -1,24 +0,0 @@ -using NzbDrone.Core.Messaging.Commands; - -namespace NzbDrone.Core.MediaFiles.Commands -{ - public class RenameSeasonCommand : Command - { - public int SeriesId { get; set; } - public int SeasonNumber { get; set; } - - public override bool SendUpdatesToClient - { - get - { - return true; - } - } - - public RenameSeasonCommand(int seriesId, int seasonNumber) - { - SeriesId = seriesId; - SeasonNumber = seasonNumber; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs index 44f67174c..a49d667df 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using NLog; @@ -17,6 +18,7 @@ namespace NzbDrone.Core.MediaFiles List GetFilesBySeason(int seriesId, int seasonNumber); List FilterExistingFiles(List files, int seriesId); EpisodeFile Get(int id); + List Get(IEnumerable ids); } public class MediaFileService : IMediaFileService, IHandleAsync @@ -75,6 +77,11 @@ namespace NzbDrone.Core.MediaFiles return _mediaFileRepository.Get(id); } + public List Get(IEnumerable ids) + { + return _mediaFileRepository.Get(ids).ToList(); + } + public void HandleAsync(SeriesDeletedEvent message) { var files = GetFilesBySeries(message.Series.Id); diff --git a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFilePreview.cs b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFilePreview.cs new file mode 100644 index 000000000..a3c68fd5b --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFilePreview.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.MediaFiles +{ + public class RenameEpisodeFilePreview + { + public Int32 SeriesId { get; set; } + public Int32 SeasonNumber { get; set; } + public List EpisodeNumbers { get; set; } + public Int32 EpisodeFileId { get; set; } + public String ExistingPath { get; set; } + public String NewPath { get; set; } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs index 8cbfc51a2..7441dd3dd 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameEpisodeFileService.cs @@ -1,37 +1,100 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using NLog; +using NzbDrone.Common; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Organizer; using NzbDrone.Core.Tv; namespace NzbDrone.Core.MediaFiles { - public class RenameEpisodeFileService : IExecute, IExecute + public interface IRenameEpisodeFileService + { + List GetRenamePreviews(int seriesId); + List GetRenamePreviews(int seriesId, int seasonNumber); + } + + public class RenameEpisodeFileService : IRenameEpisodeFileService, + IExecute { private readonly ISeriesService _seriesService; private readonly IMediaFileService _mediaFileService; private readonly IMoveEpisodeFiles _episodeFileMover; private readonly IEventAggregator _eventAggregator; + private readonly IEpisodeService _episodeService; + private readonly IBuildFileNames _filenameBuilder; private readonly Logger _logger; public RenameEpisodeFileService(ISeriesService seriesService, IMediaFileService mediaFileService, IMoveEpisodeFiles episodeFileMover, IEventAggregator eventAggregator, + IEpisodeService episodeService, + IBuildFileNames filenameBuilder, Logger logger) { _seriesService = seriesService; _mediaFileService = mediaFileService; _episodeFileMover = episodeFileMover; _eventAggregator = eventAggregator; + _episodeService = episodeService; + _filenameBuilder = filenameBuilder; _logger = logger; } + public List GetRenamePreviews(int seriesId) + { + var series = _seriesService.GetSeries(seriesId); + var episodes = _episodeService.GetEpisodeBySeries(seriesId); + var files = _mediaFileService.GetFilesBySeries(seriesId); + + return GetPreviews(series, episodes, files).ToList(); + } + + public List GetRenamePreviews(int seriesId, int seasonNumber) + { + var series = _seriesService.GetSeries(seriesId); + var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber); + var files = _mediaFileService.GetFilesBySeason(seriesId, seasonNumber); + + return GetPreviews(series, episodes, files).ToList(); + } + + private IEnumerable GetPreviews(Series series, List episodes, List files) + { + foreach (var file in files) + { + var episodesInFile = episodes.Where(e => e.EpisodeFileId == file.Id).ToList(); + var seasonNumber = episodesInFile.First().SeasonNumber; + var newName = _filenameBuilder.BuildFilename(episodesInFile, series, file); + var newPath = _filenameBuilder.BuildFilePath(series, seasonNumber, newName, Path.GetExtension(file.Path)); + + if (!file.Path.PathEquals(newPath)) + { + yield return new RenameEpisodeFilePreview + { + SeriesId = series.Id, + SeasonNumber = seasonNumber, + EpisodeNumbers = episodesInFile.Select(e => e.EpisodeNumber).ToList(), + EpisodeFileId = file.Id, + ExistingPath = GetRelativePath(series.Path, file.Path), + NewPath = GetRelativePath(series.Path, newPath) + }; + } + } + } + + private string GetRelativePath(string seriesPath, string path) + { + return path.Substring(seriesPath.Length + 1); + } + private void RenameFiles(List episodeFiles, Series series) { var renamed = new List(); @@ -64,24 +127,14 @@ namespace NzbDrone.Core.MediaFiles } } - public void Execute(RenameSeasonCommand message) + public void Execute(RenameFilesCommand message) { var series = _seriesService.GetSeries(message.SeriesId); - var episodeFiles = _mediaFileService.GetFilesBySeason(message.SeriesId, message.SeasonNumber); - - _logger.ProgressInfo("Renaming {0} files for {1} season {2}", episodeFiles.Count, series.Title, message.SeasonNumber); - RenameFiles(episodeFiles, series); - _logger.ProgressInfo("Episode Fies renamed for {0} season {1}", series.Title, message.SeasonNumber); - } - - public void Execute(RenameSeriesCommand message) - { - var series = _seriesService.GetSeries(message.SeriesId); - var episodeFiles = _mediaFileService.GetFilesBySeries(message.SeriesId); + var episodeFiles = _mediaFileService.Get(message.Files); _logger.ProgressInfo("Renaming {0} files for {1}", episodeFiles.Count, series.Title); RenameFiles(episodeFiles, series); - _logger.ProgressInfo("Episode Fies renamed for {0}", series.Title); + _logger.ProgressInfo("Selected Episode Files renamed for {0}", series.Title); } } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 142c9d9e8..ae95fbd39 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -278,9 +278,11 @@ + + @@ -343,8 +345,6 @@ - - diff --git a/src/ServiceHelpers/ServiceInstall/obj/x86/Debug/ServiceInstall.csproj.FileListAbsolute.txt b/src/ServiceHelpers/ServiceInstall/obj/x86/Debug/ServiceInstall.csproj.FileListAbsolute.txt new file mode 100644 index 000000000..f07066f35 --- /dev/null +++ b/src/ServiceHelpers/ServiceInstall/obj/x86/Debug/ServiceInstall.csproj.FileListAbsolute.txt @@ -0,0 +1,6 @@ +C:\Dropbox\Dev\NzbDrone\_output\ServiceInstall.exe.config +C:\Dropbox\Dev\NzbDrone\_output\ServiceInstall.exe +C:\Dropbox\Dev\NzbDrone\_output\ServiceInstall.pdb +C:\Dropbox\Dev\NzbDrone\src\ServiceHelpers\ServiceInstall\obj\x86\Debug\ServiceInstall.csprojResolveAssemblyReference.cache +C:\Dropbox\Dev\NzbDrone\src\ServiceHelpers\ServiceInstall\obj\x86\Debug\ServiceInstall.exe +C:\Dropbox\Dev\NzbDrone\src\ServiceHelpers\ServiceInstall\obj\x86\Debug\ServiceInstall.pdb diff --git a/src/ServiceHelpers/ServiceInstall/obj/x86/Debug/ServiceInstall.exe b/src/ServiceHelpers/ServiceInstall/obj/x86/Debug/ServiceInstall.exe new file mode 100644 index 000000000..1c443d08e Binary files /dev/null and b/src/ServiceHelpers/ServiceInstall/obj/x86/Debug/ServiceInstall.exe differ diff --git a/src/ServiceHelpers/ServiceUninstall/obj/x86/Debug/ServiceUninstall.csproj.FileListAbsolute.txt b/src/ServiceHelpers/ServiceUninstall/obj/x86/Debug/ServiceUninstall.csproj.FileListAbsolute.txt new file mode 100644 index 000000000..a2704133c --- /dev/null +++ b/src/ServiceHelpers/ServiceUninstall/obj/x86/Debug/ServiceUninstall.csproj.FileListAbsolute.txt @@ -0,0 +1,5 @@ +C:\Dropbox\Dev\NzbDrone\_output\ServiceUninstall.exe.config +C:\Dropbox\Dev\NzbDrone\_output\ServiceUninstall.exe +C:\Dropbox\Dev\NzbDrone\_output\ServiceUninstall.pdb +C:\Dropbox\Dev\NzbDrone\src\ServiceHelpers\ServiceUninstall\obj\x86\Debug\ServiceUninstall.exe +C:\Dropbox\Dev\NzbDrone\src\ServiceHelpers\ServiceUninstall\obj\x86\Debug\ServiceUninstall.pdb diff --git a/src/ServiceHelpers/ServiceUninstall/obj/x86/Debug/ServiceUninstall.exe b/src/ServiceHelpers/ServiceUninstall/obj/x86/Debug/ServiceUninstall.exe new file mode 100644 index 000000000..388d95519 Binary files /dev/null and b/src/ServiceHelpers/ServiceUninstall/obj/x86/Debug/ServiceUninstall.exe differ diff --git a/src/UI/Content/bootstrap.toggle-switch.css b/src/UI/Content/bootstrap.toggle-switch.css index 5604b2415..ce924d3ce 100644 --- a/src/UI/Content/bootstrap.toggle-switch.css +++ b/src/UI/Content/bootstrap.toggle-switch.css @@ -49,7 +49,6 @@ https://github.com/ghinda/css-toggle-switch left: -100px; width: 100%; margin: 0; - padding-right: 100px; text-align: left; } diff --git a/src/UI/Content/theme.less b/src/UI/Content/theme.less index 7c1decd1f..f3a77ecbe 100644 --- a/src/UI/Content/theme.less +++ b/src/UI/Content/theme.less @@ -183,4 +183,18 @@ footer { #errors{ display : none; +} + +.rename-preview-item { + margin-bottom: 5px; + padding: 5px; + border-bottom: 1px solid #e5e5e5; + + .checkbox { + width: 80px; + margin-left: 0px; + display: inline-block; + padding-top: 0px; + margin-bottom: 0px; + } } \ No newline at end of file diff --git a/src/UI/Rename/RenamePreviewCollection.js b/src/UI/Rename/RenamePreviewCollection.js new file mode 100644 index 000000000..0f55f9634 --- /dev/null +++ b/src/UI/Rename/RenamePreviewCollection.js @@ -0,0 +1,38 @@ +'use strict'; +define( + [ + 'backbone', + 'Rename/RenamePreviewModel' + ], function (Backbone, RenamePreviewModel) { + return Backbone.Collection.extend({ + url : window.NzbDrone.ApiRoot + '/rename', + model: RenamePreviewModel, + + originalFetch: Backbone.Collection.prototype.fetch, + + initialize: function (options) { + if (!options.seriesId) { + throw 'seriesId is required'; + } + + this.seriesId = options.seriesId; + this.seasonNumber = options.seasonNumber; + }, + + fetch: function (options) { + if (!this.seriesId) { + throw 'seriesId is required'; + } + + options = options || {}; + options.data = {}; + options.data.seriesId = this.seriesId; + + if (this.seasonNumber) { + options.data.seasonNumber = this.seasonNumber; + } + + return this.originalFetch.call(this, options); + } + }); + }); diff --git a/src/UI/Rename/RenamePreviewCollectionView.js b/src/UI/Rename/RenamePreviewCollectionView.js new file mode 100644 index 000000000..7906b81e0 --- /dev/null +++ b/src/UI/Rename/RenamePreviewCollectionView.js @@ -0,0 +1,11 @@ +'use strict'; +define( + [ + 'marionette', + 'Rename/RenamePreviewItemView' + ], function (Marionette, RenamePreviewItemView) { + return Marionette.CollectionView.extend({ + + itemView : RenamePreviewItemView + }); + }); diff --git a/src/UI/Rename/RenamePreviewEmptyCollectionView.js b/src/UI/Rename/RenamePreviewEmptyCollectionView.js new file mode 100644 index 000000000..8101ddc05 --- /dev/null +++ b/src/UI/Rename/RenamePreviewEmptyCollectionView.js @@ -0,0 +1,10 @@ +'use strict'; +define( + [ + 'vent', + 'marionette' + ], function (vent, Marionette) { + return Marionette.ItemView.extend({ + template: 'Rename/RenamePreviewEmptyCollectionViewTemplate' + }); + }); diff --git a/src/UI/Rename/RenamePreviewEmptyCollectionViewTemplate.html b/src/UI/Rename/RenamePreviewEmptyCollectionViewTemplate.html new file mode 100644 index 000000000..1975f8a6a --- /dev/null +++ b/src/UI/Rename/RenamePreviewEmptyCollectionViewTemplate.html @@ -0,0 +1,3 @@ +
+ Success! My work is done, no files to rename. +
\ No newline at end of file diff --git a/src/UI/Rename/RenamePreviewItemView.js b/src/UI/Rename/RenamePreviewItemView.js new file mode 100644 index 000000000..71994e208 --- /dev/null +++ b/src/UI/Rename/RenamePreviewItemView.js @@ -0,0 +1,13 @@ +'use strict'; +define( + [ + 'vent', + 'marionette', + 'Mixins/AsModelBoundView' + ], function (vent, Marionette, AsModelBoundView) { + var view = Marionette.ItemView.extend({ + template: 'Rename/RenamePreviewItemViewTemplate' + }); + + return AsModelBoundView.apply(view); + }); diff --git a/src/UI/Rename/RenamePreviewItemViewTemplate.html b/src/UI/Rename/RenamePreviewItemViewTemplate.html new file mode 100644 index 000000000..cb6dc2288 --- /dev/null +++ b/src/UI/Rename/RenamePreviewItemViewTemplate.html @@ -0,0 +1,25 @@ +
+
+
+
+
\ No newline at end of file diff --git a/src/UI/Rename/RenamePreviewLayout.js b/src/UI/Rename/RenamePreviewLayout.js new file mode 100644 index 000000000..469f75a7d --- /dev/null +++ b/src/UI/Rename/RenamePreviewLayout.js @@ -0,0 +1,93 @@ +'use strict'; +define( + [ + 'underscore', + 'vent', + 'marionette', + 'Rename/RenamePreviewCollection', + 'Rename/RenamePreviewCollectionView', + 'Rename/RenamePreviewEmptyCollectionView', + 'Shared/LoadingView', + 'Commands/CommandController' + ], function (_, vent, Marionette, RenamePreviewCollection, RenamePreviewCollectionView, EmptyCollectionView, LoadingView, CommandController) { + + return Marionette.Layout.extend({ + template: 'Rename/RenamePreviewLayoutTemplate', + + regions: { + renamePreviews : '#rename-previews' + }, + + ui: { + pathInfo: '.x-path-info' + }, + + events: { + 'click .x-organize': '_organizeFiles' + }, + + initialize: function (options) { + this.model = options.series; + this.seasonNumber = options.seasonNumber; + + var viewOptions = {}; + viewOptions.seriesId = this.model.id; + viewOptions.seasonNumber = this.seasonNumber; + + this.collection = new RenamePreviewCollection(viewOptions); + this.listenTo(this.collection, 'sync', this._showPreviews); + + this.collection.fetch(); + }, + + onRender: function() { + this.renamePreviews.show(new LoadingView()); + }, + + _showPreviews: function () { + if (this.collection.length === 0) { + this.ui.pathInfo.hide(); + this.renamePreviews.show(new EmptyCollectionView()); + return; + } + + this.collection.invoke('set', { rename: true }); + this.renamePreviews.show(new RenamePreviewCollectionView({ collection: this.collection })); + }, + + _organizeFiles: function () { + if (this.collection.length === 0) { + vent.trigger(vent.Commands.CloseModalCommand); + } + + var files = _.map(this.collection.where({ rename: true }), function (model) { + return model.get('episodeFileId'); + }); + + if (files.length === 0) { + vent.trigger(vent.Commands.CloseModalCommand); + return; + } + + if (this.seasonNumber) { + CommandController.Execute('renameFiles', { + name : 'renameFiles', + seriesId : this.model.id, + seasonNumber: this.seasonNumber, + files : files + }); + } + + else { + CommandController.Execute('renameFiles', { + name : 'renameFiles', + seriesId : this.model.id, + seasonNumber: -1, + files : files + }); + } + + vent.trigger(vent.Commands.CloseModalCommand); + } + }); + }); diff --git a/src/UI/Rename/RenamePreviewLayoutTemplate.html b/src/UI/Rename/RenamePreviewLayoutTemplate.html new file mode 100644 index 000000000..5151e6257 --- /dev/null +++ b/src/UI/Rename/RenamePreviewLayoutTemplate.html @@ -0,0 +1,19 @@ +
+ + + +
+ diff --git a/src/UI/Rename/RenamePreviewModel.js b/src/UI/Rename/RenamePreviewModel.js new file mode 100644 index 000000000..19de2d6e1 --- /dev/null +++ b/src/UI/Rename/RenamePreviewModel.js @@ -0,0 +1,9 @@ +'use strict'; +define( + [ + 'backbone' + ], function (Backbone) { + return Backbone.Model.extend({ + }); + }); + diff --git a/src/UI/Series/Details/SeasonLayout.js b/src/UI/Series/Details/SeasonLayout.js index 8015ce0e1..0492f1768 100644 --- a/src/UI/Series/Details/SeasonLayout.js +++ b/src/UI/Series/Details/SeasonLayout.js @@ -12,7 +12,7 @@ define( 'Commands/CommandController', 'moment', 'underscore' - ], function (vent, Marionette, Backgrid, ToggleCell, EpisodeTitleCell, RelativeDateCell, EpisodeStatusCell, EpisodeActionsCell, CommandController, Moment,_) { + ], function (vent, Marionette, Backgrid, ToggleCell, EpisodeTitleCell, RelativeDateCell, EpisodeStatusCell, EpisodeActionsCell, CommandController, Moment, _) { return Marionette.Layout.extend({ template: 'Series/Details/SeasonLayoutTemplate', @@ -114,7 +114,7 @@ define( CommandController.bindToCommand({ element: this.ui.seasonRename, command: { - name : 'renameSeason', + name : 'renameFiles', seriesId : this.series.id, seasonNumber: this.model.get('seasonNumber') } @@ -131,12 +131,7 @@ define( }, _seasonRename: function () { - - CommandController.Execute('renameSeason', { - name : 'renameSeason', - seriesId : this.series.id, - seasonNumber: this.model.get('seasonNumber') - }); + vent.trigger(vent.Commands.ShowRenamePreview, { series: this.series, seasonNumber: this.model.get('seasonNumber') }); }, _seasonMonitored: function () { @@ -172,11 +167,6 @@ define( } }, - - _afterRename: function () { - vent.trigger(vent.Events.SeasonRenamed, { series: this.series, seasonNumber: this.model.get('seasonNumber') }); - }, - _showEpisodes: function () { this.episodeGrid.show(new Backgrid.Grid({ columns : this.columns, diff --git a/src/UI/Series/Details/SeriesDetailsLayout.js b/src/UI/Series/Details/SeriesDetailsLayout.js index b04aa0aa9..bc2d77b58 100644 --- a/src/UI/Series/Details/SeriesDetailsLayout.js +++ b/src/UI/Series/Details/SeriesDetailsLayout.js @@ -2,6 +2,7 @@ define( [ 'jquery', + 'underscore', 'vent', 'reqres', 'marionette', @@ -13,10 +14,21 @@ define( 'Series/Details/InfoView', 'Commands/CommandController', 'Shared/LoadingView', - 'underscore', 'backstrech', 'Mixins/backbone.signalr.mixin' - ], function ($,vent,reqres, Marionette, Backbone, EpisodeCollection, EpisodeFileCollection, SeasonCollection, SeasonCollectionView, InfoView, CommandController, LoadingView, _) { + ], function ($, + _, + vent, + reqres, + Marionette, + Backbone, + EpisodeCollection, + EpisodeFileCollection, + SeasonCollection, + SeasonCollectionView, + InfoView, + CommandController, + LoadingView) { return Marionette.Layout.extend({ itemViewContainer: '.x-series-seasons', @@ -47,7 +59,6 @@ define( initialize: function () { this.listenTo(this.model, 'change:monitored', this._setMonitoredState); this.listenTo(vent, vent.Events.SeriesDeleted, this._onSeriesDeleted); - this.listenTo(vent, vent.Events.SeasonRenamed, this._onSeasonRenamed); vent.on(vent.Events.CommandComplete, this._commandComplete, this); }, @@ -86,7 +97,9 @@ define( CommandController.bindToCommand({ element: this.ui.rename, command: { - name: 'renameSeries' + name : 'renameFiles', + seriesId : this.model.id, + seasonNumber: -1 } }); }, @@ -154,11 +167,7 @@ define( }, _renameSeries: function () { - CommandController.Execute('renameSeries', { - name : 'renameSeries', - seriesId: this.model.id - }); - + vent.trigger(vent.Commands.ShowRenamePreview, { series: this.model }); }, _seriesSearch: function () { @@ -196,14 +205,8 @@ define( this.info.show(new InfoView({ model: this.model })); }, - _onSeasonRenamed: function (event) { - if (this.model.get('id') === event.series.get('id')) { - this.episodeFileCollection.fetch(); - } - }, - _commandComplete: function (options) { - if (options.command.get('name') === 'refreshseries' || options.command.get('name') === 'renameseries') { + if (options.command.get('name') === 'refreshseries' || options.command.get('name') === 'renamefiles') { if (options.command.get('seriesId') === this.model.get('id')) { this._showSeasons(); this._setMonitoredState(); diff --git a/src/UI/Shared/Modal/Controller.js b/src/UI/Shared/Modal/Controller.js index 199aed4e6..177092de3 100644 --- a/src/UI/Shared/Modal/Controller.js +++ b/src/UI/Shared/Modal/Controller.js @@ -9,7 +9,8 @@ define( 'Episode/EpisodeDetailsLayout', 'History/Details/HistoryDetailsView', 'System/Logs/Table/Details/LogDetailsView', - ], function (vent, AppLayout, Marionette, EditSeriesView, DeleteSeriesView, EpisodeDetailsLayout, HistoryDetailsView, LogDetailsView) { + 'Rename/RenamePreviewLayout' + ], function (vent, AppLayout, Marionette, EditSeriesView, DeleteSeriesView, EpisodeDetailsLayout, HistoryDetailsView, LogDetailsView, RenamePreviewLayout) { return Marionette.AppRouter.extend({ @@ -21,6 +22,7 @@ define( vent.on(vent.Commands.ShowEpisodeDetails, this._showEpisode, this); vent.on(vent.Commands.ShowHistoryDetails, this._showHistory, this); vent.on(vent.Commands.ShowLogDetails, this._showLogDetails, this); + vent.on(vent.Commands.ShowRenamePreview, this._showRenamePreview, this); }, _openModal: function (view) { @@ -54,6 +56,11 @@ define( _showLogDetails: function (options) { var view = new LogDetailsView({ model: options.model }); AppLayout.modalRegion.show(view); + }, + + _showRenamePreview: function (options) { + var view = new RenamePreviewLayout(options); + AppLayout.modalRegion.show(view); } }); }); diff --git a/src/UI/vent.js b/src/UI/vent.js index 348e9c461..fdd869153 100644 --- a/src/UI/vent.js +++ b/src/UI/vent.js @@ -10,7 +10,6 @@ define( vent.Events = { SeriesAdded : 'series:added', SeriesDeleted : 'series:deleted', - SeasonRenamed : 'season:renamed', CommandComplete: 'command:complete', ServerUpdated : 'server:updated' }; @@ -25,7 +24,7 @@ define( ShowLogDetails : 'ShowLogDetails', SaveSettings : 'saveSettings', ShowLogFile : 'showLogFile', - ShowNamingWizard : 'showNamingWizard' + ShowRenamePreview : 'showRenamePreview' }; return vent;