Rename series added

pull/3113/head
Mark McDowall 11 years ago
parent c6d82bf98b
commit 637b101975

@ -0,0 +1,16 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.MediaFiles.Commands
{
public class RenameSeasonCommand : ICommand
{
public int SeriesId { get; private set; }
public int SeasonNumber { get; private set; }
public RenameSeasonCommand(int seriesId, int seasonNumber)
{
SeriesId = seriesId;
SeasonNumber = seasonNumber;
}
}
}

@ -0,0 +1,14 @@
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.MediaFiles.Commands
{
public class RenameSeriesCommand : ICommand
{
public int SeriesId { get; private set; }
public RenameSeriesCommand(int seriesId)
{
SeriesId = seriesId;
}
}
}

@ -14,6 +14,7 @@ namespace NzbDrone.Core.MediaFiles
public interface IMoveEpisodeFiles public interface IMoveEpisodeFiles
{ {
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile); EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile);
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series);
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
} }
@ -61,6 +62,15 @@ namespace NzbDrone.Core.MediaFiles
return episodeFile; return episodeFile;
} }
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series)
{
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
var newFileName = _buildFileNames.BuildFilename(episodes, series, episodeFile);
var destinationFilename = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.Path));
return MoveFile(episodeFile, destinationFilename);
}
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
{ {
var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile); var newFileName = _buildFileNames.BuildFilename(localEpisode.Episodes, localEpisode.Series, episodeFile);

@ -1,7 +1,7 @@
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.MediaFiles.Events
{ {
public class SeriesRenamedEvent : IEvent public class SeriesRenamedEvent : IEvent
{ {

@ -9,6 +9,7 @@ namespace NzbDrone.Core.MediaFiles
{ {
EpisodeFile GetFileByPath(string path); EpisodeFile GetFileByPath(string path);
List<EpisodeFile> GetFilesBySeries(int seriesId); List<EpisodeFile> GetFilesBySeries(int seriesId);
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
bool Exists(string path); bool Exists(string path);
} }
@ -20,21 +21,26 @@ namespace NzbDrone.Core.MediaFiles
{ {
} }
public EpisodeFile GetFileByPath(string path) public EpisodeFile GetFileByPath(string path)
{ {
return Query.SingleOrDefault(c => c.Path == path); return Query.SingleOrDefault(c => c.Path == path);
} }
public bool Exists(string path) public List<EpisodeFile> GetFilesBySeries(int seriesId)
{ {
return Query.Any(c => c.Path == path); return Query.Where(c => c.SeriesId == seriesId).ToList();
} }
public List<EpisodeFile> GetFilesBySeries(int seriesId) public List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber)
{ {
return Query.Where(c => c.SeriesId == seriesId).ToList(); return Query.Where(c => c.SeriesId == seriesId)
.AndWhere(c => c.SeasonNumber == seasonNumber)
.ToList();
} }
public bool Exists(string path)
{
return Query.Any(c => c.Path == path);
}
} }
} }

@ -17,21 +17,19 @@ namespace NzbDrone.Core.MediaFiles
bool Exists(string path); bool Exists(string path);
EpisodeFile GetFileByPath(string path); EpisodeFile GetFileByPath(string path);
List<EpisodeFile> GetFilesBySeries(int seriesId); List<EpisodeFile> GetFilesBySeries(int seriesId);
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
List<string> FilterExistingFiles(List<string> files, int seriesId); List<string> FilterExistingFiles(List<string> files, int seriesId);
} }
public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent> public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent>
{ {
private readonly IConfigService _configService;
private readonly IMessageAggregator _messageAggregator; private readonly IMessageAggregator _messageAggregator;
private readonly Logger _logger;
private readonly IMediaFileRepository _mediaFileRepository; private readonly IMediaFileRepository _mediaFileRepository;
private readonly Logger _logger;
public MediaFileService(IMediaFileRepository mediaFileRepository, IConfigService configService, IMessageAggregator messageAggregator, Logger logger) public MediaFileService(IMediaFileRepository mediaFileRepository, IMessageAggregator messageAggregator, Logger logger)
{ {
_mediaFileRepository = mediaFileRepository; _mediaFileRepository = mediaFileRepository;
_configService = configService;
_messageAggregator = messageAggregator; _messageAggregator = messageAggregator;
_logger = logger; _logger = logger;
} }

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.MediaFiles.Commands;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles
{
public interface IRenameEpisodeFiles
{
}
public class RenameEpisodeFileService : IRenameEpisodeFiles, IExecute<RenameSeasonCommand>, IExecute<RenameSeriesCommand>
{
private readonly ISeriesService _seriesService;
private readonly IMediaFileService _mediaFileService;
private readonly IMoveEpisodeFiles _episodeFileMover;
private readonly IMessageAggregator _messageAggregator;
private readonly Logger _logger;
public RenameEpisodeFileService(ISeriesService seriesService,
IMediaFileService mediaFileService,
IMoveEpisodeFiles episodeFileMover,
IMessageAggregator messageAggregator,
Logger logger)
{
_seriesService = seriesService;
_mediaFileService = mediaFileService;
_episodeFileMover = episodeFileMover;
_messageAggregator = messageAggregator;
_logger = logger;
}
private void RenameFiles(List<EpisodeFile> episodeFiles, Series series)
{
var renamed = new List<EpisodeFile>();
foreach (var file in episodeFiles)
{
var episodeFile = file;
_logger.Trace("Renaming episode file: {0}", episodeFile);
episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, series);
if (episodeFile != null)
{
_mediaFileService.Update(episodeFile);
renamed.Add(episodeFile);
}
_logger.Trace("Renamed episode file: {0}", episodeFile);
}
if (renamed.Any())
{
_messageAggregator.PublishEvent(new SeriesRenamedEvent(series));
}
}
public void Execute(RenameSeasonCommand message)
{
var series = _seriesService.GetSeries(message.SeriesId);
var episodeFiles = _mediaFileService.GetFilesBySeason(message.SeriesId, message.SeasonNumber);
_logger.Info("Renaming {0} files for {1} season {2}", episodeFiles.Count, series.Title, message.SeasonNumber);
RenameFiles(episodeFiles, series);
_logger.Debug("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);
_logger.Info("Renaming {0} files for {1}", episodeFiles.Count, series.Title);
RenameFiles(episodeFiles, series);
_logger.Debug("Episode Fies renamed for {0}", series.Title);
}
}
}

@ -270,6 +270,8 @@
<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" />
@ -281,7 +283,8 @@
<Compile Include="MediaFiles\Events\EpisodeImportedEvent.cs" /> <Compile Include="MediaFiles\Events\EpisodeImportedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" /> <Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
<Compile Include="Download\EpisodeGrabbedEvent.cs" /> <Compile Include="Download\EpisodeGrabbedEvent.cs" />
<Compile Include="Download\SeriesRenamedEvent.cs" /> <Compile Include="MediaFiles\Events\SeriesRenamedEvent.cs" />
<Compile Include="MediaFiles\RenameEpisodeFileService.cs" />
<Compile Include="MediaFiles\UpgradeMediaFileService.cs" /> <Compile Include="MediaFiles\UpgradeMediaFileService.cs" />
<Compile Include="Notifications\Email\TestEmailCommand.cs" /> <Compile Include="Notifications\Email\TestEmailCommand.cs" />
<Compile Include="Notifications\Growl\GrowlSettings.cs" /> <Compile Include="Notifications\Growl\GrowlSettings.cs" />

@ -54,6 +54,15 @@
color : @errorText; color : @errorText;
} }
.icon-nd-spinner:before {
.icon(@spinner);
.icon-spin;
}
.icon-nd-rename:before {
.icon(@pencil)
}
.icon-nd-add:before { .icon-nd-add:before {
.icon(@plus); .icon(@plus);
} }

@ -15,12 +15,14 @@ define(
ui: { ui: {
seasonSearch : '.x-season-search', seasonSearch : '.x-season-search',
seasonMonitored: '.x-season-monitored' seasonMonitored: '.x-season-monitored',
seasonRename : '.x-season-rename'
}, },
events: { events: {
'click .x-season-search' : '_seasonSearch', 'click .x-season-search' : '_seasonSearch',
'click .x-season-monitored': '_seasonMonitored' 'click .x-season-monitored': '_seasonMonitored',
'click .x-season-rename' : '_seasonRename'
}, },
regions: { regions: {
@ -151,6 +153,40 @@ define(
this.ui.seasonMonitored.addClass('icon-bookmark-empty'); this.ui.seasonMonitored.addClass('icon-bookmark-empty');
this.ui.seasonMonitored.removeClass('icon-bookmark'); this.ui.seasonMonitored.removeClass('icon-bookmark');
} }
},
_seasonRename: function () {
var command = 'renameSeason';
this.idle = false;
this.ui.seasonRename.toggleClass('icon-nd-rename icon-nd-spinner');
var properties = {
seriesId : this.model.get('seriesId'),
seasonNumber: this.model.get('seasonNumber')
};
var self = this;
var commandPromise = CommandController.Execute(command, properties);
commandPromise.fail(function (options) {
if (options.readyState === 0 || options.status === 0) {
return;
}
Messenger.show({
message: 'Season rename failed',
type : 'error'
});
});
commandPromise.always(function () {
if (!self.isClosed) {
self.ui.seasonRename.toggleClass('icon-nd-rename icon-nd-spinner');
self.idle = true;
}
});
} }
}); });
}); });

@ -2,11 +2,12 @@
<h2> <h2>
<i class="x-season-monitored clickable" title="Toggle season monitored status"/> <i class="x-season-monitored clickable" title="Toggle season monitored status"/>
{{#if seasonNumber}} {{#if seasonNumber}}
Season {{seasonNumber}} Season {{seasonNumber}}
{{else}} {{else}}
Specials Specials
{{/if}} {{/if}}
<span class="season-actions pull-right"> <span class="season-actions pull-right">
<i class="icon-nd-rename x-season-rename" title="Rename all episodes in season {{seasonNumber}}"/>
<i class="icon-search x-season-search" title="Search for all episodes in season {{seasonNumber}}"/> <i class="icon-search x-season-search" title="Search for all episodes in season {{seasonNumber}}"/>
</span> </span>
</h2> </h2>

@ -24,13 +24,15 @@ define(
header : '.x-header', header : '.x-header',
monitored: '.x-monitored', monitored: '.x-monitored',
edit : '.x-edit', edit : '.x-edit',
refresh : '.x-refresh' refresh : '.x-refresh',
rename : '.x-rename'
}, },
events: { events: {
'click .x-monitored': '_toggleMonitored', 'click .x-monitored': '_toggleMonitored',
'click .x-edit' : '_editSeries', 'click .x-edit' : '_editSeries',
'click .x-refresh' : '_refreshSeries' 'click .x-refresh' : '_refreshSeries',
'click .x-rename' : '_renameSeries'
}, },
initialize: function () { initialize: function () {
@ -136,6 +138,39 @@ define(
if (this.model.get('id') === event.series.get('id')) { if (this.model.get('id') === event.series.get('id')) {
App.Router.navigate('/', { trigger: true }); App.Router.navigate('/', { trigger: true });
} }
},
_renameSeries: function () {
var command = 'renameSeries';
this.idle = false;
this.ui.rename.toggleClass('icon-nd-rename icon-nd-spinner');
var properties = {
seriesId : this.model.get('id')
};
var self = this;
var commandPromise = CommandController.Execute(command, properties);
commandPromise.fail(function (options) {
if (options.readyState === 0 || options.status === 0) {
return;
}
Messenger.show({
message: 'Season rename failed',
type : 'error'
});
});
commandPromise.always(function () {
if (!self.isClosed) {
self.ui.rename.toggleClass('icon-nd-rename icon-nd-spinner');
self.idle = true;
}
});
} }
}); });
}); });

@ -5,6 +5,7 @@
<i class="icon-bookmark x-monitored clickable" title="Toggle monitored state for entire series"/> <i class="icon-bookmark x-monitored clickable" title="Toggle monitored state for entire series"/>
{{title}} {{title}}
<span class="series-actions pull-right"> <span class="series-actions pull-right">
<i class="icon-nd-rename x-rename" title="Rename Series"/>
<i class="icon-refresh x-refresh" title="Update Series"/> <i class="icon-refresh x-refresh" title="Update Series"/>
<i class="icon-nd-edit x-edit" title="Edit series"/> <i class="icon-nd-edit x-edit" title="Edit series"/>
</span> </span>

Loading…
Cancel
Save