mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
moved cleanup of deleted files to their own service.
detaching of episodes when files are deleted is done through events now.
This commit is contained in:
parent
40a959a309
commit
9a8414edde
@ -13,7 +13,7 @@
|
|||||||
namespace NzbDrone.Core.Test.IndexerSearchTests
|
namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||||
{
|
{
|
||||||
public abstract class IndexerSearchTestBase<TSearch> : CoreTest<TSearch>
|
public abstract class IndexerSearchTestBase<TSearch> : CoreTest<TSearch>
|
||||||
where TSearch : SearchBase
|
where TSearch : IndexerSearchBase
|
||||||
{
|
{
|
||||||
protected Series _series;
|
protected Series _series;
|
||||||
protected Episode _episode;
|
protected Episode _episode;
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.Test.IndexerSearchTests
|
namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||||
{
|
{
|
||||||
public class TestSearch : SearchBase
|
public class TestSearch : IndexerSearchBase
|
||||||
{
|
{
|
||||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
64
NzbDrone.Core.Test/MediaFileTests/GhostFileCleanupFixture.cs
Normal file
64
NzbDrone.Core.Test/MediaFileTests/GhostFileCleanupFixture.cs
Normal file
@ -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<GhostFileCleanupService>
|
||||||
|
{
|
||||||
|
|
||||||
|
private void GiveEpisodeFiles(IEnumerable<EpisodeFile> episodeFiles)
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IMediaFileService>()
|
||||||
|
.Setup(c => c.GetFilesBySeries(It.IsAny<int>()))
|
||||||
|
.Returns(episodeFiles.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private const string DeletedPath = "ANY FILE WITH THIS PATH IS CONSIDERED DELETED!";
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<DiskProvider>()
|
||||||
|
.Setup(e => e.FileExists(It.Is<String>(c => c != DeletedPath)))
|
||||||
|
.Returns(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_skip_files_that_exist_in_disk()
|
||||||
|
{
|
||||||
|
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(10)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
GiveEpisodeFiles(episodeFiles);
|
||||||
|
|
||||||
|
Subject.RemoveNonExistingFiles(0);
|
||||||
|
|
||||||
|
Mocker.GetMock<IEpisodeService>().Verify(c => c.UpdateEpisode(It.IsAny<Episode>()), Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_delete_none_existing_files()
|
||||||
|
{
|
||||||
|
var episodeFiles = Builder<EpisodeFile>.CreateListOfSize(10)
|
||||||
|
.Random(2)
|
||||||
|
.With(c => c.Path = DeletedPath)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
GiveEpisodeFiles(episodeFiles);
|
||||||
|
|
||||||
|
Subject.RemoveNonExistingFiles(0);
|
||||||
|
|
||||||
|
Mocker.GetMock<IMediaFileService>().Verify(c => c.Delete(It.Is<EpisodeFile>(e => e.Path == DeletedPath)), Times.Exactly(2));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -151,6 +151,7 @@
|
|||||||
<Compile Include="JobTests\RenameSeasonJobFixture.cs" />
|
<Compile Include="JobTests\RenameSeasonJobFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\LanguageSpecificationFixture.cs" />
|
||||||
<Compile Include="MediaCoverTests\MediaCoverServiceFixture.cs" />
|
<Compile Include="MediaCoverTests\MediaCoverServiceFixture.cs" />
|
||||||
|
<Compile Include="MediaFileTests\GhostFileCleanupFixture.cs" />
|
||||||
<Compile Include="MediaFileTests\MediaFileRepositoryFixture.cs" />
|
<Compile Include="MediaFileTests\MediaFileRepositoryFixture.cs" />
|
||||||
<Compile Include="ProviderTests\DownloadClientTests\NzbgetProviderTests\DownloadNzbFixture.cs" />
|
<Compile Include="ProviderTests\DownloadClientTests\NzbgetProviderTests\DownloadNzbFixture.cs" />
|
||||||
<Compile Include="ProviderTests\DownloadClientTests\NzbgetProviderTests\QueueFixture.cs" />
|
<Compile Include="ProviderTests\DownloadClientTests\NzbgetProviderTests\QueueFixture.cs" />
|
||||||
@ -172,7 +173,6 @@
|
|||||||
<Compile Include="Configuration\ConfigCachingFixture.cs" />
|
<Compile Include="Configuration\ConfigCachingFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\CustomStartDateSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\CustomStartDateSpecificationFixture.cs" />
|
||||||
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpFixture.cs" />
|
|
||||||
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpDropFolderFixture.cs" />
|
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpDropFolderFixture.cs" />
|
||||||
<Compile Include="ProviderTests\DiskScanProviderTests\GetVideoFilesFixture.cs" />
|
<Compile Include="ProviderTests\DiskScanProviderTests\GetVideoFilesFixture.cs" />
|
||||||
<Compile Include="ProviderTests\DiskScanProviderTests\ScanFixture.cs" />
|
<Compile Include="ProviderTests\DiskScanProviderTests\ScanFixture.cs" />
|
||||||
|
@ -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<EpisodeFile>.CreateListOfSize(10).Build();
|
|
||||||
|
|
||||||
Mocker.GetMock<DiskProvider>()
|
|
||||||
.Setup(e => e.FileExists(It.IsAny<String>()))
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
|
|
||||||
//Act
|
|
||||||
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Mocker.VerifyAllMocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_delete_none_existing_files()
|
|
||||||
{
|
|
||||||
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
|
|
||||||
|
|
||||||
Mocker.GetMock<DiskProvider>()
|
|
||||||
.Setup(e => e.FileExists(It.IsAny<String>()))
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeService>()
|
|
||||||
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
|
|
||||||
.Returns(new List<Episode>());
|
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
|
||||||
.Setup(e => e.Delete(It.IsAny<int>()));
|
|
||||||
|
|
||||||
|
|
||||||
//Act
|
|
||||||
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Mocker.VerifyAllMocks();
|
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeService>()
|
|
||||||
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
|
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
|
||||||
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_delete_none_existing_files_remove_links_to_episodes()
|
|
||||||
{
|
|
||||||
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
|
|
||||||
|
|
||||||
Mocker.GetMock<DiskProvider>()
|
|
||||||
.Setup(e => e.FileExists(It.IsAny<String>()))
|
|
||||||
.Returns(false);
|
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeService>()
|
|
||||||
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
|
|
||||||
.Returns(new List<Episode> { new Episode { EpisodeFile = new EpisodeFile { Id = 10 } }, new Episode { EpisodeFile = new EpisodeFile { Id = 10 } } });
|
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeService>()
|
|
||||||
.Setup(e => e.UpdateEpisode(It.IsAny<Episode>()));
|
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
|
||||||
.Setup(e => e.Delete(It.IsAny<int>()));
|
|
||||||
|
|
||||||
Mocker.GetMock<IConfigService>()
|
|
||||||
.SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes)
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
//Act
|
|
||||||
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Mocker.VerifyAllMocks();
|
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeService>()
|
|
||||||
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
|
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeService>()
|
|
||||||
.Verify(e => e.UpdateEpisode(It.Is<Episode>(g => g.EpisodeFileId == 0)), Times.Exactly(20));
|
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
|
||||||
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
|
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
|
||||||
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,8 +42,8 @@ private static void RegisterAssembly(this ContainerBuilder container, string ass
|
|||||||
.As<IndexerBase>().SingleInstance();
|
.As<IndexerBase>().SingleInstance();
|
||||||
|
|
||||||
container.RegisterAssemblyTypes(assembly)
|
container.RegisterAssemblyTypes(assembly)
|
||||||
.Where(t => t.IsSubclassOf(typeof(SearchBase)))
|
.Where(t => t.IsSubclassOf(typeof(IndexerSearchBase)))
|
||||||
.As<SearchBase>().SingleInstance();
|
.As<IndexerSearchBase>().SingleInstance();
|
||||||
|
|
||||||
container.RegisterAssemblyTypes(assembly)
|
container.RegisterAssemblyTypes(assembly)
|
||||||
.Where(t => t.IsSubclassOf(typeof(ExternalNotificationBase)))
|
.Where(t => t.IsSubclassOf(typeof(ExternalNotificationBase)))
|
||||||
|
@ -14,6 +14,7 @@ namespace NzbDrone.Core.Datastore
|
|||||||
TModel Update(TModel model);
|
TModel Update(TModel model);
|
||||||
TModel UpSert(TModel model);
|
TModel UpSert(TModel model);
|
||||||
void Delete(int id);
|
void Delete(int id);
|
||||||
|
void Delete(TModel model);
|
||||||
IList<TModel> InsertMany(IList<TModel> model);
|
IList<TModel> InsertMany(IList<TModel> model);
|
||||||
IList<TModel> UpdateMany(IList<TModel> model);
|
IList<TModel> UpdateMany(IList<TModel> model);
|
||||||
void DeleteMany(List<TModel> model);
|
void DeleteMany(List<TModel> model);
|
||||||
@ -67,6 +68,11 @@ public TModel Update(TModel model)
|
|||||||
return ObjectDatabase.Update(model);
|
return ObjectDatabase.Update(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Delete(TModel model)
|
||||||
|
{
|
||||||
|
ObjectDatabase.Delete(model);
|
||||||
|
}
|
||||||
|
|
||||||
public IList<TModel> InsertMany(IList<TModel> model)
|
public IList<TModel> InsertMany(IList<TModel> model)
|
||||||
{
|
{
|
||||||
return ObjectDatabase.InsertMany(model);
|
return ObjectDatabase.InsertMany(model);
|
||||||
|
@ -7,11 +7,10 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Model;
|
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();
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.IndexerSearch
|
namespace NzbDrone.Core.IndexerSearch
|
||||||
{
|
{
|
||||||
public class DailyEpisodeSearch : SearchBase
|
public class DailyEpisodeSearch : IndexerSearchBase
|
||||||
{
|
{
|
||||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.IndexerSearch
|
namespace NzbDrone.Core.IndexerSearch
|
||||||
{
|
{
|
||||||
public class EpisodeSearch : SearchBase
|
public class EpisodeSearch : IndexerSearchBase
|
||||||
{
|
{
|
||||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
@ -13,18 +13,19 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.IndexerSearch
|
namespace NzbDrone.Core.IndexerSearch
|
||||||
{
|
{
|
||||||
public abstract class SearchBase
|
public abstract class IndexerSearchBase
|
||||||
{
|
{
|
||||||
private readonly ISeriesRepository _seriesRepository;
|
private readonly ISeriesRepository _seriesRepository;
|
||||||
protected readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
protected readonly DownloadProvider _downloadProvider;
|
private readonly DownloadProvider _downloadProvider;
|
||||||
|
private readonly ISceneMappingService _sceneMappingService;
|
||||||
|
private readonly IDownloadDirector DownloadDirector;
|
||||||
|
|
||||||
protected readonly IIndexerService _indexerService;
|
protected readonly IIndexerService _indexerService;
|
||||||
protected readonly ISceneMappingService _sceneMappingService;
|
|
||||||
protected readonly IDownloadDirector DownloadDirector;
|
|
||||||
|
|
||||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
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,
|
IIndexerService indexerService, ISceneMappingService sceneMappingService,
|
||||||
IDownloadDirector downloadDirector)
|
IDownloadDirector downloadDirector)
|
||||||
{
|
{
|
||||||
@ -36,7 +37,7 @@ protected SearchBase(ISeriesRepository seriesRepository, IEpisodeService episode
|
|||||||
DownloadDirector = downloadDirector;
|
DownloadDirector = downloadDirector;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SearchBase()
|
protected IndexerSearchBase()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.IndexerSearch
|
namespace NzbDrone.Core.IndexerSearch
|
||||||
{
|
{
|
||||||
public class PartialSeasonSearch : SearchBase
|
public class PartialSeasonSearch : IndexerSearchBase
|
||||||
{
|
{
|
||||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
14
NzbDrone.Core/MediaFiles/Events/EpisodeFileDeletedEvent.cs
Normal file
14
NzbDrone.Core/MediaFiles/Events/EpisodeFileDeletedEvent.cs
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
NzbDrone.Core/MediaFiles/GhostFileCleanupService.cs
Normal file
48
NzbDrone.Core/MediaFiles/GhostFileCleanupService.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Eventing;
|
using NzbDrone.Common.Eventing;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Tv.Events;
|
using NzbDrone.Core.Tv.Events;
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ public interface IMediaFileService
|
|||||||
{
|
{
|
||||||
EpisodeFile Add(EpisodeFile episodeFile);
|
EpisodeFile Add(EpisodeFile episodeFile);
|
||||||
void Update(EpisodeFile episodeFile);
|
void Update(EpisodeFile episodeFile);
|
||||||
void Delete(int episodeFileId);
|
void Delete(EpisodeFile episodeFile);
|
||||||
bool Exists(string path);
|
bool Exists(string path);
|
||||||
EpisodeFile GetFileByPath(string path);
|
EpisodeFile GetFileByPath(string path);
|
||||||
List<EpisodeFile> GetFilesBySeries(int seriesId);
|
List<EpisodeFile> GetFilesBySeries(int seriesId);
|
||||||
@ -23,15 +24,17 @@ public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEve
|
|||||||
{
|
{
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
private readonly IMediaFileRepository _mediaFileRepository;
|
private readonly IMediaFileRepository _mediaFileRepository;
|
||||||
|
|
||||||
|
|
||||||
public MediaFileService(IMediaFileRepository mediaFileRepository, IConfigService configService, IEpisodeService episodeService, Logger logger)
|
public MediaFileService(IMediaFileRepository mediaFileRepository, IConfigService configService, IEpisodeService episodeService, IEventAggregator eventAggregator, Logger logger)
|
||||||
{
|
{
|
||||||
_mediaFileRepository = mediaFileRepository;
|
_mediaFileRepository = mediaFileRepository;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,16 +48,10 @@ public void Update(EpisodeFile episodeFile)
|
|||||||
_mediaFileRepository.Update(episodeFile);
|
_mediaFileRepository.Update(episodeFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(int episodeFileId)
|
public void Delete(EpisodeFile episodeFile)
|
||||||
{
|
{
|
||||||
_mediaFileRepository.Delete(episodeFileId);
|
_mediaFileRepository.Delete(episodeFile);
|
||||||
|
_eventAggregator.Publish(new EpisodeFileDeletedEvent(episodeFile));
|
||||||
var ep = _episodeService.GetEpisodesByFileId(episodeFileId);
|
|
||||||
|
|
||||||
foreach (var episode in ep)
|
|
||||||
{
|
|
||||||
_episodeService.SetEpisodeIgnore(episode.Id, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists(string path)
|
public bool Exists(string path)
|
||||||
|
@ -201,6 +201,7 @@
|
|||||||
<Compile Include="DecisionEngine\QualityUpgradableSpecification.cs" />
|
<Compile Include="DecisionEngine\QualityUpgradableSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\UpgradeDiskSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\UpgradeDiskSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\UpgradeHistorySpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\UpgradeHistorySpecification.cs" />
|
||||||
|
<Compile Include="Download\Clients\Sabnzbd\SabAutoConfigureService.cs" />
|
||||||
<Compile Include="Download\EpisodeDownloadedEvent.cs" />
|
<Compile Include="Download\EpisodeDownloadedEvent.cs" />
|
||||||
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
|
<Compile Include="Download\EpisodeGrabbedEvent.cs" />
|
||||||
<Compile Include="Download\SeriesRenamedEvent.cs" />
|
<Compile Include="Download\SeriesRenamedEvent.cs" />
|
||||||
@ -215,7 +216,7 @@
|
|||||||
<Compile Include="IndexerSearch\DailyEpisodeSearch.cs" />
|
<Compile Include="IndexerSearch\DailyEpisodeSearch.cs" />
|
||||||
<Compile Include="IndexerSearch\EpisodeSearch.cs" />
|
<Compile Include="IndexerSearch\EpisodeSearch.cs" />
|
||||||
<Compile Include="IndexerSearch\PartialSeasonSearch.cs" />
|
<Compile Include="IndexerSearch\PartialSeasonSearch.cs" />
|
||||||
<Compile Include="IndexerSearch\SearchBase.cs" />
|
<Compile Include="IndexerSearch\IndexerSearchBase.cs" />
|
||||||
<Compile Include="Indexers\IndexerRepository.cs" />
|
<Compile Include="Indexers\IndexerRepository.cs" />
|
||||||
<Compile Include="Indexers\NewznabRepository.cs" />
|
<Compile Include="Indexers\NewznabRepository.cs" />
|
||||||
<Compile Include="Instrumentation\LogInjectionModule.cs" />
|
<Compile Include="Instrumentation\LogInjectionModule.cs" />
|
||||||
@ -249,6 +250,8 @@
|
|||||||
<Compile Include="Lifecycle\AppRestartJob.cs" />
|
<Compile Include="Lifecycle\AppRestartJob.cs" />
|
||||||
<Compile Include="Lifecycle\IInitializable.cs" />
|
<Compile Include="Lifecycle\IInitializable.cs" />
|
||||||
<Compile Include="MediaCover\MediaCover.cs" />
|
<Compile Include="MediaCover\MediaCover.cs" />
|
||||||
|
<Compile Include="MediaFiles\Events\EpisodeFileDeletedEvent.cs" />
|
||||||
|
<Compile Include="MediaFiles\GhostFileCleanupService.cs" />
|
||||||
<Compile Include="MediaFiles\MediaFileRepository.cs" />
|
<Compile Include="MediaFiles\MediaFileRepository.cs" />
|
||||||
<Compile Include="Organizer\EpisodeSortingType.cs" />
|
<Compile Include="Organizer\EpisodeSortingType.cs" />
|
||||||
<Compile Include="Organizer\FileNameBuilder.cs" />
|
<Compile Include="Organizer\FileNameBuilder.cs" />
|
||||||
@ -338,9 +341,6 @@
|
|||||||
<Compile Include="Providers\XemProvider.cs" />
|
<Compile Include="Providers\XemProvider.cs" />
|
||||||
<Compile Include="Qualities\Quality.cs" />
|
<Compile Include="Qualities\Quality.cs" />
|
||||||
<Compile Include="Tv\Season.cs" />
|
<Compile Include="Tv\Season.cs" />
|
||||||
<Compile Include="Providers\AutoConfigureProvider.cs">
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Providers\BackupProvider.cs">
|
<Compile Include="Providers\BackupProvider.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -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 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 DiskProvider _diskProvider;
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IEpisodeService _episodeService;
|
||||||
|
private readonly ICleanGhostFiles _ghostFileCleaner;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly IBuildFileNames _buildFileNames;
|
private readonly IBuildFileNames _buildFileNames;
|
||||||
private readonly RecycleBinProvider _recycleBinProvider;
|
private readonly RecycleBinProvider _recycleBinProvider;
|
||||||
private readonly MediaInfoProvider _mediaInfoProvider;
|
private readonly MediaInfoProvider _mediaInfoProvider;
|
||||||
private readonly ISeriesRepository _seriesRepository;
|
private readonly ISeriesRepository _seriesRepository;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
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)
|
RecycleBinProvider recycleBinProvider, MediaInfoProvider mediaInfoProvider, ISeriesRepository seriesRepository, IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
|
_ghostFileCleaner = ghostFileCleaner;
|
||||||
_mediaFileService = mediaFileService;
|
_mediaFileService = mediaFileService;
|
||||||
_configService = configService;
|
|
||||||
_buildFileNames = buildFileNames;
|
_buildFileNames = buildFileNames;
|
||||||
_recycleBinProvider = recycleBinProvider;
|
_recycleBinProvider = recycleBinProvider;
|
||||||
_mediaInfoProvider = mediaInfoProvider;
|
_mediaInfoProvider = mediaInfoProvider;
|
||||||
@ -74,8 +74,7 @@ public virtual List<EpisodeFile> Scan(Series series, string path)
|
|||||||
return new List<EpisodeFile>();
|
return new List<EpisodeFile>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var seriesFile = _mediaFileService.GetFilesBySeries(series.Id);
|
_ghostFileCleaner.RemoveNonExistingFiles(series.Id);
|
||||||
CleanUp(seriesFile);
|
|
||||||
|
|
||||||
var mediaFileList = GetVideoFiles(path);
|
var mediaFileList = GetVideoFiles(path);
|
||||||
var importedFiles = new List<EpisodeFile>();
|
var importedFiles = new List<EpisodeFile>();
|
||||||
@ -237,43 +236,7 @@ public virtual EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, bool newDown
|
|||||||
return episodeFile;
|
return episodeFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes files that no longer exist on disk from the database
|
|
||||||
/// </summary>
|
|
||||||
/// <param name = "files">list of files to verify</param>
|
|
||||||
public virtual void CleanUp(IList<EpisodeFile> 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)
|
public virtual void CleanUpDropFolder(string path)
|
||||||
{
|
{
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Eventing;
|
using NzbDrone.Common.Eventing;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.MetadataSource;
|
using NzbDrone.Core.MetadataSource;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
using NzbDrone.Core.Providers;
|
using NzbDrone.Core.Providers;
|
||||||
@ -37,6 +39,7 @@ public interface IEpisodeService
|
|||||||
|
|
||||||
public class EpisodeService : IEpisodeService,
|
public class EpisodeService : IEpisodeService,
|
||||||
IHandle<EpisodeGrabbedEvent>,
|
IHandle<EpisodeGrabbedEvent>,
|
||||||
|
IHandle<EpisodeFileDeletedEvent>,
|
||||||
IHandleAsync<SeriesDeletedEvent>
|
IHandleAsync<SeriesDeletedEvent>
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -46,13 +49,17 @@ public class EpisodeService : IEpisodeService,
|
|||||||
private readonly ISeasonRepository _seasonRepository;
|
private readonly ISeasonRepository _seasonRepository;
|
||||||
private readonly IEpisodeRepository _episodeRepository;
|
private readonly IEpisodeRepository _episodeRepository;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
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;
|
_tvDbProxy = tvDbProxyProxy;
|
||||||
_seasonRepository = seasonRepository;
|
_seasonRepository = seasonRepository;
|
||||||
_episodeRepository = episodeRepository;
|
_episodeRepository = episodeRepository;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
_configService = configService;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEpisode(Episode episode)
|
public void AddEpisode(Episode episode)
|
||||||
@ -379,5 +386,18 @@ public void HandleAsync(SeriesDeletedEvent message)
|
|||||||
var episodes = GetEpisodeBySeries(message.Series.Id);
|
var episodes = GetEpisodeBySeries(message.Series.Id);
|
||||||
_episodeRepository.DeleteMany(episodes);
|
_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -102,9 +102,16 @@ protected string GetTestFilePath(string fileName)
|
|||||||
return Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName);
|
return Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected void VerifyEventPublished<TEvent>() where TEvent : IEvent
|
protected void VerifyEventPublished<TEvent>() where TEvent : IEvent
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IEventAggregator>().Verify(c => c.Publish(It.IsAny<TEvent>()), Times.Once());
|
VerifyEventPublished<TEvent>(Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void VerifyEventPublished<TEvent>(Times times) where TEvent : IEvent
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IEventAggregator>().Verify(c => c.Publish(It.IsAny<TEvent>()), times);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void VerifyEventNotPublished<TEvent>() where TEvent : IEvent
|
protected void VerifyEventNotPublished<TEvent>() where TEvent : IEvent
|
||||||
|
Loading…
Reference in New Issue
Block a user