using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NLog; using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Tv; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Search; namespace NzbDrone.Core.Providers.Search { public class PartialSeasonSearch : SearchBase { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); public PartialSeasonSearch(ISeriesService seriesService, IEpisodeService episodeService, DownloadProvider downloadProvider, IIndexerService indexerService, SceneMappingProvider sceneMappingProvider, AllowedDownloadSpecification allowedDownloadSpecification, SearchHistoryProvider searchHistoryProvider,ISeriesRepository seriesRepository) : base(seriesService, seriesRepository, episodeService, downloadProvider, indexerService, sceneMappingProvider, allowedDownloadSpecification, searchHistoryProvider) { } public PartialSeasonSearch() { } public override List PerformSearch(Series series, dynamic options, ProgressNotification notification) { if (options.SeasonNumber == null || options.SeasonNumber < 0) throw new ArgumentException("SeasonNumber is invalid"); if (options.Episodes == null) throw new ArgumentException("Episodes were not provided"); List episodes = options.Episodes; if (!episodes.Any()) throw new ArgumentException("Episodes were not provided"); notification.CurrentMessage = String.Format("Looking for {0} - Season {1}", series.Title, options.SeasonNumber); var reports = new List(); object reportsLock = new object(); var title = GetSearchTitle(series); var prefixes = GetEpisodeNumberPrefixes(episodes.Select(e => e.EpisodeNumber)); foreach(var p in prefixes) { var prefix = p; Parallel.ForEach(_indexerService.GetEnabledIndexers(), indexer => { try { lock(reportsLock) { reports.AddRange(indexer.FetchPartialSeason(title, options.SeasonNumber, prefix)); } } catch(Exception e) { logger.ErrorException( String.Format( "An error has occurred while searching for {0} Season {1:00} Prefix: {2} from: {3}", series.Title, options.SeasonNumber, prefix, indexer.Name), e); } }); } return reports; } public override SearchHistoryItem CheckReport(Series series, dynamic options, EpisodeParseResult episodeParseResult, SearchHistoryItem item) { if(options.SeasonNumber != episodeParseResult.SeasonNumber) { logger.Trace("Season number does not match searched season number, skipping."); item.SearchError = ReportRejectionType.WrongSeason; return item; } return item; } protected override void FinalizeSearch(Series series, dynamic options, Boolean reportsFound, ProgressNotification notification) { logger.Warn("Unable to find {0} - Season {1} in any of indexers.", series.Title, options.SeasonNumber); notification.CurrentMessage = reportsFound ? String.Format("Sorry, couldn't find {0} Season {1:00}, that matches your preferences.", series.Title, options.SeasonNumber) : String.Format("Sorry, couldn't find {0} Season {1:00} in any of indexers.", series.Title, options.SeasonNumber); } private List GetEpisodeNumberPrefixes(IEnumerable episodeNumbers) { var results = new List(); foreach (var i in episodeNumbers) { results.Add(i / 10); } return results.Distinct().ToList(); } } }