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:
parent
229986033c
commit
fd42ddec1b
@ -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);
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user