diff --git a/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs b/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs index 95b3c11f6..dae700425 100644 --- a/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs +++ b/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs @@ -94,8 +94,8 @@ public void should_move_new_downloads() { Subject.Import(new List {_approvedDecisions.First()}, true); - Mocker.GetMock() - .Verify(v => v.MoveEpisodeFile(It.IsAny(), _approvedDecisions.First().LocalEpisode), + Mocker.GetMock() + .Verify(v => v.UpgradeEpisodeFile(It.IsAny(), _approvedDecisions.First().LocalEpisode), Times.Once()); } @@ -113,8 +113,8 @@ public void should_not_move_existing_files() { Subject.Import(new List { _approvedDecisions.First() }); - Mocker.GetMock() - .Verify(v => v.MoveEpisodeFile(It.IsAny(), _approvedDecisions.First().LocalEpisode), + Mocker.GetMock() + .Verify(v => v.UpgradeEpisodeFile(It.IsAny(), _approvedDecisions.First().LocalEpisode), Times.Never()); } diff --git a/NzbDrone.Core.Test/MediaFileTests/UpgradeMediaFileServiceFixture.cs b/NzbDrone.Core.Test/MediaFileTests/UpgradeMediaFileServiceFixture.cs new file mode 100644 index 000000000..80dbeac3f --- /dev/null +++ b/NzbDrone.Core.Test/MediaFileTests/UpgradeMediaFileServiceFixture.cs @@ -0,0 +1,125 @@ +using System.IO; +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using Marr.Data; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Organizer; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.MediaFileTests +{ + public class UpgradeMediaFileServiceFixture : CoreTest + { + private EpisodeFile _episodeFile; + private LocalEpisode _localEpisode; + + [SetUp] + public void Setup() + { + _localEpisode = new LocalEpisode(); + + _episodeFile = Builder + .CreateNew() + .Build(); + } + + private void GivenSingleEpisodeWithSingleEpisodeFile() + { + _localEpisode.Episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.EpisodeFileId = 1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Id = 1, + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + })) + .Build() + .ToList(); + } + + private void GivenMultipleEpisodesWithSingleEpisodeFile() + { + _localEpisode.Episodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.EpisodeFileId = 1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Id = 1, + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + })) + .Build() + .ToList(); + } + + private void GivenMultipleEpisodesWithMultipleEpisodeFiles() + { + _localEpisode.Episodes = Builder.CreateListOfSize(2) + .TheFirst(1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Id = 1, + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e01.avi", + })) + .TheNext(1) + .With(e => e.EpisodeFile = new LazyLoaded( + new EpisodeFile + { + Id = 2, + Path = @"C:\Test\30 Rock\Season 01\30.rock.s01e02.avi", + })) + .Build() + .ToList(); + } + + [Test] + public void should_delete_single_episode_file_once() + { + GivenSingleEpisodeWithSingleEpisodeFile(); + + Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); + + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Once()); + } + + [Test] + public void should_delete_the_same_episode_file_only_once() + { + GivenMultipleEpisodesWithSingleEpisodeFile(); + + Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); + + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Once()); + } + + [Test] + public void should_delete_multiple_different_episode_files() + { + GivenMultipleEpisodesWithMultipleEpisodeFiles(); + + Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); + + Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Exactly(2)); + } + + [Test] + public void should_delete_episode_file_from_database() + { + GivenSingleEpisodeWithSingleEpisodeFile(); + + Subject.UpgradeEpisodeFile(_episodeFile, _localEpisode); + + Mocker.GetMock().Verify(v => v.Delete(It.IsAny()), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 791bfd19b..1eaf4cc46 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -147,6 +147,7 @@ + diff --git a/NzbDrone.Core/MediaFiles/EpisodeFile.cs b/NzbDrone.Core/MediaFiles/EpisodeFile.cs index b95b3e1e5..943bc731a 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeFile.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeFile.cs @@ -12,9 +12,12 @@ public class EpisodeFile : ModelBase public long Size { get; set; } public DateTime DateAdded { get; set; } public string SceneName { get; set; } - public QualityModel Quality { get; set; } - public LazyList Episodes { get; set; } + + public override string ToString() + { + return String.Format("[{0}] {1}", Id, Path); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs b/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs index ae925428c..c59df33d5 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs @@ -27,7 +27,13 @@ public class MoveEpisodeFiles : IMoveEpisodeFiles private readonly IDiskProvider _diskProvider; private readonly Logger _logger; - public MoveEpisodeFiles(ISeriesRepository seriesRepository, IEpisodeService episodeService, IBuildFileNames buildFileNames, IMediaFileService mediaFileService, IMessageAggregator messageAggregator, IDiskProvider diskProvider, Logger logger) + public MoveEpisodeFiles(ISeriesRepository seriesRepository, + IEpisodeService episodeService, + IBuildFileNames buildFileNames, + IMediaFileService mediaFileService, + IMessageAggregator messageAggregator, + IDiskProvider diskProvider, + Logger logger) { _seriesRepository = seriesRepository; _episodeService = episodeService; diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index e4557d2e3..ffb029344 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -17,19 +17,19 @@ public interface IImportApprovedEpisodes public class ImportApprovedEpisodes : IImportApprovedEpisodes { - private readonly IMoveEpisodeFiles _episodeFileMover; + private readonly IUpgradeMediaFiles _episodeFileUpgrader; private readonly IMediaFileService _mediaFileService; private readonly IDiskProvider _diskProvider; private readonly IMessageAggregator _messageAggregator; private readonly Logger _logger; - public ImportApprovedEpisodes(IMoveEpisodeFiles episodeFileMover, + public ImportApprovedEpisodes(IUpgradeMediaFiles episodeFileUpgrader, IMediaFileService mediaFileService, IDiskProvider diskProvider, IMessageAggregator messageAggregator, Logger logger) { - _episodeFileMover = episodeFileMover; + _episodeFileUpgrader = episodeFileUpgrader; _mediaFileService = mediaFileService; _diskProvider = diskProvider; _messageAggregator = messageAggregator; @@ -68,7 +68,7 @@ public List Import(List decisions, bool newDownl if (newDownload) { - episodeFile = _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode); + episodeFile = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode); _messageAggregator.PublishEvent(new EpisodeImportedEvent(episodeFile)); } diff --git a/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs b/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs index fbf664d11..dc946bed9 100644 --- a/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs +++ b/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs @@ -9,7 +9,15 @@ namespace NzbDrone.Core.MediaFiles { - public class RecycleBinProvider : IHandleAsync, IExecute + public interface IRecycleBinProvider + { + void DeleteFolder(string path); + void DeleteFile(string path); + void Empty(); + void Cleanup(); + } + + public class RecycleBinProvider : IHandleAsync, IExecute, IRecycleBinProvider { private readonly IDiskProvider _diskProvider; private readonly IConfigService _configService; @@ -22,11 +30,7 @@ public RecycleBinProvider(IDiskProvider diskProvider, IConfigService configServi _configService = configService; } - public RecycleBinProvider() - { - } - - public virtual void DeleteFolder(string path) + public void DeleteFolder(string path) { logger.Trace("Attempting to send '{0}' to recycling bin", path); var recyclingBin = _configService.RecycleBin; @@ -56,7 +60,7 @@ public virtual void DeleteFolder(string path) } } - public virtual void DeleteFile(string path) + public void DeleteFile(string path) { logger.Trace("Attempting to send '{0}' to recycling bin", path); var recyclingBin = _configService.RecycleBin; @@ -79,7 +83,7 @@ public virtual void DeleteFile(string path) } } - public virtual void Empty() + public void Empty() { if (String.IsNullOrWhiteSpace(_configService.RecycleBin)) { @@ -102,7 +106,7 @@ public virtual void Empty() logger.Trace("Recycling Bin has been emptied."); } - public virtual void Cleanup() + public void Cleanup() { if (String.IsNullOrWhiteSpace(_configService.RecycleBin)) { diff --git a/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs b/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs new file mode 100644 index 000000000..d6ee99bbb --- /dev/null +++ b/NzbDrone.Core/MediaFiles/UpgradeMediaFileService.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog; +using NzbDrone.Common; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles +{ + public interface IUpgradeMediaFiles + { + EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode); + } + + public class UpgradeMediaFileService : IUpgradeMediaFiles + { + private readonly IRecycleBinProvider _recycleBinProvider; + private readonly IMediaFileService _mediaFileService; + private readonly IMoveEpisodeFiles _episodeFileMover; + private readonly Logger _logger; + + public UpgradeMediaFileService(IRecycleBinProvider recycleBinProvider, + IMediaFileService mediaFileService, + IMoveEpisodeFiles episodeFileMover, + Logger logger) + { + _recycleBinProvider = recycleBinProvider; + _mediaFileService = mediaFileService; + _episodeFileMover = episodeFileMover; + _logger = logger; + } + + public EpisodeFile UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode) + { + var existingFiles = localEpisode.Episodes + .Where(e => e.EpisodeFileId > 0) + .Select(e => e.EpisodeFile.Value) + .GroupBy(e => e.Id); + + foreach (var existingFile in existingFiles) + { + var file = existingFile.First(); + _logger.Trace("Removing existing episode file: {0}", file); + + _recycleBinProvider.DeleteFile(file.Path); + _mediaFileService.Delete(file); + } + + _logger.Trace("Moving episode file: {0}", episodeFile); + return _episodeFileMover.MoveEpisodeFile(episodeFile, localEpisode); + } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index e289f24d3..990cee3a1 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -287,6 +287,7 @@ +