1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-09 04:22:30 +01:00

Fixed: Respect delays when searching after a failed DownloadRelease

Closes #1292
This commit is contained in:
Mark McDowall 2016-05-12 21:02:57 -07:00
parent 229986033c
commit fd42ddec1b
10 changed files with 64 additions and 54 deletions

View File

@ -89,7 +89,7 @@ private List<ReleaseResource> GetEpisodeReleases(int episodeId)
{
try
{
var decisions = _nzbSearchService.EpisodeSearch(episodeId);
var decisions = _nzbSearchService.EpisodeSearch(episodeId, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions);

View File

@ -87,9 +87,20 @@ private void GivenUpgradeForExistingFile()
}
[Test]
public void should_be_true_when_search()
public void should_be_true_when_user_invoked_search()
{
Subject.IsSatisfiedBy(new RemoteEpisode(), new SingleEpisodeSearchCriteria()).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(new RemoteEpisode(), new SingleEpisodeSearchCriteria { UserInvokedSearch = true }).Accepted.Should().BeTrue();
}
[Test]
public void should_be_false_when_system_invoked_search_and_release_is_younger_than_delay()
{
_remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.SDTV);
_remoteEpisode.Release.PublishDate = DateTime.UtcNow;
_delayProfile.UsenetDelay = 720;
Subject.IsSatisfiedBy(_remoteEpisode, new SingleEpisodeSearchCriteria()).Accepted.Should().BeFalse();
}
[Test]

View File

@ -127,7 +127,7 @@ public void scene_episodesearch()
var allCriteria = WatchForSearchCriteria();
Subject.EpisodeSearch(_xemEpisodes.First());
Subject.EpisodeSearch(_xemEpisodes.First(), true);
var criteria = allCriteria.OfType<SingleEpisodeSearchCriteria>().ToList();
@ -143,7 +143,7 @@ public void scene_seasonsearch()
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, 1, false);
Subject.SeasonSearch(_xemSeries.Id, 1, false, true);
var criteria = allCriteria.OfType<SeasonSearchCriteria>().ToList();
@ -158,7 +158,7 @@ public void scene_seasonsearch_should_search_multiple_seasons()
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, 2, false);
Subject.SeasonSearch(_xemSeries.Id, 2, false, true);
var criteria = allCriteria.OfType<SeasonSearchCriteria>().ToList();
@ -174,7 +174,7 @@ public void scene_seasonsearch_should_search_single_episode_if_possible()
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, 4, false);
Subject.SeasonSearch(_xemSeries.Id, 4, false, true);
var criteria1 = allCriteria.OfType<SeasonSearchCriteria>().ToList();
var criteria2 = allCriteria.OfType<SingleEpisodeSearchCriteria>().ToList();
@ -194,7 +194,7 @@ public void scene_seasonsearch_should_use_seasonnumber_if_no_scene_number_is_ava
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, 7, false);
Subject.SeasonSearch(_xemSeries.Id, 7, false, true);
var criteria = allCriteria.OfType<SeasonSearchCriteria>().ToList();
@ -212,7 +212,7 @@ public void season_search_for_anime_should_search_for_each_monitored_episode()
var seasonNumber = 1;
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, seasonNumber, true);
Subject.SeasonSearch(_xemSeries.Id, seasonNumber, true, true);
var criteria = allCriteria.OfType<AnimeEpisodeSearchCriteria>().ToList();
@ -230,7 +230,7 @@ public void season_search_for_anime_should_not_search_for_unmonitored_episodes()
var seasonNumber = 1;
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, seasonNumber, false);
Subject.SeasonSearch(_xemSeries.Id, seasonNumber, false, true);
var criteria = allCriteria.OfType<AnimeEpisodeSearchCriteria>().ToList();
@ -247,7 +247,7 @@ public void season_search_for_anime_should_not_search_for_episodes_with_files()
var seasonNumber = 1;
var allCriteria = WatchForSearchCriteria();
Subject.SeasonSearch(_xemSeries.Id, seasonNumber, true);
Subject.SeasonSearch(_xemSeries.Id, seasonNumber, true, true);
var criteria = allCriteria.OfType<AnimeEpisodeSearchCriteria>().ToList();

View File

@ -32,7 +32,7 @@ public void Setup()
.Returns(_series);
Mocker.GetMock<ISearchForNzb>()
.Setup(s => s.SeasonSearch(_series.Id, It.IsAny<int>(), false))
.Setup(s => s.SeasonSearch(_series.Id, It.IsAny<int>(), false, true))
.Returns(new List<DownloadDecision>());
Mocker.GetMock<IProcessDownloadDecisions>()
@ -52,7 +52,7 @@ public void should_only_include_monitored_seasons()
Subject.Execute(new SeriesSearchCommand{ SeriesId = _series.Id });
Mocker.GetMock<ISearchForNzb>()
.Verify(v => v.SeasonSearch(_series.Id, It.IsAny<int>(), false), Times.Exactly(_series.Seasons.Count(s => s.Monitored)));
.Verify(v => v.SeasonSearch(_series.Id, It.IsAny<int>(), false, true), Times.Exactly(_series.Seasons.Count(s => s.Monitored)));
}
[Test]
@ -68,7 +68,7 @@ public void should_start_with_lower_seasons_first()
};
Mocker.GetMock<ISearchForNzb>()
.Setup(s => s.SeasonSearch(_series.Id, It.IsAny<int>(), false))
.Setup(s => s.SeasonSearch(_series.Id, It.IsAny<int>(), false, true))
.Returns(new List<DownloadDecision>())
.Callback<int, int, bool>((seriesId, seasonNumber, missingOnly) => seasonOrder.Add(seasonNumber));

View File

@ -30,12 +30,9 @@ public DelaySpecification(IPendingReleaseService pendingReleaseService,
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
//How do we want to handle drone being off and the automatic search being triggered?
//TODO: Add a flag to the search to state it is a "scheduled" search
if (searchCriteria != null)
if (searchCriteria != null && searchCriteria.UserInvokedSearch)
{
_logger.Debug("Ignore delay for searches");
_logger.Debug("Ignoring delay for user invoked search");
return Decision.Accept();
}
@ -71,7 +68,7 @@ public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase
}
}
//If quality meets or exceeds the best allowed quality in the profile accept it immediately
// If quality meets or exceeds the best allowed quality in the profile accept it immediately
var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality());
var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0;

View File

@ -18,6 +18,7 @@ public abstract class SearchCriteriaBase
public List<string> SceneTitles { get; set; }
public List<Episode> Episodes { get; set; }
public virtual bool MonitoredEpisodesOnly { get; set; }
public virtual bool UserInvokedSearch { get; set; }
public List<string> QueryTitles
{

View File

@ -34,7 +34,7 @@ public EpisodeSearchService(ISearchForNzb nzbSearchService,
_logger = logger;
}
private void SearchForMissingEpisodes(List<Episode> episodes)
private void SearchForMissingEpisodes(List<Episode> episodes, bool userInvokedSearch)
{
_logger.ProgressInfo("Performing missing search for {0} episodes", episodes.Count);
var downloadedCount = 0;
@ -49,7 +49,7 @@ private void SearchForMissingEpisodes(List<Episode> episodes)
{
try
{
decisions = _nzbSearchService.SeasonSearch(series.Key, season.Key, true);
decisions = _nzbSearchService.SeasonSearch(series.Key, season.Key, true, userInvokedSearch);
}
catch (Exception ex)
{
@ -63,7 +63,7 @@ private void SearchForMissingEpisodes(List<Episode> episodes)
{
try
{
decisions = _nzbSearchService.EpisodeSearch(season.First());
decisions = _nzbSearchService.EpisodeSearch(season.First(), userInvokedSearch);
}
catch (Exception ex)
{
@ -86,7 +86,7 @@ public void Execute(EpisodeSearchCommand message)
{
foreach (var episodeId in message.EpisodeIds)
{
var decisions = _nzbSearchService.EpisodeSearch(episodeId);
var decisions = _nzbSearchService.EpisodeSearch(episodeId, message.Trigger == CommandTrigger.Manual);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Episode search completed. {0} reports downloaded.", processed.Grabbed.Count);
@ -125,7 +125,7 @@ public void Execute(MissingEpisodeSearchCommand message)
var queue = _queueService.GetQueue().Select(q => q.Episode.Id);
var missing = episodes.Where(e => !queue.Contains(e.Id)).ToList();
SearchForMissingEpisodes(missing);
SearchForMissingEpisodes(missing, message.Trigger == CommandTrigger.Manual);
}
}
}

View File

@ -17,9 +17,9 @@ namespace NzbDrone.Core.IndexerSearch
{
public interface ISearchForNzb
{
List<DownloadDecision> EpisodeSearch(int episodeId);
List<DownloadDecision> EpisodeSearch(Episode episode);
List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool missingOnly);
List<DownloadDecision> EpisodeSearch(int episodeId, bool userInvokedSearch);
List<DownloadDecision> EpisodeSearch(Episode episode, bool userInvokedSearch);
List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool missingOnly, bool userInvokedSearch);
}
public class NzbSearchService : ISearchForNzb
@ -46,14 +46,14 @@ public NzbSearchService(IIndexerFactory indexerFactory,
_logger = logger;
}
public List<DownloadDecision> EpisodeSearch(int episodeId)
public List<DownloadDecision> EpisodeSearch(int episodeId, bool userInvokedSearch)
{
var episode = _episodeService.GetEpisode(episodeId);
return EpisodeSearch(episode);
return EpisodeSearch(episode, userInvokedSearch);
}
public List<DownloadDecision> EpisodeSearch(Episode episode)
public List<DownloadDecision> EpisodeSearch(Episode episode, bool userInvokedSearch)
{
var series = _seriesService.GetSeries(episode.SeriesId);
@ -64,23 +64,23 @@ public List<DownloadDecision> EpisodeSearch(Episode episode)
throw new InvalidOperationException("Daily episode is missing AirDate. Try to refresh series info.");
}
return SearchDaily(series, episode);
return SearchDaily(series, episode, userInvokedSearch);
}
if (series.SeriesType == SeriesTypes.Anime)
{
return SearchAnime(series, episode);
return SearchAnime(series, episode, userInvokedSearch);
}
if (episode.SeasonNumber == 0)
{
// search for special episodes in season 0
return SearchSpecial(series, new List<Episode> { episode });
return SearchSpecial(series, new List<Episode> { episode }, userInvokedSearch);
}
return SearchSingle(series, episode);
return SearchSingle(series, episode, userInvokedSearch);
}
public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool missingOnly)
public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool missingOnly, bool userInvokedSearch)
{
var series = _seriesService.GetSeries(seriesId);
var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber);
@ -92,13 +92,13 @@ public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool
if (series.SeriesType == SeriesTypes.Anime)
{
return SearchAnimeSeason(series, episodes);
return SearchAnimeSeason(series, episodes, userInvokedSearch);
}
if (seasonNumber == 0)
{
// search for special episodes in season 0
return SearchSpecial(series, episodes);
return SearchSpecial(series, episodes, userInvokedSearch);
}
var downloadDecisions = new List<DownloadDecision>();
@ -119,7 +119,7 @@ public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool
if (sceneSeasonEpisodes.Count() == 1)
{
var episode = sceneSeasonEpisodes.First();
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, sceneSeasonEpisodes.ToList());
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, sceneSeasonEpisodes.ToList(), userInvokedSearch);
searchSpec.SeasonNumber = sceneSeasonEpisodes.Key;
searchSpec.MonitoredEpisodesOnly = true;
@ -138,7 +138,7 @@ public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool
}
else
{
var searchSpec = Get<SeasonSearchCriteria>(series, sceneSeasonEpisodes.ToList());
var searchSpec = Get<SeasonSearchCriteria>(series, sceneSeasonEpisodes.ToList(), userInvokedSearch);
searchSpec.SeasonNumber = sceneSeasonEpisodes.Key;
var decisions = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
@ -148,7 +148,7 @@ public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool
}
else
{
var searchSpec = Get<SeasonSearchCriteria>(series, episodes);
var searchSpec = Get<SeasonSearchCriteria>(series, episodes, userInvokedSearch);
searchSpec.SeasonNumber = seasonNumber;
var decisions = Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
@ -158,9 +158,9 @@ public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber, bool
return downloadDecisions;
}
private List<DownloadDecision> SearchSingle(Series series, Episode episode)
private List<DownloadDecision> SearchSingle(Series series, Episode episode, bool userInvokedSearch)
{
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, new List<Episode>{episode});
var searchSpec = Get<SingleEpisodeSearchCriteria>(series, new List<Episode>{episode}, userInvokedSearch);
if (series.UseSceneNumbering && episode.SceneSeasonNumber.HasValue && episode.SceneEpisodeNumber.HasValue)
{
@ -176,18 +176,18 @@ private List<DownloadDecision> SearchSingle(Series series, Episode episode)
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
}
private List<DownloadDecision> SearchDaily(Series series, Episode episode)
private List<DownloadDecision> SearchDaily(Series series, Episode episode, bool userInvokedSearch)
{
var airDate = DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture);
var searchSpec = Get<DailyEpisodeSearchCriteria>(series, new List<Episode>{ episode });
var searchSpec = Get<DailyEpisodeSearchCriteria>(series, new List<Episode>{ episode }, userInvokedSearch);
searchSpec.AirDate = airDate;
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
}
private List<DownloadDecision> SearchAnime(Series series, Episode episode)
private List<DownloadDecision> SearchAnime(Series series, Episode episode, bool userInvokedSearch)
{
var searchSpec = Get<AnimeEpisodeSearchCriteria>(series, new List<Episode> { episode });
var searchSpec = Get<AnimeEpisodeSearchCriteria>(series, new List<Episode> { episode }, userInvokedSearch);
if (episode.SceneAbsoluteEpisodeNumber.HasValue)
{
@ -205,9 +205,9 @@ private List<DownloadDecision> SearchAnime(Series series, Episode episode)
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
}
private List<DownloadDecision> SearchSpecial(Series series, List<Episode> episodes)
private List<DownloadDecision> SearchSpecial(Series series, List<Episode> episodes, bool userInvokedSearch)
{
var searchSpec = Get<SpecialEpisodeSearchCriteria>(series, episodes);
var searchSpec = Get<SpecialEpisodeSearchCriteria>(series, episodes, userInvokedSearch);
// build list of queries for each episode in the form: "<series> <episode-title>"
searchSpec.EpisodeQueryTitles = episodes.Where(e => !string.IsNullOrWhiteSpace(e.Title))
.SelectMany(e => searchSpec.QueryTitles.Select(title => title + " " + SearchCriteriaBase.GetQueryTitle(e.Title)))
@ -216,19 +216,19 @@ private List<DownloadDecision> SearchSpecial(Series series, List<Episode> episod
return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec);
}
private List<DownloadDecision> SearchAnimeSeason(Series series, List<Episode> episodes)
private List<DownloadDecision> SearchAnimeSeason(Series series, List<Episode> episodes, bool userInvokedSearch)
{
var downloadDecisions = new List<DownloadDecision>();
foreach (var episode in episodes.Where(e => e.Monitored))
{
downloadDecisions.AddRange(SearchAnime(series, episode));
downloadDecisions.AddRange(SearchAnime(series, episode, userInvokedSearch));
}
return downloadDecisions;
}
private TSpec Get<TSpec>(Series series, List<Episode> episodes) where TSpec : SearchCriteriaBase, new()
private TSpec Get<TSpec>(Series series, List<Episode> episodes, bool userInvokedSearch) where TSpec : SearchCriteriaBase, new()
{
var spec = new TSpec();
@ -243,6 +243,7 @@ private List<DownloadDecision> SearchAnimeSeason(Series series, List<Episode> ep
spec.Episodes = episodes;
spec.SceneTitles.Add(series.Title);
spec.UserInvokedSearch = userInvokedSearch;
return spec;
}

View File

@ -22,7 +22,7 @@ public SeasonSearchService(ISearchForNzb nzbSearchService,
public void Execute(SeasonSearchCommand message)
{
var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, message.SeasonNumber, false);
var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, message.SeasonNumber, false, message.Trigger == CommandTrigger.Manual);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Season search completed. {0} reports downloaded.", processed.Grabbed.Count);

View File

@ -39,7 +39,7 @@ public void Execute(SeriesSearchCommand message)
continue;
}
var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, season.SeasonNumber, false);
var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, season.SeasonNumber, false, message.Trigger == CommandTrigger.Manual);
downloadedCount += _processDownloadDecisions.ProcessDecisions(decisions).Grabbed.Count;
}