mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-17 15:02:34 +02:00
Fixed: Series/season metadata also created on episode import.
This commit is contained in:
parent
4a6778c609
commit
b540307dbd
@ -5,11 +5,15 @@
|
||||
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 @@ public class MoveEpisodeFileFixture : CoreTest<EpisodeFileMovingService>
|
||||
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 @@ public void Setup()
|
||||
|
||||
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 @@ public void should_catch_InvalidOperationException_during_folder_inheritance()
|
||||
|
||||
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.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 @@ public class EpisodeFileMovingService : IMoveEpisodeFiles
|
||||
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 @@ public EpisodeFileMovingService(IEpisodeService episodeService,
|
||||
IBuildFileNames buildFileNames,
|
||||
IDiskProvider diskProvider,
|
||||
IMediaFileAttributeService mediaFileAttributeService,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
@ -43,6 +47,7 @@ public EpisodeFileMovingService(IEpisodeService episodeService,
|
||||
_buildFileNames = buildFileNames;
|
||||
_diskProvider = diskProvider;
|
||||
_mediaFileAttributeService = mediaFileAttributeService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -53,6 +58,8 @@ public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series)
|
||||
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 @@ public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEp
|
||||
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 @@ public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEp
|
||||
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 @@ private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Ep
|
||||
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 @@ private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Ep
|
||||
|
||||
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>,
|
||||
IHandle<EpisodeImportedEvent>,
|
||||
IHandle<EpisodeFolderCreatedEvent>,
|
||||
IHandle<SeriesRenamedEvent>
|
||||
{
|
||||
private readonly IMetadataFactory _metadataFactory;
|
||||
@ -100,6 +101,35 @@ public void Handle(EpisodeImportedEvent message)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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" />
|
||||
|
@ -17,6 +17,7 @@ public interface IBuildFileNames
|
||||
{
|
||||
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 @@ public string BuildFilePath(Series series, int seasonNumber, string fileName, st
|
||||
{
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user