From 9a8414eddeaa97ac93cf4f7e043ff4b9dc3f7d7b Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 7 Mar 2013 13:34:56 +0900 Subject: [PATCH] moved cleanup of deleted files to their own service. detaching of episodes when files are deleted is done through events now. --- .../IndexerSearchTestBase.cs | 2 +- .../IndexerSearchTests/TestSearch.cs | 2 +- .../MediaFileTests/GhostFileCleanupFixture.cs | 64 ++++++++++ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 2 +- .../DiskScanProviderTests/CleanUpFixture.cs | 117 ------------------ NzbDrone.Core/ContainerExtensions.cs | 4 +- NzbDrone.Core/Datastore/BasicRepository.cs | 6 + .../Sabnzbd/SabAutoConfigureService.cs} | 5 +- .../IndexerSearch/DailyEpisodeSearch.cs | 2 +- NzbDrone.Core/IndexerSearch/EpisodeSearch.cs | 2 +- .../{SearchBase.cs => IndexerSearchBase.cs} | 15 +-- .../IndexerSearch/PartialSeasonSearch.cs | 2 +- .../Events/EpisodeFileDeletedEvent.cs | 14 +++ .../MediaFiles/GhostFileCleanupService.cs | 48 +++++++ NzbDrone.Core/MediaFiles/MediaFileService.cs | 19 ++- NzbDrone.Core/NzbDrone.Core.csproj | 8 +- NzbDrone.Core/Providers/DiskScanProvider.cs | 45 +------ NzbDrone.Core/Tv/EpisodeService.cs | 22 +++- NzbDrone.Test.Common/TestBase.cs | 9 +- 19 files changed, 195 insertions(+), 193 deletions(-) create mode 100644 NzbDrone.Core.Test/MediaFileTests/GhostFileCleanupFixture.cs delete mode 100644 NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs rename NzbDrone.Core/{Providers/AutoConfigureProvider.cs => Download/Clients/Sabnzbd/SabAutoConfigureService.cs} (96%) rename NzbDrone.Core/IndexerSearch/{SearchBase.cs => IndexerSearchBase.cs} (91%) create mode 100644 NzbDrone.Core/MediaFiles/Events/EpisodeFileDeletedEvent.cs create mode 100644 NzbDrone.Core/MediaFiles/GhostFileCleanupService.cs diff --git a/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs b/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs index c38d0908f..81d8fa71f 100644 --- a/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs +++ b/NzbDrone.Core.Test/IndexerSearchTests/IndexerSearchTestBase.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests { public abstract class IndexerSearchTestBase : CoreTest - where TSearch : SearchBase + where TSearch : IndexerSearchBase { protected Series _series; protected Episode _episode; diff --git a/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs b/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs index 333fa5882..9bc8104c2 100644 --- a/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs +++ b/NzbDrone.Core.Test/IndexerSearchTests/TestSearch.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests { - public class TestSearch : SearchBase + public class TestSearch : IndexerSearchBase { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); diff --git a/NzbDrone.Core.Test/MediaFileTests/GhostFileCleanupFixture.cs b/NzbDrone.Core.Test/MediaFileTests/GhostFileCleanupFixture.cs new file mode 100644 index 000000000..4f8b9e32f --- /dev/null +++ b/NzbDrone.Core.Test/MediaFileTests/GhostFileCleanupFixture.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using System.Linq; + +namespace NzbDrone.Core.Test.MediaFileTests +{ + public class GhostFileCleanupFixture : CoreTest + { + + private void GiveEpisodeFiles(IEnumerable episodeFiles) + { + Mocker.GetMock() + .Setup(c => c.GetFilesBySeries(It.IsAny())) + .Returns(episodeFiles.ToList()); + } + + + private const string DeletedPath = "ANY FILE WITH THIS PATH IS CONSIDERED DELETED!"; + + [SetUp] + public void SetUp() + { + Mocker.GetMock() + .Setup(e => e.FileExists(It.Is(c => c != DeletedPath))) + .Returns(true); + } + + [Test] + public void should_skip_files_that_exist_in_disk() + { + var episodeFiles = Builder.CreateListOfSize(10) + .Build(); + + GiveEpisodeFiles(episodeFiles); + + Subject.RemoveNonExistingFiles(0); + + Mocker.GetMock().Verify(c => c.UpdateEpisode(It.IsAny()), Times.Never()); + } + + [Test] + public void should_delete_none_existing_files() + { + var episodeFiles = Builder.CreateListOfSize(10) + .Random(2) + .With(c => c.Path = DeletedPath) + .Build(); + + GiveEpisodeFiles(episodeFiles); + + Subject.RemoveNonExistingFiles(0); + + Mocker.GetMock().Verify(c => c.Delete(It.Is(e => e.Path == DeletedPath)), Times.Exactly(2)); + + } + } +} diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 36d6d7c52..f2a92f00b 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -151,6 +151,7 @@ + @@ -172,7 +173,6 @@ - diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs deleted file mode 100644 index c9bdca91d..000000000 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Configuration; -using NzbDrone.Core.MediaFiles; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; - -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests -{ - // ReSharper disable InconsistentNaming - public class CleanUpFixture : CoreTest - { - [Test] - public void should_skip_existing_files() - { - var episodes = Builder.CreateListOfSize(10).Build(); - - Mocker.GetMock() - .Setup(e => e.FileExists(It.IsAny())) - .Returns(true); - - - //Act - Mocker.Resolve().CleanUp(episodes); - - //Assert - Mocker.VerifyAllMocks(); - } - - [Test] - public void should_delete_none_existing_files() - { - var episodes = Builder.CreateListOfSize(10).Build(); - - Mocker.GetMock() - .Setup(e => e.FileExists(It.IsAny())) - .Returns(false); - - Mocker.GetMock() - .Setup(e => e.GetEpisodesByFileId(It.IsAny())) - .Returns(new List()); - - Mocker.GetMock() - .Setup(e => e.Delete(It.IsAny())); - - - //Act - Mocker.Resolve().CleanUp(episodes); - - //Assert - Mocker.VerifyAllMocks(); - - Mocker.GetMock() - .Verify(e => e.GetEpisodesByFileId(It.IsAny()), Times.Exactly(10)); - - Mocker.GetMock() - .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); - - } - - [Test] - public void should_delete_none_existing_files_remove_links_to_episodes() - { - var episodes = Builder.CreateListOfSize(10).Build(); - - Mocker.GetMock() - .Setup(e => e.FileExists(It.IsAny())) - .Returns(false); - - Mocker.GetMock() - .Setup(e => e.GetEpisodesByFileId(It.IsAny())) - .Returns(new List { new Episode { EpisodeFile = new EpisodeFile { Id = 10 } }, new Episode { EpisodeFile = new EpisodeFile { Id = 10 } } }); - - Mocker.GetMock() - .Setup(e => e.UpdateEpisode(It.IsAny())); - - Mocker.GetMock() - .Setup(e => e.Delete(It.IsAny())); - - Mocker.GetMock() - .SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes) - .Returns(true); - - //Act - Mocker.Resolve().CleanUp(episodes); - - //Assert - Mocker.VerifyAllMocks(); - - Mocker.GetMock() - .Verify(e => e.GetEpisodesByFileId(It.IsAny()), Times.Exactly(10)); - - Mocker.GetMock() - .Verify(e => e.UpdateEpisode(It.Is(g => g.EpisodeFileId == 0)), Times.Exactly(20)); - - Mocker.GetMock() - .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); - - Mocker.GetMock() - .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); - - } - } -} diff --git a/NzbDrone.Core/ContainerExtensions.cs b/NzbDrone.Core/ContainerExtensions.cs index 6bdd41ae4..006040aef 100644 --- a/NzbDrone.Core/ContainerExtensions.cs +++ b/NzbDrone.Core/ContainerExtensions.cs @@ -42,8 +42,8 @@ private static void RegisterAssembly(this ContainerBuilder container, string ass .As().SingleInstance(); container.RegisterAssemblyTypes(assembly) - .Where(t => t.IsSubclassOf(typeof(SearchBase))) - .As().SingleInstance(); + .Where(t => t.IsSubclassOf(typeof(IndexerSearchBase))) + .As().SingleInstance(); container.RegisterAssemblyTypes(assembly) .Where(t => t.IsSubclassOf(typeof(ExternalNotificationBase))) diff --git a/NzbDrone.Core/Datastore/BasicRepository.cs b/NzbDrone.Core/Datastore/BasicRepository.cs index 57bfd60c8..740092a67 100644 --- a/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/NzbDrone.Core/Datastore/BasicRepository.cs @@ -14,6 +14,7 @@ namespace NzbDrone.Core.Datastore TModel Update(TModel model); TModel UpSert(TModel model); void Delete(int id); + void Delete(TModel model); IList InsertMany(IList model); IList UpdateMany(IList model); void DeleteMany(List model); @@ -67,6 +68,11 @@ public TModel Update(TModel model) return ObjectDatabase.Update(model); } + public void Delete(TModel model) + { + ObjectDatabase.Delete(model); + } + public IList InsertMany(IList model) { return ObjectDatabase.InsertMany(model); diff --git a/NzbDrone.Core/Providers/AutoConfigureProvider.cs b/NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs similarity index 96% rename from NzbDrone.Core/Providers/AutoConfigureProvider.cs rename to NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs index e431f6fa3..994e4ae5c 100644 --- a/NzbDrone.Core/Providers/AutoConfigureProvider.cs +++ b/NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs @@ -7,11 +7,10 @@ using System.Text.RegularExpressions; using NLog; using NzbDrone.Core.Model; -using NzbDrone.Core.Download.Clients.Sabnzbd; -namespace NzbDrone.Core.Providers +namespace NzbDrone.Core.Download.Clients.Sabnzbd { - public class AutoConfigureProvider + public class SabAutoConfigureService { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); diff --git a/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs b/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs index 25f68d6f1..679df3919 100644 --- a/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs +++ b/NzbDrone.Core/IndexerSearch/DailyEpisodeSearch.cs @@ -14,7 +14,7 @@ namespace NzbDrone.Core.IndexerSearch { - public class DailyEpisodeSearch : SearchBase + public class DailyEpisodeSearch : IndexerSearchBase { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); diff --git a/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs b/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs index 8772e5b96..f21f62a5f 100644 --- a/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs +++ b/NzbDrone.Core/IndexerSearch/EpisodeSearch.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.IndexerSearch { - public class EpisodeSearch : SearchBase + public class EpisodeSearch : IndexerSearchBase { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); diff --git a/NzbDrone.Core/IndexerSearch/SearchBase.cs b/NzbDrone.Core/IndexerSearch/IndexerSearchBase.cs similarity index 91% rename from NzbDrone.Core/IndexerSearch/SearchBase.cs rename to NzbDrone.Core/IndexerSearch/IndexerSearchBase.cs index 83476cfe3..4b4148ddb 100644 --- a/NzbDrone.Core/IndexerSearch/SearchBase.cs +++ b/NzbDrone.Core/IndexerSearch/IndexerSearchBase.cs @@ -13,18 +13,19 @@ namespace NzbDrone.Core.IndexerSearch { - public abstract class SearchBase + public abstract class IndexerSearchBase { private readonly ISeriesRepository _seriesRepository; - protected readonly IEpisodeService _episodeService; - protected readonly DownloadProvider _downloadProvider; + private readonly IEpisodeService _episodeService; + private readonly DownloadProvider _downloadProvider; + private readonly ISceneMappingService _sceneMappingService; + private readonly IDownloadDirector DownloadDirector; + protected readonly IIndexerService _indexerService; - protected readonly ISceneMappingService _sceneMappingService; - protected readonly IDownloadDirector DownloadDirector; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - protected SearchBase(ISeriesRepository seriesRepository, IEpisodeService episodeService, DownloadProvider downloadProvider, + protected IndexerSearchBase(ISeriesRepository seriesRepository, IEpisodeService episodeService, DownloadProvider downloadProvider, IIndexerService indexerService, ISceneMappingService sceneMappingService, IDownloadDirector downloadDirector) { @@ -36,7 +37,7 @@ protected SearchBase(ISeriesRepository seriesRepository, IEpisodeService episode DownloadDirector = downloadDirector; } - protected SearchBase() + protected IndexerSearchBase() { } diff --git a/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs b/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs index d816e7c8c..23dbe212b 100644 --- a/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs +++ b/NzbDrone.Core/IndexerSearch/PartialSeasonSearch.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.IndexerSearch { - public class PartialSeasonSearch : SearchBase + public class PartialSeasonSearch : IndexerSearchBase { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); diff --git a/NzbDrone.Core/MediaFiles/Events/EpisodeFileDeletedEvent.cs b/NzbDrone.Core/MediaFiles/Events/EpisodeFileDeletedEvent.cs new file mode 100644 index 000000000..275635ea9 --- /dev/null +++ b/NzbDrone.Core/MediaFiles/Events/EpisodeFileDeletedEvent.cs @@ -0,0 +1,14 @@ +using NzbDrone.Common.Eventing; + +namespace NzbDrone.Core.MediaFiles.Events +{ + public class EpisodeFileDeletedEvent : IEvent + { + public EpisodeFile EpisodeFile { get; private set; } + + public EpisodeFileDeletedEvent(EpisodeFile episodeFile) + { + EpisodeFile = episodeFile; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/GhostFileCleanupService.cs b/NzbDrone.Core/MediaFiles/GhostFileCleanupService.cs new file mode 100644 index 000000000..fd42666e1 --- /dev/null +++ b/NzbDrone.Core/MediaFiles/GhostFileCleanupService.cs @@ -0,0 +1,48 @@ +using System; +using NLog; +using NzbDrone.Common; + +namespace NzbDrone.Core.MediaFiles +{ + public interface ICleanGhostFiles + { + void RemoveNonExistingFiles(int seriesId); + } + + + public class GhostFileCleanupService : ICleanGhostFiles + { + private readonly IMediaFileService _mediaFileService; + private readonly DiskProvider _diskProvider; + private readonly Logger _logger; + + public GhostFileCleanupService(IMediaFileService mediaFileService, DiskProvider diskProvider, Logger logger) + { + _mediaFileService = mediaFileService; + _diskProvider = diskProvider; + _logger = logger; + } + + public void RemoveNonExistingFiles(int seriesId) + { + var seriesFile = _mediaFileService.GetFilesBySeries(seriesId); + + foreach (var episodeFile in seriesFile) + { + try + { + if (!_diskProvider.FileExists(episodeFile.Path)) + { + _logger.Trace("File [{0}] no longer exists on disk. removing from db", episodeFile.Path); + _mediaFileService.Delete(episodeFile); + } + } + catch (Exception ex) + { + var message = String.Format("Unable to cleanup EpisodeFile in DB: {0}", episodeFile.Id); + _logger.ErrorException(message, ex); + } + } + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/MediaFileService.cs b/NzbDrone.Core/MediaFiles/MediaFileService.cs index e5810efe6..0c6b7ed19 100644 --- a/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -3,6 +3,7 @@ using NLog; using NzbDrone.Common.Eventing; using NzbDrone.Core.Configuration; +using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; @@ -12,7 +13,7 @@ public interface IMediaFileService { EpisodeFile Add(EpisodeFile episodeFile); void Update(EpisodeFile episodeFile); - void Delete(int episodeFileId); + void Delete(EpisodeFile episodeFile); bool Exists(string path); EpisodeFile GetFileByPath(string path); List GetFilesBySeries(int seriesId); @@ -23,15 +24,17 @@ public class MediaFileService : IMediaFileService, IHandleAsync + @@ -215,7 +216,7 @@ - + @@ -249,6 +250,8 @@ + + @@ -338,9 +341,6 @@ - - Code - Code diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index ed28cd467..cd569600f 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -20,21 +20,21 @@ public class DiskScanProvider private static readonly string[] MediaExtensions = new[] { ".mkv", ".avi", ".wmv", ".mp4", ".mpg", ".mpeg", ".xvid", ".flv", ".mov", ".rm", ".rmvb", ".divx", ".dvr-ms", ".ts", ".ogm", ".m4v", ".strm" }; private readonly DiskProvider _diskProvider; private readonly IEpisodeService _episodeService; + private readonly ICleanGhostFiles _ghostFileCleaner; private readonly IMediaFileService _mediaFileService; - private readonly IConfigService _configService; private readonly IBuildFileNames _buildFileNames; private readonly RecycleBinProvider _recycleBinProvider; private readonly MediaInfoProvider _mediaInfoProvider; private readonly ISeriesRepository _seriesRepository; private readonly IEventAggregator _eventAggregator; - public DiskScanProvider(DiskProvider diskProvider, IEpisodeService episodeService, IMediaFileService mediaFileService, IConfigService configService, IBuildFileNames buildFileNames, + public DiskScanProvider(DiskProvider diskProvider, IEpisodeService episodeService, ICleanGhostFiles ghostFileCleaner, IMediaFileService mediaFileService, IConfigService configService, IBuildFileNames buildFileNames, RecycleBinProvider recycleBinProvider, MediaInfoProvider mediaInfoProvider, ISeriesRepository seriesRepository, IEventAggregator eventAggregator) { _diskProvider = diskProvider; _episodeService = episodeService; + _ghostFileCleaner = ghostFileCleaner; _mediaFileService = mediaFileService; - _configService = configService; _buildFileNames = buildFileNames; _recycleBinProvider = recycleBinProvider; _mediaInfoProvider = mediaInfoProvider; @@ -74,8 +74,7 @@ public virtual List Scan(Series series, string path) return new List(); } - var seriesFile = _mediaFileService.GetFilesBySeries(series.Id); - CleanUp(seriesFile); + _ghostFileCleaner.RemoveNonExistingFiles(series.Id); var mediaFileList = GetVideoFiles(path); var importedFiles = new List(); @@ -237,43 +236,7 @@ public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDown return episodeFile; } - /// - /// Removes files that no longer exist on disk from the database - /// - /// list of files to verify - public virtual void CleanUp(IList files) - { - foreach (var episodeFile in files) - { - try - { - if (!_diskProvider.FileExists(episodeFile.Path)) - { - Logger.Trace("File [{0}] no longer exists on disk. removing from db", episodeFile.Path); - //Set the EpisodeFileId for each episode attached to this file to 0 - foreach (var episode in _episodeService.GetEpisodesByFileId(episodeFile.Id)) - { - Logger.Trace("Detaching episode {0} from file.", episode.Id); - episode.EpisodeFile = null; - episode.Ignored = _configService.AutoIgnorePreviouslyDownloadedEpisodes; - episode.GrabDate = null; - episode.PostDownloadStatus = PostDownloadStatusType.Unknown; - _episodeService.UpdateEpisode(episode); - } - - //Delete it from the DB - Logger.Trace("Removing EpisodeFile from DB."); - _mediaFileService.Delete(episodeFile.Id); - } - } - catch (Exception ex) - { - var message = String.Format("Unable to cleanup EpisodeFile in DB: {0}", episodeFile.Id); - Logger.ErrorException(message, ex); - } - } - } public virtual void CleanUpDropFolder(string path) { diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index 540e19fdd..50de26d04 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -3,8 +3,10 @@ using System.Linq; using NLog; using NzbDrone.Common.Eventing; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download; +using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.MetadataSource; using NzbDrone.Core.Model; using NzbDrone.Core.Providers; @@ -37,6 +39,7 @@ public interface IEpisodeService public class EpisodeService : IEpisodeService, IHandle, + IHandle, IHandleAsync { @@ -46,13 +49,17 @@ public class EpisodeService : IEpisodeService, private readonly ISeasonRepository _seasonRepository; private readonly IEpisodeRepository _episodeRepository; private readonly IEventAggregator _eventAggregator; + private readonly IConfigService _configService; + private readonly Logger _logger; - public EpisodeService(TvDbProxy tvDbProxyProxy, ISeasonRepository seasonRepository, IEpisodeRepository episodeRepository, IEventAggregator eventAggregator) + public EpisodeService(TvDbProxy tvDbProxyProxy, ISeasonRepository seasonRepository, IEpisodeRepository episodeRepository, IEventAggregator eventAggregator, IConfigService configService, Logger logger) { _tvDbProxy = tvDbProxyProxy; _seasonRepository = seasonRepository; _episodeRepository = episodeRepository; _eventAggregator = eventAggregator; + _configService = configService; + _logger = logger; } public void AddEpisode(Episode episode) @@ -379,5 +386,18 @@ public void HandleAsync(SeriesDeletedEvent message) var episodes = GetEpisodeBySeries(message.Series.Id); _episodeRepository.DeleteMany(episodes); } + + public void Handle(EpisodeFileDeletedEvent message) + { + foreach (var episode in GetEpisodesByFileId(message.EpisodeFile.Id)) + { + _logger.Trace("Detaching episode {0} from file.", episode.Id); + episode.EpisodeFile = null; + episode.Ignored = _configService.AutoIgnorePreviouslyDownloadedEpisodes; + episode.GrabDate = null; + episode.PostDownloadStatus = PostDownloadStatusType.Unknown; + UpdateEpisode(episode); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Test.Common/TestBase.cs b/NzbDrone.Test.Common/TestBase.cs index 2c0055a66..52197c3f5 100644 --- a/NzbDrone.Test.Common/TestBase.cs +++ b/NzbDrone.Test.Common/TestBase.cs @@ -102,9 +102,16 @@ protected string GetTestFilePath(string fileName) return Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName); } + + protected void VerifyEventPublished() where TEvent : IEvent { - Mocker.GetMock().Verify(c => c.Publish(It.IsAny()), Times.Once()); + VerifyEventPublished(Times.Once()); + } + + protected void VerifyEventPublished(Times times) where TEvent : IEvent + { + Mocker.GetMock().Verify(c => c.Publish(It.IsAny()), times); } protected void VerifyEventNotPublished() where TEvent : IEvent