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

pull/4/head
Taloth Saldono 10 years ago
parent 4a6778c609
commit b540307dbd

@ -5,11 +5,15 @@ using FizzWare.NBuilder;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
{ {
@ -24,7 +28,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
public void Setup() public void Setup()
{ {
_series = Builder<Series>.CreateNew() _series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\Series") .With(s => s.Path = @"C:\Test\TV\Series".AsOsAgnostic())
.Build(); .Build();
_episodeFile = Builder<EpisodeFile>.CreateNew() _episodeFile = Builder<EpisodeFile>.CreateNew()
@ -43,7 +47,15 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
Mocker.GetMock<IBuildFileNames>() Mocker.GetMock<IBuildFileNames>()
.Setup(s => s.BuildFilePath(It.IsAny<Series>(), It.IsAny<Int32>(), It.IsAny<String>(), It.IsAny<String>())) .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>() Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(It.IsAny<String>())) .Setup(s => s.FileExists(It.IsAny<String>()))
@ -73,5 +85,39 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeFileMovingServiceTests
Subject.MoveEpisodeFile(_episodeFile, _localEpisode); 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());
}
} }
} }

@ -7,6 +7,8 @@ using NzbDrone.Common.Disk;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -27,6 +29,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IBuildFileNames _buildFileNames; private readonly IBuildFileNames _buildFileNames;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly IMediaFileAttributeService _mediaFileAttributeService; private readonly IMediaFileAttributeService _mediaFileAttributeService;
private readonly IEventAggregator _eventAggregator;
private readonly IConfigService _configService; private readonly IConfigService _configService;
private readonly Logger _logger; private readonly Logger _logger;
@ -35,6 +38,7 @@ namespace NzbDrone.Core.MediaFiles
IBuildFileNames buildFileNames, IBuildFileNames buildFileNames,
IDiskProvider diskProvider, IDiskProvider diskProvider,
IMediaFileAttributeService mediaFileAttributeService, IMediaFileAttributeService mediaFileAttributeService,
IEventAggregator eventAggregator,
IConfigService configService, IConfigService configService,
Logger logger) Logger logger)
{ {
@ -43,6 +47,7 @@ namespace NzbDrone.Core.MediaFiles
_buildFileNames = buildFileNames; _buildFileNames = buildFileNames;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_mediaFileAttributeService = mediaFileAttributeService; _mediaFileAttributeService = mediaFileAttributeService;
_eventAggregator = eventAggregator;
_configService = configService; _configService = configService;
_logger = logger; _logger = logger;
} }
@ -53,6 +58,8 @@ namespace NzbDrone.Core.MediaFiles
var newFileName = _buildFileNames.BuildFileName(episodes, series, episodeFile); var newFileName = _buildFileNames.BuildFileName(episodes, series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.RelativePath)); 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); _logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
return TransferFile(episodeFile, series, episodes, filePath, TransferMode.Move); 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 newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path)); 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); _logger.Debug("Moving episode file: {0} to {1}", episodeFile.Path, filePath);
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move); 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 newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path)); var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
if (_configService.CopyUsingHardlinks) if (_configService.CopyUsingHardlinks)
{ {
_logger.Debug("Hardlinking episode file: {0} to {1}", episodeFile.Path, filePath); _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); 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); _logger.Debug("{0} [{1}] > [{2}]", mode, episodeFilePath, destinationFilename);
_diskProvider.TransferFile(episodeFilePath, destinationFilename, mode); _diskProvider.TransferFile(episodeFilePath, destinationFilename, mode);
@ -150,5 +140,74 @@ namespace NzbDrone.Core.MediaFiles
return episodeFile; 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);
}
} }
} }

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

@ -19,6 +19,7 @@ namespace NzbDrone.Core.Metadata
{ {
public class MetadataService : IHandle<MediaCoversUpdatedEvent>, public class MetadataService : IHandle<MediaCoversUpdatedEvent>,
IHandle<EpisodeImportedEvent>, IHandle<EpisodeImportedEvent>,
IHandle<EpisodeFolderCreatedEvent>,
IHandle<SeriesRenamedEvent> IHandle<SeriesRenamedEvent>
{ {
private readonly IMetadataFactory _metadataFactory; 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) public void Handle(SeriesRenamedEvent message)
{ {
var seriesMetadata = _metadataFileService.GetFilesBySeries(message.Series.Id); var seriesMetadata = _metadataFileService.GetFilesBySeries(message.Series.Id);

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

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

Loading…
Cancel
Save