using AutoMapper; using Ombi.Api.TvMaze; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Core.Rules; using Ombi.Helpers; using Ombi.Store.Entities; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Engine.Interfaces; using Ombi.Core.IdentityResolver; using Ombi.Core.Rule; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository.Requests; namespace Ombi.Core.Engine { public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine { public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IMapper map, IRuleEvaluator rule, IUserIdentityManager manager, ITvSender sender) : base(user, requestService, rule) { TvApi = tvApi; NotificationHelper = helper; Mapper = map; UserManager = manager; TvSender = sender; } private INotificationHelper NotificationHelper { get; } private ITvMazeApi TvApi { get; } private IMapper Mapper { get; } private IUserIdentityManager UserManager { get; } private ITvSender TvSender {get;} public async Task RequestTvShow(SearchTvShowViewModel tv) { var showInfo = await TvApi.ShowLookupByTheTvDbId(tv.Id); DateTime.TryParse(showInfo.premiered, out DateTime firstAir); // For some reason the poster path is always http var posterPath = showInfo.image?.medium.Replace("http:", "https:"); var tvRequests = new List(); // Only have the TV requests we actually requested and not everything foreach (var season in tv.SeasonRequests) { for (int i = season.Episodes.Count - 1; i >= 0; i--) { if (!season.Episodes[i].Requested) { season.Episodes.RemoveAt(i); // Remove the episode since it's not requested } } if (season.Episodes.Any()) { tvRequests.Add(season); } } var user = await UserManager.GetUser(User.Identity.Name); var childRequest = new ChildRequests { Id = tv.Id, RequestType = RequestType.TvShow, //Overview = showInfo.summary.RemoveHtml(), //PosterPath = posterPath, //Title = showInfo.name, //ReleaseDate = firstAir, //Status = showInfo.status, RequestedDate = DateTime.UtcNow, Approved = false, RequestedUserId = user.Id, SeasonRequests = new List() }; if (tv.RequestAll) { var episodes = await TvApi.EpisodeLookup(showInfo.id); var seasonRequests = new List(); foreach (var ep in episodes) { var episodesRequests = new List(); var season = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == ep.season); if (season == null) { childRequest.SeasonRequests.Add(new SeasonRequests { Episodes = new List{ new EpisodeRequests { EpisodeNumber = ep.number, AirDate = DateTime.Parse(ep.airdate), Title = ep.name, Url = ep.url } }, SeasonNumber = ep.season, }); } else { season.Episodes.Add(new EpisodeRequests { EpisodeNumber = ep.number, AirDate = DateTime.Parse(ep.airdate), Title = ep.name, Url = ep.url }); } } } else if (tv.LatestSeason) { var episodes = await TvApi.EpisodeLookup(showInfo.id); var latest = episodes.OrderBy(x => x.season).FirstOrDefault(); var episodesRequests = new List(); foreach (var ep in episodes) { episodesRequests.Add(new EpisodeRequests { EpisodeNumber = ep.number, AirDate = DateTime.Parse(ep.airdate), Title = ep.name, Url = ep.url }); } childRequest.SeasonRequests.Add(new SeasonRequests { Episodes = episodesRequests, SeasonNumber = latest.season, }); } else if (tv.FirstSeason) { var episodes = await TvApi.EpisodeLookup(showInfo.id); var first = episodes.OrderByDescending(x => x.season).FirstOrDefault(); var episodesRequests = new List(); foreach (var ep in episodes) { if (ep.season == first.season) { episodesRequests.Add(new EpisodeRequests { EpisodeNumber = ep.number, AirDate = DateTime.Parse(ep.airdate), Title = ep.name, Url = ep.url }); } } childRequest.SeasonRequests.Add(new SeasonRequests { Episodes = episodesRequests, SeasonNumber = first.season, }); } else { // It's a custom request childRequest.SeasonRequests = tvRequests; } var ruleResults = await RunRequestRules(childRequest); var results = ruleResults as RuleResult[] ?? ruleResults.ToArray(); if (results.Any(x => !x.Success)) { return new RequestEngineResult { ErrorMessage = results.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message }; } var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.Id); if (existingRequest != null) { // Remove requests we already have, we just want new ones var existingSeasons = existingRequest.ChildRequests.Select(x => x.SeasonRequests); foreach (var existingSeason in existingRequest.ChildRequests) foreach (var existing in existingSeason.SeasonRequests) { var newChild = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == existing.SeasonNumber); if (newChild != null) { // We have some requests in this season... // Let's find the episodes. foreach (var existingEp in existing.Episodes) { var duplicateEpisode = newChild.Episodes.FirstOrDefault(x => x.EpisodeNumber == existingEp.EpisodeNumber); if (duplicateEpisode != null) { // Remove it. newChild.Episodes.Remove(duplicateEpisode); } } if (!newChild.Episodes.Any()) { // We may have removed all episodes childRequest.SeasonRequests.Remove(newChild); } } } // Remove the ID since this is a new child childRequest.Id = 0; return await AddExistingRequest(childRequest, existingRequest); } // This is a new request var model = new TvRequests { Id = tv.Id, Overview = showInfo.summary.RemoveHtml(), PosterPath = posterPath, Title = showInfo.name, ReleaseDate = firstAir, Status = showInfo.status, ImdbId = showInfo.externals?.imdb ?? string.Empty, TvDbId = tv.Id, ChildRequests = new List(), TotalSeasons = tv.SeasonRequests.Count() }; model.ChildRequests.Add(childRequest); return await AddRequest(model); } public async Task> GetRequests(int count, int position) { var allRequests = await TvRepository.Get().Skip(position).Take(count).ToListAsync(); return allRequests; } public async Task> GetRequests() { var allRequests = TvRepository.Get(); return await allRequests.ToListAsync(); } public async Task> SearchTvRequest(string search) { var allRequests = TvRepository.Get(); var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)); return results; } public async Task UpdateTvRequest(TvRequests request) { var allRequests = TvRepository.Get(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id); results = Mapper.Map(request); // TODO need to check if we need to approve any child requests since they may have updated await TvRepository.Update(results); return results; } public async Task RemoveTvRequest(int requestId) { var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId); await TvRepository.Delete(request); } private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest) { // Add the child existingRequest.ChildRequests.Add(newRequest); await TvRepository.Update(existingRequest); if (newRequest.Approved) // The auto approve rule { // TODO Auto Approval Code } return await AfterRequest(newRequest); } private async Task AddRequest(TvRequests model) { await TvRepository.Add(model); // This is a new request so we should only have 1 child return await AfterRequest(model.ChildRequests.FirstOrDefault()); } private Task AfterRequest(ChildRequests model) { if (ShouldSendNotification(RequestType.TvShow)) { //NotificationHelper.NewRequest(model.ParentRequest); } if(model.Approved) { // Autosend TvSender.SendToSonarr(model); } //var limit = await RequestLimitRepo.GetAllAsync(); //var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type); //if (usersLimit == null) //{ // await RequestLimitRepo.InsertAsync(new RequestLimit // { // Username = Username, // RequestType = model.Type, // FirstRequestDate = DateTime.UtcNow, // RequestCount = 1 // }); //} //else //{ // usersLimit.RequestCount++; // await RequestLimitRepo.UpdateAsync(usersLimit); //} return Task.FromResult(new RequestEngineResult { RequestAdded = true }); } public async Task> GetApprovedRequests() { //var allRequests = TvRepository.Get(); //return await allRequests.Where(x => x.Approved && !x.Available).ToListAsync(); return null; } public async Task> GetNewRequests() { //var allRequests = await TvRepository.GetAllAsync(); //return allRequests.Where(x => !x.Approved && !x.Available); return null; } public async Task> GetAvailableRequests() { //var allRequests = await TvRepository.GetAllAsync(); //return allRequests.Where(x => x.Available); return null; } } }