This commit is contained in:
tidusjar 2016-06-24 15:33:38 +01:00
parent f0fb065237
commit 3650c4f943
14 changed files with 769 additions and 587 deletions

View file

@ -32,6 +32,13 @@ namespace PlexRequests.Core.SettingModels
{ {
public class PlexRequestSettings : Settings public class PlexRequestSettings : Settings
{ {
public PlexRequestSettings()
{
TvWeeklyRequestLimit = 0;
MovieWeeklyRequestLimit = 0;
AlbumWeeklyRequestLimit = 0;
}
public int Port { get; set; } public int Port { get; set; }
public string BaseUrl { get; set; } public string BaseUrl { get; set; }
public bool SearchForMovies { get; set; } public bool SearchForMovies { get; set; }
@ -42,7 +49,9 @@ namespace PlexRequests.Core.SettingModels
public bool RequireMusicApproval { get; set; } public bool RequireMusicApproval { get; set; }
public bool UsersCanViewOnlyOwnRequests { get; set; } public bool UsersCanViewOnlyOwnRequests { get; set; }
public bool UsersCanViewOnlyOwnIssues { 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 string NoApprovalUsers { get; set; }
public bool CollectAnalyticData { get; set; } public bool CollectAnalyticData { get; set; }
public bool IgnoreNotifyForAutoApprovedRequests { get; set; } public bool IgnoreNotifyForAutoApprovedRequests { get; set; }

View file

@ -36,6 +36,7 @@ namespace PlexRequests.Core.SettingModels
CouchPotatoCacher = 10; CouchPotatoCacher = 10;
StoreBackup = 24; StoreBackup = 24;
StoreCleanup = 24; StoreCleanup = 24;
UserRequestLimitResetter = 12;
} }
public int PlexAvailabilityChecker { get; set; } public int PlexAvailabilityChecker { get; set; }
public int SickRageCacher { get; set; } public int SickRageCacher { get; set; }
@ -43,5 +44,6 @@ namespace PlexRequests.Core.SettingModels
public int CouchPotatoCacher { get; set; } public int CouchPotatoCacher { get; set; }
public int StoreBackup { get; set; } public int StoreBackup { get; set; }
public int StoreCleanup { get; set; } public int StoreCleanup { get; set; }
public int UserRequestLimitResetter { get; set; }
} }
} }

View file

@ -100,7 +100,6 @@ namespace PlexRequests.Core
RequireMovieApproval = true, RequireMovieApproval = true,
SearchForMovies = true, SearchForMovies = true,
SearchForTvShows = true, SearchForTvShows = true,
WeeklyRequestLimit = 0,
BaseUrl = baseUrl ?? string.Empty, BaseUrl = baseUrl ?? string.Empty,
CollectAnalyticData = true, CollectAnalyticData = true,
}; };

View file

@ -34,5 +34,6 @@ namespace PlexRequests.Services.Jobs
public const string SrCacher = "SickRage Cacher"; public const string SrCacher = "SickRage Cacher";
public const string PlexChecker = "Plex Availability Cacher"; public const string PlexChecker = "Plex Availability Cacher";
public const string StoreCleanup = "Database Cleanup"; public const string StoreCleanup = "Database Cleanup";
public const string RequestLimitReset = "Request Limit Reset";
} }
} }

View file

@ -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<RequestLimit> repo, ISettingsService<PlexRequestSettings> settings)
{
Record = record;
Repo = repo;
Settings = settings;
}
private IJobRecord Record { get; }
private IRepository<RequestLimit> Repo { get; }
private ISettingsService<PlexRequestSettings> 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<RequestLimit> allUsers)
{
if (s.MovieWeeklyRequestLimit == 0)
{
return; // The limit has not been set
}
CheckAndDelete(allUsers, RequestType.Movie);
}
public void TvLimit(PlexRequestSettings s, IEnumerable<RequestLimit> allUsers)
{
if (s.TvWeeklyRequestLimit == 0)
{
return; // The limit has not been set
}
CheckAndDelete(allUsers, RequestType.TvShow);
}
public void AlbumLimit(PlexRequestSettings s, IEnumerable<RequestLimit> allUsers)
{
if (s.AlbumWeeklyRequestLimit == 0)
{
return; // The limit has not been set
}
CheckAndDelete(allUsers, RequestType.Album);
}
private void CheckAndDelete(IEnumerable<RequestLimit> 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);
}
}
}
}
}

View file

@ -1,139 +1,140 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{566EFA49-68F8-4716-9693-A6B3F2624DEA}</ProjectGuid> <ProjectGuid>{566EFA49-68F8-4716-9693-A6B3F2624DEA}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PlexRequests.Services</RootNamespace> <RootNamespace>PlexRequests.Services</RootNamespace>
<AssemblyName>PlexRequests.Services</AssemblyName> <AssemblyName>PlexRequests.Services</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.Build.Framework" /> <Reference Include="Microsoft.Build.Framework" />
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.4\lib\net45\NLog.dll</HintPath> <HintPath>..\packages\NLog.4.3.4\lib\net45\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Security" /> <Reference Include="System.Security" />
<Reference Include="System.Web" /> <Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="BouncyCastle, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942"> <Reference Include="BouncyCastle, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll</HintPath> <HintPath>..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll</HintPath>
</Reference> </Reference>
<Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e"> <Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
<HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath> <HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath>
</Reference> </Reference>
<Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e"> <Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath> <HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="MailKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b"> <Reference Include="MailKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b">
<HintPath>..\packages\MailKit.1.2.21\lib\net451\MailKit.dll</HintPath> <HintPath>..\packages\MailKit.1.2.21\lib\net451\MailKit.dll</HintPath>
</Reference> </Reference>
<Reference Include="MimeKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=bede1c8a46c66814"> <Reference Include="MimeKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=bede1c8a46c66814">
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll</HintPath> <HintPath>..\packages\MimeKit.1.2.22\lib\net45\MimeKit.dll</HintPath>
</Reference> </Reference>
<Reference Include="Mono.Data.Sqlite"> <Reference Include="Mono.Data.Sqlite">
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath> <HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
</Reference> </Reference>
<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>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Interfaces\IJobRecord.cs" /> <Compile Include="Interfaces\IJobRecord.cs" />
<Compile Include="Jobs\JobRecord.cs" /> <Compile Include="Jobs\JobRecord.cs" />
<Compile Include="Jobs\JobNames.cs" /> <Compile Include="Jobs\JobNames.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="Models\PlexAlbum.cs" /> <Compile Include="Jobs\UserRequestLimitResetter.cs" />
<Compile Include="Models\PlexMovie.cs" /> <Compile Include="Models\PlexAlbum.cs" />
<Compile Include="Models\PlexTvShow.cs" /> <Compile Include="Models\PlexMovie.cs" />
<Compile Include="Interfaces\ISickRageCacher.cs" /> <Compile Include="Models\PlexTvShow.cs" />
<Compile Include="Interfaces\ISonarrCacher.cs" /> <Compile Include="Interfaces\ISickRageCacher.cs" />
<Compile Include="Interfaces\ICouchPotatoCacher.cs" /> <Compile Include="Interfaces\ISonarrCacher.cs" />
<Compile Include="Interfaces\IAvailabilityChecker.cs" /> <Compile Include="Interfaces\ICouchPotatoCacher.cs" />
<Compile Include="Interfaces\IIntervals.cs" /> <Compile Include="Interfaces\IAvailabilityChecker.cs" />
<Compile Include="Interfaces\INotification.cs" /> <Compile Include="Interfaces\IIntervals.cs" />
<Compile Include="Interfaces\INotificationService.cs" /> <Compile Include="Interfaces\INotification.cs" />
<Compile Include="Notification\EmailMessageNotification.cs" /> <Compile Include="Interfaces\INotificationService.cs" />
<Compile Include="Notification\NotificationModel.cs" /> <Compile Include="Notification\EmailMessageNotification.cs" />
<Compile Include="Notification\NotificationService.cs" /> <Compile Include="Notification\NotificationModel.cs" />
<Compile Include="Notification\NotificationType.cs" /> <Compile Include="Notification\NotificationService.cs" />
<Compile Include="Notification\PushoverNotification.cs" /> <Compile Include="Notification\NotificationType.cs" />
<Compile Include="Notification\PushbulletNotification.cs" /> <Compile Include="Notification\PushoverNotification.cs" />
<Compile Include="Notification\SlackNotification.cs" /> <Compile Include="Notification\PushbulletNotification.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Notification\SlackNotification.cs" />
</ItemGroup> <Compile Include="Properties\AssemblyInfo.cs" />
<ItemGroup> </ItemGroup>
<None Include="app.config" /> <ItemGroup>
<None Include="packages.config" /> <None Include="app.config" />
</ItemGroup> <None Include="packages.config" />
<ItemGroup> </ItemGroup>
<ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj"> <ItemGroup>
<Project>{95834072-A675-415D-AA8F-877C91623810}</Project> <ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj">
<Name>PlexRequests.Api.Interfaces</Name> <Project>{95834072-A675-415D-AA8F-877C91623810}</Project>
</ProjectReference> <Name>PlexRequests.Api.Interfaces</Name>
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj"> </ProjectReference>
<Project>{CB37A5F8-6DFC-4554-99D3-A42B502E4591}</Project> <ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
<Name>PlexRequests.Api.Models</Name> <Project>{CB37A5F8-6DFC-4554-99D3-A42B502E4591}</Project>
</ProjectReference> <Name>PlexRequests.Api.Models</Name>
<ProjectReference Include="..\PlexRequests.Api\PlexRequests.Api.csproj"> </ProjectReference>
<Project>{8CB8D235-2674-442D-9C6A-35FCAEEB160D}</Project> <ProjectReference Include="..\PlexRequests.Api\PlexRequests.Api.csproj">
<Name>PlexRequests.Api</Name> <Project>{8CB8D235-2674-442D-9C6A-35FCAEEB160D}</Project>
</ProjectReference> <Name>PlexRequests.Api</Name>
<ProjectReference Include="..\PlexRequests.Core\PlexRequests.Core.csproj"> </ProjectReference>
<Project>{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}</Project> <ProjectReference Include="..\PlexRequests.Core\PlexRequests.Core.csproj">
<Name>PlexRequests.Core</Name> <Project>{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}</Project>
</ProjectReference> <Name>PlexRequests.Core</Name>
<ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj"> </ProjectReference>
<Project>{1252336D-42A3-482A-804C-836E60173DFA}</Project> <ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj">
<Name>PlexRequests.Helpers</Name> <Project>{1252336D-42A3-482A-804C-836E60173DFA}</Project>
</ProjectReference> <Name>PlexRequests.Helpers</Name>
<ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj"> </ProjectReference>
<Project>{92433867-2B7B-477B-A566-96C382427525}</Project> <ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj">
<Name>PlexRequests.Store</Name> <Project>{92433867-2B7B-477B-A566-96C382427525}</Project>
</ProjectReference> <Name>PlexRequests.Store</Name>
</ItemGroup> </ProjectReference>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Other similar extension points exist, see Microsoft.Common.targets. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
<Target Name="BeforeBuild"> Other similar extension points exist, see Microsoft.Common.targets.
</Target> <Target Name="BeforeBuild">
<Target Name="AfterBuild"> </Target>
</Target> <Target Name="AfterBuild">
--> </Target>
-->
</Project> </Project>

View file

@ -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; }
}
}

View file

@ -1,125 +1,126 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{92433867-2B7B-477B-A566-96C382427525}</ProjectGuid> <ProjectGuid>{92433867-2B7B-477B-A566-96C382427525}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PlexRequests.Store</RootNamespace> <RootNamespace>PlexRequests.Store</RootNamespace>
<AssemblyName>PlexRequests.Store</AssemblyName> <AssemblyName>PlexRequests.Store</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Dapper, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Dapper, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll</HintPath> <HintPath>..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Dapper.Contrib, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Dapper.Contrib, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.Contrib.1.50.0-beta8\lib\net45\Dapper.Contrib.dll</HintPath> <HintPath>..\packages\Dapper.Contrib.1.50.0-beta8\lib\net45\Dapper.Contrib.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Mono.Data.Sqlite"> <Reference Include="Mono.Data.Sqlite">
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath> <HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.4\lib\net45\NLog.dll</HintPath> <HintPath>..\packages\NLog.4.3.4\lib\net45\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data.Linq" /> <Reference Include="System.Data.Linq" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed"> <Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DbConfiguration.cs" /> <Compile Include="DbConfiguration.cs" />
<Compile Include="Entity.cs" /> <Compile Include="Entity.cs" />
<Compile Include="Models\IssueBlobs.cs" /> <Compile Include="Models\IssueBlobs.cs" />
<Compile Include="Models\ScheduledJobs.cs" /> <Compile Include="Models\ScheduledJobs.cs" />
<Compile Include="Models\UsersToNotify.cs" /> <Compile Include="Models\RequestLimit.cs" />
<Compile Include="Repository\BaseGenericRepository.cs" /> <Compile Include="Models\UsersToNotify.cs" />
<Compile Include="Repository\IRequestRepository.cs" /> <Compile Include="Repository\BaseGenericRepository.cs" />
<Compile Include="Repository\ISettingsRepository.cs" /> <Compile Include="Repository\IRequestRepository.cs" />
<Compile Include="ISqliteConfiguration.cs" /> <Compile Include="Repository\ISettingsRepository.cs" />
<Compile Include="Repository\IRepository.cs" /> <Compile Include="ISqliteConfiguration.cs" />
<Compile Include="Models\GlobalSettings.cs" /> <Compile Include="Repository\IRepository.cs" />
<Compile Include="Models\LogEntity.cs" /> <Compile Include="Models\GlobalSettings.cs" />
<Compile Include="Models\RequestBlobs.cs" /> <Compile Include="Models\LogEntity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Models\RequestBlobs.cs" />
<Compile Include="Repository\SettingsJsonRepository.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Repository\RequestJsonRepository.cs" /> <Compile Include="Repository\SettingsJsonRepository.cs" />
<Compile Include="Repository\GenericRepository.cs" /> <Compile Include="Repository\RequestJsonRepository.cs" />
<Compile Include="RequestedModel.cs" /> <Compile Include="Repository\GenericRepository.cs" />
<Compile Include="UserEntity.cs" /> <Compile Include="RequestedModel.cs" />
<Compile Include="UsersModel.cs" /> <Compile Include="UserEntity.cs" />
<Compile Include="UserRepository.cs" /> <Compile Include="UsersModel.cs" />
<Compile Include="Sql.Designer.cs"> <Compile Include="UserRepository.cs" />
<AutoGen>True</AutoGen> <Compile Include="Sql.Designer.cs">
<DesignTime>True</DesignTime> <AutoGen>True</AutoGen>
<DependentUpon>Sql.resx</DependentUpon> <DesignTime>True</DesignTime>
</Compile> <DependentUpon>Sql.resx</DependentUpon>
<Compile Include="TableCreation.cs" /> </Compile>
<Compile Include="Models\Audit.cs" /> <Compile Include="TableCreation.cs" />
</ItemGroup> <Compile Include="Models\Audit.cs" />
<ItemGroup> </ItemGroup>
<None Include="app.config" /> <ItemGroup>
<None Include="sqlite3.dll"> <None Include="app.config" />
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <None Include="sqlite3.dll">
</None> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
<Content Include="SqlTables.sql"> </None>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <Content Include="SqlTables.sql">
</Content> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</ItemGroup> </Content>
<ItemGroup> </ItemGroup>
<EmbeddedResource Include="Sql.resx"> <ItemGroup>
<Generator>ResXFileCodeGenerator</Generator> <EmbeddedResource Include="Sql.resx">
<LastGenOutput>Sql.Designer.cs</LastGenOutput> <Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource> <LastGenOutput>Sql.Designer.cs</LastGenOutput>
</ItemGroup> </EmbeddedResource>
<ItemGroup> </ItemGroup>
<None Include="packages.config" /> <ItemGroup>
</ItemGroup> <None Include="packages.config" />
<ItemGroup> </ItemGroup>
<ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj"> <ItemGroup>
<Project>{1252336D-42A3-482A-804C-836E60173DFA}</Project> <ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj">
<Name>PlexRequests.Helpers</Name> <Project>{1252336D-42A3-482A-804C-836E60173DFA}</Project>
</ProjectReference> <Name>PlexRequests.Helpers</Name>
</ItemGroup> </ProjectReference>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Other similar extension points exist, see Microsoft.Common.targets. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
<Target Name="BeforeBuild"> Other similar extension points exist, see Microsoft.Common.targets.
</Target> <Target Name="BeforeBuild">
<Target Name="AfterBuild"> </Target>
</Target> <Target Name="AfterBuild">
--> </Target>
-->
</Project> </Project>

View file

@ -1,84 +1,94 @@
--Any DB changes need to be made in this file. --Any DB changes need to be made in this file.
CREATE TABLE IF NOT EXISTS Users CREATE TABLE IF NOT EXISTS Users
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
UserGuid varchar(50) NOT NULL , UserGuid varchar(50) NOT NULL ,
UserName varchar(50) NOT NULL, UserName varchar(50) NOT NULL,
Salt BLOB NOT NULL, Salt BLOB NOT NULL,
Hash BLOB NOT NULL, Hash BLOB NOT NULL,
Claims BLOB NOT NULL, Claims BLOB NOT NULL,
UserProperties BLOB UserProperties BLOB
); );
CREATE TABLE IF NOT EXISTS GlobalSettings CREATE TABLE IF NOT EXISTS GlobalSettings
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
SettingsName varchar(50) NOT NULL, SettingsName varchar(50) NOT NULL,
Content varchar(100) NOT NULL Content varchar(100) NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS GlobalSettings_Id ON GlobalSettings (Id); CREATE UNIQUE INDEX IF NOT EXISTS GlobalSettings_Id ON GlobalSettings (Id);
CREATE TABLE IF NOT EXISTS RequestBlobs CREATE TABLE IF NOT EXISTS RequestBlobs
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
ProviderId INTEGER NOT NULL, ProviderId INTEGER NOT NULL,
Type INTEGER NOT NULL, Type INTEGER NOT NULL,
Content BLOB NOT NULL, Content BLOB NOT NULL,
MusicId TEXT MusicId TEXT
); );
CREATE UNIQUE INDEX IF NOT EXISTS RequestBlobs_Id ON RequestBlobs (Id); CREATE UNIQUE INDEX IF NOT EXISTS RequestBlobs_Id ON RequestBlobs (Id);
CREATE TABLE IF NOT EXISTS Logs CREATE TABLE IF NOT EXISTS Logs
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
Date varchar(100) NOT NULL, Date varchar(100) NOT NULL,
Level varchar(100) NOT NULL, Level varchar(100) NOT NULL,
Logger varchar(100) NOT NULL, Logger varchar(100) NOT NULL,
Message varchar(100) NOT NULL, Message varchar(100) NOT NULL,
CallSite varchar(100) NOT NULL, CallSite varchar(100) NOT NULL,
Exception varchar(100) NOT NULL Exception varchar(100) NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id); CREATE UNIQUE INDEX IF NOT EXISTS Logs_Id ON Logs (Id);
CREATE TABLE IF NOT EXISTS Audit CREATE TABLE IF NOT EXISTS Audit
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
Date varchar(100) NOT NULL, Date varchar(100) NOT NULL,
Username varchar(100) NOT NULL, Username varchar(100) NOT NULL,
ChangeType varchar(100) NOT NULL, ChangeType varchar(100) NOT NULL,
OldValue varchar(100), OldValue varchar(100),
NewValue varchar(100) NewValue varchar(100)
); );
CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id); CREATE UNIQUE INDEX IF NOT EXISTS Audit_Id ON Audit (Id);
CREATE TABLE IF NOT EXISTS DBInfo CREATE TABLE IF NOT EXISTS DBInfo
( (
SchemaVersion INTEGER SchemaVersion INTEGER
); );
CREATE TABLE IF NOT EXISTS ScheduledJobs CREATE TABLE IF NOT EXISTS ScheduledJobs
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name varchar(100) NOT NULL, Name varchar(100) NOT NULL,
LastRun varchar(100) NOT NULL LastRun varchar(100) NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id); CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id);
CREATE TABLE IF NOT EXISTS UsersToNotify CREATE TABLE IF NOT EXISTS UsersToNotify
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
Username varchar(100) NOT NULL Username varchar(100) NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id); CREATE UNIQUE INDEX IF NOT EXISTS UsersToNotify_Id ON UsersToNotify (Id);
CREATE TABLE IF NOT EXISTS IssueBlobs CREATE TABLE IF NOT EXISTS IssueBlobs
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
RequestId INTEGER, RequestId INTEGER,
Type INTEGER NOT NULL, Type INTEGER NOT NULL,
Content BLOB NOT NULL Content BLOB NOT NULL
); );
CREATE UNIQUE INDEX IF NOT EXISTS IssueBlobs_Id ON IssueBlobs (Id); 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);

View file

@ -193,6 +193,7 @@ namespace PlexRequests.UI
container.Register<IRepository<UsersToNotify>, GenericRepository<UsersToNotify>>(); container.Register<IRepository<UsersToNotify>, GenericRepository<UsersToNotify>>();
container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>(); container.Register<IRepository<ScheduledJobs>, GenericRepository<ScheduledJobs>>();
container.Register<IRepository<IssueBlobs>, GenericRepository<IssueBlobs>>(); container.Register<IRepository<IssueBlobs>, GenericRepository<IssueBlobs>>();
container.Register<IRepository<RequestLimit>, GenericRepository<RequestLimit>>();
container.Register<IRequestService, JsonRequestModelRequestService>(); container.Register<IRequestService, JsonRequestModelRequestService>();
container.Register<IIssueService, IssueJsonService>(); container.Register<IIssueService, IssueJsonService>();
container.Register<ISettingsRepository, SettingsJsonRepository>(); container.Register<ISettingsRepository, SettingsJsonRepository>();

View file

@ -62,6 +62,7 @@ namespace PlexRequests.UI.Jobs
var cp = JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build(); var cp = JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build();
var store = JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(); var store = JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build();
var storeClean = JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(); var storeClean = JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build();
var userRequestLimitReset = JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build();
jobs.Add(plex); jobs.Add(plex);
jobs.Add(sickrage); jobs.Add(sickrage);
@ -69,6 +70,7 @@ namespace PlexRequests.UI.Jobs
jobs.Add(cp); jobs.Add(cp);
jobs.Add(store); jobs.Add(store);
jobs.Add(storeClean); jobs.Add(storeClean);
jobs.Add(userRequestLimitReset);
return jobs; return jobs;
} }
@ -150,6 +152,13 @@ namespace PlexRequests.UI.Jobs
.WithSimpleSchedule(x => x.WithIntervalInHours(s.StoreCleanup).RepeatForever()) .WithSimpleSchedule(x => x.WithIntervalInHours(s.StoreCleanup).RepeatForever())
.Build(); .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(plexAvailabilityChecker);
triggers.Add(srCacher); triggers.Add(srCacher);
@ -157,6 +166,7 @@ namespace PlexRequests.UI.Jobs
triggers.Add(cpCacher); triggers.Add(cpCacher);
triggers.Add(storeBackup); triggers.Add(storeBackup);
triggers.Add(storeCleanup); triggers.Add(storeCleanup);
triggers.Add(userRequestLimiter);
return triggers; return triggers;
} }

View file

@ -72,7 +72,7 @@ namespace PlexRequests.UI.Modules
INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi, ISettingsService<HeadphonesSettings> hpService, INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi, ISettingsService<HeadphonesSettings> hpService,
ICouchPotatoCacher cpCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi, ICouchPotatoCacher cpCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi,
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth, IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email, ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth, IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
IIssueService issue, IAnalytics a) : base("search", prSettings) IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl) : base("search", prSettings)
{ {
Auth = auth; Auth = auth;
PlexService = plexService; PlexService = plexService;
@ -99,6 +99,7 @@ namespace PlexRequests.UI.Modules
EmailNotificationSettings = email; EmailNotificationSettings = email;
IssueService = issue; IssueService = issue;
Analytics = a; Analytics = a;
RequestLimitRepo = rl;
Get["/", true] = async (x, ct) => await RequestLoad(); Get["/", true] = async (x, ct) => await RequestLoad();
@ -145,6 +146,7 @@ namespace PlexRequests.UI.Modules
private IRepository<UsersToNotify> UsersToNotifyRepo { get; } private IRepository<UsersToNotify> UsersToNotifyRepo { get; }
private IIssueService IssueService { get; } private IIssueService IssueService { get; }
private IAnalytics Analytics { get; } private IAnalytics Analytics { get; }
private IRepository<RequestLimit> RequestLimitRepo { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
private async Task<Negotiator> RequestLoad() private async Task<Negotiator> RequestLoad()
@ -423,13 +425,17 @@ namespace PlexRequests.UI.Modules
private async Task<Response> RequestMovie(int movieId) private async Task<Response> 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)); await Analytics.TrackEventAsync(Category.Search, Action.Request, "Movie", Username, CookieHelper.GetAnalyticClientId(Cookies));
var movieInfo = MovieApi.GetMovieInformation(movieId).Result; var movieInfo = MovieApi.GetMovieInformation(movieId).Result;
var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}"; var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}";
Log.Trace("Getting movie info from TheMovieDb"); Log.Trace("Getting movie info from TheMovieDb");
var settings = await PrService.GetSettingsAsync();
// check if the movie has already been requested // check if the movie has already been requested
Log.Info("Requesting movie with id {0}", movieId); Log.Info("Requesting movie with id {0}", movieId);
var existingRequest = await RequestService.CheckRequestAsync(movieId); var existingRequest = await RequestService.CheckRequestAsync(movieId);
@ -491,64 +497,23 @@ namespace PlexRequests.UI.Modules
Log.Debug("Adding movie to CP result {0}", result); Log.Debug("Adding movie to CP result {0}", result);
if (result) if (result)
{ {
model.Approved = true; return await AddRequest(model, settings, $"{fullMovieName} was successfully added!");
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
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 Result = false,
{ Message =
Title = model.Title, "Something went wrong adding the movie to CouchPotato! Please check your settings."
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!");
} }
try try
{ {
Log.Info("Adding movie to database"); return await AddRequest(model, settings, $"{fullMovieName} was successfully added!");
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!" });
} }
catch (Exception e) catch (Exception e)
{ {
@ -566,6 +531,11 @@ namespace PlexRequests.UI.Modules
/// <returns></returns> /// <returns></returns>
private async Task<Response> RequestTvShow(int showId, string seasons) private async Task<Response> 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)); await Analytics.TrackEventAsync(Category.Search, Action.Request, "TvShow", Username, CookieHelper.GetAnalyticClientId(Cookies));
var tvApi = new TvMazeApi(); var tvApi = new TvMazeApi();
@ -575,7 +545,6 @@ namespace PlexRequests.UI.Modules
string fullShowName = $"{showInfo.name} ({firstAir.Year})"; string fullShowName = $"{showInfo.name} ({firstAir.Year})";
//#if !DEBUG //#if !DEBUG
var settings = await PrService.GetSettingsAsync();
// check if the show has already been requested // check if the show has already been requested
Log.Info("Requesting tv show with id {0}", showId); Log.Info("Requesting tv show with id {0}", showId);
@ -669,28 +638,10 @@ namespace PlexRequests.UI.Modules
var result = sender.SendToSonarr(sonarrSettings, model); var result = sender.SendToSonarr(sonarrSettings, model);
if (!string.IsNullOrEmpty(result?.title)) if (!string.IsNullOrEmpty(result?.title))
{ {
model.Approved = true; return await AddRequest(model, settings, $"{fullShowName} was successfully added!");
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 Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages)); return Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages));
} }
var srSettings = SickRageService.GetSettings(); var srSettings = SickRageService.GetSettings();
@ -699,57 +650,20 @@ namespace PlexRequests.UI.Modules
var result = sender.SendToSickRage(srSettings, model); var result = sender.SendToSickRage(srSettings, model);
if (result?.result == "success") if (result?.result == "success")
{ {
model.Approved = true; return await AddRequest(model, settings, $"{fullShowName} was successfully added!");
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 Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Something went wrong adding the movie to SickRage! Please check your settings." }); return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "<b>Message From SickRage: </b>" + result.message : "Something went wrong adding the movie to SickRage! Please check your settings." });
} }
if (!srSettings.Enabled && !sonarrSettings.Enabled) if (!srSettings.Enabled && !sonarrSettings.Enabled)
{ {
model.Approved = true; return await AddRequest(model, settings, $"{fullShowName} was successfully added!");
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 Response.AsJson(new JsonResponseModel { Result = false, Message = "The request of TV Shows is not correctly set up. Please contact your admin." }); return Response.AsJson(new JsonResponseModel { Result = false, Message = "The request of TV Shows is not correctly set up. Please contact your admin." });
} }
return await AddRequest(model, settings, $"{fullShowName} was successfully added!");
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!" });
} }
private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings) private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings)
@ -770,8 +684,12 @@ namespace PlexRequests.UI.Modules
private async Task<Response> RequestAlbum(string releaseId) private async Task<Response> RequestAlbum(string releaseId)
{ {
await Analytics.TrackEventAsync(Category.Search, Action.Request, "Album", Username, CookieHelper.GetAnalyticClientId(Cookies));
var settings = await PrService.GetSettingsAsync(); 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); var existingRequest = await RequestService.CheckRequestAsync(releaseId);
Log.Debug("Checking for an existing request"); Log.Debug("Checking for an existing request");
@ -849,48 +767,10 @@ namespace PlexRequests.UI.Modules
var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService); var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService);
await sender.AddAlbum(model); await sender.AddAlbum(model);
model.Approved = true; return await AddRequest(model, settings, $"{model.Title} was successfully added!");
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!"
});
} }
if (ShouldSendNotification(RequestType.Album, settings)) return await AddRequest(model, settings, $"{model.Title} was successfully added!");
{
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!"
});
} }
private string GetMusicBrainzCoverArt(string id) private string GetMusicBrainzCoverArt(string id)
@ -994,5 +874,84 @@ namespace PlexRequests.UI.Modules
var model = seasons.Select(x => x.number); var model = seasons.Select(x => x.number);
return Response.AsJson(model); return Response.AsJson(model);
} }
private async Task<bool> 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<Response> 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 });
}
} }
} }

View file

@ -61,6 +61,15 @@
<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>
<div class="form-group">
<label for="UserRequestLimitResetter" class="control-label">User Request Limit Reset (hour)</label>
<div>
<input type="text" class="form-control form-control-custom " id="UserRequestLimitResetter" name="UserRequestLimitResetter" value="@Model.UserRequestLimitResetter">
</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>

View file

@ -77,87 +77,87 @@
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
@if (Model.SearchForMovies) @if (Model.SearchForMovies)
{ {
<input type="checkbox" id="SearchForMovies" name="SearchForMovies" checked="checked"><label for="SearchForMovies">Search for Movies</label> <input type="checkbox" id="SearchForMovies" name="SearchForMovies" checked="checked"><label for="SearchForMovies">Search for Movies</label>
} }
else else
{ {
<input type="checkbox" id="SearchForMovies" name="SearchForMovies"><label for="SearchForMovies">Search for Movies</label> <input type="checkbox" id="SearchForMovies" name="SearchForMovies"><label for="SearchForMovies">Search for Movies</label>
} }
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
@if (Model.SearchForTvShows) @if (Model.SearchForTvShows)
{ {
<input type="checkbox" id="SearchForTvShows" name="SearchForTvShows" checked="checked"><label for="SearchForTvShows">Search for TV Shows</label> <input type="checkbox" id="SearchForTvShows" name="SearchForTvShows" checked="checked"><label for="SearchForTvShows">Search for TV Shows</label>
} }
else else
{ {
<input type="checkbox" id="SearchForTvShows" name="SearchForTvShows"><label for="SearchForTvShows">Search for TV Shows</label> <input type="checkbox" id="SearchForTvShows" name="SearchForTvShows"><label for="SearchForTvShows">Search for TV Shows</label>
} }
</div>
</div>
<div class="form-group">
<div class="checkbox">
@if (Model.SearchForMusic)
{
<input type="checkbox" id="SearchForMusic" name="SearchForMusic" checked="checked"><label for="SearchForMusic">Search for Music</label>
}
else
{
<input type="checkbox" id="SearchForMusic" name="SearchForMusic"><label for="SearchForMusic">Search for Music</label>
}
</div>
</div>
<div class="form-group">
<div class="checkbox">
@if (Model.RequireMovieApproval)
{
<input type="checkbox" id="RequireMovieApproval" name="RequireMovieApproval" checked="checked"><label for="RequireMovieApproval">Require approval of Movie requests</label>
}
else
{
<input type="checkbox" id="RequireMovieApproval" name="RequireMovieApproval"><label for="RequireMovieApproval">Require approval of Movie requests</label>
}
</div>
</div>
<div class="form-group">
<div class="checkbox">
@if (Model.RequireTvShowApproval)
{
<input type="checkbox" id="RequireTvShowApproval" name="RequireTvShowApproval" checked="checked"><label for="RequireTvShowApproval">Require approval of TV show requests</label>
}
else
{
<input type="checkbox" id="RequireTvShowApproval" name="RequireTvShowApproval"><label for="RequireTvShowApproval">Require approval of TV show requests</label>
}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
@if (Model.RequireMusicApproval) @if (Model.SearchForMusic)
{ {
<input type="checkbox" id="RequireMusicApproval" name="RequireMusicApproval" checked="checked"><label for="RequireMusicApproval">Require approval of Music requests</label> <input type="checkbox" id="SearchForMusic" name="SearchForMusic" checked="checked"><label for="SearchForMusic">Search for Music</label>
} }
else else
{ {
<input type="checkbox" id="RequireMusicApproval" name="RequireMusicApproval"><label for="RequireMusicApproval">Require approval of Music requests</label> <input type="checkbox" id="SearchForMusic" name="SearchForMusic"><label for="SearchForMusic">Search for Music</label>
} }
</div>
</div>
<div class="form-group">
<div class="checkbox">
@if (Model.RequireMovieApproval)
{
<input type="checkbox" id="RequireMovieApproval" name="RequireMovieApproval" checked="checked"><label for="RequireMovieApproval">Require approval of Movie requests</label>
}
else
{
<input type="checkbox" id="RequireMovieApproval" name="RequireMovieApproval"><label for="RequireMovieApproval">Require approval of Movie requests</label>
}
</div>
</div>
<div class="form-group">
<div class="checkbox">
@if (Model.RequireTvShowApproval)
{
<input type="checkbox" id="RequireTvShowApproval" name="RequireTvShowApproval" checked="checked"><label for="RequireTvShowApproval">Require approval of TV show requests</label>
}
else
{
<input type="checkbox" id="RequireTvShowApproval" name="RequireTvShowApproval"><label for="RequireTvShowApproval">Require approval of TV show requests</label>
}
</div>
</div>
<div class="form-group">
<div class="checkbox">
@if (Model.RequireMusicApproval)
{
<input type="checkbox" id="RequireMusicApproval" name="RequireMusicApproval" checked="checked"><label for="RequireMusicApproval">Require approval of Music requests</label>
}
else
{
<input type="checkbox" id="RequireMusicApproval" name="RequireMusicApproval"><label for="RequireMusicApproval">Require approval of Music requests</label>
}
</div> </div>
</div> </div>
@ -234,16 +234,36 @@
</div> </div>
</div> </div>
@*<div class="form-group">
<label for="WeeklyRequestLimit" class="control-label">Weekly Request Limit</label> <p class="form-group">If the request limits are set to 0 then no request limit is applied.</p>
<div class="form-group">
<label for="MovieWeeklyRequestLimit" class="control-label">Movie Weekly Request Limit</label>
<div>
<label>
<input type="number" id="MovieWeeklyRequestLimit" name="MovieWeeklyRequestLimit" class="form-control form-control-custom " value="@Model.MovieWeeklyRequestLimit">
</label>
</div>
</div>
<div class="form-group">
<label for="TvWeeklyRequestLimit" class="control-label">TV Show Weekly Request Limit</label>
<div>
<label>
<input type="number" id="TvWeeklyRequestLimit" name="TvWeeklyRequestLimit" class="form-control form-control-custom " value="@Model.TvWeeklyRequestLimit">
</label>
</div>
</div>
<div class="form-group">
<label for="AlbumWeeklyRequestLimit" class="control-label">Album Weekly Request Limit</label>
<div> <div>
<label> <label>
<input type="number" id="WeeklyRequestLimit" name="WeeklyRequestLimit" class="form-control form-control-custom " value="@Model.WeeklyRequestLimit"> <input type="number" id="AlbumWeeklyRequestLimit" name="AlbumWeeklyRequestLimit" class="form-control form-control-custom " value="@Model.AlbumWeeklyRequestLimit">
</label> </label>
</div> </div>
</div> //TODO: Need to implement this*@ </div>
<div> <div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div> <div>
@ -255,9 +275,9 @@
</div> </div>
<script> <script>
$(function() { $(function () {
$('#save').click(function (e) { $('#save').click(function (e) {
e.preventDefault(); e.preventDefault();
var theme = $("#themes option:selected").val(); var theme = $("#themes option:selected").val();
var $form = $("#mainForm"); var $form = $("#mainForm");
@ -285,11 +305,11 @@ $(function() {
}); });
$('#refreshKey').click(function (e) { $('#refreshKey').click(function (e) {
e.preventDefault(); e.preventDefault();
var base = '@Html.GetBaseUrl()'; var base = '@Html.GetBaseUrl()';
var url = createBaseUrl(base, '/admin/createapikey'); var url = createBaseUrl(base, '/admin/createapikey');
$.ajax({ $.ajax({
type: "post", type: "post",
url: url, url: url,
dataType: "json", dataType: "json",
@ -297,7 +317,7 @@ $(function() {
if (response) { if (response) {
generateNotify("Success!", "success"); generateNotify("Success!", "success");
$('#apiKey').val(response); $('#apiKey').val(response);
} }
}, },
error: function (e) { error: function (e) {
console.log(e); console.log(e);
@ -305,5 +325,5 @@ $(function() {
} }
}); });
}); });
}); });
</script> </script>