diff --git a/PlexRequests.Api.Interfaces/IPlexApi.cs b/PlexRequests.Api.Interfaces/IPlexApi.cs index 7bd6c8095..cb2a52049 100644 --- a/PlexRequests.Api.Interfaces/IPlexApi.cs +++ b/PlexRequests.Api.Interfaces/IPlexApi.cs @@ -44,5 +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); } } \ No newline at end of file diff --git a/PlexRequests.Api.Models/Plex/RecentlyAdded.cs b/PlexRequests.Api.Models/Plex/RecentlyAdded.cs new file mode 100644 index 000000000..b5536625a --- /dev/null +++ b/PlexRequests.Api.Models/Plex/RecentlyAdded.cs @@ -0,0 +1,84 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: RecentlyAdded.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 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 string type { get; set; } + public string title { get; set; } + public string parentKey { get; set; } + public string parentTitle { get; set; } + public string parentSummary { 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 int addedAt { get; set; } + public int updatedAt { get; set; } + public List _children { get; set; } + public string studio { get; set; } + public string contentRating { get; set; } + public string rating { 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 originalTitle { get; set; } + } + + public class RecentlyAdded + { + public string _elementType { get; set; } + public string allowSync { get; set; } + public string identifier { get; set; } + public string mediaTagPrefix { get; set; } + public string mediaTagVersion { get; set; } + public string mixedParents { 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 6426f8050..10d5aa1e8 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -73,6 +73,7 @@ + diff --git a/PlexRequests.Api/PlexApi.cs b/PlexRequests.Api/PlexApi.cs index 233bb66ff..83e06d7cd 100644 --- a/PlexRequests.Api/PlexApi.cs +++ b/PlexRequests.Api/PlexApi.cs @@ -347,6 +347,39 @@ namespace PlexRequests.Api return servers; } + public RecentlyAdded RecentlyAdded(string authToken, Uri plexFullHost) + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "library/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"); + + try + { + var lib = RetryHandler.Execute(() => Api.ExecuteJson(request, plexFullHost), + (exception, timespan) => Log.Error(exception, "Exception when calling RecentlyAdded for Plex, Retrying {0}", timespan), new[] { + TimeSpan.FromSeconds (5), + TimeSpan.FromSeconds(10), + TimeSpan.FromSeconds(30) + }); + + return lib; + } + catch (Exception e) + { + Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAdded"); + return new RecentlyAdded(); + } + } + private void AddHeaders(ref RestRequest request, string authToken) { request.AddHeader("X-Plex-Token", authToken); diff --git a/PlexRequests.Api/TheMovieDbApi.cs b/PlexRequests.Api/TheMovieDbApi.cs index 8137ad05f..0e860d58a 100644 --- a/PlexRequests.Api/TheMovieDbApi.cs +++ b/PlexRequests.Api/TheMovieDbApi.cs @@ -74,6 +74,12 @@ namespace PlexRequests.Api return movies; } + public async Task GetMovieInformation(string imdbId) + { + var movies = await Client.GetMovie(imdbId); + return movies; + } + [Obsolete("Should use TvMaze for TV")] public async Task GetTvShowInformation(int tmdbId) { diff --git a/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html b/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html index c7d7e6c9d..3d005619e 100644 --- a/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html +++ b/PlexRequests.Core/Notification/Templates/BasicRequestTemplate.html @@ -135,7 +135,7 @@
- + diff --git a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs index 6c1d95595..078798b3e 100644 --- a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs @@ -58,6 +58,7 @@ namespace PlexRequests.Core.SettingModels public bool Wizard { get; set; } public bool DisableTvRequestsByEpisode { get; set; } public bool DisableTvRequestsBySeason { get; set; } + public bool SendRecentlyAddedEmail { get; set; } /// /// The CSS name of the theme we want diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index 15e83d2b2..c881e8565 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -38,6 +38,7 @@ namespace PlexRequests.Core.SettingModels StoreCleanup = 24; UserRequestLimitResetter = 12; PlexEpisodeCacher = 12; + RecentlyAdded = 168; } public int PlexAvailabilityChecker { get; set; } @@ -48,5 +49,6 @@ namespace PlexRequests.Core.SettingModels public int StoreCleanup { get; set; } public int UserRequestLimitResetter { get; set; } public int PlexEpisodeCacher { get; set; } + public int RecentlyAdded { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Services/Interfaces/ISonarrCacher.cs b/PlexRequests.Services/Interfaces/ISonarrCacher.cs index a7cf8f9fa..291216d98 100644 --- a/PlexRequests.Services/Interfaces/ISonarrCacher.cs +++ b/PlexRequests.Services/Interfaces/ISonarrCacher.cs @@ -1,8 +1,11 @@ -namespace PlexRequests.Services.Interfaces -{ - public interface ISonarrCacher - { - void Queued(); - int[] QueuedIds(); - } -} +using System.Collections.Generic; +using PlexRequests.Services.Models; + +namespace PlexRequests.Services.Interfaces +{ + public interface ISonarrCacher + { + void Queued(); + IEnumerable QueuedIds(); + } +} diff --git a/PlexRequests.Services/Jobs/JobNames.cs b/PlexRequests.Services/Jobs/JobNames.cs index 29418ee96..77f177713 100644 --- a/PlexRequests.Services/Jobs/JobNames.cs +++ b/PlexRequests.Services/Jobs/JobNames.cs @@ -36,5 +36,6 @@ namespace PlexRequests.Services.Jobs public const string StoreCleanup = "Database Cleanup"; public const string RequestLimitReset = "Request Limit Reset"; public const string EpisodeCacher = "Plex Episode Cacher"; + public const string RecentlyAddedEmail = "Recently Added Email Notification"; } } \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/RecentlyAdded.cs b/PlexRequests.Services/Jobs/RecentlyAdded.cs new file mode 100644 index 000000000..60fe31d88 --- /dev/null +++ b/PlexRequests.Services/Jobs/RecentlyAdded.cs @@ -0,0 +1,239 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: RecentlyAdded.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.Linq; +using System.Text; +using MailKit.Net.Smtp; +using MimeKit; +using NLog; +using PlexRequests.Api; +using PlexRequests.Api.Interfaces; +using PlexRequests.Api.Models.Plex; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers; +using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Jobs.Templates; +using Quartz; + + +namespace PlexRequests.Services.Jobs +{ + public class RecentlyAdded : IJob + { + public RecentlyAdded(IPlexApi api, ISettingsService plexSettings, ISettingsService email, + ISettingsService scheduledService, IJobRecord rec) + { + JobRecord = rec; + Api = api; + PlexSettings = plexSettings; + EmailSettings = email; + ScheduledJobsSettings = scheduledService; + } + + private IPlexApi Api { get; } + private TvMazeApi TvApi = new TvMazeApi(); + private readonly TheMovieDbApi _movieApi = new TheMovieDbApi(); + private ISettingsService PlexSettings { get; } + private ISettingsService EmailSettings { get; } + private ISettingsService ScheduledJobsSettings { get; } + private IJobRecord JobRecord { get; } + + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + public void Execute(IJobExecutionContext context) + { + try + { + var jobs = JobRecord.GetJobs(); + var thisJob = + jobs.FirstOrDefault( + x => x.Name.Equals(JobNames.RecentlyAddedEmail, StringComparison.CurrentCultureIgnoreCase)); + + var settings = ScheduledJobsSettings.GetSettings(); + + if (thisJob?.LastRun > DateTime.Now.AddHours(-settings.RecentlyAdded)) + { + return; + } + + Start(); + } + catch (Exception e) + { + Log.Error(e); + } + finally + { + JobRecord.Record(JobNames.RecentlyAddedEmail); + } + } + + private void Start() + { + var sb = new StringBuilder(); + var plexSettings = PlexSettings.GetSettings(); + var recentlyAdded = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri); + + 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()); + + GenerateMovieHtml(movies, plexSettings, ref sb); + GenerateTvHtml(tv, plexSettings, ref sb); + + var template = new RecentlyAddedTemplate(); + var html = template.LoadTemplate(sb.ToString()); + + Send(html, plexSettings); + } + + private void GenerateMovieHtml(IEnumerable movies, PlexSettings plexSettings, ref StringBuilder sb) + { + sb.Append("

New Movies:



"); + sb.Append("
"); + foreach (var movie in movies) + { + 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("
"); + sb.AppendFormat("", info.BackdropPath); + sb.Append("
"); + + sb.AppendFormat("

{1} {2}

", + info.ImdbId, info.Title, info.ReleaseDate?.ToString("yyyy") ?? string.Empty); + + sb.AppendFormat("

Genre: {0}

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

{0}

", info.Overview); + + sb.Append(""); + sb.Append("
"); + sb.Append("
"); + sb.Append("
"); + + } + sb.Append("



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

New Episodes:



"); + sb.Append(""); + foreach (var t in tv) + { + 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)) + { + banner = banner.Replace("http", "https"); // Always use the Https banners + } + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + 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("
"); + } + sb.Append("



"); + } + + private void Send(string html, PlexSettings plexSettings) + { + var users = Api.GetUsers(plexSettings.PlexAuthToken); + var settings = EmailSettings.GetSettings(); + var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." }; + var message = new MimeMessage + { + Body = body.ToMessageBody(), + Subject = "New Content on Plex!", + }; + + foreach (var user in users.User) + { + message.Bcc.Add(new MailboxAddress(user.Username, user.Email)); + } + + message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender)); + try + { + using (var client = new SmtpClient()) + { + client.Connect(settings.EmailHost, settings.EmailPort); // Let MailKit figure out the correct SecureSocketOptions. + + // Note: since we don't have an OAuth2 token, disable + // the XOAUTH2 authentication mechanism. + client.AuthenticationMechanisms.Remove("XOAUTH2"); + + if (settings.Authentication) + { + client.Authenticate(settings.EmailUsername, settings.EmailPassword); + } + Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated); + client.Send(message); + client.Disconnect(true); + } + } + catch (Exception e) + { + Log.Error(e); + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/SonarrCacher.cs b/PlexRequests.Services/Jobs/SonarrCacher.cs index 26e2b36cb..d94561604 100644 --- a/PlexRequests.Services/Jobs/SonarrCacher.cs +++ b/PlexRequests.Services/Jobs/SonarrCacher.cs @@ -35,6 +35,7 @@ using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Models; using PlexRequests.Store.Models; using PlexRequests.Store.Repository; @@ -84,10 +85,29 @@ namespace PlexRequests.Services.Jobs } // we do not want to set here... - public int[] QueuedIds() + public IEnumerable QueuedIds() { + var result = new List(); + var series = Cache.Get>(CacheKeys.SonarrQueued); - return series?.Select(x => x.tvdbId).ToArray() ?? new int[] { }; + if (series != null) + { + foreach (var s in series) + { + var cached = new SonarrCachedResult {TvdbId = s.tvdbId}; + foreach (var season in s.seasons) + { + cached.Seasons.Add(new SonarrSeasons + { + SeasonNumber = season.seasonNumber, + Monitored = season.monitored + }); + } + + result.Add(cached); + } + } + return result; } public void Execute(IJobExecutionContext context) diff --git a/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.cs b/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.cs new file mode 100644 index 000000000..c375e04f2 --- /dev/null +++ b/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.cs @@ -0,0 +1,58 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: RecentlyAddedTemplate.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.IO; +using System.Text; +using System.Windows.Forms; +using NLog; + +namespace PlexRequests.Services.Jobs.Templates +{ + public class RecentlyAddedTemplate + { + public string TemplateLocation => Path.Combine(Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty, "Jobs", "Templates", "RecentlyAddedTemplate.html"); + private static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + private const string RecentlyAddedKey = "{@RECENTLYADDED}"; + + public string LoadTemplate(string html) + { + try + { + var sb = new StringBuilder(File.ReadAllText(TemplateLocation)); + sb.Replace(RecentlyAddedKey, html); + return sb.ToString(); + } + catch (Exception e) + { + Log.Error(e); + return string.Empty; + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html b/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html new file mode 100644 index 000000000..0ab085dce --- /dev/null +++ b/PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html @@ -0,0 +1,187 @@ + + + + + + Plex Requests .Net + + + + + + + + + +
  +
+ + + + + + + + + + + +
+ + + + + + + +
+ +
+
+
+

Here is a list of Movies and TV Shows that have recently been added to Plex!

+ +
+ + {@RECENTLYADDED} + +
+ + + + + + +
+
 
+ + \ No newline at end of file diff --git a/PlexRequests.Services/Models/SonarrCachedResult.cs b/PlexRequests.Services/Models/SonarrCachedResult.cs new file mode 100644 index 000000000..1398a7ce4 --- /dev/null +++ b/PlexRequests.Services/Models/SonarrCachedResult.cs @@ -0,0 +1,47 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SonarrCachedResult.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.Services.Models +{ + public class SonarrCachedResult + { + public SonarrCachedResult() + { + Seasons = new List( ); + } + public List Seasons { get; set; } + public int TvdbId { get; set; } + } + + public class SonarrSeasons + { + public int SeasonNumber { get; set; } + public bool Monitored { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Notification/EmailMessageNotification.cs b/PlexRequests.Services/Notification/EmailMessageNotification.cs index 5dd48b59d..3f6bf9c91 100644 --- a/PlexRequests.Services/Notification/EmailMessageNotification.cs +++ b/PlexRequests.Services/Notification/EmailMessageNotification.cs @@ -129,7 +129,7 @@ namespace PlexRequests.Services.Notification $"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!", $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}", model.ImgSrc); - var body = new BodyBuilder { HtmlBody = html, }; + var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." }; var message = new MimeMessage { @@ -150,7 +150,7 @@ namespace PlexRequests.Services.Notification $"Plex Requests: New issue for {model.Title}!", $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!", model.ImgSrc); - var body = new BodyBuilder { HtmlBody = html, }; + var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." }; var message = new MimeMessage { @@ -175,7 +175,7 @@ namespace PlexRequests.Services.Notification $"Plex Requests: {model.Title} is now available!", $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)", model.ImgSrc); - var body = new BodyBuilder { HtmlBody = html, }; + var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." }; var message = new MimeMessage { diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index 6aed947bc..9819e387f 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -72,6 +72,10 @@ ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll + + False + ..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll + @@ -79,12 +83,14 @@ + + @@ -97,6 +103,7 @@ + @@ -136,6 +143,11 @@ PlexRequests.Store + + + PreserveNewest + +