diff --git a/PlexRequests.Api.Interfaces/IPlexApi.cs b/PlexRequests.Api.Interfaces/IPlexApi.cs index cb2a52049..3588b0ecd 100644 --- a/PlexRequests.Api.Interfaces/IPlexApi.cs +++ b/PlexRequests.Api.Interfaces/IPlexApi.cs @@ -44,6 +44,6 @@ namespace PlexRequests.Api.Interfaces PlexSearch GetAllEpisodes(string authToken, Uri host, string section, int startPage, int returnCount); PlexServer GetServer(string authToken); PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey); - RecentlyAdded RecentlyAdded(string authToken, Uri plexFullHost); + RecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId); } } \ No newline at end of file diff --git a/PlexRequests.Api.Interfaces/ISonarrApi.cs b/PlexRequests.Api.Interfaces/ISonarrApi.cs index da16e8344..63c698b74 100644 --- a/PlexRequests.Api.Interfaces/ISonarrApi.cs +++ b/PlexRequests.Api.Interfaces/ISonarrApi.cs @@ -39,6 +39,10 @@ namespace PlexRequests.Api.Interfaces int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false); + SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, + int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, + bool searchForMissingEpisodes = false); + SystemStatus SystemStatus(string apiKey, Uri baseUrl); List GetSeries(string apiKey, Uri baseUrl); diff --git a/PlexRequests.Api.Models/Plex/PlexAuthentication.cs b/PlexRequests.Api.Models/Plex/PlexAuthentication.cs index 7d4a1a0be..40ff14fab 100644 --- a/PlexRequests.Api.Models/Plex/PlexAuthentication.cs +++ b/PlexRequests.Api.Models/Plex/PlexAuthentication.cs @@ -1,63 +1,64 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: PlexAuthentication.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.Api.Models.Plex -{ - public class PlexAuthentication - { - public User user { get; set; } - } - public class Subscription - { - public bool active { get; set; } - public string status { get; set; } - public object plan { get; set; } - public object features { get; set; } - } - - public class Roles - { - public List roles { get; set; } - } - - public class User - { - public string email { get; set; } - public string joined_at { get; set; } - public string username { get; set; } - public string title { get; set; } - public string authentication_token { get; set; } - public Subscription subscription { get; set; } - public Roles roles { get; set; } - public List entitlements { get; set; } - public object confirmed_at { get; set; } - public int forum_id { get; set; } - } -} - +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexAuthentication.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.Api.Models.Plex +{ + public class PlexAuthentication + { + public User user { get; set; } + } + public class Subscription + { + public bool active { get; set; } + public string status { get; set; } + public object plan { get; set; } + public object features { get; set; } + } + + public class Roles + { + public List roles { get; set; } + } + + public class User + { + public string email { get; set; } + public string uuid { get; set; } + public string joined_at { get; set; } + public string username { get; set; } + public string title { get; set; } + public string authentication_token { get; set; } + public Subscription subscription { get; set; } + public Roles roles { get; set; } + public List entitlements { get; set; } + public object confirmed_at { get; set; } + public int forum_id { get; set; } + } +} + diff --git a/PlexRequests.Api.Models/Plex/RecentlyAdded.cs b/PlexRequests.Api.Models/Plex/RecentlyAddedModel.cs similarity index 56% rename from PlexRequests.Api.Models/Plex/RecentlyAdded.cs rename to PlexRequests.Api.Models/Plex/RecentlyAddedModel.cs index b5536625a..136f48a81 100644 --- a/PlexRequests.Api.Models/Plex/RecentlyAdded.cs +++ b/PlexRequests.Api.Models/Plex/RecentlyAddedModel.cs @@ -1,7 +1,7 @@ #region Copyright // /************************************************************************ // Copyright (c) 2016 Jamie Rees -// File: RecentlyAdded.cs +// File: RecentlyAddedModel.cs // Created By: Jamie Rees // // Permission is hereby granted, free of charge, to any person obtaining @@ -32,53 +32,102 @@ namespace PlexRequests.Api.Models.Plex public class RecentlyAddedChild { public string _elementType { get; set; } - public string allowSync { get; set; } - public string librarySectionID { get; set; } - public string librarySectionTitle { get; set; } - public string librarySectionUUID { get; set; } public int ratingKey { get; set; } public string key { get; set; } public int parentRatingKey { get; set; } + public int grandparentRatingKey { get; set; } public string type { get; set; } public string title { get; set; } + public string grandparentKey { get; set; } public string parentKey { get; set; } - public string parentTitle { get; set; } - public string parentSummary { get; set; } + public string grandparentTitle { get; set; } public string summary { get; set; } public int index { get; set; } public int parentIndex { get; set; } public string thumb { get; set; } public string art { get; set; } - public string parentThumb { get; set; } - public int leafCount { get; set; } - public int viewedLeafCount { get; set; } + public string grandparentThumb { get; set; } + public string grandparentArt { get; set; } + public int duration { get; set; } public int addedAt { get; set; } public int updatedAt { get; set; } - public List _children { get; set; } - public string studio { get; set; } + public string chapterSource { get; set; } + public List _children { get; set; } public string contentRating { get; set; } - public string rating { get; set; } + public int? year { get; set; } + public string parentThumb { get; set; } + public string grandparentTheme { get; set; } + public string originallyAvailableAt { get; set; } + public string titleSort { get; set; } public int? viewCount { get; set; } public int? lastViewedAt { get; set; } - public int? year { get; set; } - public int? duration { get; set; } - public string originallyAvailableAt { get; set; } - public string chapterSource { get; set; } - public string parentTheme { get; set; } - public string titleSort { get; set; } - public string tagline { get; set; } public int? viewOffset { get; set; } + public string rating { get; set; } + public string studio { get; set; } + public string tagline { get; set; } public string originalTitle { get; set; } + public string audienceRating { get; set; } + public string audienceRatingImage { get; set; } + public string ratingImage { get; set; } + } + public class Child3 + { + public string _elementType { get; set; } + public string id { get; set; } + public string key { get; set; } + public double duration { get; set; } + public string file { get; set; } + public double size { get; set; } + public string audioProfile { get; set; } + public string container { get; set; } + public string videoProfile { get; set; } + public string deepAnalysisVersion { get; set; } + public string requiredBandwidths { get; set; } + public string hasThumbnail { get; set; } + public bool? has64bitOffsets { get; set; } + public bool? optimizedForStreaming { get; set; } + public bool? hasChapterTextStream { get; set; } } - public class RecentlyAdded + public class Child2 + { + public string _elementType { get; set; } + public string videoResolution { get; set; } + public int id { get; set; } + public int duration { get; set; } + public int bitrate { get; set; } + public int width { get; set; } + public int height { get; set; } + public string aspectRatio { get; set; } + public int audioChannels { get; set; } + public string audioCodec { get; set; } + public string videoCodec { get; set; } + public string container { get; set; } + public string videoFrameRate { get; set; } + public string audioProfile { get; set; } + public string videoProfile { get; set; } + public List _children { get; set; } + public string tag { get; set; } + } + + public class RecentlyAddedModel { public string _elementType { get; set; } public string allowSync { get; set; } + public string art { get; set; } public string identifier { get; set; } + public string librarySectionID { get; set; } + public string librarySectionTitle { get; set; } + public string librarySectionUUID { get; set; } public string mediaTagPrefix { get; set; } public string mediaTagVersion { get; set; } public string mixedParents { get; set; } + public string nocache { get; set; } + public string thumb { get; set; } + public string title1 { get; set; } + public string title2 { get; set; } + public string viewGroup { get; set; } + public string viewMode { get; set; } public List _children { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj index 2694cb2bf..a1638df6f 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -73,7 +73,7 @@ - + diff --git a/PlexRequests.Api.Models/Tv/TVMazeShow.cs b/PlexRequests.Api.Models/Tv/TVMazeShow.cs index 840edfdf8..cbff57b65 100644 --- a/PlexRequests.Api.Models/Tv/TVMazeShow.cs +++ b/PlexRequests.Api.Models/Tv/TVMazeShow.cs @@ -1,79 +1,89 @@ -using System.Collections.Generic; - -namespace PlexRequests.Api.Models.Tv -{ - public class TvMazeShow - { - public int id { get; set; } - public string url { get; set; } - public string name { get; set; } - public string type { get; set; } - public string language { get; set; } - public List genres { get; set; } - public string status { get; set; } - public int runtime { get; set; } - public string premiered { get; set; } - public Schedule schedule { get; set; } - public Rating rating { get; set; } - public int weight { get; set; } - public Network network { get; set; } - public object webChannel { get; set; } - public Externals externals { get; set; } - public Image image { get; set; } - public string summary { get; set; } - public int updated { get; set; } - public Links _links { get; set; } - public int seasonCount { get; set; } - public Embedded _embedded { get; set; } - } - - public class Season - { - public int id { get; set; } - public string url { get; set; } - public int number { get; set; } - public string name { get; set; } - public int? episodeOrder { get; set; } - public string premiereDate { get; set; } - public string endDate { get; set; } - public Network2 network { get; set; } - public object webChannel { get; set; } - public Image2 image { get; set; } - public string summary { get; set; } - public Links2 _links { get; set; } - } - public class Country2 -{ - public string name { get; set; } - public string code { get; set; } - public string timezone { get; set; } -} - -public class Network2 -{ - public int id { get; set; } - public string name { get; set; } - public Country2 country { get; set; } -} - -public class Image2 -{ - public string medium { get; set; } - public string original { get; set; } -} - -public class Self2 -{ - public string href { get; set; } -} - -public class Links2 -{ - public Self2 self { get; set; } -} - - public class Embedded - { - public List seasons { get; set; } - } +using System.Collections.Generic; + +namespace PlexRequests.Api.Models.Tv +{ + public class TvMazeShow + { + public TvMazeShow() + { + Season = new List(); + } + public int id { get; set; } + public string url { get; set; } + public string name { get; set; } + public string type { get; set; } + public string language { get; set; } + public List genres { get; set; } + public string status { get; set; } + public int runtime { get; set; } + public string premiered { get; set; } + public Schedule schedule { get; set; } + public Rating rating { get; set; } + public int weight { get; set; } + public Network network { get; set; } + public object webChannel { get; set; } + public Externals externals { get; set; } + public Image image { get; set; } + public string summary { get; set; } + public int updated { get; set; } + public Links _links { get; set; } + public List Season { get; set; } + public Embedded _embedded { get; set; } + } + + public class TvMazeCustomSeason + { + public int SeasonNumber { get; set; } + public int EpisodeNumber { get; set; } + } + + public class Season + { + public int id { get; set; } + public string url { get; set; } + public int number { get; set; } + public string name { get; set; } + public int? episodeOrder { get; set; } + public string premiereDate { get; set; } + public string endDate { get; set; } + public Network2 network { get; set; } + public object webChannel { get; set; } + public Image2 image { get; set; } + public string summary { get; set; } + public Links2 _links { get; set; } + } + public class Country2 +{ + public string name { get; set; } + public string code { get; set; } + public string timezone { get; set; } +} + +public class Network2 +{ + public int id { get; set; } + public string name { get; set; } + public Country2 country { get; set; } +} + +public class Image2 +{ + public string medium { get; set; } + public string original { get; set; } +} + +public class Self2 +{ + public string href { get; set; } +} + +public class Links2 +{ + public Self2 self { get; set; } +} + + public class Embedded + { + public List seasons { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.Api/PlexApi.cs b/PlexRequests.Api/PlexApi.cs index 83e06d7cd..2e03ad274 100644 --- a/PlexRequests.Api/PlexApi.cs +++ b/PlexRequests.Api/PlexApi.cs @@ -76,7 +76,7 @@ namespace PlexRequests.Api Method = Method.POST }; - AddHeaders(ref request); + AddHeaders(ref request, false); request.AddJsonBody(userModel); @@ -93,7 +93,7 @@ namespace PlexRequests.Api Method = Method.GET, }; - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); var users = RetryHandler.Execute(() => Api.ExecuteXml (request, new Uri(FriendsUri)), (exception, timespan) => Log.Error (exception, "Exception when calling GetUsers for Plex, Retrying {0}", timespan), null); @@ -118,7 +118,7 @@ namespace PlexRequests.Api }; request.AddUrlSegment("searchTerm", searchTerm); - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); var search = RetryHandler.Execute(() => Api.ExecuteXml (request, plexFullHost), (exception, timespan) => Log.Error (exception, "Exception when calling SearchContent for Plex, Retrying {0}", timespan), null); @@ -133,7 +133,7 @@ namespace PlexRequests.Api Method = Method.GET, }; - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); var users = RetryHandler.Execute(() => Api.ExecuteXml (request, uri), (exception, timespan) => Log.Error (exception, "Exception when calling GetStatus for Plex, Retrying {0}", timespan), null); @@ -148,7 +148,7 @@ namespace PlexRequests.Api Method = Method.GET, }; - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); var account = RetryHandler.Execute(() => Api.ExecuteXml (request, new Uri(GetAccountUri)), (exception, timespan) => Log.Error (exception, "Exception when calling GetAccount for Plex, Retrying {0}", timespan), null); @@ -164,7 +164,7 @@ namespace PlexRequests.Api Resource = "library/sections" }; - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); try { @@ -193,7 +193,7 @@ namespace PlexRequests.Api }; request.AddUrlSegment("libraryId", libraryId); - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); try { @@ -228,7 +228,7 @@ namespace PlexRequests.Api }; request.AddUrlSegment("ratingKey", ratingKey); - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); try { @@ -253,10 +253,9 @@ namespace PlexRequests.Api }; request.AddQueryParameter("type", 4.ToString()); - request.AddQueryParameter("X-Plex-Container-Start", startPage.ToString()); - request.AddQueryParameter("X-Plex-Container-Size", returnCount.ToString()); + AddLimitHeaders(ref request, startPage, returnCount); request.AddUrlSegment("section", section); - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); try { @@ -281,7 +280,7 @@ namespace PlexRequests.Api }; request.AddUrlSegment("itemId", itemId); - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); try { @@ -311,7 +310,7 @@ namespace PlexRequests.Api }; request.AddUrlSegment("ratingKey", ratingKey); - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); try { @@ -338,7 +337,7 @@ namespace PlexRequests.Api Method = Method.GET, }; - AddHeaders(ref request, authToken); + AddHeaders(ref request, authToken, false); var servers = RetryHandler.Execute(() => Api.ExecuteXml(request, new Uri(ServerUri)), (exception, timespan) => Log.Error(exception, "Exception when calling GetServer for Plex, Retrying {0}", timespan)); @@ -347,25 +346,22 @@ namespace PlexRequests.Api return servers; } - public RecentlyAdded RecentlyAdded(string authToken, Uri plexFullHost) + public RecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId) { var request = new RestRequest { Method = Method.GET, - Resource = "library/recentlyAdded" + Resource = "library/sections/{sectionId}/recentlyAdded" }; - - request.AddHeader("X-Plex-Token", authToken); - request.AddHeader("X-Plex-Client-Identifier", $"PlexRequests.Net{Version}"); - request.AddHeader("X-Plex-Product", "Plex Requests .Net"); - request.AddHeader("X-Plex-Version", Version); - request.AddHeader("Content-Type", "application/json"); - request.AddHeader("Accept", "application/json"); + + request.AddUrlSegment("sectionId", sectionId); + AddHeaders(ref request, authToken, true); + AddLimitHeaders(ref request, 0, 25); try { - var lib = RetryHandler.Execute(() => Api.ExecuteJson(request, plexFullHost), - (exception, timespan) => Log.Error(exception, "Exception when calling RecentlyAdded for Plex, Retrying {0}", timespan), new[] { + var lib = RetryHandler.Execute(() => Api.ExecuteJson(request, plexFullHost), + (exception, timespan) => Log.Error(exception, "Exception when calling RecentlyAddedModel for Plex, Retrying {0}", timespan), new[] { TimeSpan.FromSeconds (5), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) @@ -375,23 +371,30 @@ namespace PlexRequests.Api } catch (Exception e) { - Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAdded"); - return new RecentlyAdded(); + Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAddedModel"); + return new RecentlyAddedModel(); } } - private void AddHeaders(ref RestRequest request, string authToken) + private void AddLimitHeaders(ref RestRequest request, int from, int to) { - request.AddHeader("X-Plex-Token", authToken); - AddHeaders(ref request); + request.AddHeader("X-Plex-Container-Start", from.ToString()); + request.AddHeader("X-Plex-Container-Size", to.ToString()); } - private void AddHeaders(ref RestRequest request) + private void AddHeaders(ref RestRequest request, string authToken, bool json) + { + request.AddHeader("X-Plex-Token", authToken); + AddHeaders(ref request, json); + } + + private void AddHeaders(ref RestRequest request, bool json) { request.AddHeader("X-Plex-Client-Identifier", $"PlexRequests.Net{Version}"); request.AddHeader("X-Plex-Product", "Plex Requests .Net"); request.AddHeader("X-Plex-Version", Version); - request.AddHeader("Content-Type", "application/xml"); + request.AddHeader("Content-Type", json ? "application/json" : "application/xml"); + request.AddHeader("Accept", json ? "application/json" : "application/xml"); } } } diff --git a/PlexRequests.Api/PlexRequests.Api.csproj b/PlexRequests.Api/PlexRequests.Api.csproj index 3e3e2da3b..3039d30c9 100644 --- a/PlexRequests.Api/PlexRequests.Api.csproj +++ b/PlexRequests.Api/PlexRequests.Api.csproj @@ -62,8 +62,9 @@ ..\packages\Nancy.1.4.3\lib\net40\Nancy.dll - + ..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll + True diff --git a/PlexRequests.Api/SonarrApi.cs b/PlexRequests.Api/SonarrApi.cs index e90c061cd..961f04ea2 100644 --- a/PlexRequests.Api/SonarrApi.cs +++ b/PlexRequests.Api/SonarrApi.cs @@ -113,6 +113,75 @@ namespace PlexRequests.Api request.AddHeader("X-Api-Key", apiKey); request.AddJsonBody(options); + SonarrAddSeries result; + try + { + var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] { + TimeSpan.FromSeconds (1), + TimeSpan.FromSeconds(2), + }); + + result = policy.Execute(() => Api.ExecuteJson(request, baseUrl)); + } + catch (JsonSerializationException jse) + { + Log.Error(jse); + var error = Api.ExecuteJson>(request, baseUrl); + var messages = error?.Select(x => x.errorMessage).ToList(); + messages?.ForEach(x => Log.Error(x)); + result = new SonarrAddSeries { ErrorMessages = messages }; + } + + return result; + } + + public SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) + { + var request = new RestRequest + { + Resource = "/api/Series?", + Method = Method.POST + }; + + var options = new SonarrAddSeries + { + seasonFolder = seasonFolders, + title = title, + qualityProfileId = qualityId, + tvdbId = tvdbId, + titleSlug = title, + seasons = new List(), + rootFolderPath = rootPath, + monitored = monitor + }; + + if (!searchForMissingEpisodes) + { + options.addOptions = new AddOptions + { + searchForMissingEpisodes = false, + ignoreEpisodesWithFiles = true, + ignoreEpisodesWithoutFiles = true + }; + } + + for (var i = 1; i <= seasons.Length; i++) + { + var season = new Season + { + seasonNumber = i, + // ReSharper disable once SimplifyConditionalTernaryExpression + monitored = true + }; + options.seasons.Add(season); + } + + Log.Debug("Sonarr API Options:"); + Log.Debug(options.DumpJson()); + + request.AddHeader("X-Api-Key", apiKey); + request.AddJsonBody(options); + SonarrAddSeries result; try { diff --git a/PlexRequests.Api/TvMazeApi.cs b/PlexRequests.Api/TvMazeApi.cs index 5d5c60c0f..98f95306e 100644 --- a/PlexRequests.Api/TvMazeApi.cs +++ b/PlexRequests.Api/TvMazeApi.cs @@ -93,8 +93,18 @@ namespace PlexRequests.Api request.AddHeader("Content-Type", "application/json"); var obj = Api.Execute(request, new Uri(Uri)); - obj.seasonCount = GetSeasonCount(obj.id); + var episodes = EpisodeLookup(obj.id).ToList(); + + foreach (var e in episodes) + { + obj.Season.Add(new TvMazeCustomSeason + { + SeasonNumber = e.season, + EpisodeNumber = e.number + }); + } + return obj; } @@ -110,6 +120,7 @@ namespace PlexRequests.Api return Api.Execute>(request, new Uri(Uri)); } + public int GetSeasonCount(int id) { var obj = GetSeasons(id); diff --git a/PlexRequests.Api/packages.config b/PlexRequests.Api/packages.config index b08bcacea..ce5ccf24e 100644 --- a/PlexRequests.Api/packages.config +++ b/PlexRequests.Api/packages.config @@ -6,5 +6,6 @@ + \ No newline at end of file diff --git a/PlexRequests.Core.Migration/IMigration.cs b/PlexRequests.Core.Migration/IMigration.cs new file mode 100644 index 000000000..572ebc930 --- /dev/null +++ b/PlexRequests.Core.Migration/IMigration.cs @@ -0,0 +1,37 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: IMigration.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.Data; + +namespace PlexRequests.Core.Migration +{ + public interface IMigration + { + int Version { get; } + void Start(IDbConnection con); + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/IMigrationRunner.cs b/PlexRequests.Core.Migration/IMigrationRunner.cs new file mode 100644 index 000000000..4ccd615e8 --- /dev/null +++ b/PlexRequests.Core.Migration/IMigrationRunner.cs @@ -0,0 +1,7 @@ +namespace PlexRequests.Core.Migration +{ + public interface IMigrationRunner + { + void MigrateToLatest(); + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/Migrate.cs b/PlexRequests.Core.Migration/Migrate.cs new file mode 100644 index 000000000..e5266aa5b --- /dev/null +++ b/PlexRequests.Core.Migration/Migrate.cs @@ -0,0 +1,33 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Migrate.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.Core.Migration +{ + public class Migrate + { + + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/MigrationAttribute.cs b/PlexRequests.Core.Migration/MigrationAttribute.cs new file mode 100644 index 000000000..b117c1127 --- /dev/null +++ b/PlexRequests.Core.Migration/MigrationAttribute.cs @@ -0,0 +1,43 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Migration.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; + +namespace PlexRequests.Core.Migration +{ + [AttributeUsage(AttributeTargets.Class)] + public class Migration : Attribute + { + public Migration(int version, string description) + { + Version = version; + Description = description; + } + public int Version { get; private set; } + public string Description { get; private set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/MigrationRunner.cs b/PlexRequests.Core.Migration/MigrationRunner.cs new file mode 100644 index 000000000..c8935031a --- /dev/null +++ b/PlexRequests.Core.Migration/MigrationRunner.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Reflection; +using Ninject; +using PlexRequests.Store; + +namespace PlexRequests.Core.Migration +{ + public class MigrationRunner : IMigrationRunner + { + public MigrationRunner(ISqliteConfiguration db, IKernel kernel) + { + Db = db; + Kernel = kernel; + } + + private IKernel Kernel { get; } + private ISqliteConfiguration Db { get; } + + public void MigrateToLatest() + { + var con = Db.DbConnection(); + var versions = GetMigrations().OrderBy(x => x.Key); + + var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault(); + if (dbVersion == null) + { + dbVersion = new TableCreation.VersionInfo { Version = 0 }; + } + foreach (var v in versions) + { + if (v.Value.Version > dbVersion.Version) + { + // Assuming only one constructor + var ctor = v.Key.GetConstructors().FirstOrDefault(); + var dependencies = new List(); + + foreach (var param in ctor.GetParameters()) + { + Console.WriteLine(string.Format( + "Param {0} is named {1} and is of type {2}", + param.Position, param.Name, param.ParameterType)); + + var dep = Kernel.Get(param.ParameterType); + dependencies.Add(dep); + } + + var method = v.Key.GetMethod("Start"); + if (method != null) + { + object result = null; + var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null); + + var parametersArray = new object[] { Db.DbConnection() }; + + method.Invoke(classInstance, parametersArray); + } + } + } + } + + public static Dictionary GetMigrations() + { + var migrationTypes = GetTypesWithHelpAttribute(Assembly.GetAssembly(typeof(MigrationRunner))); + + var version = new Dictionary(); + + foreach (var t in migrationTypes) + { + var customAttributes = (Migration[])t.GetCustomAttributes(typeof(Migration), true); + if (customAttributes.Length > 0) + { + var attr = customAttributes[0]; + + version.Add(t, new MigrationModel { Version = attr.Version, Description = attr.Description }); + } + } + return version; + } + + private static IEnumerable GetTypesWithHelpAttribute(Assembly assembly) + { + return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(Migration), true).Length > 0); + } + + public class MigrationModel + { + public int Version { get; set; } + public string Description { get; set; } + } + } +} diff --git a/PlexRequests.Core.Migration/Migrations/BaseMigration.cs b/PlexRequests.Core.Migration/Migrations/BaseMigration.cs new file mode 100644 index 000000000..e6bcff34b --- /dev/null +++ b/PlexRequests.Core.Migration/Migrations/BaseMigration.cs @@ -0,0 +1,53 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: BaseMigration.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; +using System.Data; +using System.Linq; +using PlexRequests.Store; + +namespace PlexRequests.Core.Migration.Migrations +{ + public abstract class BaseMigration + { + protected void UpdateSchema(IDbConnection con, int version) + { + var migrations = MigrationRunner.GetMigrations(); + var model = migrations.Select(x => x.Value).FirstOrDefault(x => x.Version == version); + + if (model != null) + { + con.AddVersionInfo(new TableCreation.VersionInfo { Version = model.Version, Description = model.Description }); + } + } + + protected IEnumerable GetVersionInfo(IDbConnection con) + { + return con.GetVersionInfo(); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/Migrations/Version195.cs b/PlexRequests.Core.Migration/Migrations/Version195.cs new file mode 100644 index 000000000..790fef19c --- /dev/null +++ b/PlexRequests.Core.Migration/Migrations/Version195.cs @@ -0,0 +1,99 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Version195.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.Data; +using PlexRequests.Core.SettingModels; +using PlexRequests.Store; +using Quartz; + +namespace PlexRequests.Core.Migration.Migrations +{ + [Migration(1950, "v1.9.5.0")] + public class Version195 : BaseMigration, IMigration + { + public Version195(ISettingsService plexRequestSettings, ISettingsService news, ISettingsService jobs) + { + PlexRequestSettings = plexRequestSettings; + NewsletterSettings = news; + Jobs = jobs; + } + public int Version => 1950; + + private ISettingsService PlexRequestSettings { get; } + private ISettingsService NewsletterSettings { get; } + private ISettingsService Jobs { get; } + + public void Start(IDbConnection con) + { + UpdateApplicationSettings(); + UpdateDb(con); + + UpdateSchema(con, Version); + } + + private void UpdateDb(IDbConnection con) + { + } + + private void UpdateApplicationSettings() + { + var plex = PlexRequestSettings.GetSettings(); + var jobSettings = Jobs.GetSettings(); + var newsLetter = NewsletterSettings.GetSettings(); + + newsLetter.SendToPlexUsers = true; + UpdateScheduledSettings(jobSettings); + + if (plex.SendRecentlyAddedEmail) + { + newsLetter.SendRecentlyAddedEmail = plex.SendRecentlyAddedEmail; + plex.SendRecentlyAddedEmail = false; + PlexRequestSettings.SaveSettings(plex); + } + + + NewsletterSettings.SaveSettings(newsLetter); + Jobs.SaveSettings(jobSettings); + } + + private void UpdateScheduledSettings(ScheduledJobsSettings settings) + { + settings.PlexAvailabilityChecker = 60; + settings.SickRageCacher = 60; + settings.SonarrCacher = 60; + settings.CouchPotatoCacher = 60; + settings.StoreBackup = 24; + settings.StoreCleanup = 24; + settings.UserRequestLimitResetter = 12; + settings.PlexEpisodeCacher = 12; + + var cron = (Quartz.Impl.Triggers.CronTriggerImpl)CronScheduleBuilder.WeeklyOnDayAndHourAndMinute(DayOfWeek.Friday, 7, 0).Build(); + settings.RecentlyAddedCron = cron.CronExpressionString; // Weekly CRON at 7 am on Mondays + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj b/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj new file mode 100644 index 000000000..a92ae74d5 --- /dev/null +++ b/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + {8406EE57-D533-47C0-9302-C6B5F8C31E55} + Library + Properties + PlexRequests.Core.Migration + PlexRequests.Core.Migration + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll + True + + + ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll + True + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll + + + ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll + True + + + + + + + + + + + + + + + + + + + + + + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + + + + + + Designer + + + + + + \ No newline at end of file diff --git a/PlexRequests.Core.Migration/Properties/AssemblyInfo.cs b/PlexRequests.Core.Migration/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6e44d89b7 --- /dev/null +++ b/PlexRequests.Core.Migration/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PlexRequests.Core.Migration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PlexRequests.Core.Migration")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8406ee57-d533-47c0-9302-c6b5f8c31e55")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PlexRequests.Core.Migration/app.config b/PlexRequests.Core.Migration/app.config new file mode 100644 index 000000000..8460dd432 --- /dev/null +++ b/PlexRequests.Core.Migration/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Core.Migration/job_scheduling_data_2_0.xsd b/PlexRequests.Core.Migration/job_scheduling_data_2_0.xsd new file mode 100644 index 000000000..d1dabc1a9 --- /dev/null +++ b/PlexRequests.Core.Migration/job_scheduling_data_2_0.xsd @@ -0,0 +1,361 @@ + + + + + + + Root level node + + + + + + Commands to be executed before scheduling the jobs and triggers in this file. + + + + + Directives to be followed while scheduling the jobs and triggers in this file. + + + + + + + + + + + + + + Version of the XML Schema instance + + + + + + + + + + Delete all jobs, if any, in the identified group. "*" can be used to identify all groups. Will also result in deleting all triggers related to the jobs. + + + + + Delete all triggers, if any, in the identified group. "*" can be used to identify all groups. Will also result in deletion of related jobs that are non-durable. + + + + + Delete the identified job if it exists (will also result in deleting all triggers related to it). + + + + + + + + + + + Delete the identified trigger if it exists (will also result in deletion of related jobs that are non-durable). + + + + + + + + + + + + + + + + Whether the existing scheduling data (with same identifiers) will be overwritten. If false, and ignore-duplicates is not false, and jobs or triggers with the same names already exist as those in the file, an error will occur. + + + + + If true (and overwrite-existing-data is false) then any job/triggers encountered in this file that have names that already exist in the scheduler will be ignored, and no error will be produced. + + + + + If true trigger's start time is calculated based on earlier run time instead of fixed value. Trigger's start time must be undefined for this to work. + + + + + + + + Define a JobDetail + + + + + + + + + + + + + + + + + Define a JobDataMap + + + + + + + + + Define a JobDataMap entry + + + + + + + + + + Define a Trigger + + + + + + + + + + + Common Trigger definitions + + + + + + + + + + + + + + + + + + + + + + + Define a SimpleTrigger + + + + + + + + + + + + + + + + + Define a CronTrigger + + + + + + + + + + + + + + + Define a DateIntervalTrigger + + + + + + + + + + + + + + + + Cron expression (see JavaDoc for examples) + + Special thanks to Chris Thatcher (thatcher@butterfly.net) for the regular expression! + + Regular expressions are not my strong point but I believe this is complete, + with the caveat that order for expressions like 3-0 is not legal but will pass, + and month and day names must be capitalized. + If you want to examine the correctness look for the [\s] to denote the + seperation of individual regular expressions. This is how I break them up visually + to examine them: + + SECONDS: + ( + ((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?) + | (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9])) + | ([\?]) + | ([\*]) + ) [\s] + MINUTES: + ( + ((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?) + | (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9])) + | ([\?]) + | ([\*]) + ) [\s] + HOURS: + ( + ((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?) + | (([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3])) + | ([\?]) + | ([\*]) + ) [\s] + DAY OF MONTH: + ( + ((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?) + | (([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?) + | (L(-[0-9])?) + | (L(-[1-2][0-9])?) + | (L(-[3][0-1])?) + | (LW) + | ([1-9]W) + | ([1-3][0-9]W) + | ([\?]) + | ([\*]) + )[\s] + MONTH: + ( + ((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?) + | (([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2])) + | (((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?) + | ((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)) + | ([\?]) + | ([\*]) + )[\s] + DAY OF WEEK: + ( + (([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?) + | ([1-7]/([1-7])) + | (((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?) + | ((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?) + | (([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))(L|LW)?) + | (([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?) + | ([\?]) + | ([\*]) + ) + YEAR (OPTIONAL): + ( + [\s]? + ([\*])? + | ((19[7-9][0-9])|(20[0-9][0-9]))? + | (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))? + | ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)? + ) + + + + + + + + + + Number of times to repeat the Trigger (-1 for indefinite) + + + + + + + + + + Simple Trigger Misfire Instructions + + + + + + + + + + + + + + Cron Trigger Misfire Instructions + + + + + + + + + + + Date Interval Trigger Misfire Instructions + + + + + + + + + + + Interval Units + + + + + + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.Core.Migration/packages.config b/PlexRequests.Core.Migration/packages.config new file mode 100644 index 000000000..dfdfc5ae9 --- /dev/null +++ b/PlexRequests.Core.Migration/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/PlexRequests.Core/IPlexReadOnlyDatabase.cs b/PlexRequests.Core/IPlexReadOnlyDatabase.cs new file mode 100644 index 000000000..93a4dc98f --- /dev/null +++ b/PlexRequests.Core/IPlexReadOnlyDatabase.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Core +{ + public interface IPlexReadOnlyDatabase + { + IEnumerable GetItemsAddedAfterDate(DateTime dateTime); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html b/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html index 3d005619e..bb03e8ecd 100644 --- a/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html +++ b/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html @@ -144,7 +144,7 @@ diff --git a/PlexRequests.Core/PlexReadOnlyDatabase.cs b/PlexRequests.Core/PlexReadOnlyDatabase.cs new file mode 100644 index 000000000..4c41e1e63 --- /dev/null +++ b/PlexRequests.Core/PlexReadOnlyDatabase.cs @@ -0,0 +1,82 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexReadOnlyDatabase.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.Collections.Generic; +using System.IO; +using System.Linq; +using PlexRequests.Core.SettingModels; +using PlexRequests.Store; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Core +{ + public class PlexReadOnlyDatabase : IPlexReadOnlyDatabase + { + public PlexReadOnlyDatabase(IPlexDatabase plexDatabase, ISettingsService plexSettings) + { + Plex = plexDatabase; + + var settings = plexSettings.GetSettings(); + + if (!string.IsNullOrEmpty(settings.PlexDatabaseLocationOverride)) + { + //Overriden setting + Plex.DbLocation = Path.Combine(settings.PlexDatabaseLocationOverride, "Plug-in Support", "Databases", "com.plexapp.plugins.library.db"); + } + else if (Type.GetType("Mono.Runtime") != null) + { + // Mono + Plex.DbLocation = Path.Combine("/var/lib/plexmediaserver/Library/Application Support/", "Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db"); + } + else + { + // Default Windows + Plex.DbLocation = Path.Combine(Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%"), "Plex Media Server", "Plug-in Support", "Databases", "com.plexapp.plugins.library.db"); + } + } + private IPlexDatabase Plex { get; } + + public IEnumerable GetItemsAddedAfterDate(DateTime dateTime) + { + // type 1 = Movie, type 4 = TV Episode + var movies = Plex.QueryMetadataItems(@"SELECT * FROM metadata_items + WHERE added_at > @AddedAt + AND metadata_type = 1 + AND title <> ''", new { AddedAt = dateTime }); + + // Custom query to include the series title + var tv = Plex.QueryMetadataItems(@"SELECT series.title AS SeriesTitle, mi.* FROM metadata_items mi + INNER JOIN metadata_items season ON mi.parent_id = season.id + INNER JOIN metadata_items series ON series.id = season.parent_id + WHERE mi.added_at > @AddedAt + AND mi.metadata_type = 4", new { AddedAt = dateTime }); + + return movies.Union(tv); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj index d3cd82a7b..8d81638fa 100644 --- a/PlexRequests.Core/PlexRequests.Core.csproj +++ b/PlexRequests.Core/PlexRequests.Core.csproj @@ -69,6 +69,7 @@ + @@ -85,10 +86,12 @@ + + @@ -141,7 +144,7 @@ - PreserveNewest + Always diff --git a/PlexRequests.Core/SettingModels/NewsletterSettings.cs b/PlexRequests.Core/SettingModels/NewsletterSettings.cs new file mode 100644 index 000000000..e91051b4c --- /dev/null +++ b/PlexRequests.Core/SettingModels/NewsletterSettings.cs @@ -0,0 +1,45 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: NewsletterSettings.cs +// Created By: Jim MacKenzie +// +// 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; +using Newtonsoft.Json; +using PlexRequests.Helpers; + +namespace PlexRequests.Core.SettingModels +{ + public class NewletterSettings : Settings + { + public bool SendRecentlyAddedEmail { get; set; } + public bool SendToPlexUsers { get; set; } + public string CustomUsers { get; set; } + + + + [JsonIgnore] + public IEnumerable CustomUsersEmailAddresses => CustomUsers.SplitEmailsByDelimiter(';'); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs index 65afd1559..5c7e6ddf5 100644 --- a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs @@ -58,8 +58,8 @@ namespace PlexRequests.Core.SettingModels public bool Wizard { get; set; } public bool DisableTvRequestsByEpisode { get; set; } public bool DisableTvRequestsBySeason { get; set; } + [Obsolete("Moved to NewsLetterSettings")] public bool SendRecentlyAddedEmail { get; set; } - public string CustomDonationUrl { get; set; } public bool EnableCustomDonationUrl { get; set; } public string CustomDonationMessage { get; set; } diff --git a/PlexRequests.Core/SettingModels/PlexSettings.cs b/PlexRequests.Core/SettingModels/PlexSettings.cs index 09be5fb15..108edee19 100644 --- a/PlexRequests.Core/SettingModels/PlexSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexSettings.cs @@ -40,5 +40,6 @@ namespace PlexRequests.Core.SettingModels public string PlexAuthToken { get; set; } public string MachineIdentifier { get; set; } + public string PlexDatabaseLocationOverride { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index c881e8565..234dc5774 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -24,23 +24,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion + +using System; + namespace PlexRequests.Core.SettingModels { public class ScheduledJobsSettings : Settings { - public ScheduledJobsSettings() - { - PlexAvailabilityChecker = 60; - SickRageCacher = 60; - SonarrCacher = 60; - CouchPotatoCacher = 60; - StoreBackup = 24; - StoreCleanup = 24; - UserRequestLimitResetter = 12; - PlexEpisodeCacher = 12; - RecentlyAdded = 168; - } - public int PlexAvailabilityChecker { get; set; } public int SickRageCacher { get; set; } public int SonarrCacher { get; set; } @@ -49,6 +39,8 @@ namespace PlexRequests.Core.SettingModels public int StoreCleanup { get; set; } public int UserRequestLimitResetter { get; set; } public int PlexEpisodeCacher { get; set; } + [Obsolete("We use the CRON job now")] public int RecentlyAdded { get; set; } + public string RecentlyAddedCron { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Core/Setup.cs b/PlexRequests.Core/Setup.cs index dac15a731..c5d5ddc58 100644 --- a/PlexRequests.Core/Setup.cs +++ b/PlexRequests.Core/Setup.cs @@ -60,6 +60,8 @@ namespace PlexRequests.Core TableCreation.Vacuum(Db.DbConnection()); } + + // The below code is obsolete, we should use PlexRequests.Core.Migrations.MigrationRunner var version = CheckSchema(); if (version > 0) { diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs index 8a4e491a5..24e44999f 100644 --- a/PlexRequests.Core/UserMapper.cs +++ b/PlexRequests.Core/UserMapper.cs @@ -110,31 +110,35 @@ namespace PlexRequests.Core Salt = salt, Hash = PasswordHasher.ComputeHash(password, salt), Claims = ByteConverterHelper.ReturnBytes(claims), - UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()) + UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()), }; Repo.Insert(userModel); - var userRecord = Repo.Get(userModel.UserGuid); return new Guid(userRecord.UserGuid); } + public void DeleteUser(string userId) + { + var user = Repo.Get(userId); + Repo.Delete(user); + } + public Guid? CreateAdmin(string username, string password, UserProperties properties = null) { - return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser, UserClaims.Admin }, properties); + return CreateUser(username, password, new[] { UserClaims.RegularUser, UserClaims.PowerUser, UserClaims.Admin }, properties); } public Guid? CreatePowerUser(string username, string password, UserProperties properties = null) { - return CreateUser(username, password, new[] { UserClaims.User, UserClaims.PowerUser }, properties); + return CreateUser(username, password, new[] { UserClaims.RegularUser, UserClaims.PowerUser }, properties); } public Guid? CreateRegularUser(string username, string password, UserProperties properties = null) { - return CreateUser(username, password, new[] { UserClaims.User }, properties); + return CreateUser(username, password, new[] { UserClaims.RegularUser }, properties); } - public IEnumerable GetAllClaims() { var properties = typeof(UserClaims).GetConstantsValues(); @@ -194,7 +198,6 @@ namespace PlexRequests.Core Guid? CreateAdmin(string username, string password, UserProperties properties = null); Guid? CreatePowerUser(string username, string password, UserProperties properties = null); Guid? CreateRegularUser(string username, string password, UserProperties properties = null); - - + void DeleteUser(string userId); } } diff --git a/PlexRequests.Helpers/DateTimeHelper.cs b/PlexRequests.Helpers/DateTimeHelper.cs index 1c4277c4a..3e2174ae9 100644 --- a/PlexRequests.Helpers/DateTimeHelper.cs +++ b/PlexRequests.Helpers/DateTimeHelper.cs @@ -1,42 +1,50 @@ -using System; -using System.Globalization; -using System.Linq; - -namespace PlexRequests.Helpers -{ - public static class DateTimeHelper - { - public static DateTimeOffset OffsetUTCDateTime(DateTime utcDateTime, int minuteOffset) - { - //TimeSpan ts = TimeSpan.FromMinutes(-minuteOffset); - //return new DateTimeOffset(utcDateTime).ToOffset(ts); - - // this is a workaround below to work with MONO - var tzi = FindTimeZoneFromOffset(minuteOffset); - var utcOffset = tzi.GetUtcOffset(utcDateTime); - var newDate = utcDateTime + utcOffset; - return new DateTimeOffset(newDate.Ticks, utcOffset); - } - - public static void CustomParse(string date, out DateTime dt) - { - // Try and parse it - if (DateTime.TryParse(date, out dt)) - { - return; - } - - // Maybe it's only a year? - if (DateTime.TryParseExact(date, "yyyy", CultureInfo.CurrentCulture, DateTimeStyles.None, out dt)) - { - return; - } - } - - private static TimeZoneInfo FindTimeZoneFromOffset(int minuteOffset) - { - var tzc = TimeZoneInfo.GetSystemTimeZones(); - return tzc.FirstOrDefault(x => x.BaseUtcOffset.TotalMinutes == -minuteOffset); - } - } -} +using System; +using System.Globalization; +using System.Linq; + +namespace PlexRequests.Helpers +{ + public static class DateTimeHelper + { + public static DateTimeOffset OffsetUTCDateTime(DateTime utcDateTime, int minuteOffset) + { + //TimeSpan ts = TimeSpan.FromMinutes(-minuteOffset); + //return new DateTimeOffset(utcDateTime).ToOffset(ts); + + // this is a workaround below to work with MONO + var tzi = FindTimeZoneFromOffset(minuteOffset); + var utcOffset = tzi.GetUtcOffset(utcDateTime); + var newDate = utcDateTime + utcOffset; + return new DateTimeOffset(newDate.Ticks, utcOffset); + } + + public static void CustomParse(string date, out DateTime dt) + { + // Try and parse it + if (DateTime.TryParse(date, out dt)) + { + return; + } + + // Maybe it's only a year? + if (DateTime.TryParseExact(date, "yyyy", CultureInfo.CurrentCulture, DateTimeStyles.None, out dt)) + { + return; + } + } + + private static TimeZoneInfo FindTimeZoneFromOffset(int minuteOffset) + { + var tzc = TimeZoneInfo.GetSystemTimeZones(); + return tzc.FirstOrDefault(x => x.BaseUtcOffset.TotalMinutes == -minuteOffset); + } + + public static DateTime UnixTimeStampToDateTime(this int unixTimeStamp) + { + // Unix timestamp is seconds past epoch + System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime(); + return dtDateTime; + } + } +} diff --git a/PlexRequests.Helpers/PlexHelper.cs b/PlexRequests.Helpers/PlexHelper.cs index 1e186ddba..008c737a9 100644 --- a/PlexRequests.Helpers/PlexHelper.cs +++ b/PlexRequests.Helpers/PlexHelper.cs @@ -102,6 +102,12 @@ namespace PlexRequests.Helpers $"https://app.plex.tv/web/app#!/server/{machineId}/details/%2Flibrary%2Fmetadata%2F{mediaId}"; return url; } + + public static string FormatGenres(string tags) + { + var split = tags.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries); + return string.Join(", ", split); + } } public class EpisodeModelHelper diff --git a/PlexRequests.Helpers/PlexRequests.Helpers.csproj b/PlexRequests.Helpers/PlexRequests.Helpers.csproj index 9c5f69c04..0ffffb4c8 100644 --- a/PlexRequests.Helpers/PlexRequests.Helpers.csproj +++ b/PlexRequests.Helpers/PlexRequests.Helpers.csproj @@ -87,6 +87,7 @@ + diff --git a/PlexRequests.Helpers/StringHelper.cs b/PlexRequests.Helpers/StringHelper.cs index 10ec8c1c2..de41af441 100644 --- a/PlexRequests.Helpers/StringHelper.cs +++ b/PlexRequests.Helpers/StringHelper.cs @@ -24,6 +24,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion + +using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -64,5 +66,36 @@ namespace PlexRequests.Helpers } return sb.ToString(); } + + public static IEnumerable SplitEmailsByDelimiter(this string input, char delimiter) + { + if (string.IsNullOrEmpty(input)) + { + yield return string.Empty; + } + var startIndex = 0; + var delimiterIndex = 0; + + while (delimiterIndex >= 0) + { + delimiterIndex = input.IndexOf(delimiter, startIndex); + string substring = input; + if (delimiterIndex > 0) + { + substring = input.Substring(0, delimiterIndex).Trim(); + } + + if (!substring.Contains("\"") || substring.IndexOf("\"") != substring.LastIndexOf("\"")) + { + yield return substring; + input = input.Substring(delimiterIndex + 1).Trim(); + startIndex = 0; + } + else + { + startIndex = delimiterIndex + 1; + } + } + } } } \ No newline at end of file diff --git a/PlexRequests.Helpers/UserClaims.cs b/PlexRequests.Helpers/UserClaims.cs index e53aabeab..a42023e44 100644 --- a/PlexRequests.Helpers/UserClaims.cs +++ b/PlexRequests.Helpers/UserClaims.cs @@ -4,9 +4,11 @@ namespace PlexRequests.Helpers { public class UserClaims { - public const string Admin = "Admin"; // Can do everything including creating new users and editing settings - public const string PowerUser = "PowerUser"; // Can only manage the requests, approve etc. - public const string User = "User"; // Can only request + public const string Admin = nameof(Admin); // Can do everything including creating new users and editing settings + public const string PowerUser = nameof(PowerUser); // Can only manage the requests, approve etc. + public const string RegularUser = nameof(RegularUser); // Can only request + public const string ReadOnlyUser = nameof(ReadOnlyUser); // Can only view stuff + public const string Newsletter = nameof(Newsletter); // Has newsletter feature enabled } } diff --git a/PlexRequests.Helpers/UserType.cs b/PlexRequests.Helpers/UserType.cs new file mode 100644 index 000000000..b9a3c98a2 --- /dev/null +++ b/PlexRequests.Helpers/UserType.cs @@ -0,0 +1,35 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserType.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.Helpers +{ + + public enum UserType + { + PlexUser, + LocalUser + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/HtmlTemplateGenerator.cs b/PlexRequests.Services/Jobs/HtmlTemplateGenerator.cs new file mode 100644 index 000000000..0872ef8c1 --- /dev/null +++ b/PlexRequests.Services/Jobs/HtmlTemplateGenerator.cs @@ -0,0 +1,71 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: HtmlTemplateGenerator.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.IO; +using System.Text; +using System.Web.UI; + +namespace PlexRequests.Services.Jobs +{ + public abstract class HtmlTemplateGenerator + { + protected virtual void AddParagraph(StringBuilder stringBuilder, string text, int fontSize = 14, string fontWeight = "normal") + { + stringBuilder.AppendFormat("

{0}

", text, fontSize, fontWeight); + } + + protected virtual void AddImageInsideTable(StringBuilder sb, string url) + { + sb.Append("
"); + sb.Append(""); + sb.Append(""); + } + + protected virtual void Href(StringBuilder sb, string url) + { + sb.AppendFormat("", url); + } + + protected virtual void EndTag(StringBuilder sb, string tag) + { + sb.AppendFormat("", tag); + } + + protected virtual void Header(StringBuilder sb, int size, string text, string fontWeight = "normal") + { + sb.AppendFormat( + "{1}", + size, text, fontWeight); + } + + + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs index 701a5fb50..161ea0ec3 100644 --- a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs +++ b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs @@ -123,12 +123,13 @@ namespace PlexRequests.Services.Jobs case RequestType.TvShow: if (!plexSettings.EnableTvEpisodeSearching) { - matchResult = IsTvShowAvailable(shows, r.Title, releaseDate, r.TvDbId); + matchResult = IsTvShowAvailable(shows, r.Title, releaseDate, r.TvDbId, r.SeasonList); } else { - matchResult = - r.Episodes.All(x => IsEpisodeAvailable(r.TvDbId, x.SeasonNumber, x.EpisodeNumber)); + matchResult = r.Episodes.Any() ? + r.Episodes.All(x => IsEpisodeAvailable(r.TvDbId, x.SeasonNumber, x.EpisodeNumber)) : + IsTvShowAvailable(shows, r.Title, releaseDate, r.TvDbId, r.SeasonList); } break; case RequestType.Album: @@ -270,7 +271,7 @@ namespace PlexRequests.Services.Jobs { if (advanced) { - if (seasons != null && show.ProviderId == providerId) + if (show.ProviderId == providerId && seasons != null) { if (seasons.Any(season => show.Seasons.Contains(season))) { @@ -411,6 +412,8 @@ namespace PlexRequests.Services.Jobs try { + + // TODO what the fuck was I thinking if (setCache) { results = GetLibraries(plexSettings); diff --git a/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs b/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs index 3a4af04dd..3ed43db50 100644 --- a/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs +++ b/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs @@ -112,6 +112,10 @@ namespace PlexRequests.Services.Jobs // Loop through the metadata and create the model to insert into the DB foreach (var metadataVideo in metadata.Video) { + if(string.IsNullOrEmpty(metadataVideo.GrandparentTitle)) + { + continue; + } var epInfo = PlexHelper.GetSeasonsAndEpisodesFromPlexGuid(metadataVideo.Guid); entities.TryAdd( new PlexEpisodes diff --git a/PlexRequests.Services/Jobs/RecentlyAdded.cs b/PlexRequests.Services/Jobs/RecentlyAdded.cs index c138b942b..70c7a4fa4 100644 --- a/PlexRequests.Services/Jobs/RecentlyAdded.cs +++ b/PlexRequests.Services/Jobs/RecentlyAdded.cs @@ -1,7 +1,8 @@ #region Copyright + // /************************************************************************ // Copyright (c) 2016 Jamie Rees -// File: RecentlyAdded.cs +// File: RecentlyAddedModel.cs // Created By: Jamie Rees // // Permission is hereby granted, free of charge, to any person obtaining @@ -23,6 +24,7 @@ // 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; @@ -40,32 +42,37 @@ using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Services.Interfaces; using PlexRequests.Services.Jobs.Templates; +using PlexRequests.Store.Models.Plex; using Quartz; namespace PlexRequests.Services.Jobs { - public class RecentlyAdded : IJob, IRecentlyAdded + public class RecentlyAdded : HtmlTemplateGenerator, IJob, IRecentlyAdded { - public RecentlyAdded(IPlexApi api, ISettingsService plexSettings, ISettingsService email, - ISettingsService scheduledService, IJobRecord rec, ISettingsService plexRequest) + public RecentlyAdded(IPlexApi api, ISettingsService plexSettings, + ISettingsService email, IJobRecord rec, + ISettingsService newsletter, + IPlexReadOnlyDatabase db) { JobRecord = rec; Api = api; PlexSettings = plexSettings; EmailSettings = email; - ScheduledJobsSettings = scheduledService; - PlexRequestSettings = plexRequest; + NewsletterSettings = newsletter; + PlexDb = db; } private IPlexApi Api { get; } private TvMazeApi TvApi = new TvMazeApi(); private readonly TheMovieDbApi _movieApi = new TheMovieDbApi(); + private const int MetadataTypeTv = 4; + private const int MetadataTypeMovie = 1; private ISettingsService PlexSettings { get; } private ISettingsService EmailSettings { get; } - private ISettingsService PlexRequestSettings { get; } - private ISettingsService ScheduledJobsSettings { get; } + private ISettingsService NewsletterSettings { get; } private IJobRecord JobRecord { get; } + private IPlexReadOnlyDatabase PlexDb { get; } private static readonly Logger Log = LogManager.GetCurrentClassLogger(); @@ -73,24 +80,13 @@ namespace PlexRequests.Services.Jobs { try { - var settings = PlexRequestSettings.GetSettings(); + var settings = NewsletterSettings.GetSettings(); if (!settings.SendRecentlyAddedEmail) { return; } - var jobs = JobRecord.GetJobs(); - var thisJob = - jobs.FirstOrDefault( - x => x.Name.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase)); - - var jobSettings = ScheduledJobsSettings.GetSettings(); - - if (thisJob?.LastRun > DateTime.Now.AddHours(-jobSettings.RecentlyAdded)) - { - return; - } - - Start(); + + Start(settings); } catch (Exception e) { @@ -104,115 +100,143 @@ namespace PlexRequests.Services.Jobs public void Test() { - Start(true); + var settings = NewsletterSettings.GetSettings(); + Start(settings, true); } - private void Start(bool testEmail = false) + private void Start(NewletterSettings newletterSettings, bool testEmail = false) { var sb = new StringBuilder(); var plexSettings = PlexSettings.GetSettings(); - var recentlyAdded = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri); + var libs = Api.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri); + var tvSection = libs.Directories.FirstOrDefault(x => x.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)); + var movieSection = libs.Directories.FirstOrDefault(x => x.type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)); - var movies = - recentlyAdded._children.Where(x => x.type.Equals("Movie", StringComparison.CurrentCultureIgnoreCase)); - var tv = - recentlyAdded._children.Where( - x => x.type.Equals("season", StringComparison.CurrentCultureIgnoreCase)) - .GroupBy(x => x.parentTitle) - .Select(x => x.FirstOrDefault()); + var recentlyAddedTv = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection.Key); + var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movieSection.Key); - GenerateMovieHtml(movies, plexSettings, ref sb); - GenerateTvHtml(tv, plexSettings, ref sb); + GenerateMovieHtml(recentlyAddedMovies, plexSettings, sb); + GenerateTvHtml(recentlyAddedTv, plexSettings, sb); var template = new RecentlyAddedTemplate(); var html = template.LoadTemplate(sb.ToString()); - Send(html, plexSettings, testEmail); + Send(newletterSettings, html, plexSettings, testEmail); } - private void GenerateMovieHtml(IEnumerable movies, PlexSettings plexSettings, ref StringBuilder sb) + private void GenerateMovieHtml(RecentlyAddedModel movies, PlexSettings plexSettings, StringBuilder sb) { sb.Append("

New Movies:



"); - sb.Append("
- +
"); + sb.AppendFormat( + "", + url); + sb.Append("
"); - foreach (var movie in movies) + sb.Append( + "
"); + foreach (var movie in movies._children.OrderByDescending(x => x.addedAt.UnixTimeStampToDateTime())) { - var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - movie.ratingKey.ToString()); - - var imdbId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Video.Guid); - var info = _movieApi.GetMovieInformation(imdbId).Result; - - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); + plexGUID = metaData.Video.Guid; + + var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID); + var info = _movieApi.GetMovieInformation(imdbId).Result; + + AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}"); + + sb.Append(""); + sb.Append( + "
"); - sb.AppendFormat("", info.BackdropPath); - sb.Append("
"); - - sb.AppendFormat("

{1} {2}

", - info.ImdbId, info.Title, info.ReleaseDate?.ToString("yyyy") ?? string.Empty); - - if (info.Genres.Any()) + var plexGUID = string.Empty; + try { - sb.AppendFormat( - "

Genre: {0}

", - string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())); - } - sb.AppendFormat("

{0}

", info.Overview); + var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, + movie.ratingKey.ToString()); - sb.Append(""); - sb.Append("
"); - sb.Append("
"); - sb.Append("

"); + + Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); + Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? string.Empty}"); + EndTag(sb, "a"); + + if (info.Genres.Any()) + { + AddParagraph(sb, + $"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}"); + } + + AddParagraph(sb, info.Overview); + } + catch (Exception e) + { + Log.Error(e); + Log.Error( + "Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}", + plexGUID); + } + finally + { + EndLoopHtml(sb); + } } sb.Append("


"); } - - private void GenerateTvHtml(IEnumerable tv, PlexSettings plexSettings, ref StringBuilder sb) + + private void GenerateTvHtml(RecentlyAddedModel tv, PlexSettings plexSettings, StringBuilder sb) { // TV sb.Append("

New Episodes:



"); - sb.Append(""); - foreach (var t in tv) + sb.Append( + "
"); + foreach (var t in tv._children.OrderByDescending(x => x.addedAt.UnixTimeStampToDateTime())) { - var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, - t.parentRatingKey.ToString()); - - var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(parentMetaData.Directory.Guid))); - var banner = info.image?.original; - if (!string.IsNullOrEmpty(banner)) + var plexGUID = string.Empty; + try { - banner = banner.Replace("http", "https"); // Always use the Https banners + + var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, + t.parentRatingKey.ToString()); + + plexGUID = parentMetaData.Directory.Guid; + + var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(plexGUID))); + + var banner = info.image?.original; + if (!string.IsNullOrEmpty(banner)) + { + banner = banner.Replace("http", "https"); // Always use the Https banners + } + AddImageInsideTable(sb, banner); + + sb.Append(""); + sb.Append( + ""); - sb.Append(""); - sb.Append(""); - sb.Append(""); - sb.Append(""); } sb.Append("
"); + + var title = $"{t.grandparentTitle} - {t.title} {t.originallyAvailableAt?.Substring(0, 4)}"; + + Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); + Header(sb, 3, title); + EndTag(sb, "a"); + + AddParagraph(sb, $"Season: {t.parentIndex}, Episode: {t.index}"); + if (info.genres.Any()) + { + AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); + } + + AddParagraph(sb, string.IsNullOrEmpty(t.summary) ? info.summary : t.summary); + } + catch (Exception e) + { + Log.Error(e); + Log.Error( + "Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}", + plexGUID); + } + finally + { + EndLoopHtml(sb); } - sb.Append("
"); - sb.AppendFormat("", banner); - sb.Append("
"); - - sb.AppendFormat("

{1} {2}

", - info.externals.imdb, info.name, info.premiered.Substring(0, 4)); // Only the year - - sb.AppendFormat("

Genre: {0}

", string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())); - sb.AppendFormat("

{0}

", - string.IsNullOrEmpty(parentMetaData.Directory.Summary) ? info.summary : parentMetaData.Directory.Summary); // Episode Summary - - sb.Append(""); - sb.Append("
"); - sb.Append("
"); - sb.Append("



"); } - private void Send(string html, PlexSettings plexSettings, bool testEmail = false) + private void Send(NewletterSettings newletterSettings, string html, PlexSettings plexSettings, bool testEmail = false) { var settings = EmailSettings.GetSettings(); @@ -230,13 +254,24 @@ namespace PlexRequests.Services.Jobs if (!testEmail) { - var users = Api.GetUsers(plexSettings.PlexAuthToken); - foreach (var user in users.User) + if (newletterSettings.SendToPlexUsers) { - message.Bcc.Add(new MailboxAddress(user.Username, user.Email)); + var users = Api.GetUsers(plexSettings.PlexAuthToken); + foreach (var user in users.User) + { + message.Bcc.Add(new MailboxAddress(user.Username, user.Email)); + } + } + + if (newletterSettings.CustomUsersEmailAddresses.Any()) + { + foreach (var user in newletterSettings.CustomUsersEmailAddresses) + { + message.Bcc.Add(new MailboxAddress(user, user)); + } } } - message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender)); // Include the admin + message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender)); try @@ -263,5 +298,15 @@ namespace PlexRequests.Services.Jobs Log.Error(e); } } + + private void EndLoopHtml(StringBuilder sb) + { + sb.Append(""); + sb.Append("
"); + sb.Append("
"); + sb.Append(""); + } + } } \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html b/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html index 0ab085dce..5a7258ea0 100644 --- a/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html +++ b/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html @@ -144,7 +144,7 @@ diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index 6c0f8392b..d53128879 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -80,6 +80,7 @@ + diff --git a/PlexRequests.Store/IPlexDatabase.cs b/PlexRequests.Store/IPlexDatabase.cs new file mode 100644 index 000000000..ac8b6fbea --- /dev/null +++ b/PlexRequests.Store/IPlexDatabase.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Store +{ + public interface IPlexDatabase + { + IEnumerable GetMetadata(); + string DbLocation { get; set; } + Task> GetMetadataAsync(); + IEnumerable QueryMetadataItems(string query, object param); + } +} \ No newline at end of file diff --git a/PlexRequests.Store/Models/Plex/MetadataItems.cs b/PlexRequests.Store/Models/Plex/MetadataItems.cs new file mode 100644 index 000000000..8ba55fc39 --- /dev/null +++ b/PlexRequests.Store/Models/Plex/MetadataItems.cs @@ -0,0 +1,68 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: MetadataItems.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 Dapper; +using Dapper.Contrib.Extensions; + +namespace PlexRequests.Store.Models.Plex +{ + [Table("metadata_items")] + public class MetadataItems + { + [Key] + public int id { get; set; } + + public int library_section_id { get; set; } + public int parent_id { get; set; } + public int metadata_type { get; set; } + public string guid { get; set; } + public int media_item_count { get; set; } + public string title { get; set; } + public string title_sort { get; set; } + public string original_title { get; set; } + public string studio { get; set; } + public float rating { get; set; } + public int rating_count { get; set; } + public string tagline { get; set; } + public string summary { get; set; } + public string trivia { get; set; } + public string quotes { get; set; } + public string content_rating { get; set; } + public int content_rating_age { get; set; } + public int Index { get; set; } + public string tags_genre { get; set; } + // SKIP Until Date Times + + public DateTime originally_available_at { get; set; } + public DateTime available_at { get; set; } + public DateTime expires_at { get; set; } + // Skip RefreshedAt and Year + public DateTime added_at { get; set; } + public string SeriesTitle { get; set; } // Only used in a custom query for the TV Shows + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexDatabase.cs b/PlexRequests.Store/PlexDatabase.cs new file mode 100644 index 000000000..2635cc22e --- /dev/null +++ b/PlexRequests.Store/PlexDatabase.cs @@ -0,0 +1,94 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexDatabase.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.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading.Tasks; +using Dapper; +using Dapper.Contrib.Extensions; +using Mono.Data.Sqlite; +using PlexRequests.Store.Models.Plex; + +namespace PlexRequests.Store +{ + /// + /// We should only ever READ, NEVER WRITE! + /// + public class PlexDatabase : IPlexDatabase + { + public PlexDatabase(SqliteFactory provider) + { + Factory = provider; + } + + private SqliteFactory Factory { get; } + /// + /// https://support.plex.tv/hc/en-us/articles/202915258-Where-is-the-Plex-Media-Server-data-directory-located- + /// + public string DbLocation { get; set; } + + private IDbConnection DbConnection() + { + var fact = Factory.CreateConnection(); + if (fact == null) + { + throw new SqliteException("Factory returned null"); + } + fact.ConnectionString = "Data Source=" + DbLocation; + return fact; + } + + public IEnumerable GetMetadata() + { + using (var con = DbConnection()) + { + return con.GetAll(); + } + } + + public async Task> GetMetadataAsync() + { + using (var con = DbConnection()) + { + return await con.GetAllAsync(); + } + } + + public IEnumerable QueryMetadataItems(string query, object param) + { + using (var con = DbConnection()) + { + con.Open(); + var data = con.Query(query, param); + con.Close(); + return data; + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexRequests.Store.csproj b/PlexRequests.Store/PlexRequests.Store.csproj index 1e6a22cb6..d8cf9514c 100644 --- a/PlexRequests.Store/PlexRequests.Store.csproj +++ b/PlexRequests.Store/PlexRequests.Store.csproj @@ -64,12 +64,15 @@ + + + @@ -84,6 +87,7 @@ + @@ -120,6 +124,7 @@ PlexRequests.Helpers + +
+
+ + Note: This will require you to setup your email notifications +
+ @if (Model.SendRecentlyAddedEmail) + { + + } + else + { + + } + + +
+
+ +
+
+ @if (Model.SendToPlexUsers) + { + + } + else + { + + } + +
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ + + + + + + + + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/Plex.cshtml b/PlexRequests.UI/Views/Admin/Plex.cshtml index d3b436766..67700e59f 100644 --- a/PlexRequests.UI/Views/Admin/Plex.cshtml +++ b/PlexRequests.UI/Views/Admin/Plex.cshtml @@ -16,7 +16,7 @@
Plex Settings - + @**@ @*TODO*@
@@ -87,6 +87,21 @@
+ + @*
+ +
+ +
+ + This is your Plex data directory location, if we cannot manually find it then you need to specify the location! See Here. + +
+
+
+ +
+
*@
@@ -94,6 +109,8 @@
+ +
@@ -129,6 +146,9 @@
- +