From 512d2131bddcdb64da1514185d593fa22ef1a533 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 19 Oct 2016 21:21:52 +0100 Subject: [PATCH] Fixed #553 --- PlexRequests.Api.Interfaces/IPlexApi.cs | 2 +- ...RecentlyAdded.cs => RecentlyAddedModel.cs} | 89 ++++++++++++++---- .../PlexRequests.Api.Models.csproj | 2 +- PlexRequests.Api/PlexApi.cs | 65 ++++++------- PlexRequests.Helpers/DateTimeHelper.cs | 92 ++++++++++--------- .../Jobs/PlexAvailabilityChecker.cs | 9 +- PlexRequests.Services/Jobs/RecentlyAdded.cs | 38 ++++---- PlexRequests.UI/Jobs/Scheduler.cs | 4 +- PlexRequests.UI/Startup.cs | 3 - .../Views/Admin/SchedulerSettings.cshtml | 2 +- 10 files changed, 181 insertions(+), 125 deletions(-) rename PlexRequests.Api.Models/Plex/{RecentlyAdded.cs => RecentlyAddedModel.cs} (56%) 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.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? viewCount { get; set; } - public int? lastViewedAt { get; set; } public int? year { get; set; } - public int? duration { get; set; } + public string parentThumb { get; set; } + public string grandparentTheme { 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? viewCount { get; set; } + public int? lastViewedAt { 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/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-Container-Start", from.ToString()); + request.AddHeader("X-Plex-Container-Size", to.ToString()); + } + + private void AddHeaders(ref RestRequest request, string authToken, bool json) { request.AddHeader("X-Plex-Token", authToken); - AddHeaders(ref request); + AddHeaders(ref request, json); } - private void AddHeaders(ref RestRequest request) + 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.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.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs index 701a5fb50..fc7c38778 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) { if (seasons.Any(season => show.Seasons.Contains(season))) { diff --git a/PlexRequests.Services/Jobs/RecentlyAdded.cs b/PlexRequests.Services/Jobs/RecentlyAdded.cs index 869bac323..13cf02e51 100644 --- a/PlexRequests.Services/Jobs/RecentlyAdded.cs +++ b/PlexRequests.Services/Jobs/RecentlyAdded.cs @@ -2,7 +2,7 @@ // /************************************************************************ // 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 @@ -114,7 +114,7 @@ namespace PlexRequests.Services.Jobs public void Test() { - StartDb(true); + Start(true); } private void Start(bool testEmail = false) @@ -122,18 +122,15 @@ namespace PlexRequests.Services.Jobs 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, ref sb); + GenerateTvHtml(recentlyAddedTv, plexSettings, ref sb); var template = new RecentlyAddedTemplate(); var html = template.LoadTemplate(sb.ToString()); @@ -160,12 +157,12 @@ namespace PlexRequests.Services.Jobs Send(html, plexSettings, testEmail); } - private void GenerateMovieHtml(IEnumerable movies, PlexSettings plexSettings, ref StringBuilder sb) + private void GenerateMovieHtml(RecentlyAddedModel movies, PlexSettings plexSettings, ref StringBuilder sb) { sb.Append("

New Movies:



"); sb.Append( ""); - foreach (var movie in movies.OrderByDescending(x => x.addedAt)) + foreach (var movie in movies._children.OrderByDescending(x => x.addedAt.UnixTimeStampToDateTime())) { var plexGUID = string.Empty; try @@ -264,15 +261,14 @@ namespace PlexRequests.Services.Jobs } sb.Append("


"); } - - [Obsolete("Use the new DB Version")] - private void GenerateTvHtml(IEnumerable tv, PlexSettings plexSettings, ref StringBuilder sb) + + private void GenerateTvHtml(RecentlyAddedModel tv, PlexSettings plexSettings, ref StringBuilder sb) { // TV sb.Append("

New Episodes:



"); sb.Append( ""); - foreach (var t in tv.OrderByDescending(x => x.addedAt)) + foreach (var t in tv._children.OrderByDescending(x => x.addedAt.UnixTimeStampToDateTime())) { var plexGUID = string.Empty; try @@ -298,12 +294,14 @@ namespace PlexRequests.Services.Jobs sb.Append(""); sb.Append("
"); + var title = $"{t.grandparentTitle} - {t.title} {t.originallyAvailableAt.Substring(0, 4)}"; + sb.AppendFormat("

{1} {2}

", - info.externals.imdb, info.name, info.premiered.Substring(0, 4)); // Only the year + info.externals.imdb, title); // 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 + string.IsNullOrEmpty(t.summary) ? info.summary : t.summary); // Episode Summary sb.Append(""); diff --git a/PlexRequests.UI/Jobs/Scheduler.cs b/PlexRequests.UI/Jobs/Scheduler.cs index 6c52832e1..b969c97cb 100644 --- a/PlexRequests.UI/Jobs/Scheduler.cs +++ b/PlexRequests.UI/Jobs/Scheduler.cs @@ -66,7 +66,7 @@ namespace PlexRequests.UI.Jobs JobBuilder.Create().WithIdentity("StoreBackup", "Database").Build(), JobBuilder.Create().WithIdentity("StoreCleanup", "Database").Build(), JobBuilder.Create().WithIdentity("UserRequestLimiter", "Request").Build(), - JobBuilder.Create().WithIdentity("RecentlyAdded", "Email").Build() + JobBuilder.Create().WithIdentity("RecentlyAddedModel", "Email").Build() }; @@ -168,7 +168,7 @@ namespace PlexRequests.UI.Jobs var rencentlyAdded = TriggerBuilder.Create() - .WithIdentity("RecentlyAdded", "Email") + .WithIdentity("RecentlyAddedModel", "Email") .StartNow() .WithSimpleSchedule(x => x.WithIntervalInHours(2).RepeatForever()) .Build(); diff --git a/PlexRequests.UI/Startup.cs b/PlexRequests.UI/Startup.cs index 59d26e58f..c61f8766b 100644 --- a/PlexRequests.UI/Startup.cs +++ b/PlexRequests.UI/Startup.cs @@ -67,9 +67,6 @@ namespace PlexRequests.UI Debug.WriteLine("Finished bootstrapper"); var scheduler = new Scheduler(); scheduler.StartScheduler(); - - var r = kernel.Get(); - r.Test(); } catch (Exception exception) { diff --git a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml index 4621981e7..9c7b8b862 100644 --- a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml +++ b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml @@ -82,7 +82,7 @@
- +