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.Engine.Interfaces; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Core.Models.Search.V2; 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.Security.Principal; using System.Threading; using System.Threading.Tasks; namespace Ombi.Core.Engine.V2 { public class MovieSearchEngineV2 : BaseMediaEngine, IMovieEngineV2 { public MovieSearchEngineV2(IPrincipal 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; } private IMovieDbApi MovieApi { get; } private IMapper Mapper { get; } private ILogger Logger { get; } public async Task GetFullMovieInformation(int theMovieDbId, CancellationToken cancellationToken, string langCode = null) { langCode = await DefaultLanguageCode(langCode); var movieInfo = await Cache.GetOrAdd(nameof(GetFullMovieInformation) + theMovieDbId + langCode, async () => await MovieApi.GetFullMovieInfo(theMovieDbId, cancellationToken, langCode), DateTime.Now.AddHours(12), cancellationToken); return await ProcessSingleMovie(movieInfo); } public async Task GetMovieInfoByRequestId(int requestId, CancellationToken cancellationToken, string langCode = null) { langCode = await DefaultLanguageCode(langCode); var request = await RequestService.MovieRequestService.Find(requestId); var movieInfo = await Cache.GetOrAdd(nameof(GetFullMovieInformation) + request.TheMovieDbId + langCode, async () => await MovieApi.GetFullMovieInfo(request.TheMovieDbId, cancellationToken, langCode), DateTime.Now.AddHours(12), cancellationToken); return await ProcessSingleMovie(movieInfo); } public async Task GetCollection(int collectionId, CancellationToken cancellationToken, string langCode = null) { langCode = await DefaultLanguageCode(langCode); var collections = await Cache.GetOrAdd(nameof(GetCollection) + collectionId + langCode, async () => await MovieApi.GetCollection(langCode, collectionId, cancellationToken), DateTime.Now.AddDays(1), cancellationToken); var c = await ProcessCollection(collections); c.Collection = c.Collection.OrderBy(x => x.ReleaseDate).ToList(); return c; } public async Task GetTvDbId(int theMovieDbId) { var result = await MovieApi.GetTvExternals(theMovieDbId); return result.tvdb_id; } /// /// 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.Shuffle().Take(ResultLimit)); // Take x to stop us overloading the API } return null; } /// /// Gets popular movies. /// /// public async Task> PopularMovies() { var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.PopularMovies(langCode); }, DateTime.Now.AddHours(12)); if (result != null) { return await TransformMovieResultsToResponse(result.Shuffle().Take(ResultLimit)); // Take x to stop us overloading the API } return null; } private const int _theMovieDbMaxPageItems = 20; /// /// Gets popular movies by paging /// /// public async Task> PopularMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken) { var langCode = await DefaultLanguageCode(null); var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(PopularMovies) + pagesToLoad.Page + langCode, async () => await MovieApi.PopularMovies(langCode, pagesToLoad.Page, cancellationToken), DateTime.Now.AddHours(12), cancellationToken); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } return await TransformMovieResultsToResponse(results); } /// /// Gets top rated movies. /// /// public async Task> TopRatedMovies() { var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.TopRated(langCode); }, DateTime.Now.AddHours(12)); if (result != null) { return await TransformMovieResultsToResponse(result.Shuffle().Take(ResultLimit)); // Take x to stop us overloading the API } return null; } public async Task> TopRatedMovies(int currentPosition, int amountToLoad) { var langCode = await DefaultLanguageCode(null); var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(TopRatedMovies) + pagesToLoad.Page + langCode, async () => await MovieApi.TopRated(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } return await TransformMovieResultsToResponse(results); } public async Task> NowPlayingMovies(int currentPosition, int amountToLoad) { var langCode = await DefaultLanguageCode(null); var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode, async () => await MovieApi.NowPlaying(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } return await TransformMovieResultsToResponse(results); } /// /// Gets upcoming movies. /// /// public async Task> UpcomingMovies() { var result = await Cache.GetOrAdd(CacheKeys.UpcomingMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.Upcoming(langCode); }, DateTime.Now.AddHours(12)); if (result != null) { Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Shuffle().Take(ResultLimit)); // Take x to stop us overloading the API } return null; } public async Task> UpcomingMovies(int currentPosition, int amountToLoad) { var langCode = await DefaultLanguageCode(null); var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(UpcomingMovies) + pagesToLoad.Page + langCode, async () => await MovieApi.Upcoming(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } return await TransformMovieResultsToResponse(results); } /// /// Gets now playing movies. /// /// public async Task> NowPlayingMovies() { var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => { var langCode = await DefaultLanguageCode(null); return await MovieApi.NowPlaying(langCode); }, DateTime.Now.AddHours(12)); if (result != null) { return await TransformMovieResultsToResponse(result.Shuffle().Take(ResultLimit)); // Take x to stop us overloading the API } return null; } public async Task GetMoviesByActor(int actorId, string langCode) { var result = await Cache.GetOrAdd(nameof(GetMoviesByActor) + actorId + langCode, async () => await MovieApi.GetActorMovieCredits(actorId, langCode)); // Later we run this through the rules engine return result; } protected async Task> TransformMovieResultsToResponse( IEnumerable movies) { var viewMovies = new List(); foreach (var movie in movies) { viewMovies.Add(await ProcessSingleMovie(movie)); } return viewMovies; } private async Task ProcessSingleMovie(MovieSearchResult movie) { var viewMovie = Mapper.Map(movie); return await ProcessSingleMovie(viewMovie); } private async Task ProcessSingleMovie(FullMovieInfo movie) { var viewMovie = Mapper.Map(movie); await RunSearchRules(viewMovie); // This requires the rules to be run first to populate the RequestId property await CheckForSubscription(viewMovie); var mapped = Mapper.Map(movie); mapped.Available = viewMovie.Available; mapped.Approved = viewMovie.Approved; mapped.RequestId = viewMovie.RequestId; mapped.Requested = viewMovie.Requested; mapped.PlexUrl = viewMovie.PlexUrl; mapped.EmbyUrl = viewMovie.EmbyUrl; mapped.Subscribed = viewMovie.Subscribed; mapped.ShowSubscribe = viewMovie.ShowSubscribe; return mapped; } private async Task ProcessCollection(Collections collection) { var viewMovie = Mapper.Map(collection); foreach (var movie in viewMovie.Collection) { var mappedMovie = Mapper.Map(movie); await RunSearchRules(mappedMovie); // This requires the rules to be run first to populate the RequestId property await CheckForSubscription(mappedMovie); var mapped = Mapper.Map(movie); mapped.Available = movie.Available; mapped.Approved = movie.Approved; mapped.RequestId = movie.RequestId; mapped.Requested = movie.Requested; mapped.PlexUrl = movie.PlexUrl; mapped.EmbyUrl = movie.EmbyUrl; mapped.Subscribed = movie.Subscribed; mapped.ShowSubscribe = movie.ShowSubscribe; mapped.ReleaseDate = movie.ReleaseDate; } return viewMovie; } private async Task ProcessSingleMovie(SearchMovieViewModel viewMovie) { if (viewMovie.ImdbId.IsNullOrEmpty()) { var showInfo = await Cache.GetOrAdd("GetMovieInformationWIthImdbId" + viewMovie.Id, async () => await MovieApi.GetMovieInformation(viewMovie.Id), DateTime.Now.AddHours(12)); viewMovie.Id = showInfo.Id; // TheMovieDbId viewMovie.ImdbId = showInfo.ImdbId; } viewMovie.TheMovieDbId = viewMovie.Id.ToString(); await RunSearchRules(viewMovie); // This requires the rules to be run first to populate the RequestId property await CheckForSubscription(viewMovie); return viewMovie; } private async Task CheckForSubscription(SearchViewModel viewModel) { // Check if this user requested it var user = await GetUser(); if (user == null) { return; } var request = await RequestService.MovieRequestService.GetAll() .AnyAsync(x => x.RequestedUserId.Equals(user.Id) && x.TheMovieDbId == viewModel.Id); if (request) { viewModel.ShowSubscribe = false; } else { viewModel.ShowSubscribe = true; var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => s.UserId == user.Id && s.RequestId == viewModel.RequestId && s.RequestType == RequestType.Movie); viewModel.Subscribed = sub != null; } } } }