Blacklisting will trigger episode search

This commit is contained in:
Mark McDowall 2013-10-23 22:13:04 -07:00
parent 68e40bca29
commit 8520fe3e0c
12 changed files with 197 additions and 48 deletions

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications.RssSync; using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
@ -17,9 +18,9 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
[TestFixture] [TestFixture]
public class UpgradeHistorySpecificationFixture : CoreTest<UpgradeHistorySpecification> public class HistorySpecificationFixture : CoreTest<HistorySpecification>
{ {
private UpgradeHistorySpecification _upgradeHistory; private HistorySpecification _upgradeHistory;
private RemoteEpisode _parseResultMulti; private RemoteEpisode _parseResultMulti;
private RemoteEpisode _parseResultSingle; private RemoteEpisode _parseResultSingle;
@ -31,7 +32,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void Setup() public void Setup()
{ {
Mocker.Resolve<QualityUpgradableSpecification>(); Mocker.Resolve<QualityUpgradableSpecification>();
_upgradeHistory = Mocker.Resolve<UpgradeHistorySpecification>(); _upgradeHistory = Mocker.Resolve<HistorySpecification>();
var singleEpisodeList = new List<Episode> { new Episode { Id = 1, SeasonNumber = 12, EpisodeNumber = 3 } }; var singleEpisodeList = new List<Episode> { new Episode { Id = 1, SeasonNumber = 12, EpisodeNumber = 3 } };
var doubleEpisodeList = new List<Episode> { var doubleEpisodeList = new List<Episode> {
@ -81,6 +82,18 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(2)).Returns(_upgradableQuality); Mocker.GetMock<IHistoryService>().Setup(c => c.GetBestQualityInHistory(2)).Returns(_upgradableQuality);
} }
private void GivenSabnzbdDownloadClient()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(c => c.GetDownloadClient()).Returns(Mocker.Resolve<SabnzbdClient>());
}
private void GivenMostRecentForEpisode(HistoryEventType eventType)
{
Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForEpisode(It.IsAny<int>()))
.Returns(new History.History { EventType = eventType });
}
[Test] [Test]
public void should_be_upgradable_if_only_episode_is_upgradable() public void should_be_upgradable_if_only_episode_is_upgradable()
{ {
@ -135,12 +148,38 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
} }
[Test] [Test]
public void should_return_true_if_using_sabnzbd() public void should_return_true_if_using_sabnzbd_and_nothing_in_history()
{ {
Mocker.GetMock<IProvideDownloadClient>() GivenSabnzbdDownloadClient();
.Setup(c => c.GetDownloadClient()).Returns(Mocker.Resolve<SabnzbdClient>());
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, new SeasonSearchCriteria()).Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Should().BeTrue();
}
[Test]
public void should_return_false_if_most_recent_in_history_is_grabbed()
{
GivenSabnzbdDownloadClient();
GivenMostRecentForEpisode(HistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse();
}
[Test]
public void should_return_true_if_most_recent_in_history_is_failed()
{
GivenSabnzbdDownloadClient();
GivenMostRecentForEpisode(HistoryEventType.DownloadFailed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Should().BeTrue();
}
[Test]
public void should_return_true_if_most_recent_in_history_is_imported()
{
GivenSabnzbdDownloadClient();
GivenMostRecentForEpisode(HistoryEventType.DownloadFolderImported);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Should().BeTrue();
} }
} }
} }

View File

@ -121,7 +121,6 @@
<Compile Include="Download\DownloadClientTests\NzbgetProviderTests\DownloadNzbFixture.cs" /> <Compile Include="Download\DownloadClientTests\NzbgetProviderTests\DownloadNzbFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbgetProviderTests\QueueFixture.cs" /> <Compile Include="Download\DownloadClientTests\NzbgetProviderTests\QueueFixture.cs" />
<Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" /> <Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" />
<Compile Include="Download\DownloadClientTests\SabProviderTests\QueueFixture.cs" />
<Compile Include="Download\DownloadClientTests\SabProviderTests\SabProviderFixture.cs" /> <Compile Include="Download\DownloadClientTests\SabProviderTests\SabProviderFixture.cs" />
<Compile Include="Download\DownloadServiceFixture.cs" /> <Compile Include="Download\DownloadServiceFixture.cs" />
<Compile Include="Download\FailedDownloadServiceFixture.cs" /> <Compile Include="Download\FailedDownloadServiceFixture.cs" />
@ -194,7 +193,7 @@
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesBetweenDatesFixture.cs" /> <Compile Include="TvTests\EpisodeRepositoryTests\EpisodesBetweenDatesFixture.cs" />
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\QualityAllowedByProfileSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\QualityAllowedByProfileSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\UpgradeHistorySpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\HistorySpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" />
<Compile Include="ProviderTests\DiskProviderTests\FreeDiskSpaceFixture.cs" /> <Compile Include="ProviderTests\DiskProviderTests\FreeDiskSpaceFixture.cs" />

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -6,8 +7,8 @@ namespace NzbDrone.Core.Blacklisting
{ {
public class Blacklist : ModelBase public class Blacklist : ModelBase
{ {
public int EpisodeId { get; set; }
public int SeriesId { get; set; } public int SeriesId { get; set; }
public List<int> EpisodeIds { get; set; }
public string SourceTitle { get; set; } public string SourceTitle { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public DateTime Date { get; set; } public DateTime Date { get; set; }

View File

@ -15,10 +15,12 @@ namespace NzbDrone.Core.Blacklisting
public class BlacklistService : IBlacklistService, IHandle<DownloadFailedEvent> public class BlacklistService : IBlacklistService, IHandle<DownloadFailedEvent>
{ {
private readonly IBlacklistRepository _blacklistRepository; private readonly IBlacklistRepository _blacklistRepository;
private readonly IRedownloadFailedDownloads _redownloadFailedDownloadService;
public BlacklistService(IBlacklistRepository blacklistRepository) public BlacklistService(IBlacklistRepository blacklistRepository, IRedownloadFailedDownloads redownloadFailedDownloadService)
{ {
_blacklistRepository = blacklistRepository; _blacklistRepository = blacklistRepository;
_redownloadFailedDownloadService = redownloadFailedDownloadService;
} }
public bool Blacklisted(string sourceTitle) public bool Blacklisted(string sourceTitle)
@ -30,14 +32,16 @@ namespace NzbDrone.Core.Blacklisting
{ {
var blacklist = new Blacklist var blacklist = new Blacklist
{ {
SeriesId = message.Series.Id, SeriesId = message.SeriesId,
EpisodeId = message.Episode.Id, EpisodeIds = message.EpisodeIds,
SourceTitle = message.SourceTitle, SourceTitle = message.SourceTitle,
Quality = message.Quality, Quality = message.Quality,
Date = DateTime.UtcNow Date = DateTime.UtcNow
}; };
_blacklistRepository.Insert(blacklist); _blacklistRepository.Insert(blacklist);
_redownloadFailedDownloadService.Redownload(message.SeriesId, message.EpisodeIds);
} }
} }
} }

View File

@ -10,7 +10,7 @@ namespace NzbDrone.Core.Datastore.Migration
{ {
Create.TableForModel("Blacklist") Create.TableForModel("Blacklist")
.WithColumn("SeriesId").AsInt32() .WithColumn("SeriesId").AsInt32()
.WithColumn("EpisodeId").AsInt32() .WithColumn("EpisodeIds").AsString()
.WithColumn("SourceTitle").AsString() .WithColumn("SourceTitle").AsString()
.WithColumn("Quality").AsString() .WithColumn("Quality").AsString()
.WithColumn("Date").AsDateTime(); .WithColumn("Date").AsDateTime();

View File

@ -3,18 +3,19 @@ using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Sabnzbd; using NzbDrone.Core.Download.Clients.Sabnzbd;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MetadataSource.Trakt;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{ {
public class UpgradeHistorySpecification : IDecisionEngineSpecification public class HistorySpecification : IDecisionEngineSpecification
{ {
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; private readonly QualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly IProvideDownloadClient _downloadClientProvider; private readonly IProvideDownloadClient _downloadClientProvider;
private readonly Logger _logger; private readonly Logger _logger;
public UpgradeHistorySpecification(IHistoryService historyService, public HistorySpecification(IHistoryService historyService,
QualityUpgradableSpecification qualityUpgradableSpecification, QualityUpgradableSpecification qualityUpgradableSpecification,
IProvideDownloadClient downloadClientProvider, IProvideDownloadClient downloadClientProvider,
Logger logger) Logger logger)
@ -43,7 +44,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
if (_downloadClientProvider.GetDownloadClient().GetType() == typeof (SabnzbdClient)) if (_downloadClientProvider.GetDownloadClient().GetType() == typeof (SabnzbdClient))
{ {
_logger.Trace("Skipping history check in favour of blacklist"); _logger.Trace("Performing history status check on report");
foreach (var episode in subject.Episodes)
{
_logger.Trace("Checking current status of episode [{0}] in history", episode.Id);
var mostRecent = _historyService.MostRecentForEpisode(episode.Id);
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
{
return false;
}
}
return true; return true;
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -6,8 +7,8 @@ namespace NzbDrone.Core.Download
{ {
public class DownloadFailedEvent : IEvent public class DownloadFailedEvent : IEvent
{ {
public Series Series { get; set; } public Int32 SeriesId { get; set; }
public Episode Episode { get; set; } public List<Int32> EpisodeIds { get; set; }
public QualityModel Quality { get; set; } public QualityModel Quality { get; set; }
public String SourceTitle { get; set; } public String SourceTitle { get; set; }
public String DownloadClient { get; set; } public String DownloadClient { get; set; }

View File

@ -41,15 +41,15 @@ namespace NzbDrone.Core.Download
return; return;
} }
var recentHistory = _historyService.BetweenDates(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, HistoryEventType.Grabbed); var grabbedHistory = _historyService.Grabbed();
var failedHistory = _historyService.Failed(); var failedHistory = _historyService.Failed();
foreach (var failedItem in failedItems) foreach (var failedItem in failedItems)
{ {
var failedLocal = failedItem; var failedLocal = failedItem;
var historyItems = recentHistory.Where(h => h.Data.ContainsKey(DOWNLOAD_CLIENT) && var historyItems = grabbedHistory.Where(h => h.Data.ContainsKey(DOWNLOAD_CLIENT) &&
h.Data[DOWNLOAD_CLIENT_ID].Equals(failedLocal.Id)) h.Data[DOWNLOAD_CLIENT_ID].Equals(failedLocal.Id))
.ToList(); .ToList();
if (!historyItems.Any()) if (!historyItems.Any())
{ {
@ -64,18 +64,17 @@ namespace NzbDrone.Core.Download
continue; continue;
} }
foreach (var historyItem in historyItems) var historyItem = historyItems.First();
_eventAggregator.PublishEvent(new DownloadFailedEvent
{ {
_eventAggregator.PublishEvent(new DownloadFailedEvent SeriesId = historyItem.SeriesId,
{ EpisodeIds = historyItems.Select(h => h.EpisodeId).ToList(),
Series = historyItem.Series, Quality = historyItem.Quality,
Episode = historyItem.Episode, SourceTitle = historyItem.SourceTitle,
Quality = historyItem.Quality, DownloadClient = historyItem.Data[DOWNLOAD_CLIENT],
SourceTitle = historyItem.SourceTitle, DownloadClientId = historyItem.Data[DOWNLOAD_CLIENT_ID]
DownloadClient = historyItem.Data[DOWNLOAD_CLIENT], });
DownloadClientId = historyItem.Data[DOWNLOAD_CLIENT_ID]
});
}
} }
} }

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Download
{
public interface IRedownloadFailedDownloads
{
void Redownload(int seriesId, List<int> episodeIds);
}
public class RedownloadFailedDownloadService : IRedownloadFailedDownloads
{
private readonly IEpisodeService _episodeService;
private readonly ICommandExecutor _commandExecutor;
private readonly Logger _logger;
public RedownloadFailedDownloadService(IEpisodeService episodeService, ICommandExecutor commandExecutor, Logger logger)
{
_episodeService = episodeService;
_commandExecutor = commandExecutor;
_logger = logger;
}
public void Redownload(int seriesId, List<int> episodeIds)
{
if (episodeIds.Count == 1)
{
_logger.Trace("Failed download only contains one episode, searching again");
_commandExecutor.PublishCommandAsync(new EpisodeSearchCommand
{
EpisodeIds = episodeIds.ToList()
});
return;
}
var seasonNumber = _episodeService.GetEpisode(episodeIds.First()).SeasonNumber;
var episodesInSeason = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber);
if (episodeIds.Count == episodesInSeason.Count)
{
_logger.Trace("Failed download was entire season, searching again");
_commandExecutor.PublishCommandAsync(new SeasonSearchCommand
{
SeriesId = seriesId,
SeasonNumber = seasonNumber
});
return;
}
_logger.Trace("Failed download contains multiple episodes, probably a double episode, searching again");
_commandExecutor.PublishCommandAsync(new EpisodeSearchCommand
{
EpisodeIds = episodeIds.ToList()
});
}
}
}

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using Marr.Data.QGen; using Marr.Data.QGen;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
@ -15,6 +14,8 @@ namespace NzbDrone.Core.History
List<QualityModel> GetBestQualityInHistory(int episodeId); List<QualityModel> GetBestQualityInHistory(int episodeId);
List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType); List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType);
List<History> Failed(); List<History> Failed();
List<History> Grabbed();
History MostRecentForEpisode(int episodeId);
} }
public class HistoryRepository : BasicRepository<History>, IHistoryRepository public class HistoryRepository : BasicRepository<History>, IHistoryRepository
@ -54,6 +55,18 @@ namespace NzbDrone.Core.History
return Query.Where(h => h.EventType == HistoryEventType.DownloadFailed); return Query.Where(h => h.EventType == HistoryEventType.DownloadFailed);
} }
public List<History> Grabbed()
{
return Query.Where(h => h.EventType == HistoryEventType.Grabbed);
}
public History MostRecentForEpisode(int episodeId)
{
return Query.Where(h => h.EpisodeId == episodeId)
.OrderByDescending(h => h.Date)
.FirstOrDefault();
}
public override PagingSpec<History> GetPaged(PagingSpec<History> pagingSpec) public override PagingSpec<History> GetPaged(PagingSpec<History> pagingSpec)
{ {
pagingSpec.Records = GetPagedQuery(pagingSpec).ToList(); pagingSpec.Records = GetPagedQuery(pagingSpec).ToList();

View File

@ -20,6 +20,8 @@ namespace NzbDrone.Core.History
PagingSpec<History> Paged(PagingSpec<History> pagingSpec); PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType); List<History> BetweenDates(DateTime startDate, DateTime endDate, HistoryEventType eventType);
List<History> Failed(); List<History> Failed();
List<History> Grabbed();
History MostRecentForEpisode(int episodeId);
} }
public class HistoryService : IHistoryService, IHandle<EpisodeGrabbedEvent>, IHandle<EpisodeImportedEvent>, IHandle<DownloadFailedEvent> public class HistoryService : IHistoryService, IHandle<EpisodeGrabbedEvent>, IHandle<EpisodeImportedEvent>, IHandle<DownloadFailedEvent>
@ -53,6 +55,16 @@ namespace NzbDrone.Core.History
return _historyRepository.Failed(); return _historyRepository.Failed();
} }
public List<History> Grabbed()
{
return _historyRepository.Grabbed();
}
public History MostRecentForEpisode(int episodeId)
{
return _historyRepository.MostRecentForEpisode(episodeId);
}
public void Purge() public void Purge()
{ {
_historyRepository.Purge(); _historyRepository.Purge();
@ -122,20 +134,23 @@ namespace NzbDrone.Core.History
public void Handle(DownloadFailedEvent message) public void Handle(DownloadFailedEvent message)
{ {
var history = new History foreach (var episodeId in message.EpisodeIds)
{ {
EventType = HistoryEventType.DownloadFailed, var history = new History
Date = DateTime.UtcNow, {
Quality = message.Quality, EventType = HistoryEventType.DownloadFailed,
SourceTitle = message.SourceTitle, Date = DateTime.UtcNow,
SeriesId = message.Series.Id, Quality = message.Quality,
EpisodeId = message.Episode.Id, SourceTitle = message.SourceTitle,
}; SeriesId = message.SeriesId,
EpisodeId = episodeId,
};
history.Data.Add("DownloadClient", message.DownloadClient); history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("DownloadClientId", message.DownloadClientId); history.Data.Add("DownloadClientId", message.DownloadClientId);
_historyRepository.Insert(history); _historyRepository.Insert(history);
}
} }
} }
} }

View File

@ -223,7 +223,7 @@
<Compile Include="DecisionEngine\QualityUpgradableSpecification.cs" /> <Compile Include="DecisionEngine\QualityUpgradableSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\Search\SingleEpisodeSearchMatchSpecification.cs" /> <Compile Include="DecisionEngine\Specifications\Search\SingleEpisodeSearchMatchSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\UpgradeDiskSpecification.cs" /> <Compile Include="DecisionEngine\Specifications\UpgradeDiskSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\RssSync\UpgradeHistorySpecification.cs" /> <Compile Include="DecisionEngine\Specifications\RssSync\HistorySpecification.cs" />
<Compile Include="DiskSpace\DiskSpace.cs" /> <Compile Include="DiskSpace\DiskSpace.cs" />
<Compile Include="DiskSpace\DiskSpaceService.cs" /> <Compile Include="DiskSpace\DiskSpaceService.cs" />
<Compile Include="Download\Clients\Sabnzbd\ConnectionInfoModel.cs" /> <Compile Include="Download\Clients\Sabnzbd\ConnectionInfoModel.cs" />
@ -239,6 +239,7 @@
<Compile Include="Download\DownloadClientType.cs" /> <Compile Include="Download\DownloadClientType.cs" />
<Compile Include="Download\FailedDownloadService.cs" /> <Compile Include="Download\FailedDownloadService.cs" />
<Compile Include="Download\QueueItem.cs" /> <Compile Include="Download\QueueItem.cs" />
<Compile Include="Download\RedownloadFailedDownloadService.cs" />
<Compile Include="Exceptions\BadRequestException.cs" /> <Compile Include="Exceptions\BadRequestException.cs" />
<Compile Include="Exceptions\DownstreamException.cs" /> <Compile Include="Exceptions\DownstreamException.cs" />
<Compile Include="Exceptions\NzbDroneClientException.cs" /> <Compile Include="Exceptions\NzbDroneClientException.cs" />
@ -358,7 +359,6 @@
<Compile Include="IndexerSearch\Definitions\SeasonSearchCriteria.cs" /> <Compile Include="IndexerSearch\Definitions\SeasonSearchCriteria.cs" />
<Compile Include="IndexerSearch\Definitions\SingleEpisodeSearchCriteria.cs" /> <Compile Include="IndexerSearch\Definitions\SingleEpisodeSearchCriteria.cs" />
<Compile Include="IndexerSearch\NzbSearchService.cs" /> <Compile Include="IndexerSearch\NzbSearchService.cs" />
<Compile Include="IndexerSearch\SearchAndDownloadService.cs" />
<Compile Include="Indexers\RssParserBase.cs" /> <Compile Include="Indexers\RssParserBase.cs" />
<Compile Include="Indexers\RssSyncService.cs" /> <Compile Include="Indexers\RssSyncService.cs" />
<Compile Include="Indexers\IndexerBase.cs" /> <Compile Include="Indexers\IndexerBase.cs" />