using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NLog; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Movies; using NzbDrone.Core.Queue; namespace NzbDrone.Core.IndexerSearch { public class MovieSearchService : IExecute, IExecute, IExecute { private readonly IMovieService _movieService; private readonly IMovieCutoffService _movieCutoffService; private readonly ISearchForReleases _releaseSearchService; private readonly IProcessDownloadDecisions _processDownloadDecisions; private readonly IQueueService _queueService; private readonly Logger _logger; public MovieSearchService(IMovieService movieService, IMovieCutoffService movieCutoffService, ISearchForReleases releaseSearchService, IProcessDownloadDecisions processDownloadDecisions, IQueueService queueService, Logger logger) { _movieService = movieService; _movieCutoffService = movieCutoffService; _releaseSearchService = releaseSearchService; _processDownloadDecisions = processDownloadDecisions; _queueService = queueService; _logger = logger; } public void Execute(MoviesSearchCommand message) { var userInvokedSearch = message.Trigger == CommandTrigger.Manual; var movies = _movieService.GetMovies(message.MovieIds) .Where(m => (m.Monitored && m.IsAvailable()) || userInvokedSearch) .ToList(); SearchForBulkMovies(movies, userInvokedSearch).GetAwaiter().GetResult(); } public void Execute(MissingMoviesSearchCommand message) { var pagingSpec = new PagingSpec { Page = 1, PageSize = 100000, SortDirection = SortDirection.Ascending, SortKey = "Id" }; pagingSpec.FilterExpressions.Add(v => v.Monitored == true); var movies = _movieService.MoviesWithoutFiles(pagingSpec).Records.ToList(); var queue = _queueService.GetQueue().Where(q => q.Movie != null).Select(q => q.Movie.Id); var missing = movies.Where(e => !queue.Contains(e.Id)).ToList(); SearchForBulkMovies(missing, message.Trigger == CommandTrigger.Manual).GetAwaiter().GetResult(); } public void Execute(CutoffUnmetMoviesSearchCommand message) { var pagingSpec = new PagingSpec { Page = 1, PageSize = 100000, SortDirection = SortDirection.Ascending, SortKey = "Id" }; pagingSpec.FilterExpressions.Add(v => v.Monitored == true); var movies = _movieCutoffService.MoviesWhereCutoffUnmet(pagingSpec).Records.ToList(); var queue = _queueService.GetQueue().Where(q => q.Movie != null).Select(q => q.Movie.Id); var missing = movies.Where(e => !queue.Contains(e.Id)).ToList(); SearchForBulkMovies(missing, message.Trigger == CommandTrigger.Manual).GetAwaiter().GetResult(); } private async Task SearchForBulkMovies(List movies, bool userInvokedSearch) { _logger.ProgressInfo("Performing search for {0} movies", movies.Count); var downloadedCount = 0; foreach (var movieId in movies.GroupBy(e => e.Id).OrderBy(g => g.Min(m => m.LastSearchTime ?? DateTime.MinValue))) { List decisions; try { decisions = await _releaseSearchService.MovieSearch(movieId.Key, userInvokedSearch, false); } catch (Exception ex) { _logger.Error(ex, "Unable to search for movie: [{0}]", movieId.Key); continue; } var processDecisions = await _processDownloadDecisions.ProcessDecisions(decisions); downloadedCount += processDecisions.Grabbed.Count; } _logger.ProgressInfo("Completed search for {0} movies. {1} reports downloaded.", movies.Count, downloadedCount); } } }