using System; using AutoMapper; using Ombi.Api.TvMaze; using Ombi.Api.TheMovieDb; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Helpers; using Ombi.Store.Entities; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Helpers; using Ombi.Core.Models.UI; using Ombi.Core.Rule; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Senders; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Core.Models; using System.Threading; using Microsoft.Extensions.Logging; namespace Ombi.Core.Engine { public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine { public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, ICurrentUser user, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ILogger logger, ITvSender sender, IRepository rl, ISettingsService settings, ICacheService cache, IRepository sub, IMediaCacheService mediaCacheService, IUserPlayedEpisodeRepository userPlayedEpisodeRepository) : base(user, requestService, rule, manager, cache, settings, sub) { TvApi = tvApi; MovieDbApi = movApi; NotificationHelper = helper; _logger = logger; TvSender = sender; _requestLog = rl; _mediaCacheService = mediaCacheService; _userPlayedEpisodeRepository = userPlayedEpisodeRepository; } private INotificationHelper NotificationHelper { get; } private ITvMazeApi TvApi { get; } private IMovieDbApi MovieDbApi { get; } private ITvSender TvSender { get; } private readonly ILogger _logger; private readonly IRepository _requestLog; private readonly IMediaCacheService _mediaCacheService; private readonly IUserPlayedEpisodeRepository _userPlayedEpisodeRepository; public async Task RequestTvShow(TvRequestViewModel tv) { var user = await GetUser(); var canRequestOnBehalf = false; if (tv.RequestOnBehalf.HasValue()) { canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin); if (!canRequestOnBehalf) { return new RequestEngineResult { Result = false, Message = "You do not have the correct permissions to request on behalf of users!", ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" }; } } var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi, _logger); (await tvBuilder .GetShowInfo(tv.TvDbId)) .CreateTvList(tv) .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id); await tvBuilder.BuildEpisodes(tv); var ruleResults = await RunRequestRules(tvBuilder.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 }; } // Check if we have auto approved the request, if we have then mark the episodes as approved if (tvBuilder.ChildRequest.Approved) { foreach (var seasons in tvBuilder.ChildRequest.SeasonRequests) { foreach (var ep in seasons.Episodes) { ep.Approved = true; ep.Requested = true; } } } var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.TvDbId); if (existingRequest != null) { // Remove requests we already have, we just want new ones foreach (var existingSeason in existingRequest.ChildRequests) foreach (var existing in existingSeason.SeasonRequests) { var newChild = tvBuilder.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 tvBuilder.ChildRequest.SeasonRequests.Remove(newChild); } } } // Remove the ID since this is a new child // This was a TVDBID for the request rules to run tvBuilder.ChildRequest.Id = 0; if (!tvBuilder.ChildRequest.SeasonRequests.Any()) { // Looks like we have removed them all! They were all duplicates... return new RequestEngineResult { Result = false, ErrorCode = ErrorCode.AlreadyRequested, ErrorMessage = "This has already been requested" }; } return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf, tv.RootFolderOverride.GetValueOrDefault(), tv.QualityPathOverride.GetValueOrDefault()); } // This is a new request var newRequest = tvBuilder.CreateNewRequest(tv); return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf); } public async Task RequestTvShow(TvRequestViewModelV2 tv) { var user = await GetUser(); var canRequestOnBehalf = tv.RequestOnBehalf.HasValue(); var isAdmin = Username.Equals("API", StringComparison.CurrentCultureIgnoreCase) || await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin); if (tv.RequestOnBehalf.HasValue() && !isAdmin) { return new RequestEngineResult { Result = false, ErrorCode = ErrorCode.NoPermissionsOnBehalf, Message = "You do not have the correct permissions to request on behalf of users!", ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" }; } if ((tv.RootFolderOverride.HasValue || tv.QualityPathOverride.HasValue || tv.LanguageProfile.HasValue) && !isAdmin) { return new RequestEngineResult { Result = false, ErrorCode = ErrorCode.NoPermissions, Message = "You do not have the correct permissions!", ErrorMessage = $"You do not have the correct permissions!" }; } var tvBuilder = new TvShowRequestBuilderV2(MovieDbApi); (await tvBuilder .GetShowInfo(tv.TheMovieDbId, tv.languageCode)) .CreateTvList(tv) .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id, tv.Source); await tvBuilder.BuildEpisodes(tv); var ruleResults = await RunRequestRules(tvBuilder.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 }; } // Check if we have auto approved the request, if we have then mark the episodes as approved if (tvBuilder.ChildRequest.Approved) { foreach (var seasons in tvBuilder.ChildRequest.SeasonRequests) { foreach (var ep in seasons.Episodes) { ep.Approved = true; ep.Requested = true; } } } var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.ExternalProviderId == tv.TheMovieDbId); if (existingRequest != null) { // Remove requests we already have, we just want new ones foreach (var existingSeason in existingRequest.ChildRequests) foreach (var existing in existingSeason.SeasonRequests) { var newChild = tvBuilder.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 tvBuilder.ChildRequest.SeasonRequests.Remove(newChild); } } } // Remove the ID since this is a new child // This was a TVDBID for the request rules to run tvBuilder.ChildRequest.Id = 0; if (!tvBuilder.ChildRequest.SeasonRequests.Any()) { // Looks like we have removed them all! They were all duplicates... return new RequestEngineResult { Result = false, ErrorCode = ErrorCode.AlreadyRequested, ErrorMessage = "This has already been requested" }; } return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf, tv.RootFolderOverride.GetValueOrDefault(), tv.QualityPathOverride.GetValueOrDefault()); } // This is a new request var newRequest = tvBuilder.CreateNewRequest(tv, tv.RootFolderOverride.GetValueOrDefault(), tv.QualityPathOverride.GetValueOrDefault(), tv.LanguageProfile.GetValueOrDefault()); return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf); } public async Task> GetRequests(int count, int position, OrderFilterModel type) { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.Get(shouldHide.UserId) .Include(x => x.ChildRequests) .ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.Episodes) .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()) .Skip(position).Take(count).ToListAsync(); // Filter out children FilterChildren(allRequests, shouldHide); } else { allRequests = await TvRepository.Get() .Include(x => x.ChildRequests) .ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.Episodes) .OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()) .Skip(position).Take(count).ToListAsync(); } await FillAdditionalFields(shouldHide, allRequests); return new RequestsViewModel { Collection = allRequests }; } public async Task> GetRequestsLite(int count, int position, OrderFilterModel type) { var shouldHide = await HideFromOtherUsers(); List allRequests = null; if (shouldHide.Hide) { var tv = TvRepository.GetLite(shouldHide.UserId); if (tv.Any() && tv.Select(x => x.ChildRequests).Any()) { allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync(); } // Filter out children FilterChildren(allRequests, shouldHide); } else { var tv = TvRepository.GetLite(); if (tv.Any() && tv.Select(x => x.ChildRequests).Any()) { allRequests = await tv.OrderByDescending(x => x.ChildRequests.Select(y => y.RequestedDate).FirstOrDefault()).Skip(position).Take(count).ToListAsync(); } } if (allRequests == null) { return new RequestsViewModel(); } await FillAdditionalFields(shouldHide, allRequests); return new RequestsViewModel { Collection = allRequests }; } public async Task> GetRequests() { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.Get(shouldHide.UserId).ToListAsync(); FilterChildren(allRequests, shouldHide); } else { allRequests = await TvRepository.Get().ToListAsync(); } await FillAdditionalFields(shouldHide, allRequests); return allRequests; } public async Task> GetRequests(int count, int position, string sortProperty, string sortOrder) { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.GetChild(shouldHide.UserId).ToListAsync(); // Filter out children FilterChildren(allRequests, shouldHide); } else { allRequests = await TvRepository.GetChild().ToListAsync(); } if (allRequests == null) { return new RequestsViewModel(); } var total = allRequests.Count; var prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find(sortProperty, true); if (sortProperty.Contains('.')) { // This is a navigation property currently not supported prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find("Title", true); //var properties = sortProperty.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries); //var firstProp = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(properties[0], true); //var propType = firstProp.PropertyType; //var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true); } allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase) ? allRequests.OrderBy(x => prop.GetValue(x)).ToList() : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); await FillAdditionalFields(shouldHide, allRequests); // Make sure we do not show duplicate child requests allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList(); allRequests = allRequests.Skip(position).Take(count).ToList(); return new RequestsViewModel { Collection = allRequests, Total = total, }; } public async Task> GetRequests(int count, int position, string sortProperty, string sortOrder, RequestStatus status) { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.GetChild(shouldHide.UserId).ToListAsync(); // Filter out children FilterChildren(allRequests, shouldHide); } else { allRequests = await TvRepository.GetChild().ToListAsync(); } switch (status) { case RequestStatus.PendingApproval: allRequests = allRequests.Where(x => !x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value)).ToList(); break; case RequestStatus.ProcessingRequest: allRequests = allRequests.Where(x => x.Approved && !x.Available && (!x.Denied.HasValue || !x.Denied.Value)).ToList(); break; case RequestStatus.Available: allRequests = allRequests.Where(x => x.Available && (!x.Denied.HasValue || !x.Denied.Value)).ToList(); break; case RequestStatus.Denied: allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value).ToList(); break; default: break; } if (allRequests == null) { return new RequestsViewModel(); } var total = allRequests.Count; var prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find(sortProperty, true); if (sortProperty.Contains('.')) { // This is a navigation property currently not supported prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find("Title", true); //var properties = sortProperty.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries); //var firstProp = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(properties[0], true); //var propType = firstProp.PropertyType; //var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true); } allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase) ? allRequests.OrderBy(x => prop.GetValue(x)).ToList() : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); await FillAdditionalFields(shouldHide, allRequests); // Make sure we do not show duplicate child requests allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList(); allRequests = allRequests.Skip(position).Take(count).ToList(); return new RequestsViewModel { Collection = allRequests, Total = total, }; } public async Task> GetUnavailableRequests(int count, int position, string sortProperty, string sortOrder) { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.GetChild(shouldHide.UserId).Where(x => !x.Available && x.Approved).ToListAsync(); // Filter out children FilterChildren(allRequests, shouldHide); } else { allRequests = await TvRepository.GetChild().Where(x => !x.Available && x.Approved).ToListAsync(); } if (allRequests == null) { return new RequestsViewModel(); } var total = allRequests.Count; var prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find(sortProperty, true); if (sortProperty.Contains('.')) { // This is a navigation property currently not supported prop = TypeDescriptor.GetProperties(typeof(ChildRequests)).Find("Title", true); //var properties = sortProperty.Split(new []{'.'}, StringSplitOptions.RemoveEmptyEntries); //var firstProp = TypeDescriptor.GetProperties(typeof(MovieRequests)).Find(properties[0], true); //var propType = firstProp.PropertyType; //var secondProp = TypeDescriptor.GetProperties(propType).Find(properties[1], true); } allRequests = sortOrder.Equals("asc", StringComparison.InvariantCultureIgnoreCase) ? allRequests.OrderBy(x => prop.GetValue(x)).ToList() : allRequests.OrderByDescending(x => prop.GetValue(x)).ToList(); await FillAdditionalFields(shouldHide, allRequests); // Make sure we do not show duplicate child requests allRequests = allRequests.DistinctBy(x => x.ParentRequest.Title).ToList(); allRequests = allRequests.Skip(position).Take(count).ToList(); return new RequestsViewModel { Collection = allRequests, Total = total, }; } public async Task> GetRequestsLite() { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.GetLite(shouldHide.UserId).ToListAsync(); FilterChildren(allRequests, shouldHide); } else { allRequests = await TvRepository.GetLite().ToListAsync(); } await FillAdditionalFields(shouldHide, allRequests); return allRequests; } public async Task GetTvRequest(int requestId) { var shouldHide = await HideFromOtherUsers(); TvRequests request; if (shouldHide.Hide) { request = await TvRepository.Get(shouldHide.UserId).Where(x => x.Id == requestId).FirstOrDefaultAsync(); FilterChildren(request, shouldHide); } else { request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync(); } await FillAdditionalFields(shouldHide, new List{request}); return request; } private static void FilterChildren(IEnumerable allRequests, HideResult shouldHide) { if (allRequests == null) { return; } // Filter out children foreach (var t in allRequests) { for (var j = 0; j < t.ChildRequests.Count; j++) { FilterChildren(t, shouldHide); } } } private static void FilterChildren(TvRequests t, HideResult shouldHide) { // Filter out children FilterChildren(t.ChildRequests, shouldHide); } private static void FilterChildren(List t, HideResult shouldHide) { // Filter out children for (var j = 0; j < t.Count; j++) { var child = t[j]; if (child.RequestedUserId != shouldHide.UserId) { t.RemoveAt(j); j--; } } } public async Task> GetAllChldren(int tvId) { var shouldHide = await HideFromOtherUsers(); List allRequests; if (shouldHide.Hide) { allRequests = await TvRepository.GetChild(shouldHide.UserId).Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync(); } else { allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync(); } await FillAdditionalFields(shouldHide, allRequests); return allRequests; } public async Task> SearchTvRequest(string search) { var shouldHide = await HideFromOtherUsers(); IQueryable allRequests; if (shouldHide.Hide) { allRequests = TvRepository.Get(shouldHide.UserId); } else { allRequests = TvRepository.Get(); } var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); await FillAdditionalFields(shouldHide, results); return results; } public async Task UpdateRootPath(int requestId, int rootPath) { var allRequests = TvRepository.Get(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId); results.RootFolder = rootPath; await TvRepository.Update(results); } public async Task UpdateQualityProfile(int requestId, int profileId) { var allRequests = TvRepository.Get(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == requestId); results.QualityOverride = profileId; await TvRepository.Update(results); } public async Task UpdateTvRequest(TvRequests request) { var allRequests = TvRepository.Get(); var results = await allRequests.FirstOrDefaultAsync(x => x.Id == request.Id); results.TvDbId = request.TvDbId; results.ImdbId = request.ImdbId; results.Overview = request.Overview; results.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath); results.Background = PosterPathHelper.FixBackgroundPath(request.Background); results.QualityOverride = request.QualityOverride; results.RootFolder = request.RootFolder; await TvRepository.Update(results); return results; } public async Task ApproveChildRequest(int id) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == id); if (request == null) { return new RequestEngineResult { ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } request.Approved = true; request.Denied = false; foreach (var s in request.SeasonRequests) { foreach (var ep in s.Episodes) { ep.Approved = true; ep.Requested = true; } } await TvRepository.UpdateChild(request); await _mediaCacheService.Purge(); if (request.Approved) { var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification, string.Empty); if (canNotify.Success) { await NotificationHelper.Notify(request, NotificationType.RequestApproved); } // Autosend await TvSender.Send(request); } return new RequestEngineResult { Result = true }; } public async Task DenyChildRequest(int requestId, string reason) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); if (request == null) { return new RequestEngineResult { ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } request.Denied = true; request.DeniedReason = reason; await TvRepository.UpdateChild(request); await _mediaCacheService.Purge(); await NotificationHelper.Notify(request, NotificationType.RequestDeclined); return new RequestEngineResult { Result = true }; } public async Task UpdateChildRequest(ChildRequests request) { await TvRepository.UpdateChild(request); await _mediaCacheService.Purge(); return request; } public async Task RemoveTvChild(int requestId) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); var result = await CheckCanManageRequest(request); if (result.IsError) return result; TvRepository.Db.ChildRequests.Remove(request); var all = TvRepository.Db.TvRequests.Include(x => x.ChildRequests); var parent = all.FirstOrDefault(x => x.Id == request.ParentRequestId); // Is this the only child? If so delete the parent if (parent.ChildRequests.Count <= 1) { // Delete the parent TvRepository.Db.TvRequests.Remove(parent); } await TvRepository.Db.SaveChangesAsync(); await _mediaCacheService.Purge(); return new RequestEngineResult { Result = true, }; } public async Task RemoveTvRequest(int requestId) { var request = await TvRepository.Get().FirstOrDefaultAsync(x => x.Id == requestId); await TvRepository.Delete(request); await _mediaCacheService.Purge(); } public async Task UserHasRequest(string userId) { return await TvRepository.GetChild().AnyAsync(x => x.RequestedUserId == userId); } public async Task MarkUnavailable(int modelId, bool is4K) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId); if (request == null) { return new RequestEngineResult { ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } request.Available = false; foreach (var season in request.SeasonRequests) { foreach (var e in season.Episodes) { e.Available = false; } } await TvRepository.UpdateChild(request); await _mediaCacheService.Purge(); return new RequestEngineResult { Result = true, Message = "Request is now unavailable", }; } public async Task MarkAvailable(int modelId, bool is4K) { ChildRequests request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId); if (request == null) { return new RequestEngineResult { ErrorCode = ErrorCode.ChildRequestDoesNotExist, ErrorMessage = "Child Request does not exist" }; } request.Available = true; request.MarkedAsAvailable = DateTime.Now; foreach (var season in request.SeasonRequests) { foreach (var e in season.Episodes) { e.Available = true; } } await TvRepository.UpdateChild(request); await NotificationHelper.Notify(request, NotificationType.RequestAvailable); await _mediaCacheService.Purge(); return new RequestEngineResult { Result = true, Message = "Request is now available", }; } public async Task GetTotal() { var shouldHide = await HideFromOtherUsers(); if (shouldHide.Hide) { return await TvRepository.Get(shouldHide.UserId).CountAsync(); } else { return await TvRepository.Get().CountAsync(); } } private async Task FillAdditionalFields(HideResult shouldHide, List x) { foreach (var tvRequest in x) { await FillAdditionalFields(shouldHide, tvRequest.ChildRequests); } } private async Task FillAdditionalFields(HideResult shouldHide, List childRequests) { await CheckForSubscription(shouldHide, childRequests); CheckForPlayed(shouldHide, childRequests); } private async Task CheckForSubscription(HideResult shouldHide, List childRequests) { var sub = _subscriptionRepository.GetAll(); var childIds = childRequests.Select(x => x.Id); var relevantSubs = await sub.Where(s => s.UserId == shouldHide.UserId && childIds.Contains(s.Id) && s.RequestType == RequestType.TvShow).ToListAsync(); foreach (var x in childRequests) { if (shouldHide.UserId == x.RequestedUserId) { x.ShowSubscribe = false; } else { if (!x.Available && (!x.Denied ?? true)) { x.ShowSubscribe = true; } var result = relevantSubs.FirstOrDefault(s => s.RequestId == x.Id); x.Subscribed = result != null; } } } private class EpisodeKey { public int SeasonNumber; public int EpisodeNumber; } private void CheckForPlayed(HideResult shouldHide, List childRequests) { var theMovieDbIds = childRequests.Select(x => x.Id); foreach (var request in childRequests) { var requestedEpisodes = GetEpisodesKeys(request); var playedEpisodes = _userPlayedEpisodeRepository .GetAll() .Where(x => x.TheMovieDbId == request.Id && x.UserId == request.RequestedUserId) .AsEnumerable() .Join(requestedEpisodes, played => new { played.SeasonNumber, played.EpisodeNumber }, requested => new { requested.SeasonNumber, requested.EpisodeNumber }, (played, requested) => new { played }); var playedCount = playedEpisodes.Count(); var toWatchCount = requestedEpisodes.Count(); request.RequestedUserPlayedProgress = 100 * playedCount / toWatchCount; } } private List GetEpisodesKeys(ChildRequests request) { List result = new List(); foreach(var season in request.SeasonRequests) { foreach(var episode in season.Episodes) { result.Add(new EpisodeKey { SeasonNumber = season.SeasonNumber, EpisodeNumber = episode.EpisodeNumber }); } } return result; } private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile) { // Add the child existingRequest.ChildRequests.Add(newRequest); if (qualityProfile > 0) { existingRequest.QualityOverride = qualityProfile; } if (rootFolder > 0) { existingRequest.RootFolder = rootFolder; } await TvRepository.Update(existingRequest); return await AfterRequest(newRequest, requestOnBehalf); } private async Task AddRequest(TvRequests model, string requestOnBehalf) { await TvRepository.Add(model); // This is a new request so we should only have 1 child return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf); } public async Task ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken) { var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId, cancellationToken); if (request == null) { return new RequestEngineResult { Result = false, ErrorCode = ErrorCode.RequestDoesNotExist, ErrorMessage = "Request does not exist" }; } return await ProcessSendingShow(request); } private async Task AfterRequest(ChildRequests model, string requestOnBehalf) { var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf); if (sendRuleResult.Success) { await NotificationHelper.NewRequest(model); } await _requestLog.Add(new RequestLog { UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id, RequestDate = DateTime.UtcNow, RequestId = model.Id, RequestType = RequestType.TvShow, EpisodeCount = model.SeasonRequests.Select(m => m.Episodes.Count).Sum(), }); await _mediaCacheService.Purge(); return await ProcessSendingShow(model); } private async Task ProcessSendingShow(ChildRequests model) { if (model.Approved) { // Autosend var canNotify = await RunSpecificRule(model, SpecificRules.CanSendNotification, string.Empty); if (canNotify.Success) { await NotificationHelper.Notify(model, NotificationType.RequestApproved); } var result = await TvSender.Send(model); if (result.Success) { return new RequestEngineResult { Result = true, RequestId = model.Id }; } return new RequestEngineResult { ErrorMessage = result.Message, RequestId = model.Id }; } return new RequestEngineResult { Result = true, RequestId = model.Id }; } public async Task UpdateAdvancedOptions(MediaAdvancedOptions options) { var request = await TvRepository.Find(options.RequestId); if (request == null) { return new RequestEngineResult { Result = false, ErrorCode = ErrorCode.RequestDoesNotExist, ErrorMessage = "Request does not exist" }; } request.QualityOverride = options.QualityOverride; request.RootFolder = options.RootPathOverride; if (options.LanguageProfile > 0) { request.LanguageProfile = options.LanguageProfile; } await TvRepository.Update(request); return new RequestEngineResult { Result = true }; } } }