using AutoMapper; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Authentication; using Ombi.Core.Helpers; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Ombi.Core.Engine { public class MovieSearchEngine : BaseMediaEngine, IMovieEngine { public MovieSearchEngine(ICurrentUser identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper, ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s, IRepository sub) : base(identity, service, r, um, mem, s, sub) { MovieApi = movApi; Mapper = mapper; Logger = logger; } protected IMovieDbApi MovieApi { get; } protected IMapper Mapper { get; } private ILogger Logger { get; } protected const int MovieLimit = 10; /// /// Lookups the imdb information. /// /// The movie database identifier. /// public async Task LookupImdbInformation(int theMovieDbId, string langCode = null) { langCode = await DefaultLanguageCode(langCode); var movieInfo = await Cache.GetOrAddAsync(nameof(LookupImdbInformation) + langCode + theMovieDbId, () => MovieApi.GetMovieInformationWithExtraInfo(theMovieDbId, langCode), DateTimeOffset.Now.AddHours(12)); var viewMovie = Mapper.Map(movieInfo); return await ProcessSingleMovie(viewMovie, true); } /// /// Searches the specified movie. /// public async Task> Search(string search, int? year, string langaugeCode) { langaugeCode = await DefaultLanguageCode(langaugeCode); var result = await MovieApi.SearchMovie(search, year, langaugeCode); if (result != null) { return await TransformMovieResultsToResponse(result.Take(ResultLimit)); // Take x to stop us overloading the API } return null; } public async Task> SearchActor(string search, string langaugeCode) { langaugeCode = await DefaultLanguageCode(langaugeCode); var people = await MovieApi.SearchByActor(search, langaugeCode); var person = people?.results?.Count > 0 ? people.results.FirstOrDefault() : null; var resultSet = new List(); if (person == null) { return resultSet; } // Get this person movie credits var credits = await MovieApi.GetActorMovieCredits(person.id, langaugeCode); // Grab results from both cast and crew, prefer items in cast. we can handle directors like this. var movieResults = (from role in credits.cast select new { Id = role.id, Title = role.title, ReleaseDate = role.release_date }).ToList(); movieResults.AddRange((from job in credits.crew select new { Id = job.id, Title = job.title, ReleaseDate = job.release_date }).ToList()); movieResults = movieResults.Take(10).ToList(); foreach (var movieResult in movieResults) { resultSet.Add(await LookupImdbInformation(movieResult.Id, langaugeCode)); } return resultSet; } /// /// Get similar movies to the id passed in /// /// /// public async Task> SimilarMovies(int theMovieDbId, string langCode) { langCode = await DefaultLanguageCode(langCode); var result = await MovieApi.SimilarMovies(theMovieDbId, langCode); if (result != null) { Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(ResultLimit)); // Take x to stop us overloading the API } return null; } /// /// Gets popular movies. /// /// public async Task> PopularMovies() { var result = await Cache.GetOrAddAsync(CacheKeys.PopularMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.PopularMovies(langCode); }, DateTimeOffset.Now.AddHours(12)); if (result != null) { return await TransformMovieResultsToResponse(result.Take(ResultLimit)); // Take x to stop us overloading the API } return null; } /// /// Gets top rated movies. /// /// public async Task> TopRatedMovies() { var result = await Cache.GetOrAddAsync(CacheKeys.TopRatedMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.TopRated(langCode); }, DateTimeOffset.Now.AddHours(12)); if (result != null) { return await TransformMovieResultsToResponse(result.Take(ResultLimit)); // Take x to stop us overloading the API } return null; } /// /// Gets upcoming movies. /// /// public async Task> UpcomingMovies() { var result = await Cache.GetOrAddAsync(CacheKeys.UpcomingMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.UpcomingMovies(langCode); }, DateTimeOffset.Now.AddHours(12)); if (result != null) { Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(ResultLimit)); // Take x to stop us overloading the API } return null; } /// /// Gets now playing movies. /// /// public async Task> NowPlayingMovies() { var result = await Cache.GetOrAddAsync(CacheKeys.NowPlayingMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.NowPlaying(langCode); }, DateTimeOffset.Now.AddHours(12)); if (result != null) { return await TransformMovieResultsToResponse(result.Take(ResultLimit)); // Take x to stop us overloading the API } return null; } protected async Task> TransformMovieResultsToResponse( IEnumerable movies) { var viewMovies = new List(); foreach (var movie in movies) { viewMovies.Add(await ProcessSingleMovie(movie)); } return viewMovies; } protected async Task ProcessSingleMovie(SearchMovieViewModel viewMovie, bool lookupExtraInfo = false) { if (lookupExtraInfo && viewMovie.ImdbId.IsNullOrEmpty() && viewMovie.Id > 0) { var showInfo = await MovieApi.GetMovieInformation(viewMovie.Id); viewMovie.Id = showInfo.Id; // TheMovieDbId viewMovie.ImdbId = showInfo.ImdbId; } var usDates = viewMovie.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); viewMovie.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; viewMovie.TheMovieDbId = viewMovie.Id.ToString(); await RunSearchRules(viewMovie); return viewMovie; } private async Task ProcessSingleMovie(MovieDbSearchResult movie) { var viewMovie = Mapper.Map(movie); return await ProcessSingleMovie(viewMovie); } } }