using AutoMapper; using Hangfire; using Ombi.Api.TvMaze; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Core.Rules; using Ombi.Helpers; using Ombi.Notifications; using Ombi.Notifications.Models; 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 Ombi.Core.Engine.Interfaces; using Ombi.Core.Notifications; using Ombi.Store; namespace Ombi.Core.Engine { public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine { public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, INotificationService notificationService, IMapper map, IRuleEvaluator rule) : base(user, requestService, rule) { TvApi = tvApi; NotificationService = notificationService; Mapper = map; } private INotificationService NotificationService { get; } private ITvMazeApi TvApi { get; } private IMapper Mapper { 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 childRequest = new ChildTvRequest { Id = tv.Id, Type = RequestType.TvShow, Overview = showInfo.summary.RemoveHtml(), PosterPath = posterPath, Title = showInfo.name, ReleaseDate = firstAir, Status = showInfo.status, RequestedDate = DateTime.UtcNow, Approved = false, RequestedUser = Username, Issues = IssueState.None, ProviderId = tv.Id, RequestAll = tv.RequestAll, SeasonRequests = tvRequests }; var model = new TvRequestModel { Id = tv.Id, Type = RequestType.TvShow, Overview = showInfo.summary.RemoveHtml(), PosterPath = posterPath, Title = showInfo.name, ReleaseDate = firstAir, Status = showInfo.status, Approved = false, ImdbId = showInfo.externals?.imdb ?? string.Empty, TvDbId = tv.Id.ToString(), ProviderId = tv.Id }; model.ChildRequests.Add(childRequest); //if (childRequest.SeasonRequests.Any()) //{ // var episodes = await TvApi.EpisodeLookup(showInfo.id); // foreach (var e in episodes) // { // var season = childRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season); // season?.Episodes.Add(new EpisodesRequested // { // Url = e.url, // Title = e.name, // AirDate = DateTime.Parse(e.airstamp), // EpisodeNumber = e.number // }); // } //} if (tv.LatestSeason) { var latest = showInfo.Season.OrderBy(x => x.SeasonNumber).FirstOrDefault(); foreach (var modelSeasonRequest in childRequest.SeasonRequests) { if (modelSeasonRequest.SeasonNumber == latest.SeasonNumber) { foreach (var episodesRequested in modelSeasonRequest.Episodes) { episodesRequested.Requested = true; } } } } if (tv.FirstSeason) { var first = showInfo.Season.OrderByDescending(x => x.SeasonNumber).FirstOrDefault(); foreach (var modelSeasonRequest in childRequest.SeasonRequests) { if (modelSeasonRequest.SeasonNumber == first.SeasonNumber) { foreach (var episodesRequested in modelSeasonRequest.Episodes) { episodesRequested.Requested = true; } } } } var ruleResults = RunRequestRules(model).ToList(); if (ruleResults.Any(x => !x.Success)) { return new RequestEngineResult { ErrorMessage = ruleResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message }; } var existingRequest = await TvRequestService.CheckRequestAsync(model.Id); if (existingRequest != null) { return await AddExistingRequest(model, existingRequest); } // This is a new request return await AddRequest(model); } public async Task> GetRequests(int count, int position) { var allRequests = await TvRequestService.GetAllAsync(count, position); return allRequests; } public async Task> GetRequests() { var allRequests = await TvRequestService.GetAllAsync(); return allRequests; } public async Task> SearchTvRequest(string search) { var allRequests = await TvRequestService.GetAllAsync(); var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)); return results; } public async Task UpdateTvRequest(TvRequestModel request) { var allRequests = await TvRequestService.GetAllAsync(); var results = allRequests.FirstOrDefault(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 var model = TvRequestService.UpdateRequest(results); return model; } public async Task RemoveTvRequest(int requestId) { await TvRequestService.DeleteRequestAsync(requestId); } private async Task AddExistingRequest(TvRequestModel newRequest, TvRequestModel existingRequest) { var child = newRequest.ChildRequests.FirstOrDefault(); // There will only be 1 var episodeDiff = new List(); foreach (var existingChild in existingRequest.ChildRequests) { var difference = GetListDifferences(existingChild.SeasonRequests, child.SeasonRequests).ToList(); if (difference.Any()) episodeDiff = difference; } if (episodeDiff.Any()) child.SeasonRequests = episodeDiff; existingRequest.ChildRequests.AddRange(newRequest.ChildRequests); TvRequestService.UpdateRequest(existingRequest); if (newRequest.Approved) // The auto approve rule { // TODO Auto Approval Code } return await AfterRequest(newRequest); } private IEnumerable GetListDifferences(List existing, List request) { var requestsToRemove = new List(); foreach (var r in request) { // Do we have an existing season? var existingSeason = existing.FirstOrDefault(x => x.SeasonNumber == r.SeasonNumber); if (existingSeason == null) { continue; } // Compare the episodes for (var i = r.Episodes.Count - 1; i >= 0; i--) { var existingEpisode = existingSeason.Episodes.FirstOrDefault(x => x.EpisodeNumber == r.Episodes[i].EpisodeNumber); if (existingEpisode == null) { // we are fine, we have not yet requested this } else { // We already have this request r.Episodes.RemoveAt(i); } } if (!r.Episodes.Any()) { requestsToRemove.Add(r); } } foreach (var remove in requestsToRemove) { request.Remove(remove); } return request; } private async Task AddRequest(TvRequestModel model) { await TvRequestService.AddRequestAsync(model); return await AfterRequest(model); } private async Task AfterRequest(TvRequestModel model) { if (ShouldSendNotification(model.Type)) { var n = new NotificationOptions(); n.Title = model.Title; n.RequestedUser = Username; n.DateTime = DateTime.Now; n.NotificationType = NotificationType.NewRequest; n.RequestType = model.Type; n.ImgSrc = model.PosterPath; BackgroundJob.Enqueue(() => NotificationService.Publish(n).Wait() ); } //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 new RequestEngineResult { RequestAdded = true }; } public async Task> GetApprovedRequests() { var allRequests = await TvRequestService.GetAllAsync(); return allRequests.Where(x => x.Approved && !x.Available); } public async Task> GetNewRequests() { var allRequests = await TvRequestService.GetAllAsync(); return allRequests.Where(x => !x.Approved && !x.Available); } public async Task> GetAvailableRequests() { var allRequests = await TvRequestService.GetAllAsync(); return allRequests.Where(x => x.Available); } } }