Fixed: Series/season metadata also created on episode import.

This commit is contained in:
Taloth Saldono 2015-05-24 10:51:42 +02:00
parent 4a6778c609
commit b540307dbd
6 changed files with 195 additions and 35 deletions

View File

@ -5,11 +5,15 @@ using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
{
@ -24,7 +28,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\Series")
.With(s => s.Path = @"C:\Test\TV\Series".AsOsAgnostic())
.Build();
_episodeFile = Builder<EpisodeFile>.CreateNew()
@ -43,7 +47,15 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.BuildFilePath(It.IsAny<Series>(), It.IsAny<Int32>(), It.IsAny<String>(), It.IsAny<String>()))
.Returns(@"C:\Test\TV\Series\File Name.avi");
.Returns(@"C:\Test\TV\Series\Season 01\File Name.avi".AsOsAgnostic());
Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.BuildSeasonPath(It.IsAny<Series>(), It.IsAny<int>()))
.Returns(@"C:\Test\TV\Series\Season 01".AsOsAgnostic());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(@"C:\Test\TV"))
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(It.IsAny<String>()))
@ -73,5 +85,39 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
}
[Test]
public void should_notify_on_series_folder_creation()
{
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
Mocker.GetMock<IEventAggregator>()
.Verify(s => s.PublishEvent<EpisodeFolderCreatedEvent>(It.Is<EpisodeFolderCreatedEvent>(p =>
p.SeriesFolder.IsNotNullOrWhiteSpace())), Times.Once());
}
[Test]
public void should_notify_on_season_folder_creation()
{
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
Mocker.GetMock<IEventAggregator>()
.Verify(s => s.PublishEvent<EpisodeFolderCreatedEvent>(It.Is<EpisodeFolderCreatedEvent>(p =>
p.SeasonFolder.IsNotNullOrWhiteSpace())), Times.Once());
}
[Test]
public void should_not_notify_if_series_folder_already_exists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(_series.Path))
.Returns(true);
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
Mocker.GetMock<IEventAggregator>()
.Verify(s => s.PublishEvent<EpisodeFolderCreatedEvent>(It.Is<EpisodeFolderCreatedEvent>(p =>
p.SeriesFolder.IsNotNullOrWhiteSpace())), Times.Never());
}
}
}

View File

@ -7,6 +7,8 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
@ -27,6 +29,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IBuildFileNames _buildFileNames;
private readonly IDiskProvider _diskProvider;
private readonly IMediaFileAttributeService _mediaFileAttributeService;
private readonly IEventAggregator _eventAggregator;
private readonly IConfigService _configService;
private readonly Logger _logger;
@ -35,6 +38,7 @@ namespace NzbDrone.Core.MediaFiles
IBuildFileNames buildFileNames,
IDiskProvider diskProvider,
IMediaFileAttributeService mediaFileAttributeService,
IEventAggregator eventAggregator,
IConfigService configService,
Logger logger)
{
@ -43,6 +47,7 @@ namespace NzbDrone.Core.MediaFiles
_buildFileNames = buildFileNames;
_diskProvider = diskProvider;
_mediaFileAttributeService = mediaFileAttributeService;
_eventAggregator = eventAggregator;
_configService = configService;
_logger = logger;
}
@ -53,6 +58,8 @@ namespace NzbDrone.Core.MediaFiles
var newFileName = _buildFileNames.BuildFileName(episodes, series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.RelativePath));
EnsureEpisodeFolder(episodeFile, series, episodes.Select(v => v.SeasonNumber).First(), filePath);
_logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
return TransferFile(episodeFile, series, episodes, filePath, TransferMode.Move);
@ -63,6 +70,8 @@ namespace NzbDrone.Core.MediaFiles
var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
_logger.Debug("Moving episode file: {0} to {1}", episodeFile.Path, filePath);
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move);
@ -73,6 +82,8 @@ namespace NzbDrone.Core.MediaFiles
var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
if (_configService.CopyUsingHardlinks)
{
_logger.Debug("Hardlinking episode file: {0} to {1}", episodeFile.Path, filePath);
@ -101,27 +112,6 @@ namespace NzbDrone.Core.MediaFiles
throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
}
var directoryName = new FileInfo(destinationFilename).DirectoryName;
if (!_diskProvider.FolderExists(directoryName))
{
try
{
_diskProvider.CreateFolder(directoryName);
}
catch (IOException ex)
{
_logger.ErrorException("Unable to create directory: " + directoryName, ex);
}
_mediaFileAttributeService.SetFolderPermissions(directoryName);
if (!directoryName.PathEquals(series.Path))
{
_mediaFileAttributeService.SetFolderPermissions(series.Path);
}
}
_logger.Debug("{0} [{1}] > [{2}]", mode, episodeFilePath, destinationFilename);
_diskProvider.TransferFile(episodeFilePath, destinationFilename, mode);
@ -150,5 +140,74 @@ namespace NzbDrone.Core.MediaFiles
return episodeFile;
}
private void EnsureEpisodeFolder(EpisodeFile episodeFile, LocalEpisode localEpisode, string filePath)
{
EnsureEpisodeFolder(episodeFile, localEpisode.Series, localEpisode.SeasonNumber, filePath);
}
private void EnsureEpisodeFolder(EpisodeFile episodeFile, Series series, int seasonNumber, string filePath)
{
var episodeFolder = Path.GetDirectoryName(filePath);
var seasonFolder = _buildFileNames.BuildSeasonPath(series, seasonNumber);
var seriesFolder = series.Path;
var rootFolder = Path.GetDirectoryName(seriesFolder);
if (!_diskProvider.FolderExists(rootFolder))
{
throw new DirectoryNotFoundException(string.Format("Root folder '{0}' was not found.", rootFolder));
}
var changed = false;
var newEvent = new EpisodeFolderCreatedEvent(series, episodeFile);
if (!_diskProvider.FolderExists(seriesFolder))
{
CreateFolder(seriesFolder);
newEvent.SeriesFolder = seriesFolder;
changed = true;
}
if (seriesFolder != seasonFolder && !_diskProvider.FolderExists(seasonFolder))
{
CreateFolder(seasonFolder);
newEvent.SeasonFolder = seasonFolder;
changed = true;
}
if (seasonFolder != episodeFolder && !_diskProvider.FolderExists(episodeFolder))
{
CreateFolder(episodeFolder);
newEvent.EpisodeFolder = episodeFolder;
changed = true;
}
if (changed)
{
_eventAggregator.PublishEvent(newEvent);
}
}
private void CreateFolder(string directoryName)
{
Ensure.That(directoryName, () => directoryName).IsNotNullOrWhiteSpace();
var parentFolder = Path.GetDirectoryName(directoryName);
if (!_diskProvider.FolderExists(parentFolder))
{
CreateFolder(parentFolder);
}
try
{
_diskProvider.CreateFolder(directoryName);
}
catch (IOException ex)
{
_logger.ErrorException("Unable to create directory: " + directoryName, ex);
}
_mediaFileAttributeService.SetFolderPermissions(directoryName);
}
}
}

View File

@ -0,0 +1,20 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.MediaFiles.Events
{
public class EpisodeFolderCreatedEvent : IEvent
{
public Series Series { get; private set; }
public EpisodeFile EpisodeFile { get; private set; }
public string SeriesFolder { get; set; }
public string SeasonFolder { get; set; }
public string EpisodeFolder { get; set; }
public EpisodeFolderCreatedEvent(Series series, EpisodeFile episodeFile)
{
Series = series;
EpisodeFile = episodeFile;
}
}
}

View File

@ -19,6 +19,7 @@ namespace NzbDrone.Core.Metadata
{
public class MetadataService : IHandle<MediaCoversUpdatedEvent>,
IHandle<EpisodeImportedEvent>,
IHandle<EpisodeFolderCreatedEvent>,
IHandle<SeriesRenamedEvent>
{
private readonly IMetadataFactory _metadataFactory;
@ -100,6 +101,35 @@ namespace NzbDrone.Core.Metadata
}
}
public void Handle(EpisodeFolderCreatedEvent message)
{
if (message.SeriesFolder.IsNullOrWhiteSpace() && message.SeasonFolder.IsNullOrWhiteSpace())
{
return;
}
var seriesMetadataFiles = _metadataFileService.GetFilesBySeries(message.Series.Id);
foreach (var consumer in _metadataFactory.Enabled())
{
var files = new List<MetadataFile>();
var consumerFiles = GetMetadataFilesForConsumer(consumer, seriesMetadataFiles);
if (message.SeriesFolder.IsNotNullOrWhiteSpace())
{
files.AddIfNotNull(ProcessSeriesMetadata(consumer, message.Series, consumerFiles));
files.AddRange(ProcessSeriesImages(consumer, message.Series, consumerFiles));
}
if (message.SeasonFolder.IsNotNullOrWhiteSpace())
{
files.AddRange(ProcessSeasonImages(consumer, message.Series, consumerFiles));
}
_eventAggregator.PublishEvent(new MetadataFilesUpdated(files));
}
}
public void Handle(SeriesRenamedEvent message)
{
var seriesMetadata = _metadataFileService.GetFilesBySeries(message.Series.Id);

View File

@ -608,6 +608,7 @@
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeFileDeletedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeFolderCreatedEvent.cs" />
<Compile Include="MediaFiles\Events\EpisodeImportedEvent.cs" />
<Compile Include="MediaFiles\Events\SeriesRenamedEvent.cs" />
<Compile Include="MediaFiles\Events\SeriesScanSkippedEvent.cs" />

View File

@ -17,6 +17,7 @@ namespace NzbDrone.Core.Organizer
{
string BuildFileName(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig namingConfig = null);
string BuildFilePath(Series series, Int32 seasonNumber, String fileName, String extension);
string BuildSeasonPath(Series series, Int32 seasonNumber);
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
string GetSeasonFolder(Series series, Int32 seasonNumber, NamingConfig namingConfig = null);
@ -138,29 +139,32 @@ namespace NzbDrone.Core.Organizer
{
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
string path = series.Path;
var path = BuildSeasonPath(series, seasonNumber);
return Path.Combine(path, fileName + extension);
}
public string BuildSeasonPath(Series series, int seasonNumber)
{
var path = series.Path;
if (series.SeasonFolder)
{
string seasonFolder;
if (seasonNumber == 0)
{
seasonFolder = "Specials";
path = Path.Combine(path, "Specials");
}
else
{
var nameSpec = _namingConfigService.GetConfig();
seasonFolder = GetSeasonFolder(series, seasonNumber, nameSpec);
var seasonFolder = GetSeasonFolder(series, seasonNumber);
seasonFolder = CleanFileName(seasonFolder);
path = Path.Combine(path, seasonFolder);
}
seasonFolder = CleanFileName(seasonFolder);
path = Path.Combine(path, seasonFolder);
}
return Path.Combine(path, fileName + extension);
return path;
}
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)