Merge branch 'dev' into master

This commit is contained in:
Jamie 2016-12-10 21:09:38 +00:00 committed by GitHub
commit a056ba17e5
206 changed files with 8047 additions and 2824 deletions

3
.gitattributes vendored
View file

@ -66,6 +66,3 @@
PlexRequests.UI/Content/* linguist-vendored
PlexRequests.UI/Content/* linguist-vendored
base.scss linguist-vendored=false
requests-1.7.js linguist-vendored=false
search-1.7.js linguist-vendored=false
site-1.7.js linguist-vendored=false

View file

@ -3,6 +3,8 @@ If this is a bug report please make sure you have filled the following in:
#### Plex Requests.Net Version:
#### update Branch:
#### Operating System:
@ -19,4 +21,4 @@ Logs go here (Please make sure you remove any personal information from the logs
#### Reproduction Steps:
Please include any steps to reproduce the issue, this the request that is causing the problem etc.
Please include any steps to reproduce the issue, this the request that is causing the problem etc.

View file

@ -44,6 +44,7 @@ namespace PlexRequests.Api.Interfaces
PlexSearch GetAllEpisodes(string authToken, Uri host, string section, int startPage, int returnCount);
PlexServer GetServer(string authToken);
PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey);
RecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId);
RecentlyAddedModelOld RecentlyAddedOld(string authToken, Uri plexFullHost, string sectionId);
PlexRecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId);
}
}

View file

@ -1,7 +1,7 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: RecentlyAddedModel.cs
// File: RecentlyAddedModelOld.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
@ -110,7 +110,7 @@ namespace PlexRequests.Api.Models.Plex
public string tag { get; set; }
}
public class RecentlyAddedModel
public class RecentlyAddedModelOld
{
public string _elementType { get; set; }
public string allowSync { get; set; }
@ -130,4 +130,112 @@ namespace PlexRequests.Api.Models.Plex
public string viewMode { get; set; }
public List<RecentlyAddedChild> _children { get; set; }
}
// 1.3 and forward!
public class PartRecentlyAdded
{
public int id { get; set; }
public string key { get; set; }
public int duration { get; set; }
public string file { get; set; }
public double size { get; set; }
public string audioProfile { get; set; }
public string container { get; set; }
public string videoProfile { get; set; }
public string deepAnalysisVersion { get; set; }
public string requiredBandwidths { get; set; }
}
public class Medium
{
public string videoResolution { get; set; }
public int id { get; set; }
public int duration { get; set; }
public int bitrate { get; set; }
public int width { get; set; }
public int height { get; set; }
public double aspectRatio { get; set; }
public int audioChannels { get; set; }
public string audioCodec { get; set; }
public string videoCodec { get; set; }
public string container { get; set; }
public string videoFrameRate { get; set; }
public string audioProfile { get; set; }
public string videoProfile { get; set; }
public List<PartRecentlyAdded> Part { get; set; }
}
public class DirectorRecentlyAdded
{
public string tag { get; set; }
}
public class WriterRecentlyAdded
{
public string tag { get; set; }
}
public class Metadata
{
public string ratingKey { get; set; }
public string key { get; set; }
public string parentRatingKey { get; set; }
public string grandparentRatingKey { get; set; }
public string type { get; set; }
public string title { get; set; }
public string titleSort { get; set; }
public string grandparentKey { get; set; }
public string parentKey { get; set; }
public string grandparentTitle { get; set; }
public string contentRating { get; set; }
public string summary { get; set; }
public int index { get; set; }
public int parentIndex { get; set; }
public int year { get; set; }
public string thumb { get; set; }
public string art { get; set; }
public string parentThumb { get; set; }
public string grandparentThumb { get; set; }
public string grandparentArt { get; set; }
public string grandparentTheme { get; set; }
public int duration { get; set; }
public string originallyAvailableAt { get; set; }
public int addedAt { get; set; }
public int updatedAt { get; set; }
public List<Medium> Media { get; set; }
public List<DirectorRecentlyAdded> Director { get; set; }
public List<WriterRecentlyAdded> Writer { get; set; }
public int? viewCount { get; set; }
public int? lastViewedAt { get; set; }
public double? rating { get; set; }
}
public class MediaContainer
{
public double size { get; set; }
public double totalSize { get; set; }
public bool allowSync { get; set; }
public string art { get; set; }
public string identifier { get; set; }
public int librarySectionID { get; set; }
public string librarySectionTitle { get; set; }
public string librarySectionUUID { get; set; }
public string mediaTagPrefix { get; set; }
public int mediaTagVersion { get; set; }
public bool mixedParents { get; set; }
public bool nocache { get; set; }
public int offset { get; set; }
public string thumb { get; set; }
public string title1 { get; set; }
public string title2 { get; set; }
public string viewGroup { get; set; }
public int viewMode { get; set; }
public List<Metadata> Metadata { get; set; }
}
public class PlexRecentlyAddedModel
{
public MediaContainer MediaContainer { get; set; }
}
}

View file

@ -73,7 +73,7 @@
<Compile Include="Plex\PlexStatus.cs" />
<Compile Include="Plex\PlexMediaType.cs" />
<Compile Include="Plex\PlexUserRequest.cs" />
<Compile Include="Plex\RecentlyAddedModel.cs" />
<Compile Include="Plex\RecentlyAddedModelOld.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SickRage\SickRageBase.cs" />
<Compile Include="SickRage\SickrageShows.cs" />

View file

@ -104,8 +104,7 @@ namespace PlexRequests.Api
}
var result = DeserializeXml<T>(response.Content);
return result;
}
return result;}
public T ExecuteJson<T>(IRestRequest request, Uri baseUri) where T : new()
{

View file

@ -65,9 +65,7 @@ namespace PlexRequests.Api
var obj = RetryHandler.Execute(() => Api.ExecuteJson<JObject>(request, baseUrl),
(exception, timespan) => Log.Error(exception, "Exception when calling AddMovie for CP, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (2),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)});
TimeSpan.FromSeconds (2)});
if (obj.Count > 0)

View file

@ -346,7 +346,7 @@ namespace PlexRequests.Api
return servers;
}
public RecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId)
public RecentlyAddedModelOld RecentlyAddedOld(string authToken, Uri plexFullHost, string sectionId)
{
var request = new RestRequest
{
@ -360,7 +360,7 @@ namespace PlexRequests.Api
try
{
var lib = RetryHandler.Execute(() => Api.ExecuteJson<RecentlyAddedModel>(request, plexFullHost),
var lib = RetryHandler.Execute(() => Api.ExecuteJson<RecentlyAddedModelOld>(request, plexFullHost),
(exception, timespan) => Log.Error(exception, "Exception when calling RecentlyAddedModel for Plex, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (5),
TimeSpan.FromSeconds(10),
@ -372,7 +372,37 @@ namespace PlexRequests.Api
catch (Exception e)
{
Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAddedModel");
return new RecentlyAddedModel();
return new RecentlyAddedModelOld();
}
}
public PlexRecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId)
{
var request = new RestRequest
{
Method = Method.GET,
Resource = "library/sections/{sectionId}/recentlyAdded"
};
request.AddUrlSegment("sectionId", sectionId);
AddHeaders(ref request, authToken, true);
AddLimitHeaders(ref request, 0, 25);
try
{
var lib = RetryHandler.Execute(() => Api.ExecuteJson<PlexRecentlyAddedModel>(request, plexFullHost),
(exception, timespan) => Log.Error(exception, "Exception when calling PlexRecentlyAddedModel for Plex, Retrying {0}", timespan), new[] {
TimeSpan.FromSeconds (5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(30)
});
return lib;
}
catch (Exception e)
{
Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAddedModel");
return new PlexRecentlyAddedModel();
}
}

View file

@ -33,7 +33,7 @@ namespace PlexRequests.Api
{
public static class RetryHandler
{
private static readonly TimeSpan[] DefaultRetryTime = { TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) };
private static readonly TimeSpan[] DefaultRetryTime = { TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5) };
public static T Execute<T>(Func<T> action, TimeSpan[] timeSpan = null)
{

View file

@ -117,8 +117,7 @@ namespace PlexRequests.Api
try
{
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds (2)
});
result = policy.Execute(() => Api.ExecuteJson<SonarrAddSeries>(request, baseUrl));
@ -186,8 +185,7 @@ namespace PlexRequests.Api
try
{
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds (2)
});
result = policy.Execute(() => Api.ExecuteJson<SonarrAddSeries>(request, baseUrl));
@ -228,8 +226,8 @@ namespace PlexRequests.Api
{
var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] {
TimeSpan.FromSeconds (5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(30)
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(5)
});
return policy.Execute(() => Api.ExecuteJson<List<Series>>(request, baseUrl));

View file

@ -47,25 +47,18 @@ namespace PlexRequests.Api
public async Task<List<SearchMovie>> SearchMovie(string searchTerm)
{
var results = await Client.SearchMovie(searchTerm);
return results.Results;
}
[Obsolete("Should use TvMaze for TV")]
public async Task<List<SearchTv>> SearchTv(string searchTerm)
{
var results = await Client.SearchTvShow(searchTerm);
return results.Results;
return results?.Results ?? new List<SearchMovie>();
}
public async Task<List<MovieResult>> GetCurrentPlayingMovies()
{
var movies = await Client.GetMovieList(MovieListType.NowPlaying);
return movies.Results;
return movies?.Results ?? new List<MovieResult>();
}
public async Task<List<MovieResult>> GetUpcomingMovies()
{
var movies = await Client.GetMovieList(MovieListType.Upcoming);
return movies.Results;
return movies?.Results ?? new List<MovieResult>();
}
public async Task<Movie> GetMovieInformation(int tmdbId)
@ -77,14 +70,7 @@ namespace PlexRequests.Api
public async Task<Movie> GetMovieInformation(string imdbId)
{
var movies = await Client.GetMovie(imdbId);
return movies;
}
[Obsolete("Should use TvMaze for TV")]
public async Task<TvShow> GetTvShowInformation(int tmdbId)
{
var show = await Client.GetTvShow(tmdbId);
return show;
return movies ?? new Movie();
}
}
}

View file

@ -22,42 +22,31 @@ namespace PlexRequests.Core.Migration
public void MigrateToLatest()
{
var con = Db.DbConnection();
var versions = GetMigrations().OrderBy(x => x.Key);
var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault();
if (dbVersion == null)
{
dbVersion = new TableCreation.VersionInfo { Version = 0 };
}
var versions = GetMigrations();
var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault() ??
new TableCreation.VersionInfo { Version = 0 };
foreach (var v in versions)
{
#if !DEBUG
if (v.Value.Version > dbVersion.Version)
{
// Assuming only one constructor
var ctor = v.Key.GetConstructors().FirstOrDefault();
var dependencies = new List<object>();
#endif
// Assuming only one constructor
var ctor = v.Key.GetConstructors().FirstOrDefault();
var dependencies = ctor.GetParameters().Select(param => Kernel.Get(param.ParameterType)).ToList();
foreach (var param in ctor.GetParameters())
{
Console.WriteLine(string.Format(
"Param {0} is named {1} and is of type {2}",
param.Position, param.Name, param.ParameterType));
var method = v.Key.GetMethod("Start");
if (method != null)
{
var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null);
var parametersArray = new object[] { Db.DbConnection() };
var dep = Kernel.Get(param.ParameterType);
dependencies.Add(dep);
}
var method = v.Key.GetMethod("Start");
if (method != null)
{
object result = null;
var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null);
var parametersArray = new object[] { Db.DbConnection() };
method.Invoke(classInstance, parametersArray);
}
method.Invoke(classInstance, parametersArray);
}
#if !DEBUG
}
#endif
}
}

View file

@ -0,0 +1,278 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Version1100.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.Data;
using NLog;
using System.Linq;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core.SettingModels;
using PlexRequests.Core.Users;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
namespace PlexRequests.Core.Migration.Migrations
{
[Migration(11000, "v1.10.0.0")]
public class Version1100 : BaseMigration, IMigration
{
public Version1100(IUserRepository userRepo, IRequestService requestService, ISettingsService<LogSettings> log, IPlexApi plexApi, ISettingsService<PlexSettings> plexService,
IPlexUserRepository plexusers, ISettingsService<PlexRequestSettings> prSettings, ISettingsService<UserManagementSettings> umSettings,
ISettingsService<ScheduledJobsSettings> sjs, IRepository<UsersToNotify> usersToNotify)
{
UserRepo = userRepo;
RequestService = requestService;
Log = log;
PlexApi = plexApi;
PlexSettings = plexService;
PlexUsers = plexusers;
PlexRequestSettings = prSettings;
UserManagementSettings = umSettings;
ScheduledJobSettings = sjs;
UserNotifyRepo = usersToNotify;
}
public int Version => 11000;
private IUserRepository UserRepo { get; }
private IRequestService RequestService { get; }
private ISettingsService<LogSettings> Log { get; }
private IPlexApi PlexApi { get; }
private ISettingsService<PlexSettings> PlexSettings { get; }
private IPlexUserRepository PlexUsers { get; }
private ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
private ISettingsService<UserManagementSettings> UserManagementSettings { get; }
private ISettingsService<ScheduledJobsSettings> ScheduledJobSettings { get; }
private IRepository<UsersToNotify> UserNotifyRepo { get; }
public void Start(IDbConnection con)
{
UpdateDb(con);
// Update the current admin permissions set
PopulateDefaultUserManagementSettings();
UpdateAdmin();
ResetLogLevel();
UpdatePlexUsers();
UpdateScheduledJobs();
MigrateUserNotifications();
UpdateSchema(con, Version);
}
private void MigrateUserNotifications()
{
var usersToNotify = UserNotifyRepo.GetAll();
var plexUsers = PlexUsers.GetAll().ToList();
var users = UserRepo.GetAll().ToList();
if (usersToNotify == null)
{
return;
}
foreach (var u in usersToNotify)
{
var selectedPlexUser = plexUsers.FirstOrDefault(x => x.Username.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase));
if (selectedPlexUser != null)
{
selectedPlexUser.Features += (int)Features.RequestAddedNotification;
PlexUsers.Update(selectedPlexUser);
}
var selectedLocalUser =
users.FirstOrDefault(x => x.UserName.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase));
if (selectedLocalUser != null)
{
selectedLocalUser.Features += (int)Features.RequestAddedNotification;
UserRepo.Update(selectedLocalUser);
}
}
}
private void UpdateScheduledJobs()
{
var settings = ScheduledJobSettings.GetSettings();
settings.PlexUserChecker = 24;
settings.PlexContentCacher = 60;
ScheduledJobSettings.SaveSettings(settings);
}
private void PopulateDefaultUserManagementSettings()
{
var plexRequestSettings = PlexRequestSettings.GetSettings();
UserManagementSettings.SaveSettings(new UserManagementSettings
{
AutoApproveMovies = !plexRequestSettings.RequireMovieApproval,
RequestTvShows = plexRequestSettings.SearchForTvShows,
RequestMusic = plexRequestSettings.SearchForMusic,
RequestMovies = plexRequestSettings.SearchForMovies,
AutoApproveMusic = !plexRequestSettings.RequireMusicApproval,
AutoApproveTvShows = !plexRequestSettings.RequireTvShowApproval
});
}
private void UpdatePlexUsers()
{
var settings = PlexSettings.GetSettings();
if (string.IsNullOrEmpty(settings.PlexAuthToken))
{
return;
}
var plexUsers = PlexApi.GetUsers(settings.PlexAuthToken);
if (plexUsers?.User == null)
{
return;
}
var prSettings = PlexRequestSettings.GetSettings();
var dbUsers = PlexUsers.GetAll().ToList();
foreach (var user in plexUsers.User)
{
if (dbUsers.FirstOrDefault(x => x.PlexUserId == user.Id) != null)
{
continue;
}
int permissions = 0;
if (prSettings.SearchForMovies)
{
permissions = (int)Permissions.RequestMovie;
}
if (prSettings.SearchForTvShows)
{
permissions += (int)Permissions.RequestTvShow;
}
if (prSettings.SearchForMusic)
{
permissions += (int)Permissions.RequestMusic;
}
if (!prSettings.RequireMovieApproval)
{
permissions += (int)Permissions.AutoApproveMovie;
}
if (!prSettings.RequireTvShowApproval)
{
permissions += (int)Permissions.AutoApproveTv;
}
if (!prSettings.RequireMusicApproval)
{
permissions += (int)Permissions.AutoApproveAlbum;
}
// Add report Issues
permissions += (int)Permissions.ReportIssue;
var m = new PlexUsers
{
PlexUserId = user.Id,
Permissions = permissions,
Features = 0,
UserAlias = string.Empty,
EmailAddress = user.Email,
Username = user.Username,
LoginId = Guid.NewGuid().ToString()
};
PlexUsers.Insert(m);
}
}
private void ResetLogLevel()
{
var logSettings = Log.GetSettings();
logSettings.Level = LogLevel.Error.Ordinal;
Log.SaveSettings(logSettings);
LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level));
}
private void UpdateDb(IDbConnection con)
{
// Create the two new columns
con.AlterTable("Users", "ADD", "Permissions", true, "INTEGER");
con.AlterTable("Users", "ADD", "Features", true, "INTEGER");
con.AlterTable("PlexUsers", "ADD", "Permissions", true, "INTEGER");
con.AlterTable("PlexUsers", "ADD", "Features", true, "INTEGER");
con.AlterTable("PlexUsers", "ADD", "Username", true, "VARCHAR(100)");
con.AlterTable("PlexUsers", "ADD", "EmailAddress", true, "VARCHAR(100)");
con.AlterTable("PlexUsers", "ADD", "LoginId", true, "VARCHAR(100)");
//https://image.tmdb.org/t/p/w150/https://image.tmdb.org/t/p/w150//aqhAqttDq7zgsTaBHtCD8wmTk6k.jpg
// UI = https://image.tmdb.org/t/p/w150/{{posterPath}}
// Update old invalid posters
var allRequests = RequestService.GetAll();
if (allRequests == null)
{
return;
}
var requestedModels = allRequests as RequestedModel[] ?? allRequests.ToArray();
foreach (var req in requestedModels)
{
if (req.PosterPath.Contains("https://image.tmdb.org/t/p/w150/"))
{
var newImg = req.PosterPath.Replace("https://image.tmdb.org/t/p/w150/", string.Empty);
req.PosterPath = newImg;
}
}
RequestService.BatchUpdate(requestedModels);
}
private void UpdateAdmin()
{
var users = UserRepo.GetAll().ToList();
foreach (var user in users)
{
user.Permissions = (int)
(Permissions.Administrator
| Permissions.ReportIssue
| Permissions.RequestMusic
| Permissions.RequestTvShow
| Permissions.RequestMovie
| Permissions.AutoApproveAlbum
| Permissions.AutoApproveMovie
| Permissions.AutoApproveTv);
}
UserRepo.UpdateAll(users);
}
}
}

View file

@ -45,6 +45,10 @@
<Reference Include="Ninject">
<HintPath>..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.11\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Quartz, Version=2.3.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
<Private>True</Private>
@ -65,14 +69,27 @@
<Compile Include="MigrationAttribute.cs" />
<Compile Include="MigrationRunner.cs" />
<Compile Include="Migrations\BaseMigration.cs" />
<Compile Include="Migrations\Version1100.cs" />
<Compile Include="Migrations\Version195.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PlexRequests.Api.Interfaces\PlexRequests.Api.Interfaces.csproj">
<Project>{95834072-A675-415D-AA8F-877C91623810}</Project>
<Name>PlexRequests.Api.Interfaces</Name>
</ProjectReference>
<ProjectReference Include="..\PlexRequests.Api.Models\PlexRequests.Api.Models.csproj">
<Project>{CB37A5F8-6DFC-4554-99D3-A42B502E4591}</Project>
<Name>PlexRequests.Api.Models</Name>
</ProjectReference>
<ProjectReference Include="..\PlexRequests.Core\PlexRequests.Core.csproj">
<Project>{DD7DC444-D3BF-4027-8AB9-EFC71F5EC581}</Project>
<Name>PlexRequests.Core</Name>
</ProjectReference>
<ProjectReference Include="..\PlexRequests.Helpers\PlexRequests.Helpers.csproj">
<Project>{1252336D-42A3-482A-804C-836E60173DFA}</Project>
<Name>PlexRequests.Helpers</Name>
</ProjectReference>
<ProjectReference Include="..\PlexRequests.Store\PlexRequests.Store.csproj">
<Project>{92433867-2B7B-477B-A566-96C382427525}</Project>
<Name>PlexRequests.Store</Name>

View file

@ -2,5 +2,6 @@
<packages>
<package id="Common.Logging" version="3.0.0" targetFramework="net45" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
<package id="NLog" version="4.3.11" targetFramework="net45" />
<package id="Quartz" version="2.3.3" targetFramework="net45" />
</packages>

View file

@ -60,7 +60,6 @@
</Choose>
<ItemGroup>
<Compile Include="AuthenticationSettingsTests.cs" />
<Compile Include="StatusCheckerTests.cs" />
<Compile Include="NotificationMessageResolverTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View file

@ -30,7 +30,7 @@ namespace PlexRequests.Core
{
public struct TimeFrameMinutes
{
public const int SchedulerCaching = 60;
public const int SchedulerCaching = 120;
}
public const string PlexLibaries = nameof(PlexLibaries);

View file

@ -24,18 +24,16 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Store;
namespace PlexRequests.UI.Helpers
namespace PlexRequests.Core
{
public class HeadphonesSender
{

View file

@ -0,0 +1,34 @@
using System;
using Nancy;
using Nancy.Security;
using PlexRequests.Helpers.Permissions;
namespace PlexRequests.Core
{
public interface ISecurityExtensions
{
Response AdminLoginRedirect(Permissions perm, NancyContext context);
Response AdminLoginRedirect(NancyContext context, params Permissions[] perm);
bool DoesNotHavePermissions(Permissions perm, IUserIdentity currentUser);
Response HasAnyPermissionsRedirect(NancyContext context, string routeName, HttpStatusCode code,
params Permissions[] perm);
bool DoesNotHavePermissions(int perm, IUserIdentity currentUser);
Func<NancyContext, Response> ForbiddenIfNot(Func<NancyContext, bool> test);
bool HasAnyPermissions(IUserIdentity user, params Permissions[] perm);
bool HasPermissions(IUserIdentity user, Permissions perm);
Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code);
Func<NancyContext, Response> HttpStatusCodeIfNot(HttpStatusCode statusCode, Func<NancyContext, bool> test);
bool IsLoggedIn(NancyContext context);
bool IsNormalUser(IUserIdentity user);
bool IsPlexUser(IUserIdentity user);
bool HasPermissions(string userName, Permissions perm);
/// <summary>
/// Gets the username this could be the alias! We should always use this method when getting the username
/// </summary>
/// <param name="username">The username.</param>
/// <returns><c>null</c> if we cannot find a user</returns>
string GetUsername(string username);
}
}

View file

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Octokit;
using PlexRequests.Core.Models;
namespace PlexRequests.Core
{
public interface IStatusChecker
{
Task<StatusModel> GetStatus();
}
}

View file

@ -34,6 +34,7 @@ namespace PlexRequests.Core.Models
RequestApproved,
AdminNote,
Test,
RequestDeclined,
ItemAddedToFaultQueue
}
}

View file

@ -28,7 +28,8 @@ namespace PlexRequests.Core.Models
{
public class StatusModel
{
public string Version { get; set; }
public string CurrentVersion { get; set; }
public string NewVersion { get; set; }
public bool UpdateAvailable { get; set; }
public string UpdateUri { get; set; }
public string DownloadUri { get; set; }

View file

@ -39,9 +39,17 @@
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dapper, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Data.Sqlite">
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
</Reference>
<Reference Include="Nancy.Linker, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.Linker.0.3.1\lib\net40-Client\Nancy.Linker.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
@ -54,7 +62,12 @@
<HintPath>..\packages\Quartz.2.3.3\lib\net40\Quartz.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
@ -81,7 +94,10 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CacheKeys.cs" />
<Compile Include="HeadphonesSender.cs" />
<Compile Include="IPlexReadOnlyDatabase.cs" />
<Compile Include="ISecurityExtensions.cs" />
<Compile Include="IStatusChecker.cs" />
<Compile Include="Notification\NotificationMessage.cs" />
<Compile Include="Notification\NotificationMessageContent.cs" />
<Compile Include="Notification\NotificationMessageCurlys.cs" />
@ -99,6 +115,9 @@
<Compile Include="Notification\Templates\IEmailBasicTemplate.cs" />
<Compile Include="Notification\TransportType.cs" />
<Compile Include="PlexReadOnlyDatabase.cs" />
<Compile Include="Queue\ITransientFaultQueue.cs" />
<Compile Include="Queue\TransientFaultQueue.cs" />
<Compile Include="SecurityExtensions.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\ExternalSettings.cs" />
<Compile Include="SettingModels\HeadphonesSettings.cs" />
@ -119,12 +138,22 @@
<Compile Include="SettingModels\CouchPotatoSettings.cs" />
<Compile Include="SettingModels\PlexRequestSettings.cs" />
<Compile Include="SettingModels\Settings.cs" />
<Compile Include="SettingModels\SystemSettings.cs" />
<Compile Include="SettingModels\UserManagementSettings.cs" />
<Compile Include="SettingsServiceV2.cs" />
<Compile Include="Setup.cs" />
<Compile Include="StatusChecker.cs" />
<Compile Include="StatusChecker\AppveyorArtifactResult.cs" />
<Compile Include="StatusChecker\StatusChecker.cs" />
<Compile Include="StatusChecker\AppveyorBranchResult.cs" />
<Compile Include="TvSender.cs" />
<Compile Include="TvSenderOld.cs" />
<Compile Include="Users\IUserHelper.cs" />
<Compile Include="Users\UserHelper.cs" />
<Compile Include="UserIdentity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UserMapper.cs" />
<Compile Include="Users\UserHelperModel.cs" />
<Compile Include="Users\UserManagementHelper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

View file

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Store;
using PlexRequests.Store.Models;
namespace PlexRequests.Core.Queue
{
public interface ITransientFaultQueue
{
void Dequeue();
Task DequeueAsync();
IEnumerable<RequestQueue> GetQueue();
Task<IEnumerable<RequestQueue>> GetQueueAsync();
void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType);
Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType, string message = null);
}
}

View file

@ -0,0 +1,130 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: TransientFaultQueue.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using PlexRequests.Helpers;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
namespace PlexRequests.Core.Queue
{
public class TransientFaultQueue : ITransientFaultQueue
{
public TransientFaultQueue(IRepository<RequestQueue> queue)
{
RequestQueue = queue;
}
private IRepository<RequestQueue> RequestQueue { get; }
public void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType)
{
//Ensure there is not a duplicate queued item
var existingItem = RequestQueue.Custom(
connection =>
{
connection.Open();
var result = connection.Query<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id });
return result;
}).FirstOrDefault();
if (existingItem != null)
{
// It's already in the queue
return;
}
var queue = new RequestQueue
{
Type = type,
Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = id,
FaultType = faultType
};
RequestQueue.Insert(queue);
}
public async Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType, string description = null)
{
//Ensure there is not a duplicate queued item
var existingItem = await RequestQueue.CustomAsync(async connection =>
{
connection.Open();
var result = await connection.QueryAsync<RequestQueue>("select * from RequestFaultQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id });
return result;
});
if (existingItem.FirstOrDefault() != null)
{
// It's already in the queue
return;
}
var queue = new RequestQueue
{
Type = type,
Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = id,
FaultType = faultType,
Message = description ?? string.Empty
};
await RequestQueue.InsertAsync(queue);
}
public IEnumerable<RequestQueue> GetQueue()
{
var items = RequestQueue.GetAll();
return items;
}
public async Task<IEnumerable<RequestQueue>> GetQueueAsync()
{
var items = RequestQueue.GetAllAsync();
return await items;
}
public void Dequeue()
{
RequestQueue.DeleteAll("RequestQueue");
}
public async Task DequeueAsync()
{
await RequestQueue.DeleteAllAsync("RequestQueue");
}
}
}

View file

@ -0,0 +1,302 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SecurityExtensions.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 Nancy;
using Nancy.Linker;
using Nancy.Responses;
using Nancy.Security;
using PlexRequests.Core.Models;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Store.Repository;
using ISecurityExtensions = PlexRequests.Core.ISecurityExtensions;
namespace PlexRequests.Core
{
public class SecurityExtensions : ISecurityExtensions
{
public SecurityExtensions(IUserRepository userRepository, IResourceLinker linker, IPlexUserRepository plexUsers)
{
UserRepository = userRepository;
Linker = linker;
PlexUsers = plexUsers;
}
private IUserRepository UserRepository { get; }
private IResourceLinker Linker { get; }
private IPlexUserRepository PlexUsers { get; }
public bool IsLoggedIn(NancyContext context)
{
var userName = context.Request.Session[SessionKeys.UsernameKey];
var realUser = false;
var plexUser = userName != null;
if (context.CurrentUser?.IsAuthenticated() ?? false)
{
realUser = true;
}
return realUser || plexUser;
}
public bool IsPlexUser(IUserIdentity user)
{
if (user == null)
{
return false;
}
var plexUser = PlexUsers.GetUserByUsername(user.UserName);
return plexUser != null;
}
public bool IsNormalUser(IUserIdentity user)
{
if (user == null)
{
return false;
}
var dbUser = UserRepository.GetUserByUsername(user.UserName);
return dbUser != null;
}
/// <summary>
/// Gets the username this could be the alias! We should always use this method when getting the username
/// </summary>
/// <param name="username">The username.</param>
/// <returns><c>null</c> if we cannot find a user</returns>
public string GetUsername(string username)
{
var plexUser = PlexUsers.GetUserByUsername(username);
if (plexUser != null)
{
if (!string.IsNullOrEmpty(plexUser.UserAlias))
{
return plexUser.UserAlias;
}
else
{
return plexUser.Username;
}
}
var dbUser = UserRepository.GetUserByUsername(username);
if (dbUser != null)
{
var userProps = ByteConverterHelper.ReturnObject<UserProperties>(dbUser.UserProperties);
if (!string.IsNullOrEmpty(userProps.UserAlias))
{
return userProps.UserAlias;
}
else
{
return dbUser.UserName;
}
}
return null;
}
/// <summary>
/// Creates a hook to be used in a pipeline before a route handler to ensure
/// that the request was made by an authenticated user does not have the claims.
/// </summary>
/// <param name="perm">Claims the authenticated user needs to have</param>
/// <returns>Hook that returns an Unauthorized response if the user is not
/// authenticated or does have the claims, null otherwise</returns>
private Func<NancyContext, Response> DoesNotHavePermissions(int perm)
{
return ForbiddenIfNot(ctx =>
{
var permissions = GetPermissions(ctx.CurrentUser);
var result = permissions.HasFlag((Permissions)perm);
return !result;
});
}
public bool DoesNotHavePermissions(int perm, IUserIdentity currentUser)
{
return DoesNotHavePermissions((Permissions)perm, currentUser);
}
public bool DoesNotHavePermissions(Permissions perm, IUserIdentity currentUser)
{
var permissions = GetPermissions(currentUser);
var result = permissions.HasFlag(perm);
return !result;
}
public bool HasPermissions(IUserIdentity user, Permissions perm)
{
var permissions = GetPermissions(user);
return permissions.HasFlag(perm);
}
public bool HasPermissions(string userName, Permissions perm)
{
var permissions = GetPermissions(userName);
return permissions.HasFlag(perm);
}
public bool HasAnyPermissions(IUserIdentity user, params Permissions[] perm)
{
var permissions = GetPermissions(user);
foreach (var p in perm)
{
var result = permissions.HasFlag(p);
if (result)
{
return true;
}
}
return false;
}
public Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code)
{
var url = Linker.BuildRelativeUri(context, routeName);
var response = ForbiddenIfNot(ctx =>
{
var permissions = GetPermissions(ctx.CurrentUser);
var result = permissions.HasFlag(perm);
return result;
});
var r = response(context);
return r.StatusCode == code
? new RedirectResponse($"{url.ToString()}?redirect={context.Request.Path}")
: null;
}
public Response HasAnyPermissionsRedirect(NancyContext context, string routeName, HttpStatusCode code, params Permissions[] perm)
{
var url = Linker.BuildRelativeUri(context, routeName);
var response = ForbiddenIfNot(ctx =>
{
var permissions = GetPermissions(ctx.CurrentUser);
var hasPermission = false;
foreach (var p in perm)
{
var result = permissions.HasFlag(p);
if (result)
{
hasPermission = true;
}
}
return hasPermission;
});
var r = response(context);
return r.StatusCode == code
? new RedirectResponse(url.ToString())
: null;
}
public Response AdminLoginRedirect(Permissions perm, NancyContext context)
{
// This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code).
return HasPermissionsRedirect(perm, context, "LocalLogin", HttpStatusCode.Forbidden);
}
public Response AdminLoginRedirect(NancyContext context, params Permissions[] perm)
{
// This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code).
return HasAnyPermissionsRedirect(context, "LocalLogin", HttpStatusCode.Forbidden, perm);
}
// BELOW IS A COPY FROM THE SecurityHooks CLASS!
/// <summary>
/// Creates a hook to be used in a pipeline before a route handler to ensure that
/// the request satisfies a specific test.
/// </summary>
/// <param name="test">Test that must return true for the request to continue</param>
/// <returns>Hook that returns an Forbidden response if the test fails, null otherwise</returns>
public Func<NancyContext, Response> ForbiddenIfNot(Func<NancyContext, bool> test)
{
return HttpStatusCodeIfNot(HttpStatusCode.Forbidden, test);
}
/// <summary>
/// Creates a hook to be used in a pipeline before a route handler to ensure that
/// the request satisfies a specific test.
/// </summary>
/// <param name="statusCode">HttpStatusCode to use for the response</param>
/// <param name="test">Test that must return true for the request to continue</param>
/// <returns>Hook that returns a response with a specific HttpStatusCode if the test fails, null otherwise</returns>
public Func<NancyContext, Response> HttpStatusCodeIfNot(HttpStatusCode statusCode, Func<NancyContext, bool> test)
{
return ctx =>
{
Response response = new Response
{
StatusCode = HttpStatusCode.OK
};
if (!test(ctx))
{
response = new Response
{
StatusCode = statusCode
};
}
return response;
};
}
private Permissions GetPermissions(IUserIdentity user)
{
return GetPermissions(user?.UserName);
}
private Permissions GetPermissions(string userName)
{
if (string.IsNullOrEmpty(userName)) return 0;
var dbUser = UserRepository.GetUserByUsername(userName);
if (dbUser != null)
{
var permissions = (Permissions)dbUser.Permissions;
return permissions;
}
var plexUser = PlexUsers.GetUserByUsername(userName);
if (plexUser != null)
{
var permissions = (Permissions)plexUser.Permissions;
return permissions;
}
return 0;
}
}
}

View file

@ -44,15 +44,20 @@ namespace PlexRequests.Core.SettingModels
public bool SearchForMovies { get; set; }
public bool SearchForTvShows { get; set; }
public bool SearchForMusic { get; set; }
[Obsolete("Use the user management settings")]
public bool RequireMovieApproval { get; set; }
[Obsolete("Use the user management settings")]
public bool RequireTvShowApproval { get; set; }
[Obsolete("Use the user management settings")]
public bool RequireMusicApproval { get; set; }
[Obsolete("Use the user management settings")]
public bool UsersCanViewOnlyOwnRequests { get; set; }
[Obsolete("Use the user management settings")]
public bool UsersCanViewOnlyOwnIssues { 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; }
public bool Wizard { get; set; }
@ -69,26 +74,5 @@ namespace PlexRequests.Core.SettingModels
public string ThemeName { get; set; }
public string ApiKey { get; set; }
[JsonIgnore]
public List<string> ApprovalWhiteList
{
get
{
var users = new List<string>();
if (string.IsNullOrEmpty(NoApprovalUsers))
{
return users;
}
var splitUsers = NoApprovalUsers.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var user in splitUsers)
{
if (!string.IsNullOrWhiteSpace(user))
users.Add(user.Trim());
}
return users;
}
}
}
}

View file

@ -42,5 +42,8 @@ namespace PlexRequests.Core.SettingModels
[Obsolete("We use the CRON job now")]
public int RecentlyAdded { get; set; }
public string RecentlyAddedCron { get; set; }
public int FaultQueueHandler { get; set; }
public int PlexContentCacher { get; set; }
public int PlexUserChecker { get; set; }
}
}

View file

@ -0,0 +1,63 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SystemSettings.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using PlexRequests.Core.Models;
namespace PlexRequests.Core.SettingModels
{
public class SystemSettings : Settings
{
public Branches Branch { get; set; }
public string Version { get; set; }
public StatusModel Status { get; set; }
[JsonIgnore]
public List<BranchDropdown> BranchDropdown { get; set; }
}
public class BranchDropdown
{
public bool Selected { get; set; }
public string Name { get; set; }
public Branches Value { get; set; }
}
public enum Branches
{
[Display(Name = "Stable")]
Stable,
[Display(Name = "Early Access Preview")]
EarlyAccessPreview,
[Display(Name = "Development")]
Dev,
}
}

View file

@ -0,0 +1,45 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserManagementSettings.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
namespace PlexRequests.Core.SettingModels
{
public class UserManagementSettings : Settings
{
public bool RequestMovies { get; set; }
public bool RequestTvShows { get; set; }
public bool RequestMusic { get; set; }
public bool AutoApproveMovies { get; set; }
public bool AutoApproveTvShows { get; set; }
public bool AutoApproveMusic { get; set; }
public bool ReportIssues { get; set; }
public bool UsersCanViewOnlyOwnRequests { get; set; }
public bool UsersCanViewOnlyOwnIssues { get; set; }
// Features
public bool RecentlyAddedNotification { get; set; }
public bool RecentlyAddedNewsletter { get; set; }
}
}

View file

@ -50,7 +50,7 @@ namespace PlexRequests.Core
Db = new DbConfiguration(new SqliteFactory());
var created = Db.CheckDb();
TableCreation.CreateTables(Db.DbConnection());
if (created)
{
CreateDefaultSettingsPage(urlBase);
@ -61,65 +61,35 @@ namespace PlexRequests.Core
TableCreation.Vacuum(Db.DbConnection());
}
// The below code is obsolete, we should use PlexRequests.Core.Migrations.MigrationRunner
var version = CheckSchema();
if (version > 0)
{
if (version > 1899 && version <= 1900)
{
MigrateToVersion1900();
}
if(version > 1899 && version <= 1910)
{
MigrateToVersion1910();
}
}
// Add the new 'running' item into the scheduled jobs so we can check if the cachers are running
Db.DbConnection().AlterTable("ScheduledJobs", "ADD", "Running", true, "INTEGER");
return Db.DbConnection().ConnectionString;
}
public static string ConnectionString => Db.DbConnection().ConnectionString;
private int CheckSchema()
{
var productVersion = AssemblyHelper.GetProductVersion();
var trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(productVersion, string.Empty).PadRight(4, '0');
var version = int.Parse(trimStatus);
var connection = Db.DbConnection();
var schema = connection.GetSchemaVersion();
if (schema == null)
{
connection.CreateSchema(version); // Set the default.
schema = connection.GetSchemaVersion();
}
if (version > schema.SchemaVersion)
{
Db.DbConnection().UpdateSchemaVersion(version);
schema = connection.GetSchemaVersion();
}
version = schema.SchemaVersion;
return version;
}
private void CreateDefaultSettingsPage(string baseUrl)
{
var defaultUserSettings = new UserManagementSettings
{
RequestMovies = true,
RequestTvShows = true,
ReportIssues = true,
};
var defaultSettings = new PlexRequestSettings
{
RequireTvShowApproval = true,
RequireMovieApproval = true,
SearchForMovies = true,
SearchForTvShows = true,
BaseUrl = baseUrl ?? string.Empty,
CollectAnalyticData = true,
};
var s = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
var ctor = new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider());
var s = new SettingsServiceV2<PlexRequestSettings>(ctor);
s.SaveSettings(defaultSettings);
var userSettings = new SettingsServiceV2<UserManagementSettings>(ctor);
userSettings.SaveSettings(defaultUserSettings);
var cron = (Quartz.Impl.Triggers.CronTriggerImpl)CronScheduleBuilder.WeeklyOnDayAndHourAndMinute(DayOfWeek.Friday, 7, 0).Build();
var scheduled = new ScheduledJobsSettings
@ -148,7 +118,6 @@ namespace PlexRequests.Core
Task.Run(() => { CacheSonarrQualityProfiles(mc); });
Task.Run(() => { CacheCouchPotatoQualityProfiles(mc); });
// we don't need to cache sickrage profiles, those are static
// TODO: cache headphones profiles?
}
catch (Exception)
{
@ -156,12 +125,12 @@ namespace PlexRequests.Core
}
}
private void CacheSonarrQualityProfiles(MemoryCacheProvider cacheProvider)
private void CacheSonarrQualityProfiles(ICacheProvider cacheProvider)
{
try
{
Log.Info("Executing GetSettings call to Sonarr for quality profiles");
var sonarrSettingsService = new SettingsServiceV2<SonarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
var sonarrSettingsService = new SettingsServiceV2<SonarrSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var sonarrSettings = sonarrSettingsService.GetSettings();
if (sonarrSettings.Enabled)
{
@ -178,12 +147,12 @@ namespace PlexRequests.Core
}
}
private void CacheCouchPotatoQualityProfiles(MemoryCacheProvider cacheProvider)
private void CacheCouchPotatoQualityProfiles(ICacheProvider cacheProvider)
{
try
{
Log.Info("Executing GetSettings call to CouchPotato for quality profiles");
var cpSettingsService = new SettingsServiceV2<CouchPotatoSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()));
var cpSettingsService = new SettingsServiceV2<CouchPotatoSettings>(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider));
var cpSettings = cpSettingsService.GetSettings();
if (cpSettings.Enabled)
{
@ -199,102 +168,5 @@ namespace PlexRequests.Core
Log.Error(ex, "Failed to cache CouchPotato quality profiles!");
}
}
/// <summary>
/// Migrates to version 1.9.
/// Move the Plex auth token to the new field.
/// Reconfigure the log level
/// Set the wizard flag to true if we already have settings
/// </summary>
public void MigrateToVersion1900()
{
// Need to change the Plex Token location
var authSettings = new SettingsServiceV2<AuthenticationSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var auth = authSettings.GetSettings();
var plexSettings = new SettingsServiceV2<PlexSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
if (auth != null)
{
//If we have an authToken we do not need to go through the setup
if (!string.IsNullOrEmpty(auth.OldPlexAuthToken))
{
var prServuce = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var settings = prServuce.GetSettings();
settings.Wizard = true;
prServuce.SaveSettings(settings);
}
// Clear out the old token and save it to the new field
var currentSettings = plexSettings.GetSettings();
if (!string.IsNullOrEmpty(auth.OldPlexAuthToken))
{
currentSettings.PlexAuthToken = auth.OldPlexAuthToken;
plexSettings.SaveSettings(currentSettings);
// Clear out the old value
auth.OldPlexAuthToken = string.Empty;
authSettings.SaveSettings(auth);
}
}
// Set the log level
try
{
var settingsService = new SettingsServiceV2<LogSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var logSettings = settingsService.GetSettings();
logSettings.Level = LogLevel.Error.Ordinal;
settingsService.SaveSettings(logSettings);
LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level));
}
catch (Exception e)
{
Log.Error(e);
}
// Enable analytics;
try
{
var prSettings = new SettingsServiceV2<PlexRequestSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var settings = prSettings.GetSettings();
settings.CollectAnalyticData = true;
var updated = prSettings.SaveSettings(settings);
}
catch (Exception e)
{
Log.Error(e);
}
}
/// <summary>
/// Migrates to version1910.
/// </summary>
public void MigrateToVersion1910()
{
try
{
// Get the new machine Identifier
var settings = new SettingsServiceV2<PlexSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
var plex = settings.GetSettings();
if (!string.IsNullOrEmpty(plex.PlexAuthToken))
{
var api = new PlexApi(new ApiRequest());
var server = api.GetServer(plex.PlexAuthToken); // Get the server info
plex.MachineIdentifier = server.Server.FirstOrDefault(x => x.AccessToken == plex.PlexAuthToken)?.MachineIdentifier;
settings.SaveSettings(plex); // Save the new settings
}
}
catch (Exception e)
{
Log.Error(e);
}
}
}
}

View file

@ -1,83 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: StatusChecker.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.Linq;
using System.Threading.Tasks;
using Octokit;
using PlexRequests.Core.Models;
using PlexRequests.Helpers;
namespace PlexRequests.Core
{
public class StatusChecker
{
public StatusChecker()
{
Git = new GitHubClient(new ProductHeaderValue("PlexRequests-StatusChecker"));
}
private IGitHubClient Git { get; }
private const string Owner = "tidusjar";
private const string RepoName = "PlexRequests.Net";
public async Task<Release> GetLatestRelease()
{
var releases = await Git.Repository.Release.GetAll(Owner, RepoName);
return releases.FirstOrDefault();
}
public async Task<StatusModel> GetStatus()
{
var assemblyVersion = AssemblyHelper.GetProductVersion();
var model = new StatusModel
{
Version = assemblyVersion,
};
var latestRelease = await GetLatestRelease();
if (latestRelease == null)
{
return new StatusModel { Version = "Unknown" };
}
var latestVersionArray = latestRelease.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries);
var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty;
if (!latestVersion.Equals(assemblyVersion, StringComparison.InvariantCultureIgnoreCase))
{
model.UpdateAvailable = true;
model.UpdateUri = latestRelease.HtmlUrl;
}
model.ReleaseNotes = latestRelease.Body;
model.DownloadUri = latestRelease.Assets[0].BrowserDownloadUrl;
model.ReleaseTitle = latestRelease.Name;
return model;
}
}
}

View file

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AppveyorArtifactResult.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
namespace PlexRequests.Core.StatusChecker
{
public class AppveyorArtifactResult
{
public string fileName { get; set; }
public string type { get; set; }
public int size { get; set; }
}
}

View file

@ -0,0 +1,138 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AppveyorBranchResult.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Collections.Generic;
namespace PlexRequests.Core.StatusChecker
{
public class NuGetFeed
{
public string id { get; set; }
public string name { get; set; }
public bool publishingEnabled { get; set; }
public string created { get; set; }
}
public class AccessRightDefinition
{
public string name { get; set; }
public string description { get; set; }
}
public class AccessRight
{
public string name { get; set; }
public bool allowed { get; set; }
}
public class RoleAce
{
public int roleId { get; set; }
public string name { get; set; }
public bool isAdmin { get; set; }
public List<AccessRight> accessRights { get; set; }
}
public class SecurityDescriptor
{
public List<AccessRightDefinition> accessRightDefinitions { get; set; }
public List<RoleAce> roleAces { get; set; }
}
public class Project
{
public int projectId { get; set; }
public int accountId { get; set; }
public string accountName { get; set; }
public List<object> builds { get; set; }
public string name { get; set; }
public string slug { get; set; }
public string repositoryType { get; set; }
public string repositoryScm { get; set; }
public string repositoryName { get; set; }
public string repositoryBranch { get; set; }
public bool isPrivate { get; set; }
public bool skipBranchesWithoutAppveyorYml { get; set; }
public bool enableSecureVariablesInPullRequests { get; set; }
public bool enableSecureVariablesInPullRequestsFromSameRepo { get; set; }
public bool enableDeploymentInPullRequests { get; set; }
public bool rollingBuilds { get; set; }
public bool alwaysBuildClosedPullRequests { get; set; }
public string tags { get; set; }
public NuGetFeed nuGetFeed { get; set; }
public SecurityDescriptor securityDescriptor { get; set; }
public string created { get; set; }
public string updated { get; set; }
}
public class Job
{
public string jobId { get; set; }
public string name { get; set; }
public bool allowFailure { get; set; }
public int messagesCount { get; set; }
public int compilationMessagesCount { get; set; }
public int compilationErrorsCount { get; set; }
public int compilationWarningsCount { get; set; }
public int testsCount { get; set; }
public int passedTestsCount { get; set; }
public int failedTestsCount { get; set; }
public int artifactsCount { get; set; }
public string status { get; set; }
public string started { get; set; }
public string finished { get; set; }
public string created { get; set; }
public string updated { get; set; }
}
public class Build
{
public int buildId { get; set; }
public List<Job> jobs { get; set; }
public int buildNumber { get; set; }
public string version { get; set; }
public string message { get; set; }
public string branch { get; set; }
public bool isTag { get; set; }
public string commitId { get; set; }
public string authorName { get; set; }
public string committerName { get; set; }
public string committed { get; set; }
public List<object> messages { get; set; }
public string status { get; set; }
public string started { get; set; }
public string finished { get; set; }
public string created { get; set; }
public string updated { get; set; }
}
public class AppveyorBranchResult
{
public Project project { get; set; }
public Build build { get; set; }
}
}

View file

@ -0,0 +1,183 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: StatusChecker.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Octokit;
using PlexRequests.Api;
using PlexRequests.Core.Models;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using RestSharp;
namespace PlexRequests.Core.StatusChecker
{
public class StatusChecker : IStatusChecker
{
public StatusChecker(ISettingsService<SystemSettings> ss)
{
SystemSettings = ss;
Git = new GitHubClient(new ProductHeaderValue("PlexRequests-StatusChecker"));
}
private ISettingsService<SystemSettings> SystemSettings { get; }
private IGitHubClient Git { get; }
private const string Owner = "tidusjar";
private const string RepoName = "PlexRequests.Net";
private const string AppveyorApiUrl = "https://ci.appveyor.com/api";
private const string Api =
"48Ku58C0794nBrXra8IxWav+dc6NqgkRw+PZB3/bQwbt/D0IrnJQkgtjzo0bd6nkooLMKsC8M+Ab7jyBO+ROjY14VRuxffpDopX9r0iG/fjBl6mZVvqkm+VTDNstDtzp";
public async Task<StatusModel> GetStatus()
{
var settings = await SystemSettings.GetSettingsAsync();
var stable = settings.Branch == Branches.Stable;
if (!stable)
{
// Early Access Preview Releases
return GetAppveyorRelease(settings.Branch);
}
// Stable releases
return await GetLatestGithubRelease();
}
private async Task<StatusModel> GetLatestGithubRelease()
{
var assemblyVersion = AssemblyHelper.GetProductVersion();
var model = new StatusModel
{
CurrentVersion = assemblyVersion,
};
var releases = await Git.Repository.Release.GetAll(Owner, RepoName);
var latestRelease = releases.FirstOrDefault();
if (latestRelease == null)
{
return new StatusModel { NewVersion = "Unknown" };
}
var latestVersionArray = latestRelease.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries);
var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty;
if (!latestVersion.Equals(assemblyVersion, StringComparison.InvariantCultureIgnoreCase))
{
model.UpdateAvailable = true;
model.UpdateUri = latestRelease.HtmlUrl;
model.NewVersion = latestVersion;
}
model.ReleaseNotes = latestRelease.Body;
model.DownloadUri = latestRelease.Assets[0].BrowserDownloadUrl;
model.ReleaseTitle = latestRelease.Name;
return model;
}
private StatusModel GetAppveyorRelease(Branches branch)
{
var request = new ApiRequest();
// Get latest EAP Build
var eapBranchRequest = new RestRequest
{
Method = Method.GET
};
switch (branch)
{
case Branches.Dev:
eapBranchRequest.Resource = "/projects/tidusjar/requestplex/branch/dev";
break;
case Branches.EarlyAccessPreview:
eapBranchRequest.Resource = "/projects/tidusjar/requestplex/branch/EAP";
break;
}
var api = StringCipher.Decrypt(Api,"Appveyor");
eapBranchRequest.AddHeader("Authorization", $"Bearer {api}");
eapBranchRequest.AddHeader("Content-Type", "application/json");
var branchResult = request.ExecuteJson<AppveyorBranchResult>(eapBranchRequest, new Uri(AppveyorApiUrl));
var jobId = branchResult.build.jobs.FirstOrDefault()?.jobId ?? string.Empty;
if (string.IsNullOrEmpty(jobId))
{
return new StatusModel {UpdateAvailable = false};
}
// Get artifacts from the EAP Build
var eapAtrifactRequest = new RestRequest
{
Resource = $"/buildjobs/{jobId}/artifacts",
Method = Method.GET
};
eapAtrifactRequest.AddHeader("Authorization", $"Bearer {api}");
eapAtrifactRequest.AddHeader("Content-Type", "application/json");
var artifactResults = request.ExecuteJson<List<AppveyorArtifactResult>>(eapAtrifactRequest, new Uri(AppveyorApiUrl));
var artifactResult = artifactResults.FirstOrDefault();
if (artifactResult == null)
{
return new StatusModel
{
UpdateAvailable = false
};
}
var downloadLink = $"{AppveyorApiUrl}/buildjobs/{jobId}/artifacts/{artifactResult.fileName}";
var branchDisplay = EnumHelper<Branches>.GetDisplayValue(branch);
var fileversion = AssemblyHelper.GetFileVersion();
var model = new StatusModel
{
DownloadUri = downloadLink,
ReleaseNotes = $"{branchDisplay} (See recent commits for details)",
ReleaseTitle = $"Plex Requests {branchDisplay}",
NewVersion = branchResult.build.version,
UpdateUri = downloadLink,
CurrentVersion = fileversion
};
if (!fileversion.Equals(branchResult.build.version, StringComparison.CurrentCultureIgnoreCase))
{
model.UpdateAvailable = true;
}
return model;
}
}
}

View file

@ -24,18 +24,19 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.SickRage;
using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core.SettingModels;
using PlexRequests.Store;
using System.Linq;
using System.Threading.Tasks;
namespace PlexRequests.UI.Helpers
namespace PlexRequests.Core
{
public class TvSender
{

View file

@ -36,7 +36,7 @@ using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core.SettingModels;
using PlexRequests.Store;
namespace PlexRequests.UI.Helpers
namespace PlexRequests.Core
{
public class TvSenderOld
{

View file

@ -36,6 +36,7 @@ using Nancy.Security;
using PlexRequests.Core.Models;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Store;
using PlexRequests.Store.Repository;
@ -60,7 +61,6 @@ namespace PlexRequests.Core
return new UserIdentity
{
UserName = user.UserName,
Claims = ByteConverterHelper.ReturnObject<string[]>(user.Claims)
};
}
@ -99,7 +99,7 @@ namespace PlexRequests.Core
return users.Any();
}
public Guid? CreateUser(string username, string password, string[] claims = default(string[]), UserProperties properties = null)
public Guid? CreateUser(string username, string password, UserProperties properties = null)
{
var salt = PasswordHasher.GenerateSalt();
@ -109,7 +109,7 @@ namespace PlexRequests.Core
UserGuid = Guid.NewGuid().ToString(),
Salt = salt,
Hash = PasswordHasher.ComputeHash(password, salt),
Claims = ByteConverterHelper.ReturnBytes(claims),
Claims = new byte[] {0},
UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()),
};
Repo.Insert(userModel);
@ -118,32 +118,33 @@ namespace PlexRequests.Core
return new Guid(userRecord.UserGuid);
}
public Guid? CreateUser(string username, string password, int permissions, int features, UserProperties properties = null)
{
var salt = PasswordHasher.GenerateSalt();
var userModel = new UsersModel
{
UserName = username,
UserGuid = Guid.NewGuid().ToString(),
Salt = salt,
Hash = PasswordHasher.ComputeHash(password, salt),
UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()),
Permissions = permissions,
Features = features,
Claims = new byte[] {0}
};
Repo.Insert(userModel);
var userRecord = Repo.Get(userModel.UserGuid);
return new Guid(userRecord.UserGuid);
}
public void DeleteUser(string userId)
{
var user = Repo.Get(userId);
Repo.Delete(user);
}
public Guid? CreateAdmin(string username, string password, UserProperties properties = null)
{
return CreateUser(username, password, new[] { UserClaims.RegularUser, UserClaims.PowerUser, UserClaims.Admin }, properties);
}
public Guid? CreatePowerUser(string username, string password, UserProperties properties = null)
{
return CreateUser(username, password, new[] { UserClaims.RegularUser, UserClaims.PowerUser }, properties);
}
public Guid? CreateRegularUser(string username, string password, UserProperties properties = null)
{
return CreateUser(username, password, new[] { UserClaims.RegularUser }, properties);
}
public IEnumerable<string> GetAllClaims()
{
var properties = typeof(UserClaims).GetConstantsValues<string>();
return properties;
}
public bool UpdatePassword(string username, string oldPassword, string newPassword)
{
@ -186,8 +187,8 @@ namespace PlexRequests.Core
public interface ICustomUserMapper
{
Guid? CreateUser(string username, string password, string[] claims, UserProperties props);
IEnumerable<string> GetAllClaims();
Guid? CreateUser(string username, string password, int permissions, int features,
UserProperties properties = null);
IEnumerable<UsersModel> GetUsers();
Task<IEnumerable<UsersModel>> GetUsersAsync();
UsersModel GetUser(Guid userId);
@ -195,9 +196,6 @@ namespace PlexRequests.Core
bool DoUsersExist();
Guid? ValidateUser(string username, string password);
bool UpdatePassword(string username, string oldPassword, string newPassword);
Guid? CreateAdmin(string username, string password, UserProperties properties = null);
Guid? CreatePowerUser(string username, string password, UserProperties properties = null);
Guid? CreateRegularUser(string username, string password, UserProperties properties = null);
void DeleteUser(string userId);
}
}

View file

@ -0,0 +1,12 @@
using System.Collections.Generic;
using PlexRequests.Helpers.Permissions;
namespace PlexRequests.Core.Users
{
public interface IUserHelper
{
IEnumerable<UserHelperModel> GetUsers();
IEnumerable<UserHelperModel> GetUsersWithPermission(Permissions permission);
IEnumerable<UserHelperModel> GetUsersWithFeature(Features feature);
}
}

View file

@ -0,0 +1,159 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserHelper.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Collections.Generic;
using System.Linq;
using PlexRequests.Core.Models;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Store.Repository;
namespace PlexRequests.Core.Users
{
public class UserHelper : IUserHelper
{
public UserHelper(IUserRepository userRepository, IPlexUserRepository plexUsers, ISecurityExtensions security)
{
LocalUserRepository = userRepository;
PlexUserRepository = plexUsers;
Security = security;
}
private IUserRepository LocalUserRepository { get; }
private IPlexUserRepository PlexUserRepository { get; }
private ISecurityExtensions Security { get; }
public IEnumerable<UserHelperModel> GetUsers()
{
var model = new List<UserHelperModel>();
var localUsers = LocalUserRepository.GetAll();
var plexUsers = PlexUserRepository.GetAll();
foreach (var user in localUsers)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
model.Add(new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions)user.Permissions
});
}
model.AddRange(plexUsers.Select(user => new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions
}));
return model;
}
public IEnumerable<UserHelperModel> GetUsersWithPermission(Permissions permission)
{
var model = new List<UserHelperModel>();
var localUsers = LocalUserRepository.GetAll().ToList();
var plexUsers = PlexUserRepository.GetAll().ToList();
var filteredLocal = localUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission));
var filteredPlex = plexUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission));
foreach (var user in filteredLocal)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
model.Add(new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
});
}
model.AddRange(filteredPlex.Select(user => new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
}));
return model;
}
public IEnumerable<UserHelperModel> GetUsersWithFeature(Features features)
{
var model = new List<UserHelperModel>();
var localUsers = LocalUserRepository.GetAll().ToList();
var plexUsers = PlexUserRepository.GetAll().ToList();
var filteredLocal = localUsers.Where(x => ((Features)x.Features).HasFlag(features));
var filteredPlex = plexUsers.Where(x => ((Features)x.Features).HasFlag(features));
foreach (var user in filteredLocal)
{
var props = ByteConverterHelper.ReturnObject<UserProperties>(user.UserProperties);
model.Add(new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.UserName,
UserAlias = props.UserAlias,
EmailAddress = props.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
});
}
model.AddRange(filteredPlex.Select(user => new UserHelperModel
{
Type = UserType.LocalUser,
Username = user.Username,
UserAlias = user.UserAlias,
EmailAddress = user.EmailAddress,
Permissions = (Permissions)user.Permissions,
Features = (Features)user.Features
}));
return model;
}
}
}

View file

@ -0,0 +1,42 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserHelperModel.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 PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
namespace PlexRequests.Core.Users
{
public class UserHelperModel
{
public string Username { get; set; }
public string UserAlias { get; set; }
public Permissions Permissions { get; set; }
public Features Features { get; set; }
public string EmailAddress { get; set; }
public UserType Type { get; set; }
}
}

View file

@ -0,0 +1,70 @@
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers.Permissions;
namespace PlexRequests.Core.Users
{
public static class UserManagementHelper
{
public static int GetPermissions(UserManagementSettings settings)
{
var permission = 0;
if (settings.AutoApproveMovies)
{
permission += (int)Permissions.AutoApproveMovie;
}
if (settings.AutoApproveMusic)
{
permission += (int)Permissions.AutoApproveAlbum;
}
if (settings.AutoApproveTvShows)
{
permission += (int)Permissions.AutoApproveTv;
}
if (settings.RequestMovies)
{
permission += (int)Permissions.RequestMovie;
}
if (settings.RequestMusic)
{
permission += (int)Permissions.RequestMusic;
}
if (settings.RequestTvShows)
{
permission += (int)Permissions.RequestTvShow;
}
if (settings.ReportIssues)
{
permission += (int)Permissions.ReportIssue;
}
if (settings.UsersCanViewOnlyOwnRequests)
{
permission += (int)Permissions.UsersCanViewOnlyOwnRequests;
}
if (settings.UsersCanViewOnlyOwnIssues)
{
permission += (int)Permissions.UsersCanViewOnlyOwnIssues;
}
return permission;
}
public static int GetFeatures(UserManagementSettings settings)
{
var features = 0;
if (settings.RecentlyAddedNewsletter)
{
features += (int)Features.Newsletter;
}
if (settings.RecentlyAddedNotification)
{
features += (int)Features.RequestAddedNotification;
}
return features;
}
}
}

View file

@ -2,11 +2,14 @@
<packages>
<package id="Common.Logging" version="3.0.0" targetFramework="net45" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
<package id="Dapper" version="1.50.0-beta8" targetFramework="net45" />
<package id="Nancy" version="1.4.3" targetFramework="net45" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net45" />
<package id="Nancy.Linker" version="0.3.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="NLog" version="4.3.6" targetFramework="net45" />
<package id="Octokit" version="0.19.0" targetFramework="net45" />
<package id="Quartz" version="2.3.3" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
<package id="valueinjecter" version="3.1.1.2" targetFramework="net45" />
</packages>

View file

@ -54,7 +54,7 @@ namespace PlexRequests.Helpers.Analytics
Track(HitType.@event, username, cat, act, label, clientId, value);
}
public async Task TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null)
public async void TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null)
{
var cat = category.ToString();
var act = action.ToString();
@ -146,7 +146,7 @@ namespace PlexRequests.Helpers.Analytics
request.Method = RequestMethod;
// set the Content-Length header to the correct value
request.ContentLength = Encoding.UTF8.GetByteCount(postDataString);
#if !DEBUG
// write the request body to the request
using (var writer = new StreamWriter(request.GetRequestStream()))
{
@ -165,7 +165,10 @@ namespace PlexRequests.Helpers.Analytics
{
Log.Error(ex, "Analytics tracking failed");
}
#endif
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
private async Task SendRequestAsync(string postDataString)
{
var request = (HttpWebRequest)WebRequest.Create(AnalyticsUri);
@ -173,6 +176,7 @@ namespace PlexRequests.Helpers.Analytics
// set the Content-Length header to the correct value
request.ContentLength = Encoding.UTF8.GetByteCount(postDataString);
#if !DEBUG
// write the request body to the request
using (var writer = new StreamWriter(await request.GetRequestStreamAsync()))
{
@ -191,7 +195,9 @@ namespace PlexRequests.Helpers.Analytics
{
Log.Error(ex, "Analytics tracking failed");
}
#endif
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
private Dictionary<string, string> BuildRequestData(HitType type, string username, string category, string action, string clientId, string label, int? value, string exceptionDescription, int? fatal)
{

View file

@ -38,6 +38,7 @@ namespace PlexRequests.Helpers.Analytics
Issues,
UserLogin,
Services,
Navbar
Navbar,
UserManagement
}
}

View file

@ -51,7 +51,7 @@ namespace PlexRequests.Helpers.Analytics
/// <param name="clientId">The client identifier.</param>
/// <param name="value">The value.</param>
/// <returns></returns>
Task TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null);
void TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null);
/// <summary>
/// Tracks the page view.

View file

@ -45,5 +45,13 @@ namespace PlexRequests.Helpers
var retVersion = fvi.ProductVersion;
return retVersion;
}
public static string GetFileVersion()
{
var assembly = Assembly.GetExecutingAssembly();
var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
var retVersion = fvi.FileVersion;
return retVersion;
}
}
}

View file

@ -0,0 +1,54 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EnumExtensions.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;
namespace PlexRequests.Helpers
{
public static class EnumExtensions
{
public static IEnumerable<Enum> GetUniqueFlags(this Enum flags)
{
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<Enum>())
{
var bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value))
{
yield return value;
}
}
}
}
}

View file

@ -0,0 +1,117 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: EnumHelper.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.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
namespace PlexRequests.Helpers
{
public static class EnumHelper<T>
{
public static IList<T> GetValues(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => (T) Enum.Parse(value.GetType(), fi.Name, false)).ToList();
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
public static IList<string> GetNames(Enum value)
{
return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
}
public static IList<string> GetDisplayValues(Enum value)
{
return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
}
private static string LookupResource(Type resourceManagerProvider, string resourceKey)
{
foreach (var staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
{
System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
return resourceManager.GetString(resourceKey);
}
}
return resourceKey; // Fallback with the key name
}
public static string GetDisplayValue(T value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var descriptionAttributes = fieldInfo.GetCustomAttributes(
typeof(DisplayAttribute), false) as DisplayAttribute[];
if (descriptionAttributes[0].ResourceType != null)
return LookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);
if (descriptionAttributes == null) return string.Empty;
return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
}
public static T GetValueFromName(string name)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
var attribute = Attribute.GetCustomAttribute(field,
typeof(DisplayAttribute)) as DisplayAttribute;
if (attribute != null)
{
if (attribute.Name == name)
{
return (T)field.GetValue(null);
}
}
else
{
if (field.Name == name)
return (T)field.GetValue(null);
}
}
throw new ArgumentOutOfRangeException(nameof(name));
}
public static int All()
{
return Enum.GetValues(typeof(T)).Cast<int>().Sum();
}
}
}

View file

@ -1,7 +1,7 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AuthenticationSettingsTests.cs
// File: Features.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
@ -26,21 +26,17 @@
#endregion
using System;
using NUnit.Framework;
using System.ComponentModel.DataAnnotations;
namespace PlexRequests.Core.Tests
namespace PlexRequests.Helpers.Permissions
{
[TestFixture]
public class StatusCheckerTests
[Flags]
public enum Features
{
[Test]
[Ignore("API Limit")]
public void CheckStatusTest()
{
var checker = new StatusChecker();
var status = checker.GetStatus();
[Display(Name = "Newsletter")]
Newsletter = 1,
[Display(Name = "Request Added Notification")]
RequestAddedNotification = 2,
Assert.That(status, Is.Not.Null);
}
}
}
}

View file

@ -0,0 +1,78 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Permissions.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.ComponentModel.DataAnnotations;
namespace PlexRequests.Helpers.Permissions
{
[Flags]
////
//// NOTE if any are added, make sure we change the UserManagementHelper
////
public enum Permissions
{
[Display(Name = "Access Administration Settings")]
Administrator = 1,
[Display(Name = "Request Movie")]
RequestMovie = 2,
[Display(Name = "Request TV Show")]
RequestTvShow = 4,
[Display(Name = "Request Music")]
RequestMusic = 8,
[Display(Name = "Report Issue")]
ReportIssue = 16,
[Display(Name = "Read Only User")]
ReadOnlyUser = 32,
[Display(Name = "Auto Approve Movie Requests")]
AutoApproveMovie = 64,
[Display(Name = "Auto Approve TV Show Requests")]
AutoApproveTv = 128,
[Display(Name = "Auto Approve Album Requests")]
AutoApproveAlbum = 256,
[Display(Name = "Manage Requests")]
ManageRequests = 512,
[Display(Name = "Users can only view their own requests")]
UsersCanViewOnlyOwnRequests = 1024,
[Display(Name = "Users can only view their own issues")]
UsersCanViewOnlyOwnIssues = 2048,
[Display(Name = "Bypass the request limit")]
BypassRequestLimit = 4096
}
}

View file

@ -39,6 +39,10 @@
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Ninject, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
@ -48,6 +52,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Web" />
@ -69,6 +74,7 @@
<Compile Include="ByteConverterHelper.cs" />
<Compile Include="CookieHelper.cs" />
<Compile Include="DateTimeHelper.cs" />
<Compile Include="EnumHelper.cs" />
<Compile Include="Exceptions\ApiRequestException.cs" />
<Compile Include="Exceptions\ApplicationSettingsException.cs" />
<Compile Include="HtmlRemover.cs" />
@ -78,9 +84,13 @@
<Compile Include="MemoryCacheProvider.cs" />
<Compile Include="ObjectCopier.cs" />
<Compile Include="PasswordHasher.cs" />
<Compile Include="EnumExtensions.cs" />
<Compile Include="Permissions\Features.cs" />
<Compile Include="Permissions\Permissions.cs" />
<Compile Include="PlexHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SerializerSettings.cs" />
<Compile Include="SessionKeys.cs" />
<Compile Include="StringCipher.cs" />
<Compile Include="StringHasher.cs" />
<Compile Include="StringHelper.cs" />

View file

@ -24,7 +24,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace PlexRequests.UI.Models
namespace PlexRequests.Helpers
{
public class SessionKeys
{

View file

@ -2,6 +2,7 @@
<packages>
<package id="Nancy" version="1.4.3" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="Ninject" version="3.2.0.0" targetFramework="net45" />
<package id="NLog" version="4.3.6" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
</packages>

View file

@ -1,284 +1,284 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexAvailabilityCheckerTests.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.Data;
using System.Linq;
using System.Threading.Tasks;
//#region Copyright
//// /************************************************************************
//// Copyright (c) 2016 Jamie Rees
//// File: PlexAvailabilityCheckerTests.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.Data;
//using System.Linq;
//using System.Threading.Tasks;
using Moq;
//using Moq;
using NUnit.Framework;
//using NUnit.Framework;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces;
using PlexRequests.Helpers;
using PlexRequests.Services.Jobs;
using PlexRequests.Services.Models;
using PlexRequests.Services.Notification;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
//using PlexRequests.Api.Interfaces;
//using PlexRequests.Api.Models.Plex;
//using PlexRequests.Core;
//using PlexRequests.Core.SettingModels;
//using PlexRequests.Services.Interfaces;
//using PlexRequests.Helpers;
//using PlexRequests.Services.Jobs;
//using PlexRequests.Services.Models;
//using PlexRequests.Services.Notification;
//using PlexRequests.Store.Models;
//using PlexRequests.Store.Repository;
using Ploeh.AutoFixture;
//using Ploeh.AutoFixture;
namespace PlexRequests.Services.Tests
{
[TestFixture]
public class PlexAvailabilityCheckerTests
{
public IAvailabilityChecker Checker { get; set; }
private Fixture F { get; set; } = new Fixture();
private Mock<ISettingsService<PlexSettings>> SettingsMock { get; set; }
private Mock<ISettingsService<AuthenticationSettings>> AuthMock { get; set; }
private Mock<IRequestService> RequestMock { get; set; }
private Mock<IPlexApi> PlexMock { get; set; }
private Mock<ICacheProvider> CacheMock { get; set; }
private Mock<INotificationService> NotificationMock { get; set; }
private Mock<IJobRecord> JobRec { get; set; }
private Mock<IRepository<UsersToNotify>> NotifyUsers { get; set; }
private Mock<IRepository<PlexEpisodes>> PlexEpisodes { get; set; }
private Mock<INotificationEngine> Engine
{
get;
set;
}
//namespace PlexRequests.Services.Tests
//{
// [TestFixture]
// public class PlexAvailabilityCheckerTests
// {
// public IAvailabilityChecker Checker { get; set; }
// private Fixture F { get; set; } = new Fixture();
// private Mock<ISettingsService<PlexSettings>> SettingsMock { get; set; }
// private Mock<ISettingsService<AuthenticationSettings>> AuthMock { get; set; }
// private Mock<IRequestService> RequestMock { get; set; }
// private Mock<IPlexApi> PlexMock { get; set; }
// private Mock<ICacheProvider> CacheMock { get; set; }
// private Mock<INotificationService> NotificationMock { get; set; }
// private Mock<IJobRecord> JobRec { get; set; }
// private Mock<IRepository<UsersToNotify>> NotifyUsers { get; set; }
// private Mock<IRepository<PlexEpisodes>> PlexEpisodes { get; set; }
// private Mock<INotificationEngine> Engine
// {
// get;
// set;
// }
[SetUp]
public void Setup()
{
SettingsMock = new Mock<ISettingsService<PlexSettings>>();
AuthMock = new Mock<ISettingsService<AuthenticationSettings>>();
RequestMock = new Mock<IRequestService>();
PlexMock = new Mock<IPlexApi>();
NotificationMock = new Mock<INotificationService>();
CacheMock = new Mock<ICacheProvider>();
NotifyUsers = new Mock<IRepository<UsersToNotify>>();
PlexEpisodes = new Mock<IRepository<PlexEpisodes>>();
JobRec = new Mock<IJobRecord>();
Engine = new Mock<INotificationEngine>();
Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object, Engine.Object);
// [SetUp]
// public void Setup()
// {
// SettingsMock = new Mock<ISettingsService<PlexSettings>>();
// AuthMock = new Mock<ISettingsService<AuthenticationSettings>>();
// RequestMock = new Mock<IRequestService>();
// PlexMock = new Mock<IPlexApi>();
// NotificationMock = new Mock<INotificationService>();
// CacheMock = new Mock<ICacheProvider>();
// NotifyUsers = new Mock<IRepository<UsersToNotify>>();
// PlexEpisodes = new Mock<IRepository<PlexEpisodes>>();
// JobRec = new Mock<IJobRecord>();
// Engine = new Mock<INotificationEngine>();
// Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object, Engine.Object);
}
// }
[Test]
public void InvalidSettings()
{
Checker.CheckAndUpdateAll();
PlexMock.Verify(x => x.GetLibrary(It.IsAny<string>(), It.IsAny<Uri>(), It.IsAny<string>()), Times.Never);
PlexMock.Verify(x => x.GetAccount(It.IsAny<string>()), Times.Never);
PlexMock.Verify(x => x.GetLibrarySections(It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
PlexMock.Verify(x => x.GetStatus(It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Never);
}
// [Test]
// public void InvalidSettings()
// {
// Checker.CheckAndUpdateAll();
// PlexMock.Verify(x => x.GetLibrary(It.IsAny<string>(), It.IsAny<Uri>(), It.IsAny<string>()), Times.Never);
// PlexMock.Verify(x => x.GetAccount(It.IsAny<string>()), Times.Never);
// PlexMock.Verify(x => x.GetLibrarySections(It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
// PlexMock.Verify(x => x.GetStatus(It.IsAny<string>(), It.IsAny<Uri>()), Times.Never);
// PlexMock.Verify(x => x.GetUsers(It.IsAny<string>()), Times.Never);
// }
[TestCaseSource(nameof(IsMovieAvailableTestData))]
public bool IsMovieAvailableTest(string title, string year)
{
var movies = new List<PlexMovie>
{
new PlexMovie {Title = title, ProviderId = null, ReleaseYear = year}
};
var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011");
// [TestCaseSource(nameof(IsMovieAvailableTestData))]
// public bool IsMovieAvailableTest(string title, string year)
// {
// var movies = new List<PlexMovie>
// {
// new PlexMovie {Title = title, ProviderId = null, ReleaseYear = year}
// };
// var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011");
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> IsMovieAvailableTestData
{
get
{
yield return new TestCaseData("title", "2011").Returns(true).SetName("IsMovieAvailable True");
yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsMovieAvailable False different title");
yield return new TestCaseData("title", "2001").Returns(false).SetName("IsMovieAvailable False different year");
}
}
// private static IEnumerable<TestCaseData> IsMovieAvailableTestData
// {
// get
// {
// yield return new TestCaseData("title", "2011").Returns(true).SetName("IsMovieAvailable True");
// yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsMovieAvailable False different title");
// yield return new TestCaseData("title", "2001").Returns(false).SetName("IsMovieAvailable False different year");
// }
// }
[TestCaseSource(nameof(IsMovieAvailableAdvancedTestData))]
public bool IsMovieAvailableAdvancedTest(string title, string year, string providerId)
{
var movies = new List<PlexMovie>
{
new PlexMovie {Title = title, ProviderId = providerId, ReleaseYear = year }
};
var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011", 9999.ToString());
// [TestCaseSource(nameof(IsMovieAvailableAdvancedTestData))]
// public bool IsMovieAvailableAdvancedTest(string title, string year, string providerId)
// {
// var movies = new List<PlexMovie>
// {
// new PlexMovie {Title = title, ProviderId = providerId, ReleaseYear = year }
// };
// var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011", 9999.ToString());
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> IsMovieAvailableAdvancedTestData
{
get
{
yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsMovieAvailable True");
yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsMovieAvailable False different title");
yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsMovieAvailable False different year");
yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsMovieAvailable False different providerID");
}
}
// private static IEnumerable<TestCaseData> IsMovieAvailableAdvancedTestData
// {
// get
// {
// yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsMovieAvailable True");
// yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsMovieAvailable False different title");
// yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsMovieAvailable False different year");
// yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsMovieAvailable False different providerID");
// }
// }
[TestCaseSource(nameof(IsTvAvailableTestData))]
public bool IsTvAvailableTest(string title, string year)
{
var tv = new List<PlexTvShow>
{
new PlexTvShow {Title = title, ProviderId = null, ReleaseYear = year}
};
var result = Checker.IsTvShowAvailable(tv.ToArray(), "title", "2011");
// [TestCaseSource(nameof(IsTvAvailableTestData))]
// public bool IsTvAvailableTest(string title, string year)
// {
// var tv = new List<PlexTvShow>
// {
// new PlexTvShow {Title = title, ProviderId = null, ReleaseYear = year}
// };
// var result = Checker.IsTvShowAvailable(tv.ToArray(), "title", "2011");
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> IsTvAvailableTestData
{
get
{
yield return new TestCaseData("title", "2011").Returns(true).SetName("IsTvAvailable True");
yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsTvAvailable False different title");
yield return new TestCaseData("title", "2001").Returns(false).SetName("IsTvAvailable False different year");
}
}
// private static IEnumerable<TestCaseData> IsTvAvailableTestData
// {
// get
// {
// yield return new TestCaseData("title", "2011").Returns(true).SetName("IsTvAvailable True");
// yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsTvAvailable False different title");
// yield return new TestCaseData("title", "2001").Returns(false).SetName("IsTvAvailable False different year");
// }
// }
[TestCaseSource(nameof(IsTvAvailableAdvancedTestData))]
public bool IsTvAvailableAdvancedTest(string title, string year, string providerId)
{
var movies = new List<PlexTvShow>
{
new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year }
};
var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString());
// [TestCaseSource(nameof(IsTvAvailableAdvancedTestData))]
// public bool IsTvAvailableAdvancedTest(string title, string year, string providerId)
// {
// var movies = new List<PlexTvShow>
// {
// new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year }
// };
// var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString());
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> IsTvAvailableAdvancedTestData
{
get
{
yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsTvAvailable True");
yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsTvAvailable False different title");
yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsTvAvailable False different year");
yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsTvAvailable False different providerID");
}
}
// private static IEnumerable<TestCaseData> IsTvAvailableAdvancedTestData
// {
// get
// {
// yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsTvAvailable True");
// yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsTvAvailable False different title");
// yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsTvAvailable False different year");
// yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsTvAvailable False different providerID");
// }
// }
[TestCaseSource(nameof(IsTvAvailableAdvancedSeasonsTestData))]
public bool IsTvAvailableAdvancedSeasonsTest(string title, string year, string providerId, int[] seasons)
{
var movies = new List<PlexTvShow>
{
new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year , Seasons = seasons}
};
var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString(), new[] { 1, 2, 3 });
// [TestCaseSource(nameof(IsTvAvailableAdvancedSeasonsTestData))]
// public bool IsTvAvailableAdvancedSeasonsTest(string title, string year, string providerId, int[] seasons)
// {
// var movies = new List<PlexTvShow>
// {
// new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year , Seasons = seasons}
// };
// var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString(), new[] { 1, 2, 3 });
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> IsTvAvailableAdvancedSeasonsTestData
{
get
{
yield return new TestCaseData("title", "2011", "9999", new[] { 1, 2, 3 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable True");
yield return new TestCaseData("title2", "2011", "99929", new[] { 5, 6 }).Returns(false).SetName("Advanced IsTvSeasonsAvailable False no seasons");
yield return new TestCaseData("title2", "2011", "9999", new[] { 1, 6 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable true one season");
}
}
// private static IEnumerable<TestCaseData> IsTvAvailableAdvancedSeasonsTestData
// {
// get
// {
// yield return new TestCaseData("title", "2011", "9999", new[] { 1, 2, 3 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable True");
// yield return new TestCaseData("title2", "2011", "99929", new[] { 5, 6 }).Returns(false).SetName("Advanced IsTvSeasonsAvailable False no seasons");
// yield return new TestCaseData("title2", "2011", "9999", new[] { 1, 6 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable true one season");
// }
// }
[TestCaseSource(nameof(IsEpisodeAvailableTestData))]
public bool IsEpisodeAvailableTest(string providerId, int season, int episode)
{
var expected = new List<PlexEpisodes>
{
new PlexEpisodes {EpisodeNumber = 1, ShowTitle = "The Flash",ProviderId = 23.ToString(), SeasonNumber = 1, EpisodeTitle = "Pilot"}
};
PlexEpisodes.Setup(x => x.Custom(It.IsAny<Func<IDbConnection, IEnumerable<PlexEpisodes>>>())).Returns(expected);
// [TestCaseSource(nameof(IsEpisodeAvailableTestData))]
// public bool IsEpisodeAvailableTest(string providerId, int season, int episode)
// {
// var expected = new List<PlexEpisodes>
// {
// new PlexEpisodes {EpisodeNumber = 1, ShowTitle = "The Flash",ProviderId = 23.ToString(), SeasonNumber = 1, EpisodeTitle = "Pilot"}
// };
// PlexEpisodes.Setup(x => x.Custom(It.IsAny<Func<IDbConnection, IEnumerable<PlexEpisodes>>>())).Returns(expected);
var result = Checker.IsEpisodeAvailable(providerId, season, episode);
// var result = Checker.IsEpisodeAvailable(providerId, season, episode);
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> IsEpisodeAvailableTestData
{
get
{
yield return new TestCaseData("23", 1, 1).Returns(true).SetName("IsEpisodeAvailable True S01E01");
yield return new TestCaseData("23", 1, 2).Returns(false).SetName("IsEpisodeAvailable False S01E02");
yield return new TestCaseData("23", 99, 99).Returns(false).SetName("IsEpisodeAvailable False S99E99");
yield return new TestCaseData("230", 99, 99).Returns(false).SetName("IsEpisodeAvailable False Incorrect ProviderId");
}
}
// private static IEnumerable<TestCaseData> IsEpisodeAvailableTestData
// {
// get
// {
// yield return new TestCaseData("23", 1, 1).Returns(true).SetName("IsEpisodeAvailable True S01E01");
// yield return new TestCaseData("23", 1, 2).Returns(false).SetName("IsEpisodeAvailable False S01E02");
// yield return new TestCaseData("23", 99, 99).Returns(false).SetName("IsEpisodeAvailable False S99E99");
// yield return new TestCaseData("230", 99, 99).Returns(false).SetName("IsEpisodeAvailable False Incorrect ProviderId");
// }
// }
[Test]
public void GetPlexMoviesTests()
{
var cachedMovies = F.Build<PlexSearch>().Without(x => x.Directory).CreateMany().ToList();
cachedMovies.Add(new PlexSearch
{
Video = new List<Video>
{
new Video {Type = "movie", Title = "title1", Year = "2016", ProviderId = "1212"}
}
});
CacheMock.Setup(x => x.Get<List<PlexSearch>>(CacheKeys.PlexLibaries)).Returns(cachedMovies);
SettingsMock.Setup(x => x.GetSettings()).Returns(F.Create<PlexSettings>());
var movies = Checker.GetPlexMovies();
// [Test]
// public void GetPlexMoviesTests()
// {
// var cachedMovies = F.Build<PlexSearch>().Without(x => x.Directory).CreateMany().ToList();
// cachedMovies.Add(new PlexSearch
// {
// Video = new List<Video>
// {
// new Video {Type = "movie", Title = "title1", Year = "2016", ProviderId = "1212"}
// }
// });
// CacheMock.Setup(x => x.Get<List<PlexSearch>>(CacheKeys.PlexLibaries)).Returns(cachedMovies);
// SettingsMock.Setup(x => x.GetSettings()).Returns(F.Create<PlexSettings>());
// var movies = Checker.GetPlexMovies();
Assert.That(movies.Any(x => x.ProviderId == "1212"));
}
// Assert.That(movies.Any(x => x.ProviderId == "1212"));
// }
[Test]
public void GetPlexTvShowsTests()
{
var cachedTv = F.Build<PlexSearch>().Without(x => x.Directory).CreateMany().ToList();
cachedTv.Add(new PlexSearch
{
Directory = new List<Directory1>
{
new Directory1 {Type = "show", Title = "title1", Year = "2016", ProviderId = "1212", Seasons = new List<Directory1>()}
}
});
SettingsMock.Setup(x => x.GetSettings()).Returns(F.Create<PlexSettings>());
CacheMock.Setup(x => x.Get<List<PlexSearch>>(CacheKeys.PlexLibaries)).Returns(cachedTv);
var movies = Checker.GetPlexTvShows();
// [Test]
// public void GetPlexTvShowsTests()
// {
// var cachedTv = F.Build<PlexSearch>().Without(x => x.Directory).CreateMany().ToList();
// cachedTv.Add(new PlexSearch
// {
// Directory = new List<Directory1>
// {
// new Directory1 {Type = "show", Title = "title1", Year = "2016", ProviderId = "1212", Seasons = new List<Directory1>()}
// }
// });
// SettingsMock.Setup(x => x.GetSettings()).Returns(F.Create<PlexSettings>());
// CacheMock.Setup(x => x.Get<List<PlexSearch>>(CacheKeys.PlexLibaries)).Returns(cachedTv);
// var movies = Checker.GetPlexTvShows();
Assert.That(movies.Any(x => x.ProviderId == "1212"));
}
// Assert.That(movies.Any(x => x.ProviderId == "1212"));
// }
[Test]
public async Task GetAllPlexEpisodes()
{
PlexEpisodes.Setup(x => x.GetAllAsync()).ReturnsAsync(F.CreateMany<PlexEpisodes>().ToList());
var episodes = await Checker.GetEpisodes();
// [Test]
// public async Task GetAllPlexEpisodes()
// {
// PlexEpisodes.Setup(x => x.GetAllAsync()).ReturnsAsync(F.CreateMany<PlexEpisodes>().ToList());
// var episodes = await Checker.GetEpisodes();
Assert.That(episodes.Count(), Is.GreaterThan(0));
}
}
}
// Assert.That(episodes.Count(), Is.GreaterThan(0));
// }
// }
//}

View file

@ -29,22 +29,23 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Store.Models;
using PlexRequests.Store.Models.Plex;
namespace PlexRequests.Services.Interfaces
{
public interface IAvailabilityChecker
{
void CheckAndUpdateAll();
List<PlexMovie> GetPlexMovies();
bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year, string providerId = null);
List<PlexTvShow> GetPlexTvShows();
bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year, string providerId = null, int[] seasons = null);
List<PlexAlbum> GetPlexAlbums();
bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist);
IEnumerable<PlexContent> GetPlexMovies(IEnumerable<PlexContent> content);
bool IsMovieAvailable(PlexContent[] plexMovies, string title, string year, string providerId = null);
IEnumerable<PlexContent> GetPlexTvShows(IEnumerable<PlexContent> content);
bool IsTvShowAvailable(PlexContent[] plexShows, string title, string year, string providerId = null, int[] seasons = null);
IEnumerable<PlexContent> GetPlexAlbums(IEnumerable<PlexContent> content);
bool IsAlbumAvailable(PlexContent[] plexAlbums, string title, string year, string artist);
bool IsEpisodeAvailable(string theTvDbId, int season, int episode);
PlexAlbum GetAlbum(PlexAlbum[] plexAlbums, string title, string year, string artist);
PlexMovie GetMovie(PlexMovie[] plexMovies, string title, string year, string providerId = null);
PlexTvShow GetTvShow(PlexTvShow[] plexShows, string title, string year, string providerId = null, int[] seasons = null);
PlexContent GetAlbum(PlexContent[] plexAlbums, string title, string year, string artist);
PlexContent GetMovie(PlexContent[] plexMovies, string title, string year, string providerId = null);
PlexContent GetTvShow(PlexContent[] plexShows, string title, string year, string providerId = null, int[] seasons = null);
/// <summary>
/// Gets the episode's stored in the cache.
/// </summary>

View file

@ -36,5 +36,6 @@ namespace PlexRequests.Services.Interfaces
void Record(string jobName);
Task<IEnumerable<ScheduledJobs>> GetJobsAsync();
IEnumerable<ScheduledJobs> GetJobs();
void SetRunning(bool running, string jobName);
}
}

View file

@ -27,13 +27,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Core.Models;
using PlexRequests.Store;
namespace PlexRequests.Services.Interfaces
{
public interface INotificationEngine
{
Task NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey);
Task NotifyUsers(RequestedModel modelChanged, string apiKey);
Task NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey, NotificationType type);
Task NotifyUsers(RequestedModel modelChanged, string apiKey, NotificationType type);
}
}

View file

@ -65,6 +65,7 @@ namespace PlexRequests.Services.Jobs
var settings = CpSettings.GetSettings();
if (settings.Enabled)
{
Job.SetRunning(true, JobNames.CpCacher);
Log.Trace("Getting all movies from CouchPotato");
try
{
@ -81,6 +82,7 @@ namespace PlexRequests.Services.Jobs
finally
{
Job.Record(JobNames.CpCacher);
Job.SetRunning(false, JobNames.CpCacher);
}
}
}

View file

@ -0,0 +1,327 @@
#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.Api;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using Quartz;
namespace PlexRequests.Services.Jobs
{
public class FaultQueueHandler : IJob
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public FaultQueueHandler(IJobRecord record, IRepository<RequestQueue> repo, ISonarrApi sonarrApi,
ISickRageApi srApi, ISettingsService<SonarrSettings> sonarrSettings, ISettingsService<SickRageSettings> srSettings,
ICouchPotatoApi cpApi, ISettingsService<CouchPotatoSettings> cpsettings, IRequestService requestService,
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi headphonesApi, ISettingsService<PlexRequestSettings> prSettings,
ISecurityExtensions security)
{
Record = record;
Repo = repo;
SonarrApi = sonarrApi;
SrApi = srApi;
CpApi = cpApi;
HpApi = headphonesApi;
RequestService = requestService;
SickrageSettings = srSettings;
SonarrSettings = sonarrSettings;
CpSettings = cpsettings;
HeadphoneSettings = hpSettings;
Security = security;
PrSettings = prSettings.GetSettings();
}
private IRepository<RequestQueue> Repo { get; }
private IJobRecord Record { get; }
private ISonarrApi SonarrApi { get; }
private ISickRageApi SrApi { get; }
private ICouchPotatoApi CpApi { get; }
private IHeadphonesApi HpApi { get; }
private IRequestService RequestService { get; }
private PlexRequestSettings PrSettings { get; }
private ISettingsService<SonarrSettings> SonarrSettings { get; }
private ISettingsService<SickRageSettings> SickrageSettings { get; }
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
private ISettingsService<HeadphonesSettings> HeadphoneSettings { get; }
private ISecurityExtensions Security { get; }
public void Execute(IJobExecutionContext context)
{
Record.SetRunning(true, JobNames.CpCacher);
try
{
var faultedRequests = Repo.GetAll().ToList();
var missingInfo = faultedRequests.Where(x => x.FaultType == FaultType.MissingInformation).ToList();
ProcessMissingInformation(missingInfo);
var transientErrors = faultedRequests.Where(x => x.FaultType == FaultType.RequestFault).ToList();
ProcessTransientErrors(transientErrors);
}
catch (Exception e)
{
Log.Error(e);
}
finally
{
Record.Record(JobNames.FaultQueueHandler);
Record.SetRunning(false, JobNames.CpCacher);
}
}
private void ProcessMissingInformation(List<RequestQueue> requests)
{
if (!requests.Any())
{
return;
}
var sonarrSettings = SonarrSettings.GetSettings();
var sickrageSettings = SickrageSettings.GetSettings();
var tv = requests.Where(x => x.Type == RequestType.TvShow);
// TV
var tvApi = new TvMazeApi();
foreach (var t in tv)
{
var providerId = int.Parse(t.PrimaryIdentifier);
var showInfo = tvApi.ShowLookup(providerId);
if (showInfo.externals?.thetvdb != null)
{
// We now have the info
var tvModel = ByteConverterHelper.ReturnObject<RequestedModel>(t.Content);
tvModel.ProviderId = showInfo.externals.thetvdb.Value;
var result = ProcessTvShow(tvModel, sonarrSettings, sickrageSettings);
if (!result)
{
// we now have the info but couldn't add it, so add it back into the queue but with a different fault
t.Content = ByteConverterHelper.ReturnBytes(tvModel);
t.FaultType = FaultType.RequestFault;
t.LastRetry = DateTime.UtcNow;
Repo.Update(t);
}
else
{
// Successful, remove from the fault queue
Repo.Delete(t);
}
}
}
}
private bool ProcessTvShow(RequestedModel tvModel, SonarrSettings sonarr, SickRageSettings sickrage)
{
try
{
var sender = new TvSenderOld(SonarrApi, SrApi);
if (sonarr.Enabled)
{
var task = sender.SendToSonarr(sonarr, tvModel, sonarr.QualityProfile);
var a = task.Result;
if (string.IsNullOrEmpty(a?.title))
{
// Couldn't send it
return false;
}
return true;
}
if (sickrage.Enabled)
{
var result = sender.SendToSickRage(sickrage, tvModel);
if (result?.result != "success")
{
// Couldn't send it
return false;
}
// Approve it
tvModel.Approved = true;
RequestService.UpdateRequest(tvModel);
return true;
}
return false;
}
catch (Exception e)
{
Log.Error(e);
return false; // It fails so it will get added back into the queue
}
}
private bool ProcessMovies(RequestedModel model, CouchPotatoSettings cp)
{
try
{
if (cp.Enabled)
{
var result = CpApi.AddMovie(model.ImdbId, cp.ApiKey, model.Title,
cp.FullUri, cp.ProfileId);
if (result)
{
// Approve it now
model.Approved = true;
RequestService.UpdateRequest(model);
};
return result;
}
return false;
}
catch (Exception e)
{
Log.Error(e);
return false; // It fails so it will get added back into the queue
}
}
private bool ProcessAlbums(RequestedModel model, HeadphonesSettings hp)
{
try
{
if (hp.Enabled)
{
var sender = new HeadphonesSender(HpApi, hp, RequestService);
var result = sender.AddAlbum(model).Result;
if (result)
{
if (ShouldAutoApprove(model.Type, PrSettings, model.RequestedUsers))
// Approve it now
model.Approved = true;
RequestService.UpdateRequest(model);
};
return result;
}
return false;
}
catch (Exception e)
{
Log.Error(e);
return false; // It fails so it will get added back into the queue
}
}
private void ProcessTransientErrors(List<RequestQueue> requests)
{
var sonarrSettings = SonarrSettings.GetSettings();
var sickrageSettings = SickrageSettings.GetSettings();
var cpSettings = CpSettings.GetSettings();
var hpSettings = HeadphoneSettings.GetSettings();
if (!requests.Any())
{
return;
}
foreach (var request in requests)
{
var model = ByteConverterHelper.ReturnObject<RequestedModel>(request.Content);
bool result;
switch (request.Type)
{
case RequestType.Movie:
result = ProcessMovies(model, cpSettings);
break;
case RequestType.TvShow:
result = ProcessTvShow(model, sonarrSettings, sickrageSettings);
break;
case RequestType.Album:
result = ProcessAlbums(model, hpSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
if (!result)
{
// we now have the info but couldn't add it, so do nothing now.
request.LastRetry = DateTime.UtcNow;
Repo.Update(request);
}
else
{
// Successful, remove from the fault queue
Repo.Delete(request);
}
}
}
public bool ShouldAutoApprove(RequestType requestType, PlexRequestSettings prSettings, List<string> username)
{
foreach (var user in username)
{
var admin = Security.HasPermissions(user, Permissions.Administrator);
// if the user is an admin or they are whitelisted, they go ahead and allow auto-approval
if (admin) return true;
// check by request type if the category requires approval or not
switch (requestType)
{
case RequestType.Movie:
return Security.HasPermissions(user, Permissions.AutoApproveMovie);
case RequestType.TvShow:
return Security.HasPermissions(user, Permissions.AutoApproveTv);
case RequestType.Album:
return Security.HasPermissions(user, Permissions.AutoApproveAlbum);
default:
return false;
}
}
return false;
}
}
}

View file

@ -0,0 +1,10 @@
using Quartz;
namespace PlexRequests.Services.Jobs
{
public interface IPlexContentCacher
{
void CacheContent();
void Execute(IJobExecutionContext context);
}
}

View file

@ -33,9 +33,13 @@ namespace PlexRequests.Services.Jobs
public const string SonarrCacher = "Sonarr Cacher";
public const string SrCacher = "SickRage Cacher";
public const string PlexChecker = "Plex Availability Cacher";
public const string PlexCacher = "Plex Cacher";
public const string StoreCleanup = "Database Cleanup";
public const string RequestLimitReset = "Request Limit Reset";
public const string EpisodeCacher = "Plex Episode Cacher";
public const string RecentlyAddedEmail = "Recently Added Email Notification";
public const string FaultQueueHandler = "Request Fault Queue Handler";
public const string PlexUserChecker = "Plex User Checker";
}
}

View file

@ -60,6 +60,23 @@ namespace PlexRequests.Services.Jobs
}
}
public void SetRunning(bool running, string jobName)
{
var allJobs = Repo.GetAll();
var storeJob = allJobs.FirstOrDefault(x => x.Name == jobName);
if (storeJob != null)
{
storeJob.Running = running;
Repo.Update(storeJob);
}
else
{
var job = new ScheduledJobs { Running = running, Name = jobName };
Repo.Insert(job);
}
}
public async Task<IEnumerable<ScheduledJobs>> GetJobsAsync()
{
return await Repo.GetAllAsync();

View file

@ -30,7 +30,7 @@ using System.Linq;
using System.Threading.Tasks;
using Dapper;
using Newtonsoft.Json;
using NLog;
using PlexRequests.Api.Interfaces;
@ -44,6 +44,7 @@ using PlexRequests.Services.Models;
using PlexRequests.Services.Notification;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Models.Plex;
using PlexRequests.Store.Repository;
using Quartz;
@ -53,7 +54,7 @@ namespace PlexRequests.Services.Jobs
public class PlexAvailabilityChecker : IJob, IAvailabilityChecker
{
public PlexAvailabilityChecker(ISettingsService<PlexSettings> plexSettings, IRequestService request, IPlexApi plex, ICacheProvider cache,
INotificationService notify, IJobRecord rec, IRepository<UsersToNotify> users, IRepository<PlexEpisodes> repo, INotificationEngine e)
INotificationService notify, IJobRecord rec, IRepository<UsersToNotify> users, IRepository<PlexEpisodes> repo, INotificationEngine e, IRepository<PlexContent> content)
{
Plex = plexSettings;
RequestService = request;
@ -64,6 +65,7 @@ namespace PlexRequests.Services.Jobs
UserNotifyRepo = users;
EpisodeRepo = repo;
NotificationEngine = e;
PlexContent = content;
}
private ISettingsService<PlexSettings> Plex { get; }
@ -76,7 +78,7 @@ namespace PlexRequests.Services.Jobs
private IJobRecord Job { get; }
private IRepository<UsersToNotify> UserNotifyRepo { get; }
private INotificationEngine NotificationEngine { get; }
private IRepository<PlexContent> PlexContent { get; }
public void CheckAndUpdateAll()
{
@ -87,18 +89,17 @@ namespace PlexRequests.Services.Jobs
Log.Debug("Validation of the plex settings failed.");
return;
}
//var libraries = CachedLibraries(plexSettings, true); //force setting the cache (10 min intervals via scheduler)
var libraries = CachedLibraries(plexSettings, true); //force setting the cache (10 min intervals via scheduler)
if (libraries == null || !libraries.Any())
{
Log.Debug("Did not find any libraries in Plex.");
return;
}
var movies = GetPlexMovies().ToArray();
var shows = GetPlexTvShows().ToArray();
var albums = GetPlexAlbums().ToArray();
//if (libraries == null || !libraries.Any())
//{
// Log.Debug("Did not find any libraries in Plex.");
// return;
//}
var content = PlexContent.GetAll().ToList();
var movies = GetPlexMovies(content).ToArray();
var shows = GetPlexTvShows(content).ToArray();
var albums = GetPlexAlbums(content).ToArray();
var requests = RequestService.GetAll();
var requestedModels = requests as RequestedModel[] ?? requests.Where(x => !x.Available).ToArray();
@ -153,15 +154,12 @@ namespace PlexRequests.Services.Jobs
if (modifiedModel.Any())
{
NotificationEngine.NotifyUsers(modifiedModel, plexSettings.PlexAuthToken);
NotificationEngine.NotifyUsers(modifiedModel, plexSettings.PlexAuthToken, NotificationType.RequestAvailable);
RequestService.BatchUpdate(modifiedModel);
}
Job.Record(JobNames.PlexChecker);
}
public List<PlexMovie> GetPlexMovies()
public List<PlexMovie> GetPlexMoviesOld()
{
var settings = Plex.GetSettings();
var movies = new List<PlexMovie>();
@ -188,13 +186,18 @@ namespace PlexRequests.Services.Jobs
return movies;
}
public bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year, string providerId = null)
public IEnumerable<PlexContent> GetPlexMovies(IEnumerable<PlexContent> content)
{
return content.Where(x => x.Type == Store.Models.Plex.PlexMediaType.Movie);
}
public bool IsMovieAvailable(PlexContent[] plexMovies, string title, string year, string providerId = null)
{
var movie = GetMovie(plexMovies, title, year, providerId);
return movie != null;
}
public PlexMovie GetMovie(PlexMovie[] plexMovies, string title, string year, string providerId = null)
public PlexContent GetMovie(PlexContent[] plexMovies, string title, string year, string providerId = null)
{
if (plexMovies.Length == 0)
{
@ -225,45 +228,19 @@ namespace PlexRequests.Services.Jobs
return null;
}
public List<PlexTvShow> GetPlexTvShows()
public IEnumerable<PlexContent> GetPlexTvShows(IEnumerable<PlexContent> content)
{
var settings = Plex.GetSettings();
var shows = new List<PlexTvShow>();
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
if (libs != null)
{
var withDir = libs.Where(x => x.Directory != null);
var tvLibs = withDir.Where(x =>
x.Directory.Any(y =>
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
)
).ToArray();
foreach (var lib in tvLibs)
{
shows.AddRange(lib.Directory.Select(x => new PlexTvShow // shows are in the directory list
{
Title = x.Title,
ReleaseYear = x.Year,
ProviderId = x.ProviderId,
Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray(),
Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey)
}));
}
}
return shows;
return content.Where(x => x.Type == Store.Models.Plex.PlexMediaType.Show);
}
public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year, string providerId = null, int[] seasons = null)
public bool IsTvShowAvailable(PlexContent[] plexShows, string title, string year, string providerId = null, int[] seasons = null)
{
var show = GetTvShow(plexShows, title, year, providerId, seasons);
return show != null;
}
public PlexTvShow GetTvShow(PlexTvShow[] plexShows, string title, string year, string providerId = null,
public PlexContent GetTvShow(PlexContent[] plexShows, string title, string year, string providerId = null,
int[] seasons = null)
{
var advanced = !string.IsNullOrEmpty(providerId);
@ -273,7 +250,8 @@ namespace PlexRequests.Services.Jobs
{
if (show.ProviderId == providerId && seasons != null)
{
if (seasons.Any(season => show.Seasons.Contains(season)))
var showSeasons = ByteConverterHelper.ReturnObject<int[]>(show.Seasons);
if (seasons.Any(season => showSeasons.Contains(season)))
{
return show;
}
@ -359,41 +337,19 @@ namespace PlexRequests.Services.Jobs
return plexEpisodeses;
}
public List<PlexAlbum> GetPlexAlbums()
public IEnumerable<PlexContent> GetPlexAlbums(IEnumerable<PlexContent> content)
{
var settings = Plex.GetSettings();
var albums = new List<PlexAlbum>();
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
if (libs != null)
{
var albumLibs = libs.Where(x =>
x.Directory.Any(y =>
y.Type.Equals(PlexMediaType.Artist.ToString(), StringComparison.CurrentCultureIgnoreCase)
)
).ToArray();
foreach (var lib in albumLibs)
{
albums.AddRange(lib.Directory.Select(x => new PlexAlbum()
{
Title = x.Title,
ReleaseYear = x.Year,
Artist = x.ParentTitle,
Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey)
}));
}
}
return albums;
return content.Where(x => x.Type == Store.Models.Plex.PlexMediaType.Artist);
}
public bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist)
public bool IsAlbumAvailable(PlexContent[] plexAlbums, string title, string year, string artist)
{
return plexAlbums.Any(x =>
x.Title.Contains(title) &&
x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase));
}
public PlexAlbum GetAlbum(PlexAlbum[] plexAlbums, string title, string year, string artist)
public PlexContent GetAlbum(PlexContent[] plexAlbums, string title, string year, string artist)
{
return plexAlbums.FirstOrDefault(x =>
x.Title.Contains(title) &&
@ -419,11 +375,11 @@ namespace PlexRequests.Services.Jobs
results = GetLibraries(plexSettings);
if (plexSettings.AdvancedSearch)
{
for (var i = 0; i < results.Count; i++)
foreach (PlexSearch t in results)
{
for (var j = 0; j < results[i].Directory.Count; j++)
foreach (Directory1 t1 in t.Directory)
{
var currentItem = results[i].Directory[j];
var currentItem = t1;
var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
currentItem.RatingKey);
@ -434,25 +390,21 @@ namespace PlexRequests.Services.Jobs
currentItem.RatingKey);
// We do not want "all episodes" this as a season
var filtered =
seasons.Directory.Where(
x =>
!x.Title.Equals("All episodes",
StringComparison.CurrentCultureIgnoreCase));
var filtered = seasons.Directory.Where( x => !x.Title.Equals("All episodes", StringComparison.CurrentCultureIgnoreCase));
results[i].Directory[j].Seasons.AddRange(filtered);
t1.Seasons.AddRange(filtered);
}
var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Directory.Guid);
results[i].Directory[j].ProviderId = providerId;
t1.ProviderId = providerId;
}
for (var j = 0; j < results[i].Video.Count; j++)
foreach (Video t1 in t.Video)
{
var currentItem = results[i].Video[j];
var currentItem = t1;
var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
currentItem.RatingKey);
var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Video.Guid);
results[i].Video[j].ProviderId = providerId;
t1.ProviderId = providerId;
}
}
}
@ -479,10 +431,10 @@ namespace PlexRequests.Services.Jobs
{
var sections = PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
List<PlexSearch> libs = new List<PlexSearch>();
var libs = new List<PlexSearch>();
if (sections != null)
{
foreach (var dir in sections.Directories)
foreach (var dir in sections.Directories ?? new List<Directory>())
{
var lib = PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.Key);
if (lib != null)
@ -507,6 +459,8 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context)
{
Job.SetRunning(true, JobNames.PlexChecker);
try
{
CheckAndUpdateAll();
@ -515,6 +469,11 @@ namespace PlexRequests.Services.Jobs
{
Log.Error(e);
}
finally
{
Job.Record(JobNames.PlexChecker);
Job.SetRunning(false, JobNames.PlexChecker);
}
}
}
}

View file

@ -0,0 +1,361 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexAvailabilityChecker.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using Newtonsoft.Json;
using NLog;
using Org.BouncyCastle.Crypto.Modes.Gcm;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.Models;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Models;
using PlexRequests.Services.Notification;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Models.Plex;
using PlexRequests.Store.Repository;
using Quartz;
namespace PlexRequests.Services.Jobs
{
public class PlexContentCacher : IJob, IPlexContentCacher
{
public PlexContentCacher(ISettingsService<PlexSettings> plexSettings, IRequestService request, IPlexApi plex, ICacheProvider cache,
INotificationService notify, IJobRecord rec, IRepository<UsersToNotify> users, IRepository<PlexEpisodes> repo, INotificationEngine e, IRepository<PlexContent> content)
{
Plex = plexSettings;
RequestService = request;
PlexApi = plex;
Cache = cache;
Notification = notify;
Job = rec;
UserNotifyRepo = users;
EpisodeRepo = repo;
NotificationEngine = e;
PlexContent = content;
}
private ISettingsService<PlexSettings> Plex { get; }
private IRepository<PlexEpisodes> EpisodeRepo { get; }
private IRequestService RequestService { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private IPlexApi PlexApi { get; }
private ICacheProvider Cache { get; }
private INotificationService Notification { get; }
private IJobRecord Job { get; }
private IRepository<UsersToNotify> UserNotifyRepo { get; }
private INotificationEngine NotificationEngine { get; }
private IRepository<PlexContent> PlexContent { get; }
public void CacheContent()
{
var plexSettings = Plex.GetSettings();
if (!ValidateSettings(plexSettings))
{
Log.Debug("Validation of the plex settings failed.");
return;
}
var libraries = CachedLibraries(plexSettings);
if (libraries == null || !libraries.Any())
{
Log.Debug("Did not find any libraries in Plex.");
return;
}
}
public List<PlexMovie> GetPlexMovies(List<PlexSearch> libs)
{
var settings = Plex.GetSettings();
var movies = new List<PlexMovie>();
if (libs != null)
{
var movieLibs = libs.Where(x =>
x.Video.Any(y =>
y.Type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)
)
).ToArray();
foreach (var lib in movieLibs)
{
movies.AddRange(lib.Video.Select(video => new PlexMovie
{
ReleaseYear = video.Year,
Title = video.Title,
ProviderId = video.ProviderId,
Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, video.RatingKey)
}));
}
}
return movies;
}
public List<PlexTvShow> GetPlexTvShows(List<PlexSearch> libs)
{
var settings = Plex.GetSettings();
var shows = new List<PlexTvShow>();
if (libs != null)
{
var withDir = libs.Where(x => x.Directory != null);
var tvLibs = withDir.Where(x =>
x.Directory.Any(y =>
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
)
).ToArray();
foreach (var lib in tvLibs)
{
shows.AddRange(lib.Directory.Select(x => new PlexTvShow // shows are in the directory list
{
Title = x.Title,
ReleaseYear = x.Year,
ProviderId = x.ProviderId,
Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray(),
Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey),
}));
}
}
return shows;
}
public List<PlexAlbum> GetPlexAlbums(List<PlexSearch> libs)
{
var settings = Plex.GetSettings();
var albums = new List<PlexAlbum>();
if (libs != null)
{
var albumLibs = libs.Where(x =>
x.Directory.Any(y =>
y.Type.Equals(PlexMediaType.Artist.ToString(), StringComparison.CurrentCultureIgnoreCase)
)
).ToArray();
foreach (var lib in albumLibs)
{
albums.AddRange(lib.Directory.Select(x => new PlexAlbum()
{
Title = x.Title,
ProviderId = x.ProviderId,
ReleaseYear = x.Year,
Artist = x.ParentTitle,
Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey)
}));
}
}
return albums;
}
private List<PlexSearch> CachedLibraries(PlexSettings plexSettings)
{
var results = new List<PlexSearch>();
if (!ValidateSettings(plexSettings))
{
Log.Warn("The settings are not configured");
return results; // don't error out here, just let it go! let it goo!!!
}
try
{
results = GetLibraries(plexSettings);
if (plexSettings.AdvancedSearch)
{
foreach (PlexSearch t in results)
{
foreach (Directory1 t1 in t.Directory)
{
var currentItem = t1;
var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
currentItem.RatingKey);
// Get the seasons for each show
if (currentItem.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
{
var seasons = PlexApi.GetSeasons(plexSettings.PlexAuthToken, plexSettings.FullUri,
currentItem.RatingKey);
// We do not want "all episodes" this as a season
var filtered = seasons.Directory.Where(x => !x.Title.Equals("All episodes", StringComparison.CurrentCultureIgnoreCase));
t1.Seasons.AddRange(filtered);
}
var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Directory.Guid);
t1.ProviderId = providerId;
}
foreach (Video t1 in t.Video)
{
var currentItem = t1;
var metaData = PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
currentItem.RatingKey);
var providerId = PlexHelper.GetProviderIdFromPlexGuid(metaData.Video.Guid);
t1.ProviderId = providerId;
}
}
}
if (results != null)
{
var movies = GetPlexMovies(results);
// Time to destroy the plex movies from the DB
PlexContent.Custom(connection =>
{
connection.Open();
connection.Query("delete from PlexContent where type = @type", new { type = 0 });
return new List<PlexContent>();
});
foreach (var m in movies)
{
PlexContent.Insert(new PlexContent
{
ProviderId = m.ProviderId,
ReleaseYear = m.ReleaseYear ?? string.Empty,
Title = m.Title,
Type = Store.Models.Plex.PlexMediaType.Movie,
Url = m.Url
});
}
var tv = GetPlexTvShows(results);
// Time to destroy the plex tv from the DB
PlexContent.Custom(connection =>
{
connection.Open();
connection.Query("delete from PlexContent where type = @type", new { type = 1 });
return new List<PlexContent>();
});
foreach (var t in tv)
{
PlexContent.Insert(new PlexContent
{
ProviderId = t.ProviderId,
ReleaseYear = t.ReleaseYear ?? string.Empty,
Title = t.Title,
Type = Store.Models.Plex.PlexMediaType.Show,
Url = t.Url,
Seasons = ByteConverterHelper.ReturnBytes(t.Seasons)
});
}
var albums = GetPlexAlbums(results);
// Time to destroy the plex movies from the DB
PlexContent.Custom(connection =>
{
connection.Open();
connection.Query("delete from PlexContent where type = @type", new { type = 2 });
return new List<PlexContent>();
});
foreach (var a in albums)
{
PlexContent.Insert(new PlexContent
{
ProviderId = a.ProviderId,
ReleaseYear = a.ReleaseYear ?? string.Empty,
Title = a.Title,
Type = Store.Models.Plex.PlexMediaType.Artist,
Url = a.Url
});
}
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to obtain Plex libraries");
}
return results;
}
private List<PlexSearch> GetLibraries(PlexSettings plexSettings)
{
var sections = PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
var libs = new List<PlexSearch>();
if (sections != null)
{
foreach (var dir in sections.Directories ?? new List<Directory>())
{
var lib = PlexApi.GetLibrary(plexSettings.PlexAuthToken, plexSettings.FullUri, dir.Key);
if (lib != null)
{
libs.Add(lib);
}
}
}
return libs;
}
private bool ValidateSettings(PlexSettings plex)
{
if (plex?.Ip == null || plex?.PlexAuthToken == null)
{
Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token.");
return false;
}
return true;
}
public void Execute(IJobExecutionContext context)
{
Job.SetRunning(true, JobNames.PlexCacher);
try
{
CacheContent();
}
catch (Exception e)
{
Log.Error(e);
}
finally
{
Job.Record(JobNames.PlexCacher);
Job.SetRunning(false, JobNames.PlexCacher);
}
}
}
}

View file

@ -145,6 +145,7 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context)
{
try
{
var s = Plex.GetSettings();
@ -162,6 +163,7 @@ namespace PlexRequests.Services.Jobs
return;
}
}
Job.SetRunning(true, JobNames.EpisodeCacher);
CacheEpisodes(s);
}
catch (Exception e)
@ -171,6 +173,7 @@ namespace PlexRequests.Services.Jobs
finally
{
Job.Record(JobNames.EpisodeCacher);
Job.SetRunning(false, JobNames.EpisodeCacher);
}
}
}

View file

@ -0,0 +1,175 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: StoreCleanup.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.Linq;
using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Core.Users;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using Quartz;
namespace PlexRequests.Services.Jobs
{
public class PlexUserChecker : IJob
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public PlexUserChecker(IPlexUserRepository plexUsers, IPlexApi plexAPi, IJobRecord rec, ISettingsService<PlexSettings> plexSettings, ISettingsService<PlexRequestSettings> prSettings, ISettingsService<UserManagementSettings> umSettings,
IRequestService requestService, IUserRepository localUser)
{
Repo = plexUsers;
JobRecord = rec;
PlexApi = plexAPi;
PlexSettings = plexSettings;
PlexRequestSettings = prSettings;
UserManagementSettings = umSettings;
RequestService = requestService;
LocalUserRepository = localUser;
}
private IJobRecord JobRecord { get; }
private IPlexApi PlexApi { get; }
private IPlexUserRepository Repo { get; }
private ISettingsService<PlexSettings> PlexSettings { get; }
private ISettingsService<PlexRequestSettings> PlexRequestSettings { get; }
private ISettingsService<UserManagementSettings> UserManagementSettings { get; }
private IRequestService RequestService { get; }
private IUserRepository LocalUserRepository { get; }
public void Execute(IJobExecutionContext context)
{
JobRecord.SetRunning(true, JobNames.PlexUserChecker);
try
{
var settings = PlexSettings.GetSettings();
if (string.IsNullOrEmpty(settings.PlexAuthToken))
{
return;
}
var plexUsers = PlexApi.GetUsers(settings.PlexAuthToken);
var userManagementSettings = UserManagementSettings.GetSettings();
var requests = RequestService.GetAll().ToList();
var dbUsers = Repo.GetAll().ToList();
var localUsers = LocalUserRepository.GetAll().ToList();
foreach (var user in plexUsers.User)
{
var dbUser = dbUsers.FirstOrDefault(x => x.PlexUserId == user.Id);
if (dbUser != null)
{
// We already have the user, let's check if they have updated any of their info.
var needToUpdate = false;
var usernameChanged = false;
// Do we need up update any info?
if (!dbUser.EmailAddress.Equals(user.Email, StringComparison.CurrentCultureIgnoreCase))
{
dbUser.EmailAddress = user.Email;
needToUpdate = true;
}
if (!dbUser.Username.Equals(user.Username, StringComparison.CurrentCultureIgnoreCase))
{
needToUpdate = true;
usernameChanged = true;
}
if (needToUpdate)
{
if (usernameChanged)
{
// The username has changed, let's check if the username matches any local users
var localUser = localUsers.FirstOrDefault(x => x.UserName.Equals(user.Username, StringComparison.CurrentCultureIgnoreCase));
dbUser.Username = user.Username;
if (localUser != null)
{
// looks like we have a local user with the same name...
// We should delete the local user and the Plex user will become the master,
// I am not going to update the Plex Users permissions as that could end up leading to a security vulnerability
// Where anyone could change their Plex Username to the PR.Net server admins name and get all the admin permissions.
LocalUserRepository.Delete(localUser);
}
// Since the username has changed, we need to update all requests with that username (unless we are using the alias! Since the alias won't change)
if (string.IsNullOrEmpty(dbUser.UserAlias))
{
// Update all requests
var requestsWithThisUser = requests.Where(x => x.RequestedUsers.Contains(user.Username)).ToList();
foreach (var r in requestsWithThisUser)
{
r.RequestedUsers.Remove(user.Username); // Remove old
r.RequestedUsers.Add(dbUser.Username); // Add new
}
if (requestsWithThisUser.Any())
{
RequestService.BatchUpdate(requestsWithThisUser);
}
}
}
Repo.Update(dbUser);
}
continue;
}
// Looks like it's a new user!
var m = new PlexUsers
{
PlexUserId = user.Id,
Permissions = UserManagementHelper.GetPermissions(userManagementSettings),
Features = UserManagementHelper.GetFeatures(userManagementSettings),
UserAlias = string.Empty,
EmailAddress = user.Email,
Username = user.Username,
LoginId = Guid.NewGuid().ToString()
};
Repo.Insert(m);
}
}
catch (Exception e)
{
Log.Error(e);
}
finally
{
JobRecord.SetRunning(false, JobNames.PlexUserChecker);
JobRecord.Record(JobNames.PlexUserChecker);
}
}
}
}

View file

@ -39,7 +39,9 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Core.Users;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Jobs.Templates;
using PlexRequests.Store.Models.Plex;
@ -52,8 +54,8 @@ namespace PlexRequests.Services.Jobs
{
public RecentlyAdded(IPlexApi api, ISettingsService<PlexSettings> plexSettings,
ISettingsService<EmailNotificationSettings> email, IJobRecord rec,
ISettingsService<NewletterSettings> newsletter,
IPlexReadOnlyDatabase db)
ISettingsService<NewletterSettings> newsletter,
IPlexReadOnlyDatabase db, IUserHelper userHelper)
{
JobRecord = rec;
Api = api;
@ -61,6 +63,7 @@ namespace PlexRequests.Services.Jobs
EmailSettings = email;
NewsletterSettings = newsletter;
PlexDb = db;
UserHelper = userHelper;
}
private IPlexApi Api { get; }
@ -73,6 +76,7 @@ namespace PlexRequests.Services.Jobs
private ISettingsService<NewletterSettings> NewsletterSettings { get; }
private IJobRecord JobRecord { get; }
private IPlexReadOnlyDatabase PlexDb { get; }
private IUserHelper UserHelper { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
@ -85,7 +89,7 @@ namespace PlexRequests.Services.Jobs
{
return;
}
JobRecord.SetRunning(true, JobNames.RecentlyAddedEmail);
Start(settings);
}
catch (Exception e)
@ -95,11 +99,13 @@ namespace PlexRequests.Services.Jobs
finally
{
JobRecord.Record(JobNames.RecentlyAddedEmail);
JobRecord.SetRunning(false, JobNames.RecentlyAddedEmail);
}
}
public void Test()
{
Log.Debug("Starting Test Newsletter");
var settings = NewsletterSettings.GetSettings();
Start(settings, true);
}
@ -108,29 +114,103 @@ namespace PlexRequests.Services.Jobs
{
var sb = new StringBuilder();
var plexSettings = PlexSettings.GetSettings();
Log.Debug("Got Plex Settings");
var libs = Api.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri);
var tvSection = libs.Directories.FirstOrDefault(x => x.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase));
var movieSection = libs.Directories.FirstOrDefault(x => x.type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase));
Log.Debug("Getting Plex Library Sections");
var recentlyAddedTv = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection.Key);
var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movieSection.Key);
var tvSections = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib
Log.Debug("Filtered sections for TV");
var movieSection = libs.Directories.Where(x => x.type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)); // We could have more than 1 lib
Log.Debug("Filtered sections for Movies");
GenerateMovieHtml(recentlyAddedMovies, plexSettings, sb);
GenerateTvHtml(recentlyAddedTv, plexSettings, sb);
var plexVersion = Api.GetStatus(plexSettings.PlexAuthToken, plexSettings.FullUri).Version;
var html = string.Empty;
if (plexVersion.StartsWith("1.3"))
{
var tvMetadata = new List<Metadata>();
var movieMetadata = new List<Metadata>();
foreach (var tvSection in tvSections)
{
var item = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri,
tvSection?.Key);
if (item?.MediaContainer?.Metadata != null)
{
tvMetadata.AddRange(item?.MediaContainer?.Metadata);
}
}
Log.Debug("Got RecentlyAdded TV Shows");
foreach (var movie in movieSection)
{
var recentlyAddedMovies = Api.RecentlyAdded(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key);
if (recentlyAddedMovies?.MediaContainer?.Metadata != null)
{
movieMetadata.AddRange(recentlyAddedMovies?.MediaContainer?.Metadata);
}
}
Log.Debug("Got RecentlyAdded Movies");
Log.Debug("Started Generating Movie HTML");
GenerateMovieHtml(movieMetadata, plexSettings, sb);
Log.Debug("Finished Generating Movie HTML");
Log.Debug("Started Generating TV HTML");
GenerateTvHtml(tvMetadata, plexSettings, sb);
Log.Debug("Finished Generating TV HTML");
var template = new RecentlyAddedTemplate();
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
}
else
{
// Old API
var tvChild = new List<RecentlyAddedChild>();
var movieChild = new List<RecentlyAddedChild>();
foreach (var tvSection in tvSections)
{
var recentlyAddedTv = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, tvSection?.Key);
if (recentlyAddedTv?._children != null)
{
tvChild.AddRange(recentlyAddedTv?._children);
}
}
Log.Debug("Got RecentlyAdded TV Shows");
foreach (var movie in movieSection)
{
var recentlyAddedMovies = Api.RecentlyAddedOld(plexSettings.PlexAuthToken, plexSettings.FullUri, movie?.Key);
if (recentlyAddedMovies?._children != null)
{
tvChild.AddRange(recentlyAddedMovies?._children);
}
}
Log.Debug("Got RecentlyAdded Movies");
Log.Debug("Started Generating Movie HTML");
GenerateMovieHtml(movieChild, plexSettings, sb);
Log.Debug("Finished Generating Movie HTML");
Log.Debug("Started Generating TV HTML");
GenerateTvHtml(tvChild, plexSettings, sb);
Log.Debug("Finished Generating TV HTML");
var template = new RecentlyAddedTemplate();
html = template.LoadTemplate(sb.ToString());
Log.Debug("Loaded the template");
}
var template = new RecentlyAddedTemplate();
var html = template.LoadTemplate(sb.ToString());
Send(newletterSettings, html, plexSettings, testEmail);
}
private void GenerateMovieHtml(RecentlyAddedModel movies, PlexSettings plexSettings, StringBuilder sb)
private void GenerateMovieHtml(List<RecentlyAddedChild> movies, PlexSettings plexSettings, StringBuilder sb)
{
var orderedMovies = movies.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList() ?? new List<RecentlyAddedChild>();
sb.Append("<h1>New Movies:</h1><br/><br/>");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
foreach (var movie in movies._children.OrderByDescending(x => x.addedAt.UnixTimeStampToDateTime()))
foreach (var movie in orderedMovies)
{
var plexGUID = string.Empty;
try
@ -142,7 +222,10 @@ namespace PlexRequests.Services.Jobs
var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID);
var info = _movieApi.GetMovieInformation(imdbId).Result;
if (info == null)
{
throw new Exception($"Movie with Imdb id {imdbId} returned null from the MovieApi");
}
AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}");
sb.Append("<tr>");
@ -176,14 +259,131 @@ namespace PlexRequests.Services.Jobs
}
sb.Append("</table><br/><br/>");
}
private void GenerateTvHtml(RecentlyAddedModel tv, PlexSettings plexSettings, StringBuilder sb)
private void GenerateMovieHtml(List<Metadata> movies, PlexSettings plexSettings, StringBuilder sb)
{
var orderedMovies = movies.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList() ?? new List<Metadata>();
sb.Append("<h1>New Movies:</h1><br/><br/>");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
foreach (var movie in orderedMovies)
{
var plexGUID = string.Empty;
try
{
var metaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
movie.ratingKey.ToString());
plexGUID = metaData.Video.Guid;
var imdbId = PlexHelper.GetProviderIdFromPlexGuid(plexGUID);
var info = _movieApi.GetMovieInformation(imdbId).Result;
if (info == null)
{
throw new Exception($"Movie with Imdb id {imdbId} returned null from the MovieApi");
}
AddImageInsideTable(sb, $"https://image.tmdb.org/t/p/w500{info.BackdropPath}");
sb.Append("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/");
Header(sb, 3, $"{info.Title} {info.ReleaseDate?.ToString("yyyy") ?? string.Empty}");
EndTag(sb, "a");
if (info.Genres.Any())
{
AddParagraph(sb,
$"Genre: {string.Join(", ", info.Genres.Select(x => x.Name.ToString()).ToArray())}");
}
AddParagraph(sb, info.Overview);
}
catch (Exception e)
{
Log.Error(e);
Log.Error(
"Exception when trying to process a Movie, either in getting the metadata from Plex OR getting the information from TheMovieDB, Plex GUID = {0}",
plexGUID);
}
finally
{
EndLoopHtml(sb);
}
}
sb.Append("</table><br/><br/>");
}
private void GenerateTvHtml(List<RecentlyAddedChild> tv, PlexSettings plexSettings, StringBuilder sb)
{
var orderedTv = tv.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList();
// TV
sb.Append("<h1>New Episodes:</h1><br/><br/>");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
foreach (var t in tv._children.OrderByDescending(x => x.addedAt.UnixTimeStampToDateTime()))
foreach (var t in orderedTv)
{
var plexGUID = string.Empty;
try
{
var parentMetaData = Api.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
t.parentRatingKey.ToString());
plexGUID = parentMetaData.Directory.Guid;
var info = TvApi.ShowLookupByTheTvDbId(int.Parse(PlexHelper.GetProviderIdFromPlexGuid(plexGUID)));
var banner = info.image?.original;
if (!string.IsNullOrEmpty(banner))
{
banner = banner.Replace("http", "https"); // Always use the Https banners
}
AddImageInsideTable(sb, banner);
sb.Append("<tr>");
sb.Append(
"<td align=\"center\" style=\"font-family: sans-serif; font-size: 14px; vertical-align: top;\" valign=\"top\">");
var title = $"{t.grandparentTitle} - {t.title} {t.originallyAvailableAt?.Substring(0, 4)}";
Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/");
Header(sb, 3, title);
EndTag(sb, "a");
AddParagraph(sb, $"Season: {t.parentIndex}, Episode: {t.index}");
if (info.genres.Any())
{
AddParagraph(sb, $"Genre: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}");
}
AddParagraph(sb, string.IsNullOrEmpty(t.summary) ? info.summary : t.summary);
}
catch (Exception e)
{
Log.Error(e);
Log.Error(
"Exception when trying to process a TV Show, either in getting the metadata from Plex OR getting the information from TVMaze, Plex GUID = {0}",
plexGUID);
}
finally
{
EndLoopHtml(sb);
}
}
sb.Append("</table><br/><br/>");
}
private void GenerateTvHtml(List<Metadata> tv, PlexSettings plexSettings, StringBuilder sb)
{
var orderedTv = tv.OrderByDescending(x => x?.addedAt.UnixTimeStampToDateTime()).ToList();
// TV
sb.Append("<h1>New Episodes:</h1><br/><br/>");
sb.Append(
"<table border=\"0\" cellpadding=\"0\" align=\"center\" cellspacing=\"0\" style=\"border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;\" width=\"100%\">");
foreach (var t in orderedTv)
{
var plexGUID = string.Empty;
try
@ -238,6 +438,7 @@ namespace PlexRequests.Services.Jobs
private void Send(NewletterSettings newletterSettings, string html, PlexSettings plexSettings, bool testEmail = false)
{
Log.Debug("Entering Send");
var settings = EmailSettings.GetSettings();
if (!settings.Enabled || string.IsNullOrEmpty(settings.EmailHost))
@ -246,22 +447,24 @@ namespace PlexRequests.Services.Jobs
}
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = "New Content on Plex!",
};
Log.Debug("Created Plain/HTML MIME body");
if (!testEmail)
{
if (newletterSettings.SendToPlexUsers)
var users = UserHelper.GetUsersWithFeature(Features.RequestAddedNotification);
if (users != null)
{
var users = Api.GetUsers(plexSettings.PlexAuthToken);
if (users != null)
foreach (var user in users)
{
foreach (var user in users.User)
if (!string.IsNullOrEmpty(user.EmailAddress))
{
message.Bcc.Add(new MailboxAddress(user.Username, user.Email));
message.Bcc.Add(new MailboxAddress(user.Username, user.EmailAddress));
}
}
}
@ -275,6 +478,7 @@ namespace PlexRequests.Services.Jobs
}
}
}
message.Bcc.Add(new MailboxAddress(settings.EmailUsername, settings.RecipientEmail)); // Include the admin
message.From.Add(new MailboxAddress(settings.EmailUsername, settings.EmailSender));
@ -293,7 +497,9 @@ namespace PlexRequests.Services.Jobs
client.Authenticate(settings.EmailUsername, settings.EmailPassword);
}
Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated);
Log.Debug("Sending");
client.Send(message);
Log.Debug("Sent");
client.Disconnect(true);
}
}

View file

@ -63,6 +63,9 @@ namespace PlexRequests.Services.Jobs
var settings = SrSettings.GetSettings();
if (settings.Enabled)
{
Job.SetRunning(true, JobNames.SrCacher);
Log.Trace("Getting all shows from SickRage");
try
{
@ -79,6 +82,7 @@ namespace PlexRequests.Services.Jobs
finally
{
Job.Record(JobNames.SrCacher);
Job.SetRunning(false, JobNames.SrCacher);
}
}
}

View file

@ -65,6 +65,7 @@ namespace PlexRequests.Services.Jobs
var settings = SonarrSettings.GetSettings();
if (settings.Enabled)
{
Job.SetRunning(true, JobNames.SonarrCacher);
try
{
var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri);
@ -80,6 +81,7 @@ namespace PlexRequests.Services.Jobs
finally
{
Job.Record(JobNames.SonarrCacher);
Job.SetRunning(false, JobNames.SonarrCacher);
}
}
}

View file

@ -53,6 +53,7 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context)
{
JobRecord.SetRunning(true, JobNames.CpCacher);
TakeBackup();
Cleanup();
}
@ -91,6 +92,7 @@ namespace PlexRequests.Services.Jobs
finally
{
JobRecord.Record(JobNames.StoreBackup);
JobRecord.SetRunning(false, JobNames.CpCacher);
}
}

View file

@ -78,12 +78,14 @@ namespace PlexRequests.Services.Jobs
finally
{
JobRecord.Record(JobNames.StoreCleanup);
JobRecord.SetRunning(false, JobNames.CpCacher);
}
}
public void Execute(IJobExecutionContext context)
{
JobRecord.SetRunning(true, JobNames.CpCacher);
Cleanup();
}
}

View file

@ -98,6 +98,7 @@ namespace PlexRequests.Services.Jobs
public void Execute(IJobExecutionContext context)
{
Record.SetRunning(true, JobNames.CpCacher);
try
{
var settings = Settings.GetSettings();
@ -115,6 +116,7 @@ namespace PlexRequests.Services.Jobs
finally
{
Record.Record(JobNames.RequestLimitReset);
Record.SetRunning(false, JobNames.CpCacher);
}
}
}

View file

@ -6,5 +6,7 @@
public string Artist { get; set; }
public string ReleaseYear { get; set; }
public string Url { get; set; }
public string ProviderId { get; set; }
public string Id { get; set; }
}
}

View file

@ -2,6 +2,7 @@
{
public class PlexMovie
{
public string Id { get; set; }
public string Title { get; set; }
public string ReleaseYear { get; set; }
public string ProviderId { get; set; }

View file

@ -2,6 +2,7 @@
{
public class PlexTvShow
{
public string Id { get; set; }
public string Title { get; set; }
public string ReleaseYear { get; set; }
public string ProviderId { get; set; }

View file

@ -79,14 +79,22 @@ namespace PlexRequests.Services.Notification
await EmailAvailableRequest(model, emailSettings);
break;
case NotificationType.RequestApproved:
throw new NotImplementedException();
await EmailRequestApproved(model, emailSettings);
break;
case NotificationType.AdminNote:
throw new NotImplementedException();
case NotificationType.Test:
await EmailTest(model, emailSettings);
break;
case NotificationType.RequestDeclined:
await EmailRequestDeclined(model, emailSettings);
break;
case NotificationType.ItemAddedToFaultQueue:
await EmailAddedToRequestQueue(model, emailSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
@ -129,7 +137,7 @@ namespace PlexRequests.Services.Notification
$"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
$"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}" };
var message = new MimeMessage
{
@ -150,7 +158,7 @@ namespace PlexRequests.Services.Notification
$"Plex Requests: New issue for {model.Title}!",
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!"};
var message = new MimeMessage
{
@ -164,6 +172,69 @@ namespace PlexRequests.Services.Notification
await Send(message, settings);
}
private async Task EmailAddedToRequestQueue(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Plex Requests: A request could not be added.",
$"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying" };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = $"Plex Requests: A request could not be added"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail));
await Send(message, settings);
}
private async Task EmailRequestDeclined(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Plex Requests: Your request has been declined",
$"Hello! Your request for {model.Title} has been declined, Sorry!",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! Your request for {model.Title} has been declined, Sorry!", };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = $"Plex Requests: Your request has been declined"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
message.To.Add(new MailboxAddress(model.UserEmail, model.UserEmail));
await Send(message, settings);
}
private async Task EmailRequestApproved(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Plex Requests: Your request has been approved!",
$"Hello! Your request for {model.Title} has been approved!",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! Your request for {model.Title} has been approved!", };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = $"Plex Requests: Your request has been approved!"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
message.To.Add(new MailboxAddress(model.UserEmail, model.UserEmail));
await Send(message, settings);
}
private async Task EmailAvailableRequest(NotificationModel model, EmailNotificationSettings settings)
{
if (!settings.EnableUserEmailNotifications)
@ -175,7 +246,7 @@ namespace PlexRequests.Services.Notification
$"Plex Requests: {model.Title} is now available!",
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)" };
var message = new MimeMessage
{

View file

@ -34,6 +34,8 @@ using NLog.Fluent;
using PlexRequests.Api;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core.Models;
using PlexRequests.Core.Users;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store;
using PlexRequests.Store.Models;
@ -43,19 +45,21 @@ namespace PlexRequests.Services.Notification
{
public class NotificationEngine : INotificationEngine
{
public NotificationEngine(IPlexApi p, IRepository<UsersToNotify> repo, INotificationService service)
public NotificationEngine(IPlexApi p, IRepository<UsersToNotify> repo, INotificationService service, IUserHelper userHelper)
{
PlexApi = p;
UserNotifyRepo = repo;
Notification = service;
UserHelper = userHelper;
}
private IPlexApi PlexApi { get; }
private IRepository<UsersToNotify> UserNotifyRepo { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private INotificationService Notification { get; }
private IUserHelper UserHelper { get; }
public async Task NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey)
public async Task NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey, NotificationType type)
{
try
{
@ -63,24 +67,38 @@ namespace PlexRequests.Services.Notification
var userAccount = PlexApi.GetAccount(apiKey);
var adminUsername = userAccount.Username ?? string.Empty;
var users = UserNotifyRepo.GetAll().ToList();
var users = UserHelper.GetUsersWithFeature(Features.RequestAddedNotification).ToList();
Log.Debug("Notifying Users Count {0}", users.Count);
foreach (var model in modelChanged)
{
var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers, StringComparer.CurrentCultureIgnoreCase);
var selectedUsers = new List<string>();
foreach (var u in users)
{
var requestUser = model.RequestedUsers.FirstOrDefault(
x => x.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase) || x.Equals(u.UserAlias, StringComparison.CurrentCultureIgnoreCase));
if (string.IsNullOrEmpty(requestUser))
{
continue;
}
selectedUsers.Add(requestUser);
}
//var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers, StringComparer.CurrentCultureIgnoreCase);
foreach (var user in selectedUsers)
{
Log.Info("Notifying user {0}", user);
if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
{
Log.Info("This user is the Plex server owner");
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title, model.PosterPath);
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title, model.PosterPath, type);
return;
}
var email = plexUser.User.FirstOrDefault(x => x.Username.Equals(user, StringComparison.CurrentCultureIgnoreCase));
if (email == null)
if (string.IsNullOrEmpty(email?.Email))
{
Log.Info("There is no email address for this Plex user, cannot send notification");
// We do not have a plex user that requested this!
@ -88,7 +106,7 @@ namespace PlexRequests.Services.Notification
}
Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
await PublishUserNotification(email.Username, email.Email, model.Title, model.PosterPath);
await PublishUserNotification(email.Username, email.Email, model.Title, model.PosterPath, type);
}
}
}
@ -98,7 +116,7 @@ namespace PlexRequests.Services.Notification
}
}
public async Task NotifyUsers(RequestedModel model, string apiKey)
public async Task NotifyUsers(RequestedModel model, string apiKey, NotificationType type)
{
try
{
@ -107,7 +125,7 @@ namespace PlexRequests.Services.Notification
var adminUsername = userAccount.Username ?? string.Empty;
var users = UserNotifyRepo.GetAll().ToList();
var users = UserHelper.GetUsersWithFeature(Features.RequestAddedNotification).ToList();
Log.Debug("Notifying Users Count {0}", users.Count);
var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers, StringComparer.CurrentCultureIgnoreCase);
@ -117,7 +135,7 @@ namespace PlexRequests.Services.Notification
if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
{
Log.Info("This user is the Plex server owner");
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title, model.PosterPath);
await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title, model.PosterPath, type);
return;
}
@ -130,7 +148,7 @@ namespace PlexRequests.Services.Notification
}
Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
await PublishUserNotification(email.Username, email.Email, model.Title, model.PosterPath);
await PublishUserNotification(email.Username, email.Email, model.Title, model.PosterPath, type);
}
}
catch (Exception e)
@ -139,13 +157,13 @@ namespace PlexRequests.Services.Notification
}
}
private async Task PublishUserNotification(string username, string email, string title, string img)
private async Task PublishUserNotification(string username, string email, string title, string img, NotificationType type)
{
var notificationModel = new NotificationModel
{
User = username,
UserEmail = email,
NotificationType = NotificationType.RequestAvailable,
NotificationType = type,
Title = title,
ImgSrc = img
};

View file

@ -81,6 +81,11 @@ namespace PlexRequests.Services.Notification
case NotificationType.Test:
await PushTestAsync(pushSettings);
break;
case NotificationType.RequestDeclined:
break;
case NotificationType.ItemAddedToFaultQueue:
await PushFaultQueue(model, pushSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
@ -125,6 +130,13 @@ namespace PlexRequests.Services.Notification
await Push(settings, message, pushTitle);
}
private async Task PushFaultQueue(NotificationModel model, PushbulletNotificationSettings settings)
{
var message = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
var pushTitle = $"Plex Requests: The {model.RequestType.GetString()?.ToLower()} {model.Title} has been requested but could not be added!";
await Push(settings, message, pushTitle);
}
private async Task Push(PushbulletNotificationSettings settings, string message, string title)
{
try

View file

@ -81,6 +81,11 @@ namespace PlexRequests.Services.Notification
case NotificationType.Test:
await PushTestAsync(model, pushSettings);
break;
case NotificationType.RequestDeclined:
break;
case NotificationType.ItemAddedToFaultQueue:
await PushFaultQueue(model, pushSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
@ -122,6 +127,12 @@ namespace PlexRequests.Services.Notification
await Push(settings, message);
}
private async Task PushFaultQueue(NotificationModel model, PushoverNotificationSettings settings)
{
var message = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
await Push(settings, message);
}
private async Task Push(PushoverNotificationSettings settings, string message)
{
try

View file

@ -88,6 +88,11 @@ namespace PlexRequests.Services.Notification
case NotificationType.Test:
await PushTest(pushSettings);
break;
case NotificationType.RequestDeclined:
break;
case NotificationType.ItemAddedToFaultQueue:
await PushFaultQueue(model, pushSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
@ -111,6 +116,12 @@ namespace PlexRequests.Services.Notification
await Push(settings, message);
}
private async Task PushFaultQueue(NotificationModel model, SlackNotificationSettings settings)
{
var message = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
await Push(settings, message);
}
private async Task Push(SlackNotificationSettings config, string message)
{
try

View file

@ -36,6 +36,14 @@
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Nancy, Version=1.4.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.3\lib\net40\Nancy.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.3.6\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
@ -81,18 +89,22 @@
<Compile Include="Interfaces\IJobRecord.cs" />
<Compile Include="Interfaces\INotificationEngine.cs" />
<Compile Include="Jobs\HtmlTemplateGenerator.cs" />
<Compile Include="Jobs\IPlexContentCacher.cs" />
<Compile Include="Jobs\IRecentlyAdded.cs" />
<Compile Include="Jobs\JobRecord.cs" />
<Compile Include="Jobs\JobNames.cs" />
<Compile Include="Jobs\PlexContentCacher.cs" />
<Compile Include="Jobs\PlexEpisodeCacher.cs" />
<Compile Include="Jobs\RecentlyAdded.cs" />
<Compile Include="Jobs\StoreBackup.cs" />
<Compile Include="Jobs\PlexUserChecker.cs" />
<Compile Include="Jobs\StoreCleanup.cs" />
<Compile Include="Jobs\CouchPotatoCacher.cs" />
<Compile Include="Jobs\PlexAvailabilityChecker.cs" />
<Compile Include="Jobs\SickRageCacher.cs" />
<Compile Include="Jobs\SonarrCacher.cs" />
<Compile Include="Jobs\Templates\RecentlyAddedTemplate.cs" />
<Compile Include="Jobs\FaultQueueHandler.cs" />
<Compile Include="Jobs\UserRequestLimitResetter.cs" />
<Compile Include="Models\PlexAlbum.cs" />
<Compile Include="Models\PlexEpisodeModel.cs" />

View file

@ -5,6 +5,7 @@
<package id="Dapper" version="1.50.0-beta8" targetFramework="net45" />
<package id="MailKit" version="1.2.21" targetFramework="net45" requireReinstallation="True" />
<package id="MimeKit" version="1.2.22" targetFramework="net45" />
<package id="Nancy" version="1.4.3" targetFramework="net45" />
<package id="NLog" version="4.3.6" targetFramework="net45" />
<package id="Quartz" version="2.3.3" targetFramework="net45" />
</packages>

View file

@ -0,0 +1,52 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexContent.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.Data.Linq.Mapping;
namespace PlexRequests.Store.Models.Plex
{
[Table(Name = nameof(PlexContent))]
public class PlexContent : Entity
{
public string Title { get; set; }
public string ReleaseYear { get; set; }
public string ProviderId { get; set; }
public PlexMediaType Type { get; set; }
public string Url { get; set; }
/// <summary>
/// Only used for TV Shows
/// </summary>
public byte[] Seasons { get; set; }
/// <summary>
/// Only used for Albums
/// </summary>
public string Artist { get; set; }
}
}

View file

@ -0,0 +1,35 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexMediaType .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
namespace PlexRequests.Store.Models.Plex
{
public enum PlexMediaType
{
Movie = 0,
Show = 1,
Artist = 2
}
}

View file

@ -24,11 +24,21 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
using Dapper.Contrib.Extensions;
namespace PlexRequests.Store.Models
{
[Table(nameof(PlexUsers))]
public class PlexUsers : Entity
{
public int PlexUserId { get; set; }
public string PlexUserId { get; set; }
public string UserAlias { get; set; }
public int Permissions { get; set; }
public int Features { get; set; }
public string Username { get; set; }
public string EmailAddress { get; set; }
public string LoginId { get; set; }
}
}

View file

@ -0,0 +1,52 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: RequestQueue.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("RequestFaultQueue")]
public class RequestQueue : Entity
{
public string PrimaryIdentifier { get; set; }
public RequestType Type { get; set; }
public byte[] Content { get; set; }
public FaultType FaultType { get; set; }
public DateTime? LastRetry { get; set; }
public string Message { get; set; }
}
public enum FaultType
{
RequestFault,
MissingInformation
}
}

View file

@ -35,5 +35,6 @@ namespace PlexRequests.Store.Models
{
public string Name { get; set; }
public DateTime LastRun { get; set; }
public bool Running { get; set; }
}
}

View file

@ -69,6 +69,9 @@
<Compile Include="Models\PlexEpisodes.cs" />
<Compile Include="Models\PlexUsers.cs" />
<Compile Include="Models\Plex\MetadataItems.cs" />
<Compile Include="Models\Plex\PlexContent.cs" />
<Compile Include="Models\Plex\PlexMediaType .cs" />
<Compile Include="Models\RequestQueue.cs" />
<Compile Include="Models\ScheduledJobs.cs" />
<Compile Include="Models\RequestLimit.cs" />
<Compile Include="Models\UsersToNotify.cs" />
@ -85,6 +88,8 @@
<Compile Include="Repository\SettingsJsonRepository.cs" />
<Compile Include="Repository\RequestJsonRepository.cs" />
<Compile Include="Repository\GenericRepository.cs" />
<Compile Include="Repository\PlexUserRepository.cs" />
<Compile Include="Repository\UserRepository.cs" />
<Compile Include="RequestedModel.cs" />
<Compile Include="UserEntity.cs" />
<Compile Include="UserLogins.cs" />

View file

@ -327,5 +327,22 @@ namespace PlexRequests.Store.Repository
throw;
}
}
public async Task DeleteAllAsync(string tableName)
{
try
{
ResetCache();
using (var db = Config.DbConnection())
{
db.Open();
await db.ExecuteAsync($"delete from {tableName}");
}
}
catch (SqliteException e) when (e.ErrorCode == SQLiteErrorCode.Corrupt)
{
Log.Fatal(CorruptMessage);
throw;
}
}
}
}

View file

@ -85,5 +85,6 @@ namespace PlexRequests.Store.Repository
IEnumerable<T> Custom(Func<IDbConnection, IEnumerable<T>> func);
Task<IEnumerable<T>> CustomAsync(Func<IDbConnection, Task<IEnumerable<T>>> func);
void DeleteAll(string tableName);
Task DeleteAllAsync(string tableName);
}
}

View file

@ -0,0 +1,118 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserRepository.cs
// Created By:
//
// 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.Data;
using System.Threading.Tasks;
using Dapper;
using Dapper.Contrib.Extensions;
using PlexRequests.Helpers;
using PlexRequests.Store.Models;
namespace PlexRequests.Store.Repository
{
public class PlexUserRepository : BaseGenericRepository<PlexUsers>, IPlexUserRepository
{
public PlexUserRepository(ISqliteConfiguration config, ICacheProvider cache) : base(config,cache)
{
DbConfig = config;
}
private ISqliteConfiguration DbConfig { get; }
private IDbConnection Db => DbConfig.DbConnection();
public PlexUsers GetUser(string userGuid)
{
var sql = @"SELECT * FROM PlexUsers
WHERE PlexUserId = @UserGuid";
return Db.QueryFirstOrDefault<PlexUsers>(sql, new {UserGuid = userGuid});
}
public PlexUsers GetUserByUsername(string username)
{
var sql = @"SELECT * FROM PlexUsers
WHERE Username = @UserName";
return Db.QueryFirstOrDefault<PlexUsers>(sql, new {UserName = username});
}
public async Task<PlexUsers> GetUserAsync(string userguid)
{
var sql = @"SELECT * FROM PlexUsers
WHERE PlexUserId = @UserGuid";
return await Db.QueryFirstOrDefaultAsync<PlexUsers>(sql, new {UserGuid = userguid});
}
#region abstract implementation
#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member
[Obsolete]
public override PlexUsers Get(string id)
{
throw new System.NotImplementedException();
}
[Obsolete]
public override Task<PlexUsers> GetAsync(int id)
{
throw new System.NotImplementedException();
}
[Obsolete]
public override PlexUsers Get(int id)
{
throw new System.NotImplementedException();
}
[Obsolete]
public override Task<PlexUsers> GetAsync(string id)
{
throw new System.NotImplementedException();
}
#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member
#endregion
}
public interface IPlexUserRepository
{
PlexUsers GetUser(string userGuid);
PlexUsers GetUserByUsername(string username);
Task<PlexUsers> GetUserAsync(string userguid);
IEnumerable<PlexUsers> Custom(Func<IDbConnection, IEnumerable<PlexUsers>> func);
long Insert(PlexUsers entity);
void Delete(PlexUsers entity);
IEnumerable<PlexUsers> GetAll();
bool UpdateAll(IEnumerable<PlexUsers> entity);
bool Update(PlexUsers entity);
Task<IEnumerable<PlexUsers>> GetAllAsync();
Task<bool> UpdateAsync(PlexUsers users);
Task<int> InsertAsync(PlexUsers users);
}
}

View file

@ -0,0 +1,114 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserRepository.cs
// Created By:
//
// 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.Data;
using System.Threading.Tasks;
using Dapper;
using Dapper.Contrib.Extensions;
using PlexRequests.Helpers;
namespace PlexRequests.Store.Repository
{
public class UserRepository : BaseGenericRepository<UsersModel>, IUserRepository
{
public UserRepository(ISqliteConfiguration config, ICacheProvider cache) : base(config,cache)
{
DbConfig = config;
}
private ISqliteConfiguration DbConfig { get; }
private IDbConnection Db => DbConfig.DbConnection();
public UsersModel GetUser(string userGuid)
{
var sql = @"SELECT * FROM Users
WHERE Userguid = @UserGuid";
return Db.QueryFirstOrDefault<UsersModel>(sql, new {UserGuid = userGuid});
}
public UsersModel GetUserByUsername(string username)
{
var sql = @"SELECT * FROM Users
WHERE UserName = @UserName";
return Db.QueryFirstOrDefault<UsersModel>(sql, new {UserName = username});
}
public async Task<UsersModel> GetUserAsync(string userguid)
{
var sql = @"SELECT * FROM Users
WHERE UserGuid = @UserGuid";
return await Db.QueryFirstOrDefaultAsync<UsersModel>(sql, new {UserGuid = userguid});
}
#region abstract implementation
#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member
[Obsolete]
public override UsersModel Get(string id)
{
throw new System.NotImplementedException();
}
[Obsolete]
public override Task<UsersModel> GetAsync(int id)
{
throw new System.NotImplementedException();
}
[Obsolete]
public override UsersModel Get(int id)
{
throw new System.NotImplementedException();
}
[Obsolete]
public override Task<UsersModel> GetAsync(string id)
{
throw new System.NotImplementedException();
}
#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member
#endregion
}
public interface IUserRepository
{
UsersModel GetUser(string userGuid);
UsersModel GetUserByUsername(string username);
Task<UsersModel> GetUserAsync(string userguid);
IEnumerable<UsersModel> Custom(Func<IDbConnection, IEnumerable<UsersModel>> func);
long Insert(UsersModel entity);
void Delete(UsersModel entity);
IEnumerable<UsersModel> GetAll();
bool UpdateAll(IEnumerable<UsersModel> entity);
bool Update(UsersModel entity);
}
}

View file

@ -7,8 +7,10 @@ CREATE TABLE IF NOT EXISTS Users
UserName varchar(50) NOT NULL,
Salt BLOB NOT NULL,
Hash BLOB NOT NULL,
Claims BLOB NOT NULL,
UserProperties BLOB
UserProperties BLOB,
Permissions INTEGER,
Features INTEGER,
Claims BLOB
);
CREATE TABLE IF NOT EXISTS UserLogins
@ -78,7 +80,8 @@ CREATE TABLE IF NOT EXISTS ScheduledJobs
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name varchar(100) NOT NULL,
LastRun varchar(100) NOT NULL
LastRun varchar(100) NOT NULL,
Running INTEGER
);
CREATE UNIQUE INDEX IF NOT EXISTS ScheduledJobs_Id ON ScheduledJobs (Id);
@ -111,8 +114,13 @@ CREATE UNIQUE INDEX IF NOT EXISTS RequestLimit_Id ON RequestLimit (Id);
CREATE TABLE IF NOT EXISTS PlexUsers
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
PlexUserId INTEGER NOT NULL,
UserAlias varchar(100) NOT NULL
PlexUserId varchar(100) NOT NULL,
UserAlias varchar(100) NOT NULL,
Permissions INTEGER,
Features INTEGER,
Username VARCHAR(100),
EmailAddress VARCHAR(100),
LoginId VARCHAR(100)
);
CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON PlexUsers (Id);
@ -129,4 +137,31 @@ CREATE TABLE IF NOT EXISTS PlexEpisodes
);
CREATE UNIQUE INDEX IF NOT EXISTS PlexEpisodes_Id ON PlexEpisodes (Id);
CREATE INDEX IF NOT EXISTS PlexEpisodes_ProviderId ON PlexEpisodes (ProviderId);
CREATE TABLE IF NOT EXISTS RequestFaultQueue
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
PrimaryIdentifier VARCHAR(100) NOT NULL,
Type INTEGER NOT NULL,
FaultType INTEGER NOT NULL,
Content BLOB NOT NULL,
LastRetry VARCHAR(100),
Description VARCHAR(100)
);
CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON PlexUsers (Id);
CREATE TABLE IF NOT EXISTS PlexContent
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Title VARCHAR(100) NOT NULL,
ReleaseYear VARCHAR(100) NOT NULL,
ProviderId VARCHAR(100) NOT NULL,
Url VARCHAR(100) NOT NULL,
Artist VARCHAR(100),
Seasons BLOB,
Type INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS PlexContent_Id ON PlexContent (Id);
COMMIT;

View file

@ -57,12 +57,13 @@ namespace PlexRequests.Store
}
}
public static void AddColumn(this IDbConnection connection, string tableName, string alterType, string newColumn, bool allowNulls, string dataType)
public static void AlterTable(this IDbConnection connection, string tableName, string alterType, string newColumn, bool allowNulls, string dataType)
{
connection.Open();
var result = connection.Query<TableInfo>($"PRAGMA table_info({tableName});");
if (result.Any(x => x.name == newColumn))
{
connection.Close();
return;
}

View file

@ -35,7 +35,10 @@ namespace PlexRequests.Store
{
public byte[] Hash { get; set; }
public byte[] Salt { get; set; }
[Obsolete]
public byte[] Claims { get; set; }
public byte[] UserProperties { get; set; }
public int Permissions { get; set; }
public int Features { get; set; }
}
}

View file

@ -1,164 +1,167 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SearchModuleTests.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
//#region Copyright
//// /************************************************************************
//// Copyright (c) 2016 Jamie Rees
//// File: SearchModuleTests.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 Moq;
using NUnit.Framework;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Analytics;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Jobs;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using PlexRequests.UI.Modules;
using Ploeh.AutoFixture;
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using Moq;
//using NUnit.Framework;
//using PlexRequests.Api.Interfaces;
//using PlexRequests.Api.Models.Plex;
//using PlexRequests.Core;
//using PlexRequests.Core.Queue;
//using PlexRequests.Core.SettingModels;
//using PlexRequests.Helpers;
//using PlexRequests.Helpers.Analytics;
//using PlexRequests.Services.Interfaces;
//using PlexRequests.Services.Jobs;
//using PlexRequests.Store;
//using PlexRequests.Store.Models;
//using PlexRequests.Store.Repository;
//using PlexRequests.UI.Modules;
//using Ploeh.AutoFixture;
namespace PlexRequests.UI.Tests
{
[TestFixture]
public class SearchModuleTests
{
private Mock<ISettingsService<HeadphonesSettings>> _headphonesSettings;
private Mock<INotificationService> _notificationService;
private Mock<ISettingsService<SickRageSettings>> _sickRageSettingsMock;
private Mock<ICouchPotatoApi> _cpApi;
private Mock<ISettingsService<SonarrSettings>> _sonarrSettingsMock;
private Mock<ISonarrApi> _sonarrApiMock;
private Mock<ISettingsService<PlexSettings>> _plexSettingsMock;
private Mock<ISettingsService<CouchPotatoSettings>> _cpMock;
private Mock<ISettingsService<PlexRequestSettings>> _plexRequestMock;
private Mock<ISettingsService<AuthenticationSettings>> _authMock;
private Mock<IAnalytics> _analytics;
private Mock<IAvailabilityChecker> _availabilityMock;
private Mock<IRequestService> _rServiceMock;
private Mock<ISickRageApi> _srApi;
private Mock<IMusicBrainzApi> _music;
private Mock<IHeadphonesApi> _hpAPi;
private Mock<ICouchPotatoCacher> _cpCache;
private Mock<ISonarrCacher> _sonarrCache;
private Mock<ISickRageCacher> _srCache;
private Mock<IPlexApi> _plexApi;
private Mock<IRepository<UsersToNotify>> _userRepo;
private Mock<ISettingsService<EmailNotificationSettings>> _emailSettings;
private Mock<IIssueService> _issueService;
private Mock<ICacheProvider> _cache;
private Mock<IRepository<RequestLimit>> RequestLimitRepo { get; set; }
private SearchModule Search { get; set; }
private readonly Fixture F = new Fixture();
//namespace PlexRequests.UI.Tests
//{
// [TestFixture]
// public class SearchModuleTests
// {
// private Mock<ISettingsService<HeadphonesSettings>> _headphonesSettings;
// private Mock<INotificationService> _notificationService;
// private Mock<ISettingsService<SickRageSettings>> _sickRageSettingsMock;
// private Mock<ICouchPotatoApi> _cpApi;
// private Mock<ISettingsService<SonarrSettings>> _sonarrSettingsMock;
// private Mock<ISonarrApi> _sonarrApiMock;
// private Mock<ISettingsService<PlexSettings>> _plexSettingsMock;
// private Mock<ISettingsService<CouchPotatoSettings>> _cpMock;
// private Mock<ISettingsService<PlexRequestSettings>> _plexRequestMock;
// private Mock<ISettingsService<AuthenticationSettings>> _authMock;
// private Mock<IAnalytics> _analytics;
// private Mock<IAvailabilityChecker> _availabilityMock;
// private Mock<IRequestService> _rServiceMock;
// private Mock<ISickRageApi> _srApi;
// private Mock<IMusicBrainzApi> _music;
// private Mock<IHeadphonesApi> _hpAPi;
// private Mock<ICouchPotatoCacher> _cpCache;
// private Mock<ISonarrCacher> _sonarrCache;
// private Mock<ISickRageCacher> _srCache;
// private Mock<IPlexApi> _plexApi;
// private Mock<IRepository<UsersToNotify>> _userRepo;
// private Mock<ISettingsService<EmailNotificationSettings>> _emailSettings;
// private Mock<IIssueService> _issueService;
// private Mock<ICacheProvider> _cache;
// private Mock<ITransientFaultQueue> _faultQueue;
// private Mock<IRepository<RequestLimit>> RequestLimitRepo { get; set; }
// private SearchModule Search { get; set; }
// private readonly Fixture F = new Fixture();
[Test]
public void CheckNoRequestLimitTest()
{
var settings = new PlexRequestSettings { AlbumWeeklyRequestLimit = 0, MovieWeeklyRequestLimit = 2, TvWeeklyRequestLimit = 0 };
var result = Search.CheckRequestLimit(settings, RequestType.Movie).Result;
// [Test]
// public void CheckNoRequestLimitTest()
// {
// var settings = new PlexRequestSettings { AlbumWeeklyRequestLimit = 0, MovieWeeklyRequestLimit = 2, TvWeeklyRequestLimit = 0 };
// var result = Search.CheckRequestLimit(settings, RequestType.Movie).Result;
Assert.That(result, Is.True);
RequestLimitRepo.Verify(x => x.GetAllAsync(), Times.Once);
}
// Assert.That(result, Is.True);
// RequestLimitRepo.Verify(x => x.GetAllAsync(), Times.Once);
// }
[TestCaseSource(nameof(MovieLimitData))]
public bool CheckMovieLimitTest(int requestCount)
{
var users = F.CreateMany<RequestLimit>().ToList();
users.Add(new RequestLimit { Username = "", RequestCount = requestCount, RequestType = RequestType.Movie});
RequestLimitRepo.Setup(x => x.GetAllAsync()).ReturnsAsync(users);
var settings = new PlexRequestSettings { AlbumWeeklyRequestLimit = 0, MovieWeeklyRequestLimit = 5, TvWeeklyRequestLimit = 0 };
var result = Search.CheckRequestLimit(settings, RequestType.Movie).Result;
// [TestCaseSource(nameof(MovieLimitData))]
// public bool CheckMovieLimitTest(int requestCount)
// {
// var users = F.CreateMany<RequestLimit>().ToList();
// users.Add(new RequestLimit { Username = "", RequestCount = requestCount, RequestType = RequestType.Movie});
// RequestLimitRepo.Setup(x => x.GetAllAsync()).ReturnsAsync(users);
// var settings = new PlexRequestSettings { AlbumWeeklyRequestLimit = 0, MovieWeeklyRequestLimit = 5, TvWeeklyRequestLimit = 0 };
// var result = Search.CheckRequestLimit(settings, RequestType.Movie).Result;
RequestLimitRepo.Verify(x => x.GetAllAsync(), Times.Once);
// RequestLimitRepo.Verify(x => x.GetAllAsync(), Times.Once);
return result;
}
// return result;
// }
private static IEnumerable<TestCaseData> MovieLimitData
{
get
{
yield return new TestCaseData(1).Returns(true).SetName("1 Request of 5");
yield return new TestCaseData(2).Returns(true).SetName("2 Request of 5");
yield return new TestCaseData(3).Returns(true).SetName("3 Request of 5");
yield return new TestCaseData(4).Returns(true).SetName("4 Request of 5");
yield return new TestCaseData(5).Returns(false).SetName("5 Request of 5");
yield return new TestCaseData(6).Returns(false).SetName("6 Request of 5");
yield return new TestCaseData(0).Returns(true).SetName("0 Request of 5");
}
}
// private static IEnumerable<TestCaseData> MovieLimitData
// {
// get
// {
// yield return new TestCaseData(1).Returns(true).SetName("1 Request of 5");
// yield return new TestCaseData(2).Returns(true).SetName("2 Request of 5");
// yield return new TestCaseData(3).Returns(true).SetName("3 Request of 5");
// yield return new TestCaseData(4).Returns(true).SetName("4 Request of 5");
// yield return new TestCaseData(5).Returns(false).SetName("5 Request of 5");
// yield return new TestCaseData(6).Returns(false).SetName("6 Request of 5");
// yield return new TestCaseData(0).Returns(true).SetName("0 Request of 5");
// }
// }
[SetUp]
public void Setup()
{
_authMock = new Mock<Core.ISettingsService<AuthenticationSettings>>();
_plexRequestMock = new Mock<ISettingsService<PlexRequestSettings>>();
_plexRequestMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings());
_cpMock = new Mock<Core.ISettingsService<CouchPotatoSettings>>();
_plexSettingsMock = new Mock<Core.ISettingsService<PlexSettings>>();
_sonarrApiMock = new Mock<ISonarrApi>();
_sonarrSettingsMock = new Mock<Core.ISettingsService<SonarrSettings>>();
_cpApi = new Mock<ICouchPotatoApi>();
_sickRageSettingsMock = new Mock<Core.ISettingsService<SickRageSettings>>();
_notificationService = new Mock<INotificationService>();
_headphonesSettings = new Mock<Core.ISettingsService<HeadphonesSettings>>();
_cache = new Mock<ICacheProvider>();
// [SetUp]
// public void Setup()
// {
// _authMock = new Mock<Core.ISettingsService<AuthenticationSettings>>();
// _plexRequestMock = new Mock<ISettingsService<PlexRequestSettings>>();
// _plexRequestMock.Setup(x => x.GetSettings()).Returns(new PlexRequestSettings());
// _cpMock = new Mock<Core.ISettingsService<CouchPotatoSettings>>();
// _plexSettingsMock = new Mock<Core.ISettingsService<PlexSettings>>();
// _sonarrApiMock = new Mock<ISonarrApi>();
// _sonarrSettingsMock = new Mock<Core.ISettingsService<SonarrSettings>>();
// _cpApi = new Mock<ICouchPotatoApi>();
// _sickRageSettingsMock = new Mock<Core.ISettingsService<SickRageSettings>>();
// _notificationService = new Mock<INotificationService>();
// _headphonesSettings = new Mock<Core.ISettingsService<HeadphonesSettings>>();
// _cache = new Mock<ICacheProvider>();
_analytics = new Mock<IAnalytics>();
_availabilityMock = new Mock<IAvailabilityChecker>();
_rServiceMock = new Mock<IRequestService>();
_srApi = new Mock<ISickRageApi>();
_music = new Mock<IMusicBrainzApi>();
_hpAPi = new Mock<IHeadphonesApi>();
_cpCache = new Mock<ICouchPotatoCacher>();
_sonarrCache = new Mock<ISonarrCacher>();
_srCache = new Mock<ISickRageCacher>();
_plexApi = new Mock<IPlexApi>();
_userRepo = new Mock<IRepository<UsersToNotify>>();
RequestLimitRepo = new Mock<IRepository<RequestLimit>>();
_emailSettings = new Mock<ISettingsService<EmailNotificationSettings>>();
_issueService = new Mock<IIssueService>();
CreateModule();
}
// _analytics = new Mock<IAnalytics>();
// _availabilityMock = new Mock<IAvailabilityChecker>();
// _rServiceMock = new Mock<IRequestService>();
// _srApi = new Mock<ISickRageApi>();
// _music = new Mock<IMusicBrainzApi>();
// _hpAPi = new Mock<IHeadphonesApi>();
// _cpCache = new Mock<ICouchPotatoCacher>();
// _sonarrCache = new Mock<ISonarrCacher>();
// _srCache = new Mock<ISickRageCacher>();
// _plexApi = new Mock<IPlexApi>();
// _userRepo = new Mock<IRepository<UsersToNotify>>();
// RequestLimitRepo = new Mock<IRepository<RequestLimit>>();
// _emailSettings = new Mock<ISettingsService<EmailNotificationSettings>>();
// _issueService = new Mock<IIssueService>();
// _faultQueue = new Mock<ITransientFaultQueue>();
// CreateModule();
// }
private void CreateModule()
{
Search = new SearchModule(_cache.Object, _cpMock.Object, _plexRequestMock.Object, _availabilityMock.Object,
_rServiceMock.Object, _sonarrApiMock.Object, _sonarrSettingsMock.Object,
_sickRageSettingsMock.Object, _cpApi.Object, _srApi.Object, _notificationService.Object,
_music.Object, _hpAPi.Object, _headphonesSettings.Object, _cpCache.Object, _sonarrCache.Object,
_srCache.Object, _plexApi.Object, _plexSettingsMock.Object, _authMock.Object,
_userRepo.Object, _emailSettings.Object, _issueService.Object, _analytics.Object, RequestLimitRepo.Object);
}
// private void CreateModule()
// {
// Search = new SearchModule(_cache.Object, _cpMock.Object, _plexRequestMock.Object, _availabilityMock.Object,
// _rServiceMock.Object, _sonarrApiMock.Object, _sonarrSettingsMock.Object,
// _sickRageSettingsMock.Object, _cpApi.Object, _srApi.Object, _notificationService.Object,
// _music.Object, _hpAPi.Object, _headphonesSettings.Object, _cpCache.Object, _sonarrCache.Object,
// _srCache.Object, _plexApi.Object, _plexSettingsMock.Object, _authMock.Object,
// _userRepo.Object, _emailSettings.Object, _issueService.Object, _analytics.Object, RequestLimitRepo.Object, _faultQueue.Object);
// }
}
}
// }
//}

View file

@ -35,6 +35,7 @@ using NUnit.Framework;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Store;
using PlexRequests.UI.Helpers;
@ -146,32 +147,6 @@ namespace PlexRequests.UI.Tests
true, It.IsAny<bool>()), Times.Once);
}
[Test]
public async Task RequestEpisodesWithExistingSeriesTest()
{
var episodesReturned = new List<SonarrEpisodes>
{
new SonarrEpisodes {episodeNumber = 1, seasonNumber = 2, monitored = false, id=22}
};
SonarrMock.Setup(x => x.GetEpisodes(It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<Uri>())).Returns(episodesReturned);
SonarrMock.Setup(x => x.GetEpisode("22", It.IsAny<string>(), It.IsAny<Uri>())).Returns(new SonarrEpisode {id=22});
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object);
var model = new RequestedModel
{
Episodes = new List<EpisodesModel> { new EpisodesModel { EpisodeNumber = 1, SeasonNumber = 2 } }
};
var series = new Series();
await Sender.RequestEpisodesWithExistingSeries(model, series, GetSonarrSettings());
SonarrMock.Verify(x => x.UpdateEpisode(It.Is<SonarrEpisode>(e => e.monitored), It.IsAny<string>(), It.IsAny<Uri>()));
SonarrMock.Verify(x => x.GetEpisode("22", It.IsAny<string>(), It.IsAny<Uri>()),Times.Once);
SonarrMock.Verify(x => x.SearchForEpisodes(It.IsAny<int[]>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Once);
}
private SonarrSettings GetSonarrSettings()
{

View file

@ -42,6 +42,7 @@ using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Helpers.Analytics;
using PlexRequests.UI.Models;
using PlexRequests.UI.Modules;

View file

@ -0,0 +1,93 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CustomAuthenticationConfiguration.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 Nancy.Cryptography;
using PlexRequests.Store.Repository;
namespace PlexRequests.UI.Authentication
{
public class CustomAuthenticationConfiguration
{
internal const string DefaultRedirectQuerystringKey = "returnUrl";
/// <summary>
/// Gets or sets the forms authentication query string key for storing the return url
/// </summary>
public string RedirectQuerystringKey { get; set; }
/// <summary>
/// Gets or sets the redirect url for pages that require authentication
/// </summary>
public string RedirectUrl { get; set; }
/// <summary>Gets or sets the username/identifier mapper</summary>
public IUserRepository LocalUserRepository { get; set; }
public IPlexUserRepository PlexUserRepository { get; set; }
/// <summary>Gets or sets RequiresSSL property</summary>
/// <value>The flag that indicates whether SSL is required</value>
public bool RequiresSSL { get; set; }
/// <summary>
/// Gets or sets whether to redirect to login page during unauthorized access.
/// </summary>
public bool DisableRedirect { get; set; }
/// <summary>Gets or sets the domain of the auth cookie</summary>
public string Domain { get; set; }
/// <summary>Gets or sets the path of the auth cookie</summary>
public string Path { get; set; }
/// <summary>Gets or sets the cryptography configuration</summary>
public CryptographyConfiguration CryptographyConfiguration { get; set; }
/// <summary>
/// Gets a value indicating whether the configuration is valid or not.
/// </summary>
public virtual bool IsValid => (this.DisableRedirect || !string.IsNullOrEmpty(this.RedirectUrl)) && (this.LocalUserRepository != null && PlexUserRepository != null && this.CryptographyConfiguration != null) && (this.CryptographyConfiguration.EncryptionProvider != null && this.CryptographyConfiguration.HmacProvider != null);
/// <summary>
/// Initializes a new instance of the <see cref="T:Nancy.Authentication.Forms.FormsAuthenticationConfiguration" /> class.
/// </summary>
public CustomAuthenticationConfiguration()
: this(CryptographyConfiguration.Default)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Nancy.Authentication.Forms.FormsAuthenticationConfiguration" /> class.
/// </summary>
/// <param name="cryptographyConfiguration">Cryptography configuration</param>
public CustomAuthenticationConfiguration(CryptographyConfiguration cryptographyConfiguration)
{
this.CryptographyConfiguration = cryptographyConfiguration;
this.RedirectQuerystringKey = "returnUrl";
}
}
}

Some files were not shown because too many files have changed in this diff Show more