mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
This commit is contained in:
parent
9a3eb5fba6
commit
6e3e290359
22 changed files with 766 additions and 34 deletions
|
@ -44,5 +44,6 @@ namespace PlexRequests.Api.Interfaces
|
||||||
PlexSearch GetAllEpisodes(string authToken, Uri host, string section, int startPage, int returnCount);
|
PlexSearch GetAllEpisodes(string authToken, Uri host, string section, int startPage, int returnCount);
|
||||||
PlexServer GetServer(string authToken);
|
PlexServer GetServer(string authToken);
|
||||||
PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey);
|
PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey);
|
||||||
|
RecentlyAdded RecentlyAdded(string authToken, Uri plexFullHost);
|
||||||
}
|
}
|
||||||
}
|
}
|
84
PlexRequests.Api.Models/Plex/RecentlyAdded.cs
Normal file
84
PlexRequests.Api.Models/Plex/RecentlyAdded.cs
Normal file
|
@ -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<object> _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<RecentlyAddedChild> _children { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -73,6 +73,7 @@
|
||||||
<Compile Include="Plex\PlexStatus.cs" />
|
<Compile Include="Plex\PlexStatus.cs" />
|
||||||
<Compile Include="Plex\PlexMediaType.cs" />
|
<Compile Include="Plex\PlexMediaType.cs" />
|
||||||
<Compile Include="Plex\PlexUserRequest.cs" />
|
<Compile Include="Plex\PlexUserRequest.cs" />
|
||||||
|
<Compile Include="Plex\RecentlyAdded.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="SickRage\SickRageBase.cs" />
|
<Compile Include="SickRage\SickRageBase.cs" />
|
||||||
<Compile Include="SickRage\SickrageShows.cs" />
|
<Compile Include="SickRage\SickrageShows.cs" />
|
||||||
|
|
|
@ -347,6 +347,39 @@ namespace PlexRequests.Api
|
||||||
return servers;
|
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<RecentlyAdded>(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)
|
private void AddHeaders(ref RestRequest request, string authToken)
|
||||||
{
|
{
|
||||||
request.AddHeader("X-Plex-Token", authToken);
|
request.AddHeader("X-Plex-Token", authToken);
|
||||||
|
|
|
@ -74,6 +74,12 @@ namespace PlexRequests.Api
|
||||||
return movies;
|
return movies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Movie> GetMovieInformation(string imdbId)
|
||||||
|
{
|
||||||
|
var movies = await Client.GetMovie(imdbId);
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
[Obsolete("Should use TvMaze for TV")]
|
[Obsolete("Should use TvMaze for TV")]
|
||||||
public async Task<TvShow> GetTvShowInformation(int tmdbId)
|
public async Task<TvShow> GetTvShowInformation(int tmdbId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -135,7 +135,7 @@
|
||||||
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
<!-- START CENTERED WHITE CONTAINER -->
|
||||||
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">This is preheader text. Some clients will show this text as a preview.</span>
|
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Plex Requests</span>
|
||||||
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
|
||||||
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
<!-- START MAIN CONTENT AREA -->
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace PlexRequests.Core.SettingModels
|
||||||
public bool Wizard { get; set; }
|
public bool Wizard { get; set; }
|
||||||
public bool DisableTvRequestsByEpisode { get; set; }
|
public bool DisableTvRequestsByEpisode { get; set; }
|
||||||
public bool DisableTvRequestsBySeason { get; set; }
|
public bool DisableTvRequestsBySeason { get; set; }
|
||||||
|
public bool SendRecentlyAddedEmail { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The CSS name of the theme we want
|
/// The CSS name of the theme we want
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace PlexRequests.Core.SettingModels
|
||||||
StoreCleanup = 24;
|
StoreCleanup = 24;
|
||||||
UserRequestLimitResetter = 12;
|
UserRequestLimitResetter = 12;
|
||||||
PlexEpisodeCacher = 12;
|
PlexEpisodeCacher = 12;
|
||||||
|
RecentlyAdded = 168;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int PlexAvailabilityChecker { get; set; }
|
public int PlexAvailabilityChecker { get; set; }
|
||||||
|
@ -48,5 +49,6 @@ namespace PlexRequests.Core.SettingModels
|
||||||
public int StoreCleanup { get; set; }
|
public int StoreCleanup { get; set; }
|
||||||
public int UserRequestLimitResetter { get; set; }
|
public int UserRequestLimitResetter { get; set; }
|
||||||
public int PlexEpisodeCacher { get; set; }
|
public int PlexEpisodeCacher { get; set; }
|
||||||
|
public int RecentlyAdded { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
namespace PlexRequests.Services.Interfaces
|
using System.Collections.Generic;
|
||||||
{
|
using PlexRequests.Services.Models;
|
||||||
public interface ISonarrCacher
|
|
||||||
{
|
namespace PlexRequests.Services.Interfaces
|
||||||
void Queued();
|
{
|
||||||
int[] QueuedIds();
|
public interface ISonarrCacher
|
||||||
}
|
{
|
||||||
}
|
void Queued();
|
||||||
|
IEnumerable<SonarrCachedResult> QueuedIds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,5 +36,6 @@ namespace PlexRequests.Services.Jobs
|
||||||
public const string StoreCleanup = "Database Cleanup";
|
public const string StoreCleanup = "Database Cleanup";
|
||||||
public const string RequestLimitReset = "Request Limit Reset";
|
public const string RequestLimitReset = "Request Limit Reset";
|
||||||
public const string EpisodeCacher = "Plex Episode Cacher";
|
public const string EpisodeCacher = "Plex Episode Cacher";
|
||||||
|
public const string RecentlyAddedEmail = "Recently Added Email Notification";
|
||||||
}
|
}
|
||||||
}
|
}
|
239
PlexRequests.Services/Jobs/RecentlyAdded.cs
Normal file
239
PlexRequests.Services/Jobs/RecentlyAdded.cs
Normal file
|
@ -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> plexSettings, ISettingsService<EmailNotificationSettings> email,
|
||||||
|
ISettingsService<ScheduledJobsSettings> 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> PlexSettings { get; }
|
||||||
|
private ISettingsService<EmailNotificationSettings> EmailSettings { get; }
|
||||||
|
private ISettingsService<ScheduledJobsSettings> 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<RecentlyAddedChild> movies, PlexSettings plexSettings, ref StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.Append("<h1>New Movies:</h1><br/><br/>");
|
||||||
|
sb.Append("<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
|
||||||
|
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("<tr>");
|
||||||
|
sb.Append("<td align=\"center\">");
|
||||||
|
sb.AppendFormat("<img src=\"https://image.tmdb.org/t/p/w500{0}\" width=\"400px\" text-align=\"center\" />", info.BackdropPath);
|
||||||
|
sb.Append("</td>");
|
||||||
|
sb.Append("</tr>");
|
||||||
|
sb.Append("<tr>");
|
||||||
|
sb.Append("<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||||
|
|
||||||
|
sb.AppendFormat("<a href=\"https://www.imdb.com/title/{0}/\"><h3 style=\"font-family: sans-serif; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{1} {2}</p></a>",
|
||||||
|
info.ImdbId, info.Title, info.ReleaseDate?.ToString("yyyy") ?? string.Empty);
|
||||||
|
|
||||||
|
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">Genre: {0}</p>", string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray()));
|
||||||
|
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{0}</p>", info.Overview);
|
||||||
|
|
||||||
|
sb.Append("<td");
|
||||||
|
sb.Append("</tr>");
|
||||||
|
sb.Append("<hr>");
|
||||||
|
sb.Append("<br>");
|
||||||
|
sb.Append("<br>");
|
||||||
|
|
||||||
|
}
|
||||||
|
sb.Append("</table><br/><br/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateTvHtml(IEnumerable<RecentlyAddedChild> tv, PlexSettings plexSettings, ref StringBuilder sb)
|
||||||
|
{
|
||||||
|
// TV
|
||||||
|
sb.Append("<h1>New Episodes:</h1><br/><br/>");
|
||||||
|
sb.Append("<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
|
||||||
|
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("<tr>");
|
||||||
|
sb.Append("<td align=\"center\">");
|
||||||
|
sb.AppendFormat("<img src=\"{0}\" width=\"400px\" text-align=\"center\" />", banner);
|
||||||
|
sb.Append("</td>");
|
||||||
|
sb.Append("</tr>");
|
||||||
|
sb.Append("<tr>");
|
||||||
|
sb.Append("<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
|
||||||
|
|
||||||
|
sb.AppendFormat("<a href=\"https://www.imdb.com/title/{0}/\"><h3 style=\"font-family: sans-serif; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{1} {2}</p></a>",
|
||||||
|
info.externals.imdb, info.name, info.premiered.Substring(0, 4)); // Only the year
|
||||||
|
|
||||||
|
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">Genre: {0}</p>", string.Join(", ", info.genres.Select(x => x.ToString()).ToArray()));
|
||||||
|
sb.AppendFormat("<p style=\"font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;\">{0}</p>",
|
||||||
|
string.IsNullOrEmpty(parentMetaData.Directory.Summary) ? info.summary : parentMetaData.Directory.Summary); // Episode Summary
|
||||||
|
|
||||||
|
sb.Append("<td");
|
||||||
|
sb.Append("</tr>");
|
||||||
|
sb.Append("<hr>");
|
||||||
|
sb.Append("<br>");
|
||||||
|
sb.Append("<br>");
|
||||||
|
}
|
||||||
|
sb.Append("</table><br/><br/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ using PlexRequests.Core;
|
||||||
using PlexRequests.Core.SettingModels;
|
using PlexRequests.Core.SettingModels;
|
||||||
using PlexRequests.Helpers;
|
using PlexRequests.Helpers;
|
||||||
using PlexRequests.Services.Interfaces;
|
using PlexRequests.Services.Interfaces;
|
||||||
|
using PlexRequests.Services.Models;
|
||||||
using PlexRequests.Store.Models;
|
using PlexRequests.Store.Models;
|
||||||
using PlexRequests.Store.Repository;
|
using PlexRequests.Store.Repository;
|
||||||
|
|
||||||
|
@ -84,10 +85,29 @@ namespace PlexRequests.Services.Jobs
|
||||||
}
|
}
|
||||||
|
|
||||||
// we do not want to set here...
|
// we do not want to set here...
|
||||||
public int[] QueuedIds()
|
public IEnumerable<SonarrCachedResult> QueuedIds()
|
||||||
{
|
{
|
||||||
|
var result = new List<SonarrCachedResult>();
|
||||||
|
|
||||||
var series = Cache.Get<List<Series>>(CacheKeys.SonarrQueued);
|
var series = Cache.Get<List<Series>>(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)
|
public void Execute(IJobExecutionContext context)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
187
PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html
Normal file
187
PlexRequests.Services/Jobs/Templates/RecentlyAddedTemplate.html
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>Plex Requests .Net</title>
|
||||||
|
<style media="all" type="text/css">
|
||||||
|
@media all {
|
||||||
|
.btn-primary table td:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary a:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
border-color: #34495e !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all {
|
||||||
|
.btn-secondary a:hover {
|
||||||
|
border-color: #34495e !important;
|
||||||
|
color: #34495e !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 620px) {
|
||||||
|
table[class=body] h1 {
|
||||||
|
font-size: 28px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] h2 {
|
||||||
|
font-size: 22px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] h3 {
|
||||||
|
font-size: 16px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] p,
|
||||||
|
table[class=body] ul,
|
||||||
|
table[class=body] ol,
|
||||||
|
table[class=body] td,
|
||||||
|
table[class=body] span,
|
||||||
|
table[class=body] a {
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .wrapper,
|
||||||
|
table[class=body] .article {
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .container {
|
||||||
|
padding: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .header {
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .main {
|
||||||
|
border-left-width: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border-right-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .btn table {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .btn a {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .img-responsive {
|
||||||
|
height: auto !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .alert td {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .span-2,
|
||||||
|
table[class=body] .span-3 {
|
||||||
|
max-width: none !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class=body] .receipt {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all {
|
||||||
|
.ExternalClass {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExternalClass,
|
||||||
|
.ExternalClass p,
|
||||||
|
.ExternalClass span,
|
||||||
|
.ExternalClass font,
|
||||||
|
.ExternalClass td,
|
||||||
|
.ExternalClass div {
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apple-link a {
|
||||||
|
color: inherit !important;
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-size: inherit !important;
|
||||||
|
font-weight: inherit !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="" style="font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; background-color: #f6f6f6; margin: 0; padding: 0;">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;" width="100%" bgcolor="#f6f6f6">
|
||||||
|
<tr>
|
||||||
|
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td>
|
||||||
|
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto !important; max-width: 580px; padding: 10px; width: 580px;" width="580" valign="top">
|
||||||
|
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
||||||
|
|
||||||
|
<!-- START CENTERED WHITE CONTAINER -->
|
||||||
|
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Plex Requests Recently Added</span>
|
||||||
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #fff; border-radius: 3px;" width="100%">
|
||||||
|
|
||||||
|
<!-- START MAIN CONTENT AREA -->
|
||||||
|
<tr>
|
||||||
|
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;" valign="top">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<img src="http://i.imgur.com/s4nswSA.png?" width="400px" text-align="center" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top">
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<p style="font-family: sans-serif; font-size: 20px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Here is a list of Movies and TV Shows that have recently been added to Plex!</p>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{@RECENTLYADDED}
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- START FOOTER -->
|
||||||
|
<div class="footer" style="clear: both; padding-top: 10px; text-align: center; width: 100%;">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;" width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-top: 10px; padding-bottom: 10px; font-size: 12px; color: #999999; text-align: center;" valign="top" align="center">
|
||||||
|
Powered by <a href="https://github.com/tidusjar/PlexRequests.Net" style="color: #999999; font-size: 12px; text-align: center; text-decoration: underline;">Plex Requests .Net</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- END FOOTER -->
|
||||||
|
<!-- END CENTERED WHITE CONTAINER -->
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;" valign="top"> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
47
PlexRequests.Services/Models/SonarrCachedResult.cs
Normal file
47
PlexRequests.Services/Models/SonarrCachedResult.cs
Normal file
|
@ -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<SonarrSeasons>( );
|
||||||
|
}
|
||||||
|
public List<SonarrSeasons> Seasons { get; set; }
|
||||||
|
public int TvdbId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SonarrSeasons
|
||||||
|
{
|
||||||
|
public int SeasonNumber { get; set; }
|
||||||
|
public bool Monitored { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,7 +129,7 @@ namespace PlexRequests.Services.Notification
|
||||||
$"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
|
$"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")}",
|
$"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);
|
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
|
var message = new MimeMessage
|
||||||
{
|
{
|
||||||
|
@ -150,7 +150,7 @@ namespace PlexRequests.Services.Notification
|
||||||
$"Plex Requests: New issue for {model.Title}!",
|
$"Plex Requests: New issue for {model.Title}!",
|
||||||
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
|
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
|
||||||
model.ImgSrc);
|
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
|
var message = new MimeMessage
|
||||||
{
|
{
|
||||||
|
@ -175,7 +175,7 @@ namespace PlexRequests.Services.Notification
|
||||||
$"Plex Requests: {model.Title} is now available!",
|
$"Plex Requests: {model.Title} is now available!",
|
||||||
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)",
|
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)",
|
||||||
model.ImgSrc);
|
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
|
var message = new MimeMessage
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,6 +72,10 @@
|
||||||
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4">
|
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4">
|
||||||
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
|
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="TMDbLib, Version=0.9.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\TMDbLib.0.9.0.0-alpha\lib\net45\TMDbLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Interfaces\IJobRecord.cs" />
|
<Compile Include="Interfaces\IJobRecord.cs" />
|
||||||
|
@ -79,12 +83,14 @@
|
||||||
<Compile Include="Jobs\JobRecord.cs" />
|
<Compile Include="Jobs\JobRecord.cs" />
|
||||||
<Compile Include="Jobs\JobNames.cs" />
|
<Compile Include="Jobs\JobNames.cs" />
|
||||||
<Compile Include="Jobs\PlexEpisodeCacher.cs" />
|
<Compile Include="Jobs\PlexEpisodeCacher.cs" />
|
||||||
|
<Compile Include="Jobs\RecentlyAdded.cs" />
|
||||||
<Compile Include="Jobs\StoreBackup.cs" />
|
<Compile Include="Jobs\StoreBackup.cs" />
|
||||||
<Compile Include="Jobs\StoreCleanup.cs" />
|
<Compile Include="Jobs\StoreCleanup.cs" />
|
||||||
<Compile Include="Jobs\CouchPotatoCacher.cs" />
|
<Compile Include="Jobs\CouchPotatoCacher.cs" />
|
||||||
<Compile Include="Jobs\PlexAvailabilityChecker.cs" />
|
<Compile Include="Jobs\PlexAvailabilityChecker.cs" />
|
||||||
<Compile Include="Jobs\SickRageCacher.cs" />
|
<Compile Include="Jobs\SickRageCacher.cs" />
|
||||||
<Compile Include="Jobs\SonarrCacher.cs" />
|
<Compile Include="Jobs\SonarrCacher.cs" />
|
||||||
|
<Compile Include="Jobs\Templates\RecentlyAddedTemplate.cs" />
|
||||||
<Compile Include="Jobs\UserRequestLimitResetter.cs" />
|
<Compile Include="Jobs\UserRequestLimitResetter.cs" />
|
||||||
<Compile Include="Models\PlexAlbum.cs" />
|
<Compile Include="Models\PlexAlbum.cs" />
|
||||||
<Compile Include="Models\PlexEpisodeModel.cs" />
|
<Compile Include="Models\PlexEpisodeModel.cs" />
|
||||||
|
@ -97,6 +103,7 @@
|
||||||
<Compile Include="Interfaces\IIntervals.cs" />
|
<Compile Include="Interfaces\IIntervals.cs" />
|
||||||
<Compile Include="Interfaces\INotification.cs" />
|
<Compile Include="Interfaces\INotification.cs" />
|
||||||
<Compile Include="Interfaces\INotificationService.cs" />
|
<Compile Include="Interfaces\INotificationService.cs" />
|
||||||
|
<Compile Include="Models\SonarrCachedResult.cs" />
|
||||||
<Compile Include="Notification\EmailMessageNotification.cs" />
|
<Compile Include="Notification\EmailMessageNotification.cs" />
|
||||||
<Compile Include="Notification\NotificationEngine.cs" />
|
<Compile Include="Notification\NotificationEngine.cs" />
|
||||||
<Compile Include="Notification\NotificationModel.cs" />
|
<Compile Include="Notification\NotificationModel.cs" />
|
||||||
|
@ -136,6 +143,11 @@
|
||||||
<Name>PlexRequests.Store</Name>
|
<Name>PlexRequests.Store</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Jobs\Templates\RecentlyAddedTemplate.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
|
|
@ -246,7 +246,7 @@ namespace PlexRequests.UI.Helpers
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
foreach (var r in episodes)
|
foreach (var r in episodes)
|
||||||
{
|
{
|
||||||
if (r.monitored || r.hasFile) // If it's already montiored or has the file, there is no point in updating it
|
if (r.monitored || r.hasFile) // If it's already monitored or has the file, there is no point in updating it
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,8 @@ namespace PlexRequests.UI.Jobs
|
||||||
JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build(),
|
JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build(),
|
||||||
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
|
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
|
||||||
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
|
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
|
||||||
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build()
|
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
|
||||||
|
JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAdded", "Email").Build()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -165,6 +166,13 @@ namespace PlexRequests.UI.Jobs
|
||||||
.WithSimpleSchedule(x => x.WithIntervalInHours(s.PlexEpisodeCacher).RepeatForever())
|
.WithSimpleSchedule(x => x.WithIntervalInHours(s.PlexEpisodeCacher).RepeatForever())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
var rencentlyAdded =
|
||||||
|
TriggerBuilder.Create()
|
||||||
|
.WithIdentity("RecentlyAdded", "Email")
|
||||||
|
.StartNow()
|
||||||
|
.WithSimpleSchedule(x => x.WithIntervalInHours(2).RepeatForever())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
triggers.Add(plexAvailabilityChecker);
|
triggers.Add(plexAvailabilityChecker);
|
||||||
triggers.Add(srCacher);
|
triggers.Add(srCacher);
|
||||||
|
@ -174,6 +182,7 @@ namespace PlexRequests.UI.Jobs
|
||||||
triggers.Add(storeCleanup);
|
triggers.Add(storeCleanup);
|
||||||
triggers.Add(userRequestLimiter);
|
triggers.Add(userRequestLimiter);
|
||||||
triggers.Add(plexEpCacher);
|
triggers.Add(plexEpCacher);
|
||||||
|
triggers.Add(rencentlyAdded);
|
||||||
|
|
||||||
return triggers;
|
return triggers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,7 +367,7 @@ namespace PlexRequests.UI.Modules
|
||||||
viewT.Episodes = dbt.Episodes.ToList();
|
viewT.Episodes = dbt.Episodes.ToList();
|
||||||
viewT.Approved = dbt.Approved;
|
viewT.Approved = dbt.Approved;
|
||||||
}
|
}
|
||||||
if (sonarrCached.Contains(tvdbid) || sickRageCache.Contains(tvdbid)) // compare to the sonarr/sickrage db
|
if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid)) // compare to the sonarr/sickrage db
|
||||||
{
|
{
|
||||||
viewT.Requested = true;
|
viewT.Requested = true;
|
||||||
}
|
}
|
||||||
|
@ -573,7 +573,7 @@ namespace PlexRequests.UI.Modules
|
||||||
|
|
||||||
if (showInfo.externals?.thetvdb == null)
|
if (showInfo.externals?.thetvdb == null)
|
||||||
{
|
{
|
||||||
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Our TV Provider (TVMaze) doesn't have a TheTVDBId for this TV Show :( We cannot add the TV Show automatically sorry! Please report this problem to the server admin so he can sort it out!" });
|
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Our TV Provider (TVMaze) doesn't have a TheTVDBId for this TV Show :( We cannot add the TV Show automatically sorry! Please report this problem to the server admin so he/she can sort it out!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
var model = new RequestedModel
|
var model = new RequestedModel
|
||||||
|
|
|
@ -6,15 +6,19 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3"><strong>Job Name</strong></div>
|
<div class="col-md-4"><strong>Job Name</strong>
|
||||||
<div class="col-md-8"><strong>Last Run</strong></div>
|
</div>
|
||||||
|
<div class="col-md-6 col-md-push-3"><strong>Last Run</strong>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr style="margin-top: 4px; margin-bottom: 4px"/>
|
||||||
@foreach (var record in Model.JobRecorder)
|
@foreach (var record in Model.JobRecorder)
|
||||||
{
|
{
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3">@record.Key</div>
|
<div class="col-md-4">@record.Key</div>
|
||||||
<div class="col-md-8 date">@record.Value.ToString("O")</div>
|
<div class="col-md-5 col-md-push-3 date">@record.Value.ToString("O")</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr style="margin-top: 4px; margin-bottom: 4px"/>
|
||||||
}
|
}
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -35,7 +39,7 @@
|
||||||
|
|
||||||
<small>Please note, the minimum time for this to run is 11 hours, if set below 11 then we will ignore that value. This is a very resource intensive job, the less we run it the better.</small>
|
<small>Please note, the minimum time for this to run is 11 hours, if set below 11 then we will ignore that value. This is a very resource intensive job, the less we run it the better.</small>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="PlexEpisodeCacher" class="control-label">Plex Episode Cacher (hour)</label>
|
<label for="PlexEpisodeCacher" class="control-label">Plex Episode Cacher (hours)</label>
|
||||||
<input type="text" class="form-control form-control-custom " id="PlexEpisodeCacher" name="PlexEpisodeCacher" value="@Model.PlexEpisodeCacher">
|
<input type="text" class="form-control form-control-custom " id="PlexEpisodeCacher" name="PlexEpisodeCacher" value="@Model.PlexEpisodeCacher">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -54,27 +58,34 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="StoreBackup" class="control-label">Store Backup (hour)</label>
|
<label for="StoreBackup" class="control-label">Store Backup (hours)</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom " id="StoreBackup" name="StoreBackup" value="@Model.StoreBackup">
|
<input type="text" class="form-control form-control-custom " id="StoreBackup" name="StoreBackup" value="@Model.StoreBackup">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="StoreCleanup" class="control-label">Store Cleanup (hour)</label>
|
<label for="StoreCleanup" class="control-label">Store Cleanup (hours)</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom " id="StoreCleanup" name="StoreCleanup" value="@Model.StoreCleanup">
|
<input type="text" class="form-control form-control-custom " id="StoreCleanup" name="StoreCleanup" value="@Model.StoreCleanup">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<small>Please note, this will not reset the users request limit, it will just check every X hours to see if it needs to be reset.</small>
|
<small>Please note, this will not reset the users request limit, it will just check every @Model.UserRequestLimitResetter hours to see if it needs to be reset.</small>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="UserRequestLimitResetter" class="control-label">User Request Limit Reset (hour)</label>
|
<label for="UserRequestLimitResetter" class="control-label">User Request Limit Reset (hours)</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" class="form-control form-control-custom " id="UserRequestLimitResetter" name="UserRequestLimitResetter" value="@Model.UserRequestLimitResetter">
|
<input type="text" class="form-control form-control-custom " id="UserRequestLimitResetter" name="UserRequestLimitResetter" value="@Model.UserRequestLimitResetter">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="RecentlyAdded" class="control-label">Recently Added Email (hours)</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" class="form-control form-control-custom " id="RecentlyAdded" name="RecentlyAdded" value="@Model.RecentlyAdded">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div>
|
<div>
|
||||||
<button id="save" type="submit" class="btn btn-primary-outline ">Submit</button>
|
<button id="save" type="submit" class="btn btn-primary-outline ">Submit</button>
|
||||||
|
|
|
@ -65,17 +65,34 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="select" class="control-label">Theme</label>
|
||||||
|
<div id="themes">
|
||||||
|
<select class="form-control form-control-custom" id="select">
|
||||||
|
<option @plexTheme class="form-control form-control-custom" value="@Themes.PlexTheme">Plex</option>
|
||||||
|
<option @originalTheme class="form-control form-control-custom" value="@Themes.OriginalTheme">Original Blue</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="select" class="control-label">Theme</label>
|
<div class="checkbox">
|
||||||
<div id="themes">
|
|
||||||
<select class="form-control form-control-custom" id="select">
|
<small>Note: This will require you to setup your email notifications</small>
|
||||||
<option @plexTheme class="form-control form-control-custom" value="@Themes.PlexTheme">Plex</option>
|
@if (Model.SendRecentlyAddedEmail)
|
||||||
<option @originalTheme class="form-control form-control-custom" value="@Themes.OriginalTheme">Original Blue</option>
|
{
|
||||||
</select>
|
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail" checked="checked"><label for="SendRecentlyAddedEmail">Send out a weekly email of recently added content to all your Plex 'Friends'</label>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input type="checkbox" id="SendRecentlyAddedEmail" name="SendRecentlyAddedEmail"><label for="SendRecentlyAddedEmail">Send out a weekly email of recently added content to all your Plex 'Friends'</label>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
|
||||||
@if (Model.SearchForMovies)
|
@if (Model.SearchForMovies)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue