diff --git a/PlexRequests.Api.Interfaces/IPlexApi.cs b/PlexRequests.Api.Interfaces/IPlexApi.cs index 52d598aba..7bd6c8095 100644 --- a/PlexRequests.Api.Interfaces/IPlexApi.cs +++ b/PlexRequests.Api.Interfaces/IPlexApi.cs @@ -43,6 +43,6 @@ namespace PlexRequests.Api.Interfaces PlexEpisodeMetadata GetEpisodeMetaData(string authToken, Uri host, string ratingKey); PlexSearch GetAllEpisodes(string authToken, Uri host, string section, int startPage, int returnCount); PlexServer GetServer(string authToken); - PlexMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey); + PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey); } } \ No newline at end of file diff --git a/PlexRequests.Api.Models/Plex/PlexMetadata.cs b/PlexRequests.Api.Models/Plex/PlexMetadata.cs index e2ffc853e..7e0eddb1f 100644 --- a/PlexRequests.Api.Models/Plex/PlexMetadata.cs +++ b/PlexRequests.Api.Models/Plex/PlexMetadata.cs @@ -55,4 +55,29 @@ namespace PlexRequests.Api.Models.Plex public string MediaTagVersion { get; set; } } + [XmlRoot(ElementName = "MediaContainer")] + public class PlexSeasonMetadata + { + [XmlElement(ElementName = "Video")] + public Video Video { get; set; } + [XmlElement(ElementName = "Directory")] + public List Directory { get; set; } + [XmlAttribute(AttributeName = "size")] + public string Size { get; set; } + [XmlAttribute(AttributeName = "allowSync")] + public string AllowSync { get; set; } + [XmlAttribute(AttributeName = "identifier")] + public string Identifier { get; set; } + [XmlAttribute(AttributeName = "librarySectionID")] + public string LibrarySectionID { get; set; } + [XmlAttribute(AttributeName = "librarySectionTitle")] + public string LibrarySectionTitle { get; set; } + [XmlAttribute(AttributeName = "librarySectionUUID")] + public string LibrarySectionUUID { get; set; } + [XmlAttribute(AttributeName = "mediaTagPrefix")] + public string MediaTagPrefix { get; set; } + [XmlAttribute(AttributeName = "mediaTagVersion")] + public string MediaTagVersion { get; set; } + } + } \ No newline at end of file diff --git a/PlexRequests.Api.Models/Plex/PlexSearch.cs b/PlexRequests.Api.Models/Plex/PlexSearch.cs index 541f64918..c09f9ae7d 100644 --- a/PlexRequests.Api.Models/Plex/PlexSearch.cs +++ b/PlexRequests.Api.Models/Plex/PlexSearch.cs @@ -244,6 +244,10 @@ namespace PlexRequests.Api.Models.Plex [XmlRoot(ElementName = "Directory")] public class Directory1 { + public Directory1() + { + Seasons = new List(); + } public string ProviderId { get; set; } [XmlAttribute(AttributeName = "guid")] public string Guid { get; set; } diff --git a/PlexRequests.Api/PlexApi.cs b/PlexRequests.Api/PlexApi.cs index a16402680..233bb66ff 100644 --- a/PlexRequests.Api/PlexApi.cs +++ b/PlexRequests.Api/PlexApi.cs @@ -302,7 +302,7 @@ namespace PlexRequests.Api } - public PlexMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey) + public PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey) { var request = new RestRequest { @@ -315,7 +315,7 @@ namespace PlexRequests.Api try { - var lib = RetryHandler.Execute(() => Api.ExecuteXml(request, plexFullHost), + var lib = RetryHandler.Execute(() => Api.ExecuteXml(request, plexFullHost), (exception, timespan) => Log.Error(exception, "Exception when calling GetMetadata for Plex, Retrying {0}", timespan), new[] { TimeSpan.FromSeconds (5), TimeSpan.FromSeconds(10), @@ -327,7 +327,7 @@ namespace PlexRequests.Api catch (Exception e) { Log.Error(e, "There has been a API Exception when attempting to get the Plex GetMetadata"); - return new PlexMetadata(); + return new PlexSeasonMetadata(); } } diff --git a/PlexRequests.Core/SettingModels/PlexSettings.cs b/PlexRequests.Core/SettingModels/PlexSettings.cs index 2312772ac..e83e61f4d 100644 --- a/PlexRequests.Core/SettingModels/PlexSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexSettings.cs @@ -28,6 +28,10 @@ namespace PlexRequests.Core.SettingModels { public sealed class PlexSettings : ExternalSettings { + public PlexSettings() + { + AdvancedSearch = true; + } public bool AdvancedSearch { get; set; } public bool EnableTvEpisodeSearching { get; set; } diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index 2facac40c..15e83d2b2 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -30,10 +30,10 @@ namespace PlexRequests.Core.SettingModels { public ScheduledJobsSettings() { - PlexAvailabilityChecker = 10; - SickRageCacher = 10; - SonarrCacher = 10; - CouchPotatoCacher = 10; + PlexAvailabilityChecker = 60; + SickRageCacher = 60; + SonarrCacher = 60; + CouchPotatoCacher = 60; StoreBackup = 24; StoreCleanup = 24; UserRequestLimitResetter = 12; diff --git a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs index 88aea4091..becc8cb70 100644 --- a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs +++ b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs @@ -181,7 +181,7 @@ namespace PlexRequests.Services.Jobs { if (advanced) { - if (!string.IsNullOrEmpty(movie.ProviderId) && + if (!string.IsNullOrEmpty(movie.ProviderId) && movie.ProviderId.Equals(providerId, StringComparison.InvariantCultureIgnoreCase)) { return true; @@ -209,7 +209,8 @@ namespace PlexRequests.Services.Jobs ).ToArray(); foreach (var lib in tvLibs) - { + { + shows.AddRange(lib.Directory.Select(x => new PlexTvShow // shows are in the directory list { Title = x.Title, @@ -229,14 +230,15 @@ namespace PlexRequests.Services.Jobs { if (advanced) { - if (seasons != null) + if (seasons != null && show.ProviderId == providerId) { if (seasons.Any(season => show.Seasons.Contains(season))) { return true; } + return false; } - if (!string.IsNullOrEmpty(show.ProviderId) && + if (!string.IsNullOrEmpty(show.ProviderId) && show.ProviderId.Equals(providerId, StringComparison.InvariantCultureIgnoreCase)) { return true; @@ -257,14 +259,14 @@ namespace PlexRequests.Services.Jobs connection => { connection.Open(); - var result = connection.Query("select * from PlexEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId}); + var result = connection.Query("select * from PlexEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId }); return result; }).ToList(); if (!ep.Any()) { - Log.Info("Episode cache info is not available. tvdbid: {0}, season: {1}, episode: {2}",theTvDbId, season, episode); + Log.Info("Episode cache info is not available. tvdbid: {0}, season: {1}, episode: {2}", theTvDbId, season, episode); return false; } foreach (var result in ep) @@ -298,13 +300,13 @@ namespace PlexRequests.Services.Jobs /// public async Task> GetEpisodes(int theTvDbId) { - var ep = await EpisodeRepo.CustomAsync(async connection => - { - connection.Open(); - var result = await connection.QueryAsync("select * from PlexEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId }); + var ep = await EpisodeRepo.CustomAsync(async connection => + { + connection.Open(); + var result = await connection.QueryAsync("select * from PlexEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId }); - return result; - }); + return result; + }); var plexEpisodeses = ep as PlexEpisodes[] ?? ep.ToArray(); if (!plexEpisodeses.Any()) @@ -373,8 +375,23 @@ namespace PlexRequests.Services.Jobs var currentItem = results[i].Directory[j]; var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, currentItem.RatingKey); - var seasons = PlexApi.GetSeasons(plexSettings.PlexAuthToken, plexSettings.FullUri, currentItem.RatingKey); - results[i].Directory[j] = seasons.Directory; + + // Get the seasons for each show + if (currentItem.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) + { + var seasons = PlexApi.GetSeasons(plexSettings.PlexAuthToken, plexSettings.FullUri, + currentItem.RatingKey); + + // We do not want "all episodes" this as a season + var filtered = + seasons.Directory.Where( + x => + !x.Title.Equals("All episodes", + StringComparison.CurrentCultureIgnoreCase)); + + results[i].Directory[j].Seasons.AddRange(filtered); + } + var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Directory.Guid); results[i].Directory[j].ProviderId = providerId; } diff --git a/PlexRequests.UI/Content/search.js b/PlexRequests.UI/Content/search.js index 05cf526e4..73ddc6f23 100644 --- a/PlexRequests.UI/Content/search.js +++ b/PlexRequests.UI/Content/search.js @@ -584,7 +584,7 @@ $(function () { var $form = $('#form' + tvId); var model = []; - var $checkedEpisodes = $('.selectedEpisodes:checkbox:checked'); + var $checkedEpisodes = $('.selectedEpisodes:checkbox:checked:not(:disabled)'); $checkedEpisodes.each(function (index, element) { var $element = $('#' + element.id); var tempObj = {}; @@ -610,7 +610,7 @@ $(function () { success: function (response) { finishLoading("episodesRequest", "primary", origHtml); if (response.result === true) { - generateNotify(response.message); + generateNotify(response.message, "success"); } else { generateNotify(response.message, "warning"); } diff --git a/PlexRequests.UI/Content/site.js b/PlexRequests.UI/Content/site.js index ff04f6a97..1ffc4b591 100644 --- a/PlexRequests.UI/Content/site.js +++ b/PlexRequests.UI/Content/site.js @@ -26,6 +26,9 @@ function utcToLocal(date) { function generateNotify(message, type) { // type = danger, warning, info, successs + if (!type) { + type = "success"; + } $.notify({ // options message: message diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index dd85e9737..cd7d56a33 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -566,34 +566,6 @@ namespace PlexRequests.UI.Modules return Response.AsJson(new JsonResponseModel { Result = false, Message = "Our TV Provider (TVMaze) doesn't have a TheTVDBId for this item. Please report this to TVMaze. We cannot add the series sorry." }); } - // check if the show/episodes have already been requested - var existingRequest = await RequestService.CheckRequestAsync(showId); - var difference = new List(); - if (existingRequest != null) - { - if (episodeRequest) - { - difference = GetListDifferences(existingRequest.Episodes, episodeModel.Episodes).ToList(); - if (difference.Any()) - { - existingRequest.Episodes = episodeModel.Episodes - .Select(r => - new EpisodesModel - { - SeasonNumber = r.SeasonNumber, - EpisodeNumber = r.EpisodeNumber - }).ToList(); - - return await AddUserToRequest(existingRequest, settings, fullShowName); - } - // We have an episode that has not yet been requested, let's continue - } - else - { - return await AddUserToRequest(existingRequest, settings, fullShowName); - } - } - var model = new RequestedModel { ProviderId = showInfo.externals?.thetvdb ?? 0, @@ -652,6 +624,45 @@ namespace PlexRequests.UI.Modules model.SeasonList = seasonsList.ToArray(); + // check if the show/episodes have already been requested + var existingRequest = await RequestService.CheckRequestAsync(showId); + var difference = new List(); + if (existingRequest != null) + { + if (episodeRequest) + { + // Make sure we are not somehow adding dupes + difference = GetListDifferences(existingRequest.Episodes, episodeModel.Episodes).ToList(); + if (difference.Any()) + { + // Convert the request into the correct shape + var newEpisodes = episodeModel.Episodes?.Select(x => new EpisodesModel + { + SeasonNumber = x.SeasonNumber, + EpisodeNumber = x.EpisodeNumber + }); + + // Add it to the existing requests + existingRequest.Episodes.AddRange(newEpisodes ?? Enumerable.Empty()); + + // It's technically a new request now, so set the status to not approved. + existingRequest.Approved = false; + + return await AddUserToRequest(existingRequest, settings, fullShowName); + } + // We have an episode that has not yet been requested, let's continue + } + else if (model.SeasonList.Except(existingRequest.SeasonList).Any()) + { + // This is a season being requested that we do not yet have + // Let's just continue + } + else + { + return await AddUserToRequest(existingRequest, settings, fullShowName); + } + } + try { var shows = Checker.GetPlexTvShows(); @@ -673,11 +684,7 @@ namespace PlexRequests.UI.Modules } } - var episodes = await GetEpisodes(showId); - var availableEpisodes = episodes.Where(x => x.Requested).ToList(); - var availble = availableEpisodes.Select(a => new EpisodesModel { EpisodeNumber = a.EpisodeNumber, SeasonNumber = a.SeasonNumber }).ToList(); - - var diff = model.Episodes.Except(availble); + var diff = await GetEpisodeRequestDifference(showId, model); model.Episodes = diff.ToList(); } else @@ -704,6 +711,11 @@ namespace PlexRequests.UI.Modules var result = await sender.SendToSonarr(s, model); if (!string.IsNullOrEmpty(result?.title)) { + if (existingRequest != null) + { + return await UpdateRequest(model, settings, + $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"); + } return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"); } Log.Debug("Error with sending to sonarr."); @@ -735,21 +747,14 @@ namespace PlexRequests.UI.Modules private async Task AddUserToRequest(RequestedModel existingRequest, PlexRequestSettings settings, string fullShowName) { // check if the current user is already marked as a requester for this show, if not, add them - if (!existingRequest.UserHasRequested(Username) || existingRequest.Episodes.Any()) + if (!existingRequest.UserHasRequested(Username)) { existingRequest.RequestedUsers.Add(Username); - await RequestService.UpdateRequestAsync(existingRequest); } - return - Response.AsJson( - new JsonResponseModel - { - Result = true, - Message = - settings.UsersCanViewOnlyOwnRequests - ? $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}" - : $"{fullShowName} {Resources.UI.Search_AlreadyRequested}" - }); + + return await UpdateRequest(existingRequest, settings, settings.UsersCanViewOnlyOwnRequests + ? $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}" + : $"{fullShowName} {Resources.UI.Search_AlreadyRequested}"); } private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings) @@ -1103,6 +1108,44 @@ namespace PlexRequests.UI.Modules return Response.AsJson(new JsonResponseModel { Result = true, Message = message }); } + private async Task UpdateRequest(RequestedModel model, PlexRequestSettings settings, string message) + { + await RequestService.UpdateRequestAsync(model); + + if (ShouldSendNotification(model.Type, settings)) + { + var notificationModel = new NotificationModel + { + Title = model.Title, + User = Username, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = model.Type + }; + await NotificationService.Publish(notificationModel); + } + + 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 Response.AsJson(new JsonResponseModel { Result = true, Message = message }); + } + private IEnumerable GetListDifferences(IEnumerable existing, IEnumerable request) { var newRequest = request @@ -1115,5 +1158,15 @@ namespace PlexRequests.UI.Modules return newRequest.Except(existing); } + + private async Task> GetEpisodeRequestDifference(int showId, RequestedModel model) + { + var episodes = await GetEpisodes(showId); + var availableEpisodes = episodes.Where(x => x.Requested).ToList(); + var available = availableEpisodes.Select(a => new EpisodesModel { EpisodeNumber = a.EpisodeNumber, SeasonNumber = a.SeasonNumber }).ToList(); + + var diff = model.Episodes.Except(available); + return diff; + } } }