From d458dca5417748d3a628fea9a5642f3fbf94573e Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 27 Jul 2016 14:13:59 +0100 Subject: [PATCH] precheck and disable the episode boxes if we already have requested it. TODO check sonarr to see if it's already there. #254 --- PlexRequests.Api/ApiRequest.cs | 296 +++++++++--------- PlexRequests.UI/Content/search.js | 20 +- PlexRequests.UI/Helpers/TvSender.cs | 14 +- .../Models/EpisodeListViewModel.cs | 37 +++ .../Models/SearchTvShowViewModel.cs | 105 ++++--- PlexRequests.UI/Modules/SearchModule.cs | 33 +- PlexRequests.UI/PlexRequests.UI.csproj | 1 + PlexRequests.UI/Views/Search/Index.cshtml | 6 +- 8 files changed, 285 insertions(+), 227 deletions(-) create mode 100644 PlexRequests.UI/Models/EpisodeListViewModel.cs diff --git a/PlexRequests.Api/ApiRequest.cs b/PlexRequests.Api/ApiRequest.cs index c1395c561..704880f9b 100644 --- a/PlexRequests.Api/ApiRequest.cs +++ b/PlexRequests.Api/ApiRequest.cs @@ -1,148 +1,148 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: ApiRequest.cs -// Created By: Jamie Rees -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ************************************************************************/ -#endregion -using System; -using System.IO; -using System.Xml.Serialization; - -using Newtonsoft.Json; - -using NLog; - -using PlexRequests.Api.Interfaces; -using PlexRequests.Helpers.Exceptions; - -using RestSharp; - -namespace PlexRequests.Api -{ - public class ApiRequest : IApiRequest - { - private readonly JsonSerializerSettings _settings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - MissingMemberHandling = MissingMemberHandling.Ignore - }; - - private static Logger Log = LogManager.GetCurrentClassLogger(); - /// - /// An API request handler - /// - /// The type of class you want to deserialize - /// The request. - /// The base URI. - /// The type of class you want to deserialize - public T Execute(IRestRequest request, Uri baseUri) where T : new() - { - var client = new RestClient { BaseUrl = baseUri }; - - var response = client.Execute(request); - Log.Trace("Api Content Response:"); - Log.Trace(response.Content); - - if (response.ErrorException != null) - { - var message = "Error retrieving response. Check inner details for more info."; - Log.Error(response.ErrorException); - throw new ApiRequestException(message, response.ErrorException); - } - - return response.Data; - } - - public IRestResponse Execute(IRestRequest request, Uri baseUri) - { - var client = new RestClient { BaseUrl = baseUri }; - - var response = client.Execute(request); - - if (response.ErrorException != null) - { - Log.Error(response.ErrorException); - var message = "Error retrieving response. Check inner details for more info."; - throw new ApiRequestException(message, response.ErrorException); - } - - return response; - } - - public T ExecuteXml(IRestRequest request, Uri baseUri) where T : class - { - var client = new RestClient { BaseUrl = baseUri }; - - var response = client.Execute(request); - - if (response.ErrorException != null) - { - Log.Error(response.ErrorException); - var message = "Error retrieving response. Check inner details for more info."; - throw new ApiRequestException(message, response.ErrorException); - } - - var result = DeserializeXml(response.Content); - return result; - } - - public T ExecuteJson(IRestRequest request, Uri baseUri) where T : new() - { - var client = new RestClient { BaseUrl = baseUri }; - var response = client.Execute(request); - Log.Trace("Api Content Response:"); - Log.Trace(response.Content); - if (response.ErrorException != null) - { - Log.Error(response.ErrorException); - var message = "Error retrieving response. Check inner details for more info."; - throw new ApiRequestException(message, response.ErrorException); - } - - Log.Trace("Deserialzing Object"); - var json = JsonConvert.DeserializeObject(response.Content, _settings); - Log.Trace("Finished Deserialzing Object"); - - return json; - } - - private T DeserializeXml(string input) - where T : class - { - var ser = new XmlSerializer(typeof(T)); - - try - { - using (var sr = new StringReader(input)) - return (T)ser.Deserialize(sr); - } - catch (InvalidOperationException e) - { - Log.Error(e); - return null; - } - } - } - - -} +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: ApiRequest.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System; +using System.IO; +using System.Xml.Serialization; + +using Newtonsoft.Json; + +using NLog; + +using PlexRequests.Api.Interfaces; +using PlexRequests.Helpers.Exceptions; + +using RestSharp; + +namespace PlexRequests.Api +{ + public class ApiRequest : IApiRequest + { + private readonly JsonSerializerSettings _settings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + MissingMemberHandling = MissingMemberHandling.Ignore + }; + + private static Logger Log = LogManager.GetCurrentClassLogger(); + /// + /// An API request handler + /// + /// The type of class you want to deserialize + /// The request. + /// The base URI. + /// The type of class you want to deserialize + public T Execute(IRestRequest request, Uri baseUri) where T : new() + { + var client = new RestClient { BaseUrl = baseUri }; + + var response = client.Execute(request); + Log.Trace("Api Content Response:"); + Log.Trace(response.Content); + + if (response.ErrorException != null) + { + var message = "Error retrieving response. Check inner details for more info."; + Log.Error(response.ErrorException); + throw new ApiRequestException(message, response.ErrorException); + } + + return response.Data; + } + + public IRestResponse Execute(IRestRequest request, Uri baseUri) + { + var client = new RestClient { BaseUrl = baseUri }; + + var response = client.Execute(request); + + if (response.ErrorException != null) + { + Log.Error(response.ErrorException); + var message = "Error retrieving response. Check inner details for more info."; + throw new ApiRequestException(message, response.ErrorException); + } + + return response; + } + + public T ExecuteXml(IRestRequest request, Uri baseUri) where T : class + { + var client = new RestClient { BaseUrl = baseUri }; + + var response = client.Execute(request); + + if (response.ErrorException != null) + { + Log.Error(response.ErrorException); + var message = "Error retrieving response. Check inner details for more info."; + throw new ApiRequestException(message, response.ErrorException); + } + + var result = DeserializeXml(response.Content); + return result; + } + + public T ExecuteJson(IRestRequest request, Uri baseUri) where T : new() + { + var client = new RestClient { BaseUrl = baseUri }; + var response = client.Execute(request); + Log.Trace("Api Content Response:"); + Log.Trace(response.Content); + if (response.ErrorException != null) + { + Log.Error(response.ErrorException); + var message = "Error retrieving response. Check inner details for more info."; + throw new ApiRequestException(message, response.ErrorException); + } + + Log.Trace("Deserialzing Object"); + var json = JsonConvert.DeserializeObject(response.Content, _settings); + Log.Trace("Finished Deserialzing Object"); + + return json; + } + + private T DeserializeXml(string input) + where T : class + { + var ser = new XmlSerializer(typeof(T)); + + try + { + using (var sr = new StringReader(input)) + return (T)ser.Deserialize(sr); + } + catch (InvalidOperationException e) + { + Log.Error(e); + return null; + } + } + } + + +} diff --git a/PlexRequests.UI/Content/search.js b/PlexRequests.UI/Content/search.js index 6965d1351..f54f79691 100644 --- a/PlexRequests.UI/Content/search.js +++ b/PlexRequests.UI/Content/search.js @@ -442,7 +442,8 @@ $(function () { imdb: result.imdbId, requested: result.requested, approved: result.approved, - available: result.available + available: result.available, + episodes : result.episodes }; return context; } @@ -585,8 +586,6 @@ $(function () { var model = []; - - var $checkedEpisodes = $('.selectedEpisodes:checkbox:checked'); $checkedEpisodes.each(function (index, element) { var $element = $('#' + element.id); @@ -616,7 +615,7 @@ $(function () { function buildSeasonsCount(result) { return { - seasonNumber: result.season + seasonNumber: result.seasonNumber } } @@ -624,17 +623,10 @@ $(function () { function buildEpisodesView(result) { return { id: result.id, - url: result.url, name: result.name, - season: result.season, - number: result.number, - airdate: result.airdate, - airtime: result.airtime, - airstamp: result.airstamp, - runtime: result.runtime, - image: result.image, - summary: result.summary, - links : result._links + season: result.seasonNumber, + number: result.episodeNumber, + requested: result.requested } } }); diff --git a/PlexRequests.UI/Helpers/TvSender.cs b/PlexRequests.UI/Helpers/TvSender.cs index 9240009f8..ab8b05df4 100644 --- a/PlexRequests.UI/Helpers/TvSender.cs +++ b/PlexRequests.UI/Helpers/TvSender.cs @@ -33,13 +33,11 @@ using PlexRequests.Api.Interfaces; using PlexRequests.Api.Models.SickRage; using PlexRequests.Api.Models.Sonarr; using PlexRequests.Core.SettingModels; -using PlexRequests.Helpers; using PlexRequests.Store; using System.Linq; using System.Threading.Tasks; using PlexRequests.Helpers.Exceptions; -using PlexRequests.UI.Models; namespace PlexRequests.UI.Helpers { @@ -73,15 +71,16 @@ namespace PlexRequests.UI.Helpers int.TryParse(sonarrSettings.QualityProfile, out qualityProfile); } - // Does series exist? - var series = await GetSonarrSeries(sonarrSettings, model.ProviderId); + var seriesTask = GetSonarrSeries(sonarrSettings, model.ProviderId); - // Series Exists if (episodeRequest) - { + { + // Does series exist? + var series = await seriesTask; if (series != null) { + // Series Exists // Request the episodes in the existing series RequestEpisodesWithExistingSeries(model, series, sonarrSettings); } @@ -113,13 +112,12 @@ namespace PlexRequests.UI.Helpers // We now have the series in Sonarr RequestEpisodesWithExistingSeries(model, series, sonarrSettings); - + return addResult; } } - var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey, sonarrSettings.FullUri); diff --git a/PlexRequests.UI/Models/EpisodeListViewModel.cs b/PlexRequests.UI/Models/EpisodeListViewModel.cs new file mode 100644 index 000000000..555489807 --- /dev/null +++ b/PlexRequests.UI/Models/EpisodeListViewModel.cs @@ -0,0 +1,37 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EpisodeListViewModel.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +namespace PlexRequests.UI.Models +{ + public class EpisodeListViewModel + { + public int SeasonNumber { get; set; } + public int EpisodeNumber { get; set; } + public bool Requested { get; set; } + public string Name { get; set; } + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Models/SearchTvShowViewModel.cs b/PlexRequests.UI/Models/SearchTvShowViewModel.cs index 2ed46beff..7497839ac 100644 --- a/PlexRequests.UI/Models/SearchTvShowViewModel.cs +++ b/PlexRequests.UI/Models/SearchTvShowViewModel.cs @@ -1,53 +1,54 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: SearchTvShowViewModel.cs -// Created By: Jamie Rees -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ************************************************************************/ -#endregion - -using System.Collections.Generic; - -namespace PlexRequests.UI.Models -{ - public class SearchTvShowViewModel : SearchViewModel - { - public int Id { get; set; } - public string SeriesName { get; set; } - public List Aliases { get; set; } - public string Banner { get; set; } - public int SeriesId { get; set; } - public string Status { get; set; } - public string FirstAired { get; set; } - public string Network { get; set; } - public string NetworkId { get; set; } - public string Runtime { get; set; } - public List Genre { get; set; } - public string Overview { get; set; } - public int LastUpdated { get; set; } - public string AirsDayOfWeek { get; set; } - public string AirsTime { get; set; } - public string Rating { get; set; } - public string ImdbId { get; set; } - public int SiteRating { get; set; } - } +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SearchTvShowViewModel.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System.Collections.Generic; + +namespace PlexRequests.UI.Models +{ + public class SearchTvShowViewModel : SearchViewModel + { + public int Id { get; set; } + public string SeriesName { get; set; } + public List Aliases { get; set; } + public string Banner { get; set; } + public int SeriesId { get; set; } + public string Status { get; set; } + public string FirstAired { get; set; } + public string Network { get; set; } + public string NetworkId { get; set; } + public string Runtime { get; set; } + public List Genre { get; set; } + public string Overview { get; set; } + public int LastUpdated { get; set; } + public string AirsDayOfWeek { get; set; } + public string AirsTime { get; set; } + public string Rating { get; set; } + public string ImdbId { get; set; } + public int SiteRating { get; set; } + public Store.EpisodesModel[] Episodes { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index 8f7dbdd3e..5e21a0d5c 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -124,7 +124,7 @@ namespace PlexRequests.UI.Modules Get["/notifyuser", true] = async (x, ct) => await GetUserNotificationSettings(); Get["/seasons"] = x => GetSeasons(); - Get["/episodes"] = x => GetEpisodes(); + Get["/episodes", true] = async (x, ct) => await GetEpisodes(); } private TvMazeApi TvApi { get; } private IPlexApi PlexApi { get; } @@ -356,6 +356,7 @@ namespace PlexRequests.UI.Modules var dbt = dbTv[tvdbid]; viewT.Requested = true; + viewT.Episodes = dbt.Episodes; viewT.Approved = dbt.Approved; viewT.Available = dbt.Available; } @@ -545,10 +546,11 @@ namespace PlexRequests.UI.Modules if (episodeModel != null) { episodeRequest = true; + showId = episodeModel.ShowId; var s = await sonarrSettings; if (!s.Enabled) { - return Response.AsJson("This is currently only supported with Sonarr"); + return Response.AsJson(new JsonResponseModel { Message = "This is currently only supported with Sonarr", Result = false }); } } @@ -924,12 +926,35 @@ namespace PlexRequests.UI.Modules return Response.AsJson(model); } - private Response GetEpisodes() + private async Task GetEpisodes() { + var allResults = await RequestService.GetAllAsync(); + var model = new List(); var seriesId = (int)Request.Query.tvId; + var enumerable = allResults as RequestedModel[] ?? allResults.ToArray(); + + var dbDbShow = enumerable.FirstOrDefault(x => x.Type == RequestType.TvShow && x.TvDbId == seriesId.ToString()); var show = TvApi.ShowLookupByTheTvDbId(seriesId); var seasons = TvApi.EpisodeLookup(show.id); - return Response.AsJson(seasons); + + + foreach (var ep in seasons) + { + var requested = dbDbShow?.Episodes + .Any(episodesModel => + ep.number == episodesModel.EpisodeNumber && ep.season == episodesModel.SeasonNumber); + + model.Add(new EpisodeListViewModel + { + Id = show.id, + SeasonNumber = ep.season, + EpisodeNumber = ep.number, + Requested = requested ?? false, + Name = ep.name + }); + } + + return Response.AsJson(model); } public async Task CheckRequestLimit(PlexRequestSettings s, RequestType type) diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 800cbcd27..a69475ce9 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -200,6 +200,7 @@ + diff --git a/PlexRequests.UI/Views/Search/Index.cshtml b/PlexRequests.UI/Views/Search/Index.cshtml index 327f8ed1f..12485e026 100644 --- a/PlexRequests.UI/Views/Search/Index.cshtml +++ b/PlexRequests.UI/Views/Search/Index.cshtml @@ -361,7 +361,11 @@