From 3650c4f943848f645aefd7a90f50163614e297fc Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 24 Jun 2016 15:33:38 +0100 Subject: [PATCH] Finished #6 --- .../SettingModels/PlexRequestSettings.cs | 11 +- .../SettingModels/ScheduledJobsSettings.cs | 2 + PlexRequests.Core/Setup.cs | 1 - PlexRequests.Services/Jobs/JobNames.cs | 1 + .../Jobs/UserRequestLimitResetter.cs | 119 ++++++++ .../PlexRequests.Services.csproj | 277 +++++++++--------- PlexRequests.Store/Models/RequestLimit.cs | 41 +++ PlexRequests.Store/PlexRequests.Store.csproj | 249 ++++++++-------- PlexRequests.Store/SqlTables.sql | 178 +++++------ PlexRequests.UI/Bootstrapper.cs | 1 + PlexRequests.UI/Jobs/Scheduler.cs | 10 + PlexRequests.UI/Modules/SearchModule.cs | 267 +++++++---------- .../Views/Admin/SchedulerSettings.cshtml | 9 + PlexRequests.UI/Views/Admin/Settings.cshtml | 168 ++++++----- 14 files changed, 758 insertions(+), 576 deletions(-) create mode 100644 PlexRequests.Services/Jobs/UserRequestLimitResetter.cs create mode 100644 PlexRequests.Store/Models/RequestLimit.cs diff --git a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs index 4e986c555..6e65951d1 100644 --- a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs @@ -32,6 +32,13 @@ namespace PlexRequests.Core.SettingModels { public class PlexRequestSettings : Settings { + public PlexRequestSettings() + { + TvWeeklyRequestLimit = 0; + MovieWeeklyRequestLimit = 0; + AlbumWeeklyRequestLimit = 0; + } + public int Port { get; set; } public string BaseUrl { get; set; } public bool SearchForMovies { get; set; } @@ -42,7 +49,9 @@ namespace PlexRequests.Core.SettingModels public bool RequireMusicApproval { get; set; } public bool UsersCanViewOnlyOwnRequests { get; set; } public bool UsersCanViewOnlyOwnIssues { get; set; } - public int WeeklyRequestLimit { get; set; } + public int MovieWeeklyRequestLimit { get; set; } + public int TvWeeklyRequestLimit { get; set; } + public int AlbumWeeklyRequestLimit { get; set; } public string NoApprovalUsers { get; set; } public bool CollectAnalyticData { get; set; } public bool IgnoreNotifyForAutoApprovedRequests { get; set; } diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index 216f54f88..6d895f6ef 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -36,6 +36,7 @@ namespace PlexRequests.Core.SettingModels CouchPotatoCacher = 10; StoreBackup = 24; StoreCleanup = 24; + UserRequestLimitResetter = 12; } public int PlexAvailabilityChecker { get; set; } public int SickRageCacher { get; set; } @@ -43,5 +44,6 @@ namespace PlexRequests.Core.SettingModels public int CouchPotatoCacher { get; set; } public int StoreBackup { get; set; } public int StoreCleanup { get; set; } + public int UserRequestLimitResetter { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Core/Setup.cs b/PlexRequests.Core/Setup.cs index e66a15cec..a3a407aa1 100644 --- a/PlexRequests.Core/Setup.cs +++ b/PlexRequests.Core/Setup.cs @@ -100,7 +100,6 @@ namespace PlexRequests.Core RequireMovieApproval = true, SearchForMovies = true, SearchForTvShows = true, - WeeklyRequestLimit = 0, BaseUrl = baseUrl ?? string.Empty, CollectAnalyticData = true, }; diff --git a/PlexRequests.Services/Jobs/JobNames.cs b/PlexRequests.Services/Jobs/JobNames.cs index ff6791d92..8832d2c2b 100644 --- a/PlexRequests.Services/Jobs/JobNames.cs +++ b/PlexRequests.Services/Jobs/JobNames.cs @@ -34,5 +34,6 @@ namespace PlexRequests.Services.Jobs public const string SrCacher = "SickRage Cacher"; public const string PlexChecker = "Plex Availability Cacher"; public const string StoreCleanup = "Database Cleanup"; + public const string RequestLimitReset = "Request Limit Reset"; } } \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/UserRequestLimitResetter.cs b/PlexRequests.Services/Jobs/UserRequestLimitResetter.cs new file mode 100644 index 000000000..13bd34463 --- /dev/null +++ b/PlexRequests.Services/Jobs/UserRequestLimitResetter.cs @@ -0,0 +1,119 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserRequestLimitResetter.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 NLog; + +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Services.Interfaces; +using PlexRequests.Store; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; + +using Quartz; + +namespace PlexRequests.Services.Jobs +{ + public class UserRequestLimitResetter : IJob + { + public UserRequestLimitResetter(IJobRecord record, IRepository repo, ISettingsService settings) + { + Record = record; + Repo = repo; + Settings = settings; + } + private IJobRecord Record { get; } + private IRepository Repo { get; } + private ISettingsService Settings { get; } + + private static Logger Log = LogManager.GetCurrentClassLogger(); + + public void Execute(IJobExecutionContext context) + { + try + { + var settings = Settings.GetSettings(); + var users = Repo.GetAll(); + var requestLimits = users as RequestLimit[] ?? users.ToArray(); + + MovieLimit(settings, requestLimits); + TvLimit(settings, requestLimits); + AlbumLimit(settings, requestLimits); + } + catch (Exception e) + { + Log.Error(e); + } + finally + { + Record.Record(JobNames.RequestLimitReset); + } + } + + public void MovieLimit(PlexRequestSettings s, IEnumerable allUsers) + { + if (s.MovieWeeklyRequestLimit == 0) + { + return; // The limit has not been set + } + CheckAndDelete(allUsers, RequestType.Movie); + } + + public void TvLimit(PlexRequestSettings s, IEnumerable allUsers) + { + if (s.TvWeeklyRequestLimit == 0) + { + return; // The limit has not been set + } + CheckAndDelete(allUsers, RequestType.TvShow); + } + + public void AlbumLimit(PlexRequestSettings s, IEnumerable allUsers) + { + if (s.AlbumWeeklyRequestLimit == 0) + { + return; // The limit has not been set + } + CheckAndDelete(allUsers, RequestType.Album); + } + + private void CheckAndDelete(IEnumerable allUsers, RequestType type) + { + var users = allUsers.Where(x => x.RequestType == type); + foreach (var u in users) + { + if (u.FirstRequestDate > DateTime.UtcNow.AddDays(-7)) + { + Repo.Delete(u); + } + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index fc61aab55..bd385cba3 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -1,139 +1,140 @@ - - - - - Debug - AnyCPU - {566EFA49-68F8-4716-9693-A6B3F2624DEA} - Library - Properties - PlexRequests.Services - PlexRequests.Services - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - ..\packages\NLog.4.3.4\lib\net45\NLog.dll - True - - - - - - - - - - - - - - ..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll - - - ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll - - - ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll - - - ..\packages\MailKit.1.2.21\lib\net451\MailKit.dll - - - ..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll - - - ..\Assemblies\Mono.Data.Sqlite.dll - - - ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {95834072-A675-415D-AA8F-877C91623810} - PlexRequests.Api.Interfaces - - - {CB37A5F8-6DFC-4554-99D3-A42B502E4591} - PlexRequests.Api.Models - - - {8CB8D235-2674-442D-9C6A-35FCAEEB160D} - PlexRequests.Api - - - {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} - PlexRequests.Core - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - {92433867-2B7B-477B-A566-96C382427525} - PlexRequests.Store - - - - + + + + + Debug + AnyCPU + {566EFA49-68F8-4716-9693-A6B3F2624DEA} + Library + Properties + PlexRequests.Services + PlexRequests.Services + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\NLog.4.3.4\lib\net45\NLog.dll + True + + + + + + + + + + + + + + ..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll + + + ..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll + + + ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll + + + ..\packages\MailKit.1.2.21\lib\net451\MailKit.dll + + + ..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {95834072-A675-415D-AA8F-877C91623810} + PlexRequests.Api.Interfaces + + + {CB37A5F8-6DFC-4554-99D3-A42B502E4591} + PlexRequests.Api.Models + + + {8CB8D235-2674-442D-9C6A-35FCAEEB160D} + PlexRequests.Api + + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + + + + \ No newline at end of file diff --git a/PlexRequests.Store/Models/RequestLimit.cs b/PlexRequests.Store/Models/RequestLimit.cs new file mode 100644 index 000000000..1c86986c6 --- /dev/null +++ b/PlexRequests.Store/Models/RequestLimit.cs @@ -0,0 +1,41 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UsersToNotify.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System; + +using Dapper.Contrib.Extensions; + +namespace PlexRequests.Store.Models +{ + [Table("RequestLimit")] + public class RequestLimit : Entity + { + public string Username { get; set; } + public DateTime FirstRequestDate { get; set; } + public int RequestCount { get; set; } + public RequestType RequestType { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Store/PlexRequests.Store.csproj b/PlexRequests.Store/PlexRequests.Store.csproj index 335ea07db..196a48b4b 100644 --- a/PlexRequests.Store/PlexRequests.Store.csproj +++ b/PlexRequests.Store/PlexRequests.Store.csproj @@ -1,125 +1,126 @@ - - - - - Debug - AnyCPU - {92433867-2B7B-477B-A566-96C382427525} - Library - Properties - PlexRequests.Store - PlexRequests.Store - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll - True - - - ..\packages\Dapper.Contrib.1.50.0-beta8\lib\net45\Dapper.Contrib.dll - True - - - ..\Assemblies\Mono.Data.Sqlite.dll - - - ..\packages\NLog.4.3.4\lib\net45\NLog.dll - True - - - - - - - - - - - - - ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Sql.resx - - - - - - - - Always - - - Always - - - - - ResXFileCodeGenerator - Sql.Designer.cs - - - - - - - - {1252336D-42A3-482A-804C-836E60173DFA} - PlexRequests.Helpers - - - - + + + + + Debug + AnyCPU + {92433867-2B7B-477B-A566-96C382427525} + Library + Properties + PlexRequests.Store + PlexRequests.Store + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll + True + + + ..\packages\Dapper.Contrib.1.50.0-beta8\lib\net45\Dapper.Contrib.dll + True + + + ..\Assemblies\Mono.Data.Sqlite.dll + + + ..\packages\NLog.4.3.4\lib\net45\NLog.dll + True + + + + + + + + + + + + + ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Sql.resx + + + + + + + + Always + + + Always + + + + + ResXFileCodeGenerator + Sql.Designer.cs + + + + + + + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + + + + \ No newline at end of file diff --git a/PlexRequests.Store/SqlTables.sql b/PlexRequests.Store/SqlTables.sql index 6e89f589c..478599545 100644 --- a/PlexRequests.Store/SqlTables.sql +++ b/PlexRequests.Store/SqlTables.sql @@ -1,84 +1,94 @@ ---Any DB changes need to be made in this file. - -CREATE TABLE IF NOT EXISTS Users -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - UserGuid varchar(50) NOT NULL , - UserName varchar(50) NOT NULL, - Salt BLOB NOT NULL, - Hash BLOB NOT NULL, - Claims BLOB NOT NULL, - UserProperties BLOB -); - - -CREATE TABLE IF NOT EXISTS GlobalSettings -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - SettingsName varchar(50) NOT NULL, - Content varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS GlobalSettings_Id ON GlobalSettings (Id); - -CREATE TABLE IF NOT EXISTS RequestBlobs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - ProviderId INTEGER NOT NULL, - Type INTEGER NOT NULL, - Content BLOB NOT NULL, - MusicId TEXT -); -CREATE UNIQUE INDEX IF NOT EXISTS RequestBlobs_Id ON RequestBlobs (Id); - -CREATE TABLE IF NOT EXISTS Logs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Date varchar(100) NOT NULL, - Level varchar(100) NOT NULL, - Logger varchar(100) NOT NULL, - Message varchar(100) NOT NULL, - CallSite varchar(100) NOT NULL, - Exception varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id); - -CREATE TABLE IF NOT EXISTS Audit -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Date varchar(100) NOT NULL, - Username varchar(100) NOT NULL, - ChangeType varchar(100) NOT NULL, - OldValue varchar(100), - NewValue varchar(100) -); -CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id); - - -CREATE TABLE IF NOT EXISTS DBInfo -( - SchemaVersion INTEGER -); - -CREATE TABLE IF NOT EXISTS ScheduledJobs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Name varchar(100) NOT NULL, - LastRun varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id); - -CREATE TABLE IF NOT EXISTS UsersToNotify -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - Username varchar(100) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id); - -CREATE TABLE IF NOT EXISTS IssueBlobs -( - Id INTEGER PRIMARY KEY AUTOINCREMENT, - RequestId INTEGER, - Type INTEGER NOT NULL, - Content BLOB NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS IssueBlobs_Id ON IssueBlobs (Id); \ No newline at end of file +--Any DB changes need to be made in this file. + +CREATE TABLE IF NOT EXISTS Users +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + UserGuid varchar(50) NOT NULL , + UserName varchar(50) NOT NULL, + Salt BLOB NOT NULL, + Hash BLOB NOT NULL, + Claims BLOB NOT NULL, + UserProperties BLOB +); + + +CREATE TABLE IF NOT EXISTS GlobalSettings +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + SettingsName varchar(50) NOT NULL, + Content varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS GlobalSettings_Id ON GlobalSettings (Id); + +CREATE TABLE IF NOT EXISTS RequestBlobs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + ProviderId INTEGER NOT NULL, + Type INTEGER NOT NULL, + Content BLOB NOT NULL, + MusicId TEXT +); +CREATE UNIQUE INDEX IF NOT EXISTS RequestBlobs_Id ON RequestBlobs (Id); + +CREATE TABLE IF NOT EXISTS Logs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Date varchar(100) NOT NULL, + Level varchar(100) NOT NULL, + Logger varchar(100) NOT NULL, + Message varchar(100) NOT NULL, + CallSite varchar(100) NOT NULL, + Exception varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id); + +CREATE TABLE IF NOT EXISTS Audit +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Date varchar(100) NOT NULL, + Username varchar(100) NOT NULL, + ChangeType varchar(100) NOT NULL, + OldValue varchar(100), + NewValue varchar(100) +); +CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id); + + +CREATE TABLE IF NOT EXISTS DBInfo +( + SchemaVersion INTEGER +); + +CREATE TABLE IF NOT EXISTS ScheduledJobs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Name varchar(100) NOT NULL, + LastRun varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id); + +CREATE TABLE IF NOT EXISTS UsersToNotify +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Username varchar(100) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id); + +CREATE TABLE IF NOT EXISTS IssueBlobs +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + RequestId INTEGER, + Type INTEGER NOT NULL, + Content BLOB NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS IssueBlobs_Id ON IssueBlobs (Id); + +CREATE TABLE IF NOT EXISTS RequestLimit +( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + Username varchar(100) NOT NULL, + FirstRequestDate varchar(100) NOT NULL, + RequestCount INTEGER NOT NULL, + RequestType INTEGER NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS RequestLimit_Id ON RequestLimit (Id); \ No newline at end of file diff --git a/PlexRequests.UI/Bootstrapper.cs b/PlexRequests.UI/Bootstrapper.cs index 8a1df5539..2e74e0900 100644 --- a/PlexRequests.UI/Bootstrapper.cs +++ b/PlexRequests.UI/Bootstrapper.cs @@ -193,6 +193,7 @@ namespace PlexRequests.UI container.Register, GenericRepository>(); container.Register, GenericRepository>(); container.Register, GenericRepository>(); + container.Register, GenericRepository>(); container.Register(); container.Register(); container.Register(); diff --git a/PlexRequests.UI/Jobs/Scheduler.cs b/PlexRequests.UI/Jobs/Scheduler.cs index dc17a07d5..6e151f2bf 100644 --- a/PlexRequests.UI/Jobs/Scheduler.cs +++ b/PlexRequests.UI/Jobs/Scheduler.cs @@ -62,6 +62,7 @@ namespace PlexRequests.UI.Jobs var cp = JobBuilder.Create().WithIdentity("CouchPotatoCacher", "Cache").Build(); var store = JobBuilder.Create().WithIdentity("StoreBackup", "Database").Build(); var storeClean = JobBuilder.Create().WithIdentity("StoreCleanup", "Database").Build(); + var userRequestLimitReset = JobBuilder.Create().WithIdentity("UserRequestLimiter", "Request").Build(); jobs.Add(plex); jobs.Add(sickrage); @@ -69,6 +70,7 @@ namespace PlexRequests.UI.Jobs jobs.Add(cp); jobs.Add(store); jobs.Add(storeClean); + jobs.Add(userRequestLimitReset); return jobs; } @@ -150,6 +152,13 @@ namespace PlexRequests.UI.Jobs .WithSimpleSchedule(x => x.WithIntervalInHours(s.StoreCleanup).RepeatForever()) .Build(); + var userRequestLimiter = + TriggerBuilder.Create() + .WithIdentity("UserRequestLimiter", "Request") + .StartAt(DateTimeOffset.Now.AddMinutes(5)) // Everything has started on application start, lets wait 5 minutes + .WithSimpleSchedule(x => x.WithIntervalInHours(s.UserRequestLimitResetter).RepeatForever()) + .Build(); + triggers.Add(plexAvailabilityChecker); triggers.Add(srCacher); @@ -157,6 +166,7 @@ namespace PlexRequests.UI.Jobs triggers.Add(cpCacher); triggers.Add(storeBackup); triggers.Add(storeCleanup); + triggers.Add(userRequestLimiter); return triggers; } diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index ddb9f31e1..1a5855569 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -72,7 +72,7 @@ namespace PlexRequests.UI.Modules INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi, ISettingsService hpService, ICouchPotatoCacher cpCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi, ISettingsService plexService, ISettingsService auth, IRepository u, ISettingsService email, - IIssueService issue, IAnalytics a) : base("search", prSettings) + IIssueService issue, IAnalytics a, IRepository rl) : base("search", prSettings) { Auth = auth; PlexService = plexService; @@ -99,6 +99,7 @@ namespace PlexRequests.UI.Modules EmailNotificationSettings = email; IssueService = issue; Analytics = a; + RequestLimitRepo = rl; Get["/", true] = async (x, ct) => await RequestLoad(); @@ -145,6 +146,7 @@ namespace PlexRequests.UI.Modules private IRepository UsersToNotifyRepo { get; } private IIssueService IssueService { get; } private IAnalytics Analytics { get; } + private IRepository RequestLimitRepo { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); private async Task RequestLoad() @@ -423,13 +425,17 @@ namespace PlexRequests.UI.Modules private async Task RequestMovie(int movieId) { + var settings = await PrService.GetSettingsAsync(); + if (!await CheckRequestLimit(settings, RequestType.Movie)) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "You have reached your weekly request limit for Movies! Please contact your admin." }); + } + await Analytics.TrackEventAsync(Category.Search, Action.Request, "Movie", Username, CookieHelper.GetAnalyticClientId(Cookies)); var movieInfo = MovieApi.GetMovieInformation(movieId).Result; var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}"; Log.Trace("Getting movie info from TheMovieDb"); - var settings = await PrService.GetSettingsAsync(); - // check if the movie has already been requested Log.Info("Requesting movie with id {0}", movieId); var existingRequest = await RequestService.CheckRequestAsync(movieId); @@ -491,64 +497,23 @@ namespace PlexRequests.UI.Modules Log.Debug("Adding movie to CP result {0}", result); if (result) { - model.Approved = true; - Log.Info("Adding movie to database (No approval required)"); - await RequestService.AddRequestAsync(model); - - if (ShouldSendNotification(RequestType.Movie, settings)) - { - var notificationModel = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Movie - }; - await NotificationService.Publish(notificationModel); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullMovieName} was successfully added!"); } - return - Response.AsJson(new JsonResponseModel - { - Result = false, - Message = - "Something went wrong adding the movie to CouchPotato! Please check your settings." - }); - } - else - { - model.Approved = true; - Log.Info("Adding movie to database (No approval required)"); - await RequestService.AddRequestAsync(model); - if (ShouldSendNotification(RequestType.Movie, settings)) + return Response.AsJson(new JsonResponseModel { - var notificationModel = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Movie - }; - await NotificationService.Publish(notificationModel); - } - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); + Result = false, + Message = + "Something went wrong adding the movie to CouchPotato! Please check your settings." + }); } + + return await AddRequest(model, settings, $"{fullMovieName} was successfully added!"); } try { - Log.Info("Adding movie to database"); - var id = await RequestService.AddRequestAsync(model); - - var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest, RequestType = RequestType.Movie }; - await NotificationService.Publish(notificationModel); - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullMovieName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullMovieName} was successfully added!"); } catch (Exception e) { @@ -566,6 +531,11 @@ namespace PlexRequests.UI.Modules /// private async Task RequestTvShow(int showId, string seasons) { + var settings = await PrService.GetSettingsAsync(); + if (!await CheckRequestLimit(settings, RequestType.TvShow)) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "You have reached your weekly request limit for TV Shows! Please contact your admin." }); + } await Analytics.TrackEventAsync(Category.Search, Action.Request, "TvShow", Username, CookieHelper.GetAnalyticClientId(Cookies)); var tvApi = new TvMazeApi(); @@ -575,7 +545,6 @@ namespace PlexRequests.UI.Modules string fullShowName = $"{showInfo.name} ({firstAir.Year})"; //#if !DEBUG - var settings = await PrService.GetSettingsAsync(); // check if the show has already been requested Log.Info("Requesting tv show with id {0}", showId); @@ -669,28 +638,10 @@ namespace PlexRequests.UI.Modules var result = sender.SendToSonarr(sonarrSettings, model); if (!string.IsNullOrEmpty(result?.title)) { - model.Approved = true; - Log.Debug("Adding tv to database requests (No approval required & Sonarr)"); - await RequestService.AddRequestAsync(model); - - if (ShouldSendNotification(RequestType.TvShow, settings)) - { - var notify1 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.TvShow - }; - await NotificationService.Publish(notify1); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} was successfully added!"); } - return Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages)); - } var srSettings = SickRageService.GetSettings(); @@ -699,57 +650,20 @@ namespace PlexRequests.UI.Modules var result = sender.SendToSickRage(srSettings, model); if (result?.result == "success") { - model.Approved = true; - Log.Debug("Adding tv to database requests (No approval required & SickRage)"); - await RequestService.AddRequestAsync(model); - if (ShouldSendNotification(RequestType.TvShow, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.TvShow - }; - await NotificationService.Publish(notify2); - } - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} was successfully added!"); } return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "Message From SickRage: " + result.message : "Something went wrong adding the movie to SickRage! Please check your settings." }); } if (!srSettings.Enabled && !sonarrSettings.Enabled) { - model.Approved = true; - Log.Debug("Adding tv to database requests (No approval required) and Sonarr/Sickrage not setup"); - await RequestService.AddRequestAsync(model); - if (ShouldSendNotification(RequestType.TvShow, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.TvShow - }; - await NotificationService.Publish(notify2); - } - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} was successfully added!"); } return Response.AsJson(new JsonResponseModel { Result = false, Message = "The request of TV Shows is not correctly set up. Please contact your admin." }); } - - await RequestService.AddRequestAsync(model); - - var notificationModel = new NotificationModel { Title = model.Title, User = Username, DateTime = DateTime.Now, NotificationType = NotificationType.NewRequest, RequestType = RequestType.TvShow }; - await NotificationService.Publish(notificationModel); - - return Response.AsJson(new JsonResponseModel { Result = true, Message = $"{fullShowName} was successfully added!" }); + return await AddRequest(model, settings, $"{fullShowName} was successfully added!"); } private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings) @@ -770,8 +684,12 @@ namespace PlexRequests.UI.Modules private async Task RequestAlbum(string releaseId) { - await Analytics.TrackEventAsync(Category.Search, Action.Request, "Album", Username, CookieHelper.GetAnalyticClientId(Cookies)); var settings = await PrService.GetSettingsAsync(); + if (!await CheckRequestLimit(settings, RequestType.Album)) + { + return Response.AsJson(new JsonResponseModel { Result = false, Message = "You have reached your weekly request limit for Albums! Please contact your admin." }); + } + await Analytics.TrackEventAsync(Category.Search, Action.Request, "Album", Username, CookieHelper.GetAnalyticClientId(Cookies)); var existingRequest = await RequestService.CheckRequestAsync(releaseId); Log.Debug("Checking for an existing request"); @@ -849,48 +767,10 @@ namespace PlexRequests.UI.Modules var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService); await sender.AddAlbum(model); - model.Approved = true; - await RequestService.AddRequestAsync(model); - - if (ShouldSendNotification(RequestType.Album, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Album - }; - await NotificationService.Publish(notify2); - } - - return - Response.AsJson(new JsonResponseModel - { - Result = true, - Message = $"{model.Title} was successfully added!" - }); + return await AddRequest(model, settings, $"{model.Title} was successfully added!"); } - if (ShouldSendNotification(RequestType.Album, settings)) - { - var notify2 = new NotificationModel - { - Title = model.Title, - User = Username, - DateTime = DateTime.Now, - NotificationType = NotificationType.NewRequest, - RequestType = RequestType.Album - }; - await NotificationService.Publish(notify2); - } - await RequestService.AddRequestAsync(model); - return Response.AsJson(new JsonResponseModel - { - Result = true, - Message = $"{model.Title} was successfully added!" - }); + return await AddRequest(model, settings, $"{model.Title} was successfully added!"); } private string GetMusicBrainzCoverArt(string id) @@ -994,5 +874,84 @@ namespace PlexRequests.UI.Modules var model = seasons.Select(x => x.number); return Response.AsJson(model); } + + private async Task CheckRequestLimit(PlexRequestSettings s, RequestType type) + { + var requestLimit = GetRequestLimitForType(type, s); + if (requestLimit == 0) + { + return true; + } + + var limit = await RequestLimitRepo.GetAllAsync(); + var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == type); + if (usersLimit == null) + { + // Have not set a requestLimit yet + return true; + } + + return usersLimit.RequestCount >= requestLimit; + } + + private int GetRequestLimitForType(RequestType type, PlexRequestSettings s) + { + int requestLimit; + switch (type) + { + case RequestType.Movie: + requestLimit = s.MovieWeeklyRequestLimit; + break; + case RequestType.TvShow: + requestLimit = s.TvWeeklyRequestLimit; + break; + case RequestType.Album: + requestLimit = s.AlbumWeeklyRequestLimit; + break; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + return requestLimit; + } + + private async Task AddRequest(RequestedModel model, PlexRequestSettings settings, string message) + { + + model.Approved = true; + await RequestService.AddRequestAsync(model); + + if (ShouldSendNotification(RequestType.Movie, settings)) + { + var notificationModel = new NotificationModel + { + Title = model.Title, + User = Username, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = RequestType.Movie + }; + await NotificationService.Publish(notificationModel); + } + + var limit = await RequestLimitRepo.GetAllAsync(); + var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type); + if (usersLimit == null) + { + await RequestLimitRepo.InsertAsync(new RequestLimit + { + Username = Username, + RequestType = model.Type, + FirstRequestDate = DateTime.UtcNow, + RequestCount = 1 + }); + } + else + { + usersLimit.RequestCount++; + await RequestLimitRepo.UpdateAsync(usersLimit); + } + + return Response.AsJson(new JsonResponseModel { Result = true, Message = message }); + } } } diff --git a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml index 37e85e772..db4ed7c36 100644 --- a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml +++ b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml @@ -61,6 +61,15 @@ + + 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. +
+ +
+ +
+
+
diff --git a/PlexRequests.UI/Views/Admin/Settings.cshtml b/PlexRequests.UI/Views/Admin/Settings.cshtml index 91532b433..9f1a664e9 100644 --- a/PlexRequests.UI/Views/Admin/Settings.cshtml +++ b/PlexRequests.UI/Views/Admin/Settings.cshtml @@ -77,87 +77,87 @@
- - @if (Model.SearchForMovies) - { - - } - else - { - - } - + + @if (Model.SearchForMovies) + { + + } + else + { + + } +
- @if (Model.SearchForTvShows) - { - - } - else - { - - } - + @if (Model.SearchForTvShows) + { + + } + else + { + + } +
- - @if (Model.SearchForMusic) - { - - } - else - { - - } - + + @if (Model.SearchForMusic) + { + + } + else + { + + } +
- - @if (Model.RequireMovieApproval) - { - - } - else - { - - } -
+ + @if (Model.RequireMovieApproval) + { + + } + else + { + + } +
- - @if (Model.RequireTvShowApproval) - { - - } - else - { - - } - + + @if (Model.RequireTvShowApproval) + { + + } + else + { + + } +
- - @if (Model.RequireMusicApproval) - { - - } - else - { - - } - + + @if (Model.RequireMusicApproval) + { + + } + else + { + + } +
@@ -234,16 +234,36 @@
- @*
- + +

If the request limits are set to 0 then no request limit is applied.

+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
-
//TODO: Need to implement this*@ +
-
+
@@ -255,9 +275,9 @@
\ No newline at end of file