Rename preview for full series and season

New: Preview before renaming files
pull/4/head
Mark McDowall 11 years ago
parent bb37444a99
commit e42ac25657

@ -0,0 +1,42 @@
using System.Collections.Generic;
using NzbDrone.Api.REST;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Api.Episodes
{
public class RenameEpisodeModule : NzbDroneRestModule<RenameEpisodeResource>
{
private readonly IRenameEpisodeFileService _renameEpisodeFileService;
public RenameEpisodeModule(IRenameEpisodeFileService renameEpisodeFileService)
: base("rename")
{
_renameEpisodeFileService = renameEpisodeFileService;
GetResourceAll = GetEpisodes;
}
private List<RenameEpisodeResource> 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));
}
}
}

@ -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<Int32> EpisodeNumbers { get; set; }
public Int32 EpisodeFileId { get; set; }
public String ExistingPath { get; set; }
public String NewPath { get; set; }
}
}

@ -95,6 +95,8 @@
<Compile Include="Directories\DirectoryModule.cs" /> <Compile Include="Directories\DirectoryModule.cs" />
<Compile Include="Episodes\EpisodeModule.cs" /> <Compile Include="Episodes\EpisodeModule.cs" />
<Compile Include="Episodes\EpisodeResource.cs" /> <Compile Include="Episodes\EpisodeResource.cs" />
<Compile Include="Episodes\RenameEpisodeModule.cs" />
<Compile Include="Episodes\RenameEpisodeResource.cs" />
<Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" /> <Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" />
<Compile Include="Extensions\Pipelines\GZipPipeline.cs" /> <Compile Include="Extensions\Pipelines\GZipPipeline.cs" />
<Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" /> <Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" />

@ -38,22 +38,14 @@ namespace NzbDrone.Core.Test.MediaFiles
private void GivenNoEpisodeFiles() private void GivenNoEpisodeFiles()
{ {
Mocker.GetMock<IMediaFileService>() Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesBySeries(_series.Id)) .Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber))
.Returns(new List<EpisodeFile>()); .Returns(new List<EpisodeFile>());
} }
private void GivenEpisodeFiles() private void GivenEpisodeFiles()
{ {
Mocker.GetMock<IMediaFileService>() Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesBySeries(_series.Id)) .Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
.Returns(_episodeFiles);
Mocker.GetMock<IMediaFileService>()
.Setup(s => s.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber))
.Returns(_episodeFiles); .Returns(_episodeFiles);
} }
@ -68,7 +60,7 @@ namespace NzbDrone.Core.Test.MediaFiles
{ {
GivenNoEpisodeFiles(); GivenNoEpisodeFiles();
Subject.Execute(new RenameSeriesCommand(_series.Id)); Subject.Execute(new RenameFilesCommand(_series.Id, new List<int>{1}));
Mocker.GetMock<IEventAggregator>() Mocker.GetMock<IEventAggregator>()
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never()); .Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never());
@ -83,7 +75,7 @@ namespace NzbDrone.Core.Test.MediaFiles
.Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<Series>())) .Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<Series>()))
.Throws(new SameFilenameException("Same file name", "Filename")); .Throws(new SameFilenameException("Same file name", "Filename"));
Subject.Execute(new RenameSeriesCommand(_series.Id)); Subject.Execute(new RenameFilesCommand(_series.Id, new List<int> { 1 }));
Mocker.GetMock<IEventAggregator>() Mocker.GetMock<IEventAggregator>()
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never()); .Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never());
@ -95,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles
GivenEpisodeFiles(); GivenEpisodeFiles();
GivenMovedFiles(); GivenMovedFiles();
Subject.Execute(new RenameSeriesCommand(_series.Id)); Subject.Execute(new RenameFilesCommand(_series.Id, new List<int> { 1 }));
Mocker.GetMock<IEventAggregator>() Mocker.GetMock<IEventAggregator>()
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Once()); .Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Once());
@ -107,40 +99,24 @@ namespace NzbDrone.Core.Test.MediaFiles
GivenEpisodeFiles(); GivenEpisodeFiles();
GivenMovedFiles(); GivenMovedFiles();
Subject.Execute(new RenameSeriesCommand(_series.Id)); Subject.Execute(new RenameFilesCommand(_series.Id, new List<int> { 1 }));
Mocker.GetMock<IMediaFileService>() Mocker.GetMock<IMediaFileService>()
.Verify(v => v.Update(It.IsAny<EpisodeFile>()), Times.Exactly(2)); .Verify(v => v.Update(It.IsAny<EpisodeFile>()), Times.Exactly(2));
} }
[Test] [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));
Mocker.GetMock<IMediaFileService>()
.Verify(v => v.GetFilesBySeries(_series.Id), Times.Never());
Mocker.GetMock<IMediaFileService>()
.Verify(v => v.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber), Times.Once());
}
[Test]
public void rename_series_should_get_episodefiles_for_series()
{ {
GivenEpisodeFiles(); GivenEpisodeFiles();
GivenMovedFiles(); GivenMovedFiles();
Subject.Execute(new RenameSeriesCommand(_series.Id)); var files = new List<int> { 1 };
Mocker.GetMock<IMediaFileService>() Subject.Execute(new RenameFilesCommand(_series.Id, files));
.Verify(v => v.GetFilesBySeries(_series.Id), Times.Once());
Mocker.GetMock<IMediaFileService>() Mocker.GetMock<IMediaFileService>()
.Verify(v => v.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber), Times.Never()); .Verify(v => v.Get(files), Times.Once());
} }
} }
} }

@ -1,10 +1,12 @@
using System.Collections.Generic;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.MediaFiles.Commands namespace NzbDrone.Core.MediaFiles.Commands
{ {
public class RenameSeriesCommand : Command public class RenameFilesCommand : Command
{ {
public int SeriesId { get; set; } public int SeriesId { get; set; }
public List<int> Files { get; set; }
public override bool SendUpdatesToClient 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<int> files)
{ {
SeriesId = seriesId; SeriesId = seriesId;
Files = files;
} }
} }
} }

@ -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;
}
}
}

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
@ -17,6 +18,7 @@ namespace NzbDrone.Core.MediaFiles
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber); List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
List<string> FilterExistingFiles(List<string> files, int seriesId); List<string> FilterExistingFiles(List<string> files, int seriesId);
EpisodeFile Get(int id); EpisodeFile Get(int id);
List<EpisodeFile> Get(IEnumerable<int> ids);
} }
public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent> public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent>
@ -75,6 +77,11 @@ namespace NzbDrone.Core.MediaFiles
return _mediaFileRepository.Get(id); return _mediaFileRepository.Get(id);
} }
public List<EpisodeFile> Get(IEnumerable<int> ids)
{
return _mediaFileRepository.Get(ids).ToList();
}
public void HandleAsync(SeriesDeletedEvent message) public void HandleAsync(SeriesDeletedEvent message)
{ {
var files = GetFilesBySeries(message.Series.Id); var files = GetFilesBySeries(message.Series.Id);

@ -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<Int32> EpisodeNumbers { get; set; }
public Int32 EpisodeFileId { get; set; }
public String ExistingPath { get; set; }
public String NewPath { get; set; }
}
}

@ -1,37 +1,100 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common;
using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles namespace NzbDrone.Core.MediaFiles
{ {
public class RenameEpisodeFileService : IExecute<RenameSeasonCommand>, IExecute<RenameSeriesCommand> public interface IRenameEpisodeFileService
{
List<RenameEpisodeFilePreview> GetRenamePreviews(int seriesId);
List<RenameEpisodeFilePreview> GetRenamePreviews(int seriesId, int seasonNumber);
}
public class RenameEpisodeFileService : IRenameEpisodeFileService,
IExecute<RenameFilesCommand>
{ {
private readonly ISeriesService _seriesService; private readonly ISeriesService _seriesService;
private readonly IMediaFileService _mediaFileService; private readonly IMediaFileService _mediaFileService;
private readonly IMoveEpisodeFiles _episodeFileMover; private readonly IMoveEpisodeFiles _episodeFileMover;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IEpisodeService _episodeService;
private readonly IBuildFileNames _filenameBuilder;
private readonly Logger _logger; private readonly Logger _logger;
public RenameEpisodeFileService(ISeriesService seriesService, public RenameEpisodeFileService(ISeriesService seriesService,
IMediaFileService mediaFileService, IMediaFileService mediaFileService,
IMoveEpisodeFiles episodeFileMover, IMoveEpisodeFiles episodeFileMover,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
IEpisodeService episodeService,
IBuildFileNames filenameBuilder,
Logger logger) Logger logger)
{ {
_seriesService = seriesService; _seriesService = seriesService;
_mediaFileService = mediaFileService; _mediaFileService = mediaFileService;
_episodeFileMover = episodeFileMover; _episodeFileMover = episodeFileMover;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_episodeService = episodeService;
_filenameBuilder = filenameBuilder;
_logger = logger; _logger = logger;
} }
public List<RenameEpisodeFilePreview> 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<RenameEpisodeFilePreview> 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<RenameEpisodeFilePreview> GetPreviews(Series series, List<Episode> episodes, List<EpisodeFile> 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<EpisodeFile> episodeFiles, Series series) private void RenameFiles(List<EpisodeFile> episodeFiles, Series series)
{ {
var renamed = new List<EpisodeFile>(); var renamed = new List<EpisodeFile>();
@ -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 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); _logger.ProgressInfo("Renaming {0} files for {1}", episodeFiles.Count, series.Title);
RenameFiles(episodeFiles, series); RenameFiles(episodeFiles, series);
_logger.ProgressInfo("Episode Fies renamed for {0}", series.Title); _logger.ProgressInfo("Selected Episode Files renamed for {0}", series.Title);
} }
} }
} }

@ -278,9 +278,11 @@
<Compile Include="Instrumentation\Commands\DeleteLogFilesCommand.cs" /> <Compile Include="Instrumentation\Commands\DeleteLogFilesCommand.cs" />
<Compile Include="Instrumentation\Commands\TrimLogCommand.cs" /> <Compile Include="Instrumentation\Commands\TrimLogCommand.cs" />
<Compile Include="Instrumentation\DeleteLogFilesService.cs" /> <Compile Include="Instrumentation\DeleteLogFilesService.cs" />
<Compile Include="MediaFiles\Commands\RenameFilesCommand.cs" />
<Compile Include="MediaFiles\EpisodeFileMoveResult.cs" /> <Compile Include="MediaFiles\EpisodeFileMoveResult.cs" />
<Compile Include="MediaFiles\MediaFileExtensions.cs" /> <Compile Include="MediaFiles\MediaFileExtensions.cs" />
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" /> <Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
<Compile Include="MediaFiles\RenameEpisodeFilePreview.cs" />
<Compile Include="Messaging\Commands\CommandExecutor.cs" /> <Compile Include="Messaging\Commands\CommandExecutor.cs" />
<Compile Include="Messaging\Commands\ICommandExecutor.cs" /> <Compile Include="Messaging\Commands\ICommandExecutor.cs" />
<Compile Include="Messaging\Commands\IExecute.cs" /> <Compile Include="Messaging\Commands\IExecute.cs" />
@ -343,8 +345,6 @@
<Compile Include="MediaCover\CoverAlreadyExistsSpecification.cs" /> <Compile Include="MediaCover\CoverAlreadyExistsSpecification.cs" />
<Compile Include="MediaFiles\Commands\CleanMediaFileDb.cs" /> <Compile Include="MediaFiles\Commands\CleanMediaFileDb.cs" />
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" /> <Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameSeriesCommand.cs" />
<Compile Include="MediaFiles\Commands\RenameSeasonCommand.cs" />
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" /> <Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportDecision.cs" /> <Compile Include="MediaFiles\EpisodeImport\ImportDecision.cs" />
<Compile Include="MediaFiles\EpisodeImport\IImportDecisionEngineSpecification.cs" /> <Compile Include="MediaFiles\EpisodeImport\IImportDecisionEngineSpecification.cs" />

@ -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

@ -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

@ -49,7 +49,6 @@ https://github.com/ghinda/css-toggle-switch
left: -100px; left: -100px;
width: 100%; width: 100%;
margin: 0; margin: 0;
padding-right: 100px;
text-align: left; text-align: left;
} }

@ -183,4 +183,18 @@ footer {
#errors{ #errors{
display : none; 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;
}
} }

@ -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);
}
});
});

@ -0,0 +1,11 @@
'use strict';
define(
[
'marionette',
'Rename/RenamePreviewItemView'
], function (Marionette, RenamePreviewItemView) {
return Marionette.CollectionView.extend({
itemView : RenamePreviewItemView
});
});

@ -0,0 +1,10 @@
'use strict';
define(
[
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Rename/RenamePreviewEmptyCollectionViewTemplate'
});
});

@ -0,0 +1,3 @@
<div class="alert alert-success">
Success! My work is done, no files to rename.
</div>

@ -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);
});

@ -0,0 +1,25 @@
<div class="rename-preview-item">
<div class="row">
<div class="span1">
<label class="checkbox toggle well" title="Rename file">
<input type="checkbox" name="rename"/>
<p>
<span>Yes</span>
<span>No</span>
</p>
<div class="btn btn-warning slide-button"/>
</label>
</div>
<div class="span9">
<div class="row">
<div class="span1">Existing</div>
<div class="span8">{{existingPath}}</div>
</div>
<div class="row">
<div class="span1">Suggested</div>
<div class="span8">{{newPath}}</div>
</div>
</div>
</div>
</div>

@ -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);
}
});
});

@ -0,0 +1,19 @@
<div class="rename-preview-modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>
Organize & Rename
</h3>
</div>
<div class="modal-body">
<div class="alert alert-info x-path-info">All paths are relative to: <strong>{{path}}</strong></div>
<div id="rename-previews"></div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">close</button>
<button class="btn x-organize">Organize</button>
</div>
</div>

@ -0,0 +1,9 @@
'use strict';
define(
[
'backbone'
], function (Backbone) {
return Backbone.Model.extend({
});
});

@ -12,7 +12,7 @@ define(
'Commands/CommandController', 'Commands/CommandController',
'moment', 'moment',
'underscore' '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({ return Marionette.Layout.extend({
template: 'Series/Details/SeasonLayoutTemplate', template: 'Series/Details/SeasonLayoutTemplate',
@ -114,7 +114,7 @@ define(
CommandController.bindToCommand({ CommandController.bindToCommand({
element: this.ui.seasonRename, element: this.ui.seasonRename,
command: { command: {
name : 'renameSeason', name : 'renameFiles',
seriesId : this.series.id, seriesId : this.series.id,
seasonNumber: this.model.get('seasonNumber') seasonNumber: this.model.get('seasonNumber')
} }
@ -131,12 +131,7 @@ define(
}, },
_seasonRename: function () { _seasonRename: function () {
vent.trigger(vent.Commands.ShowRenamePreview, { series: this.series, seasonNumber: this.model.get('seasonNumber') });
CommandController.Execute('renameSeason', {
name : 'renameSeason',
seriesId : this.series.id,
seasonNumber: this.model.get('seasonNumber')
});
}, },
_seasonMonitored: function () { _seasonMonitored: function () {
@ -172,11 +167,6 @@ define(
} }
}, },
_afterRename: function () {
vent.trigger(vent.Events.SeasonRenamed, { series: this.series, seasonNumber: this.model.get('seasonNumber') });
},
_showEpisodes: function () { _showEpisodes: function () {
this.episodeGrid.show(new Backgrid.Grid({ this.episodeGrid.show(new Backgrid.Grid({
columns : this.columns, columns : this.columns,

@ -2,6 +2,7 @@
define( define(
[ [
'jquery', 'jquery',
'underscore',
'vent', 'vent',
'reqres', 'reqres',
'marionette', 'marionette',
@ -13,10 +14,21 @@ define(
'Series/Details/InfoView', 'Series/Details/InfoView',
'Commands/CommandController', 'Commands/CommandController',
'Shared/LoadingView', 'Shared/LoadingView',
'underscore',
'backstrech', 'backstrech',
'Mixins/backbone.signalr.mixin' '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({ return Marionette.Layout.extend({
itemViewContainer: '.x-series-seasons', itemViewContainer: '.x-series-seasons',
@ -47,7 +59,6 @@ define(
initialize: function () { initialize: function () {
this.listenTo(this.model, 'change:monitored', this._setMonitoredState); this.listenTo(this.model, 'change:monitored', this._setMonitoredState);
this.listenTo(vent, vent.Events.SeriesDeleted, this._onSeriesDeleted); this.listenTo(vent, vent.Events.SeriesDeleted, this._onSeriesDeleted);
this.listenTo(vent, vent.Events.SeasonRenamed, this._onSeasonRenamed);
vent.on(vent.Events.CommandComplete, this._commandComplete, this); vent.on(vent.Events.CommandComplete, this._commandComplete, this);
}, },
@ -86,7 +97,9 @@ define(
CommandController.bindToCommand({ CommandController.bindToCommand({
element: this.ui.rename, element: this.ui.rename,
command: { command: {
name: 'renameSeries' name : 'renameFiles',
seriesId : this.model.id,
seasonNumber: -1
} }
}); });
}, },
@ -154,11 +167,7 @@ define(
}, },
_renameSeries: function () { _renameSeries: function () {
CommandController.Execute('renameSeries', { vent.trigger(vent.Commands.ShowRenamePreview, { series: this.model });
name : 'renameSeries',
seriesId: this.model.id
});
}, },
_seriesSearch: function () { _seriesSearch: function () {
@ -196,14 +205,8 @@ define(
this.info.show(new InfoView({ model: this.model })); 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) { _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')) { if (options.command.get('seriesId') === this.model.get('id')) {
this._showSeasons(); this._showSeasons();
this._setMonitoredState(); this._setMonitoredState();

@ -9,7 +9,8 @@ define(
'Episode/EpisodeDetailsLayout', 'Episode/EpisodeDetailsLayout',
'History/Details/HistoryDetailsView', 'History/Details/HistoryDetailsView',
'System/Logs/Table/Details/LogDetailsView', '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({ return Marionette.AppRouter.extend({
@ -21,6 +22,7 @@ define(
vent.on(vent.Commands.ShowEpisodeDetails, this._showEpisode, this); vent.on(vent.Commands.ShowEpisodeDetails, this._showEpisode, this);
vent.on(vent.Commands.ShowHistoryDetails, this._showHistory, this); vent.on(vent.Commands.ShowHistoryDetails, this._showHistory, this);
vent.on(vent.Commands.ShowLogDetails, this._showLogDetails, this); vent.on(vent.Commands.ShowLogDetails, this._showLogDetails, this);
vent.on(vent.Commands.ShowRenamePreview, this._showRenamePreview, this);
}, },
_openModal: function (view) { _openModal: function (view) {
@ -54,6 +56,11 @@ define(
_showLogDetails: function (options) { _showLogDetails: function (options) {
var view = new LogDetailsView({ model: options.model }); var view = new LogDetailsView({ model: options.model });
AppLayout.modalRegion.show(view); AppLayout.modalRegion.show(view);
},
_showRenamePreview: function (options) {
var view = new RenamePreviewLayout(options);
AppLayout.modalRegion.show(view);
} }
}); });
}); });

@ -10,7 +10,6 @@ define(
vent.Events = { vent.Events = {
SeriesAdded : 'series:added', SeriesAdded : 'series:added',
SeriesDeleted : 'series:deleted', SeriesDeleted : 'series:deleted',
SeasonRenamed : 'season:renamed',
CommandComplete: 'command:complete', CommandComplete: 'command:complete',
ServerUpdated : 'server:updated' ServerUpdated : 'server:updated'
}; };
@ -25,7 +24,7 @@ define(
ShowLogDetails : 'ShowLogDetails', ShowLogDetails : 'ShowLogDetails',
SaveSettings : 'saveSettings', SaveSettings : 'saveSettings',
ShowLogFile : 'showLogFile', ShowLogFile : 'showLogFile',
ShowNamingWizard : 'showNamingWizard' ShowRenamePreview : 'showRenamePreview'
}; };
return vent; return vent;

Loading…
Cancel
Save