mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
Rename preview for full series and season
New: Preview before renaming files
This commit is contained in:
parent
bb37444a99
commit
e42ac25657
42
src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs
Normal file
42
src/NzbDrone.Api/Episodes/RenameEpisodeModule.cs
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
18
src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs
Normal file
18
src/NzbDrone.Api/Episodes/RenameEpisodeResource.cs
Normal file
@ -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="Episodes\EpisodeModule.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\GZipPipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" />
|
||||
|
@ -38,22 +38,14 @@ public void Setup()
|
||||
private void GivenNoEpisodeFiles()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(s => s.GetFilesBySeries(_series.Id))
|
||||
.Returns(new List<EpisodeFile>());
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(s => s.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber))
|
||||
.Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
|
||||
.Returns(new List<EpisodeFile>());
|
||||
}
|
||||
|
||||
private void GivenEpisodeFiles()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(s => s.GetFilesBySeries(_series.Id))
|
||||
.Returns(_episodeFiles);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(s => s.GetFilesBySeason(_series.Id, _episodeFiles.First().SeasonNumber))
|
||||
.Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
|
||||
.Returns(_episodeFiles);
|
||||
}
|
||||
|
||||
@ -68,7 +60,7 @@ public void should_not_publish_event_if_no_files_to_rename()
|
||||
{
|
||||
GivenNoEpisodeFiles();
|
||||
|
||||
Subject.Execute(new RenameSeriesCommand(_series.Id));
|
||||
Subject.Execute(new RenameFilesCommand(_series.Id, new List<int>{1}));
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never());
|
||||
@ -83,7 +75,7 @@ public void should_not_publish_event_if_no_files_are_renamed()
|
||||
.Setup(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), It.IsAny<Series>()))
|
||||
.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>()
|
||||
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never());
|
||||
@ -95,7 +87,7 @@ public void should_publish_event_if_files_are_renamed()
|
||||
GivenEpisodeFiles();
|
||||
GivenMovedFiles();
|
||||
|
||||
Subject.Execute(new RenameSeriesCommand(_series.Id));
|
||||
Subject.Execute(new RenameFilesCommand(_series.Id, new List<int> { 1 }));
|
||||
|
||||
Mocker.GetMock<IEventAggregator>()
|
||||
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Once());
|
||||
@ -107,40 +99,24 @@ public void should_update_moved_files()
|
||||
GivenEpisodeFiles();
|
||||
GivenMovedFiles();
|
||||
|
||||
Subject.Execute(new RenameSeriesCommand(_series.Id));
|
||||
Subject.Execute(new RenameFilesCommand(_series.Id, new List<int> { 1 }));
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Verify(v => v.Update(It.IsAny<EpisodeFile>()), 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<int> { 1 };
|
||||
|
||||
Subject.Execute(new RenameFilesCommand(_series.Id, files));
|
||||
|
||||
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();
|
||||
GivenMovedFiles();
|
||||
|
||||
Subject.Execute(new RenameSeriesCommand(_series.Id));
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Verify(v => v.GetFilesBySeries(_series.Id), Times.Once());
|
||||
|
||||
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;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Commands
|
||||
{
|
||||
public class RenameSeriesCommand : Command
|
||||
public class RenameFilesCommand : Command
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public List<int> Files { get; set; }
|
||||
|
||||
public override bool SendUpdatesToClient
|
||||
{
|
||||
@ -14,13 +16,14 @@ public override bool SendUpdatesToClient
|
||||
}
|
||||
}
|
||||
|
||||
public RenameSeriesCommand()
|
||||
public RenameFilesCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public RenameSeriesCommand(int seriesId)
|
||||
public RenameFilesCommand(int seriesId, List<int> files)
|
||||
{
|
||||
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.Linq;
|
||||
using NLog;
|
||||
@ -17,6 +18,7 @@ public interface IMediaFileService
|
||||
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
|
||||
List<string> FilterExistingFiles(List<string> files, int seriesId);
|
||||
EpisodeFile Get(int id);
|
||||
List<EpisodeFile> Get(IEnumerable<int> ids);
|
||||
}
|
||||
|
||||
public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent>
|
||||
@ -75,6 +77,11 @@ public EpisodeFile Get(int id)
|
||||
return _mediaFileRepository.Get(id);
|
||||
}
|
||||
|
||||
public List<EpisodeFile> Get(IEnumerable<int> ids)
|
||||
{
|
||||
return _mediaFileRepository.Get(ids).ToList();
|
||||
}
|
||||
|
||||
public void HandleAsync(SeriesDeletedEvent message)
|
||||
{
|
||||
var files = GetFilesBySeries(message.Series.Id);
|
||||
|
17
src/NzbDrone.Core/MediaFiles/RenameEpisodeFilePreview.cs
Normal file
17
src/NzbDrone.Core/MediaFiles/RenameEpisodeFilePreview.cs
Normal file
@ -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.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<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 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<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)
|
||||
{
|
||||
var renamed = new List<EpisodeFile>();
|
||||
@ -64,24 +127,14 @@ private void RenameFiles(List<EpisodeFile> episodeFiles, Series series)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,9 +278,11 @@
|
||||
<Compile Include="Instrumentation\Commands\DeleteLogFilesCommand.cs" />
|
||||
<Compile Include="Instrumentation\Commands\TrimLogCommand.cs" />
|
||||
<Compile Include="Instrumentation\DeleteLogFilesService.cs" />
|
||||
<Compile Include="MediaFiles\Commands\RenameFilesCommand.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMoveResult.cs" />
|
||||
<Compile Include="MediaFiles\MediaFileExtensions.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
|
||||
<Compile Include="MediaFiles\RenameEpisodeFilePreview.cs" />
|
||||
<Compile Include="Messaging\Commands\CommandExecutor.cs" />
|
||||
<Compile Include="Messaging\Commands\ICommandExecutor.cs" />
|
||||
<Compile Include="Messaging\Commands\IExecute.cs" />
|
||||
@ -343,8 +345,6 @@
|
||||
<Compile Include="MediaCover\CoverAlreadyExistsSpecification.cs" />
|
||||
<Compile Include="MediaFiles\Commands\CleanMediaFileDb.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\EpisodeImport\ImportDecision.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
|
Binary file not shown.
@ -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
|
Binary file not shown.
1
src/UI/Content/bootstrap.toggle-switch.css
vendored
1
src/UI/Content/bootstrap.toggle-switch.css
vendored
@ -49,7 +49,6 @@ https://github.com/ghinda/css-toggle-switch
|
||||
left: -100px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding-right: 100px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
38
src/UI/Rename/RenamePreviewCollection.js
Normal file
38
src/UI/Rename/RenamePreviewCollection.js
Normal file
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
11
src/UI/Rename/RenamePreviewCollectionView.js
Normal file
11
src/UI/Rename/RenamePreviewCollectionView.js
Normal file
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Rename/RenamePreviewItemView'
|
||||
], function (Marionette, RenamePreviewItemView) {
|
||||
return Marionette.CollectionView.extend({
|
||||
|
||||
itemView : RenamePreviewItemView
|
||||
});
|
||||
});
|
10
src/UI/Rename/RenamePreviewEmptyCollectionView.js
Normal file
10
src/UI/Rename/RenamePreviewEmptyCollectionView.js
Normal file
@ -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>
|
13
src/UI/Rename/RenamePreviewItemView.js
Normal file
13
src/UI/Rename/RenamePreviewItemView.js
Normal file
@ -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);
|
||||
});
|
25
src/UI/Rename/RenamePreviewItemViewTemplate.html
Normal file
25
src/UI/Rename/RenamePreviewItemViewTemplate.html
Normal file
@ -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>
|
93
src/UI/Rename/RenamePreviewLayout.js
Normal file
93
src/UI/Rename/RenamePreviewLayout.js
Normal file
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
19
src/UI/Rename/RenamePreviewLayoutTemplate.html
Normal file
19
src/UI/Rename/RenamePreviewLayoutTemplate.html
Normal file
@ -0,0 +1,19 @@
|
||||
<div class="rename-preview-modal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</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>
|
||||
|
9
src/UI/Rename/RenamePreviewModel.js
Normal file
9
src/UI/Rename/RenamePreviewModel.js
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user