Switched out the schedulers, this seems to be a better implimentation to the previous and is easier to add new "jobs" in.

This commit is contained in:
tidusjar 2016-04-19 13:37:58 +01:00
parent 8388514931
commit 9c61f909de
32 changed files with 1237 additions and 1091 deletions

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup></configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>

View file

@ -1,93 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AvailabilityUpdateService.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.Concurrent;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Web.Hosting;
using FluentScheduler;
using Mono.Data.Sqlite;
using NLog;
using PlexRequests.Api;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store;
using PlexRequests.Store.Repository;
using System.Threading.Tasks;
namespace PlexRequests.Services
{
public class AvailabilityUpdateService : ITask, IRegisteredObject, IAvailabilityUpdateService
{
public AvailabilityUpdateService()
{
var memCache = new MemoryCacheProvider();
var dbConfig = new DbConfiguration(new SqliteFactory());
var repo = new SettingsJsonRepository(dbConfig, memCache);
ConfigurationReader = new ConfigurationReader();
Checker = new PlexAvailabilityChecker(new SettingsServiceV2<PlexSettings>(repo), new SettingsServiceV2<AuthenticationSettings>(repo), new JsonRequestService(new RequestJsonRepository(dbConfig, memCache)), new PlexApi(), memCache);
HostingEnvironment.RegisterObject(this);
}
private static Logger Log = LogManager.GetCurrentClassLogger();
private IConfigurationReader ConfigurationReader { get; }
private IAvailabilityChecker Checker { get; }
private IDisposable UpdateSubscription { get; set; }
public void Start(Configuration c)
{
UpdateSubscription?.Dispose();
Task.Factory.StartNew(() => Checker.CheckAndUpdateAll(-1)); // cache the libraries and run the availability checks
UpdateSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(Checker.CheckAndUpdateAll);
}
public void Execute()
{
Start(ConfigurationReader.Read());
}
public void Stop(bool immediate)
{
HostingEnvironment.UnregisterObject(this);
}
}
public interface IAvailabilityUpdateService
{
void Start(Configuration c);
}
}

View file

@ -1,40 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Congifuration.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.Services.Interfaces;
namespace PlexRequests.Services
{
public class Configuration
{
public Configuration(IIntervals intervals)
{
Intervals = intervals;
}
public IIntervals Intervals { get; set; }
}
}

View file

@ -1,38 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: ConfigurationReader.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.Services.Interfaces;
namespace PlexRequests.Services
{
public class ConfigurationReader : IConfigurationReader
{
public Configuration Read()
{
return new Configuration(new UpdateInterval());
}
}
}

View file

@ -31,7 +31,7 @@ namespace PlexRequests.Services.Interfaces
{ {
public interface IAvailabilityChecker public interface IAvailabilityChecker
{ {
void CheckAndUpdateAll(long check); void CheckAndUpdateAll();
List<PlexMovie> GetPlexMovies(); List<PlexMovie> GetPlexMovies();
bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year); bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year);
List<PlexTvShow> GetPlexTvShows(); List<PlexTvShow> GetPlexTvShows();

View file

@ -1,33 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: IConfigurationReader.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.Services.Interfaces
{
public interface IConfigurationReader
{
Configuration Read();
}
}

View file

@ -2,7 +2,7 @@
{ {
public interface ICouchPotatoCacher public interface ICouchPotatoCacher
{ {
void Queued(long check); void Queued();
int[] QueuedIds(); int[] QueuedIds();
} }
} }

View file

@ -1,8 +1,8 @@
namespace PlexRequests.Services.Interfaces namespace PlexRequests.Services.Interfaces
{ {
public interface ISickRageCacher public interface ISickRageCacher
{ {
void Queued(long check); void Queued();
int[] QueuedIds(); int[] QueuedIds();
} }
} }

View file

@ -2,7 +2,7 @@
{ {
public interface ISonarrCacher public interface ISonarrCacher
{ {
void Queued(long check); void Queued();
int[] QueuedIds(); int[] QueuedIds();
} }
} }

View file

@ -1,77 +1,82 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: PlexAvailabilityChecker.cs // File: PlexAvailabilityChecker.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the // a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including // "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, // without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to // distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to // permit persons to whom the Software is furnished to do so, subject to
// the following conditions: // the following conditions:
// //
// The above copyright notice and this permission notice shall be // The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. // included in all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Linq;
using System;
using NLog; using NLog;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Api.Models.Movie;
using PlexRequests.Core.SettingModels; using PlexRequests.Core;
using PlexRequests.Helpers; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces; using PlexRequests.Helpers;
using PlexRequests.Api.Models.Movie; using PlexRequests.Services.Interfaces;
using System.Linq;
using Quartz;
namespace PlexRequests.Services
{ namespace PlexRequests.Services.Jobs
public class CouchPotatoCacher : ICouchPotatoCacher {
{ public class CouchPotatoCacher : IJob, ICouchPotatoCacher
public CouchPotatoCacher(ISettingsService<CouchPotatoSettings> cpSettings, ICouchPotatoApi cpApi, ICacheProvider cache) {
{ public CouchPotatoCacher(ISettingsService<CouchPotatoSettings> cpSettings, ICouchPotatoApi cpApi, ICacheProvider cache)
CpSettings = cpSettings; {
CpApi = cpApi; CpSettings = cpSettings;
Cache = cache; CpApi = cpApi;
} Cache = cache;
}
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
private ICacheProvider Cache { get; } private ISettingsService<CouchPotatoSettings> CpSettings { get; }
private ICouchPotatoApi CpApi { get; } private ICacheProvider Cache { get; }
private ICouchPotatoApi CpApi { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private static Logger Log = LogManager.GetCurrentClassLogger();
public void Queued(long check)
{ public void Queued()
Log.Trace("This is check no. {0}", check); {
Log.Trace("Getting the settings"); Log.Trace("Getting the settings");
var settings = CpSettings.GetSettings(); var settings = CpSettings.GetSettings();
if (settings.Enabled) if (settings.Enabled)
{ {
Log.Trace("Getting all movies from CouchPotato"); Log.Trace("Getting all movies from CouchPotato");
var movies = CpApi.GetMovies(settings.FullUri, settings.ApiKey, new[] { "active" }); var movies = CpApi.GetMovies(settings.FullUri, settings.ApiKey, new[] { "active" });
Cache.Set(CacheKeys.CouchPotatoQueued, movies, 10); Cache.Set(CacheKeys.CouchPotatoQueued, movies, 10);
} }
} }
// we do not want to set here... // we do not want to set here...
public int[] QueuedIds() public int[] QueuedIds()
{ {
var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued); var movies = Cache.Get<CouchPotatoMovies>(CacheKeys.CouchPotatoQueued);
return movies != null ? movies.movies.Select(x => x.info.tmdb_id).ToArray() : new int[] { }; return movies?.movies.Select(x => x.info.tmdb_id).ToArray() ?? new int[] { };
} }
}
public void Execute(IJobExecutionContext context)
{
Queued();
}
}
} }

View file

@ -1,297 +1,303 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: PlexAvailabilityChecker.cs // File: PlexAvailabilityChecker.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the // a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including // "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, // without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to // distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to // permit persons to whom the Software is furnished to do so, subject to
// the following conditions: // the following conditions:
// //
// The above copyright notice and this permission notice shall be // The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. // included in all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Plex; using PlexRequests.Api.Models.Plex;
using PlexRequests.Core; using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers; using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces; using PlexRequests.Services.Interfaces;
using PlexRequests.Store; using PlexRequests.Services.Models;
using PlexRequests.Services.Models; using PlexRequests.Store;
namespace PlexRequests.Services using Quartz;
{
public class PlexAvailabilityChecker : IAvailabilityChecker namespace PlexRequests.Services.Jobs
{ {
public PlexAvailabilityChecker(ISettingsService<PlexSettings> plexSettings, ISettingsService<AuthenticationSettings> auth, IRequestService request, IPlexApi plex, ICacheProvider cache) public class PlexAvailabilityChecker : IJob, IAvailabilityChecker
{ {
Plex = plexSettings; public PlexAvailabilityChecker(ISettingsService<PlexSettings> plexSettings, ISettingsService<AuthenticationSettings> auth, IRequestService request, IPlexApi plex, ICacheProvider cache)
Auth = auth; {
RequestService = request; Plex = plexSettings;
PlexApi = plex; Auth = auth;
Cache = cache; RequestService = request;
} PlexApi = plex;
Cache = cache;
private ISettingsService<PlexSettings> Plex { get; } }
private ISettingsService<AuthenticationSettings> Auth { get; }
private IRequestService RequestService { get; } private ISettingsService<PlexSettings> Plex { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private ISettingsService<AuthenticationSettings> Auth { get; }
private IPlexApi PlexApi { get; } private IRequestService RequestService { get; }
private ICacheProvider Cache { get; } private static Logger Log = LogManager.GetCurrentClassLogger();
private IPlexApi PlexApi { get; }
public void CheckAndUpdateAll(long check) private ICacheProvider Cache { get; }
{
Log.Trace("This is check no. {0}", check); public void CheckAndUpdateAll()
Log.Trace("Getting the settings"); {
var plexSettings = Plex.GetSettings(); Log.Trace("Getting the settings");
var authSettings = Auth.GetSettings(); var plexSettings = Plex.GetSettings();
Log.Trace("Getting all the requests"); var authSettings = Auth.GetSettings();
Log.Trace("Getting all the requests");
if (!ValidateSettings(plexSettings, authSettings))
{ if (!ValidateSettings(plexSettings, authSettings))
Log.Info("Validation of the plex settings failed."); {
return; Log.Info("Validation of the plex settings failed.");
} return;
}
var libraries = CachedLibraries(authSettings, plexSettings, true); //force setting the cache (10 min intervals via scheduler)
var movies = GetPlexMovies().ToArray(); var libraries = CachedLibraries(authSettings, plexSettings, true); //force setting the cache (10 min intervals via scheduler)
var shows = GetPlexTvShows().ToArray(); var movies = GetPlexMovies().ToArray();
var albums = GetPlexAlbums().ToArray(); var shows = GetPlexTvShows().ToArray();
var albums = GetPlexAlbums().ToArray();
var requests = RequestService.GetAll();
var requestedModels = requests as RequestedModel[] ?? requests.Where(x => !x.Available).ToArray(); var requests = RequestService.GetAll();
Log.Trace("Requests Count {0}", requestedModels.Length); var requestedModels = requests as RequestedModel[] ?? requests.Where(x => !x.Available).ToArray();
Log.Trace("Requests Count {0}", requestedModels.Length);
if (!requestedModels.Any())
{ if (!requestedModels.Any())
Log.Info("There are no requests to check."); {
return; Log.Info("There are no requests to check.");
} return;
}
var modifiedModel = new List<RequestedModel>();
foreach (var r in requestedModels) var modifiedModel = new List<RequestedModel>();
{ foreach (var r in requestedModels)
Log.Trace("We are going to see if Plex has the following title: {0}", r.Title); {
Log.Trace("We are going to see if Plex has the following title: {0}", r.Title);
if (libraries == null)
{ if (libraries == null)
libraries = new List<PlexSearch>() { PlexApi.SearchContent(authSettings.PlexAuthToken, r.Title, plexSettings.FullUri) }; {
if (libraries == null) libraries = new List<PlexSearch>() { PlexApi.SearchContent(authSettings.PlexAuthToken, r.Title, plexSettings.FullUri) };
{ if (libraries == null)
Log.Trace("Could not find any matching result for this title."); {
continue; Log.Trace("Could not find any matching result for this title.");
} continue;
} }
}
Log.Trace("Search results from Plex for the following request: {0}", r.Title);
//Log.Trace(results.DumpJson()); Log.Trace("Search results from Plex for the following request: {0}", r.Title);
//Log.Trace(results.DumpJson());
var releaseDate = r.ReleaseDate == DateTime.MinValue ? string.Empty : r.ReleaseDate.ToString("yyyy");
var releaseDate = r.ReleaseDate == DateTime.MinValue ? string.Empty : r.ReleaseDate.ToString("yyyy");
bool matchResult;
switch (r.Type) bool matchResult;
{ switch (r.Type)
case RequestType.Movie: {
matchResult = IsMovieAvailable(movies, r.Title, releaseDate); case RequestType.Movie:
break; matchResult = IsMovieAvailable(movies, r.Title, releaseDate);
case RequestType.TvShow: break;
matchResult = IsTvShowAvailable(shows, r.Title, releaseDate); case RequestType.TvShow:
break; matchResult = IsTvShowAvailable(shows, r.Title, releaseDate);
case RequestType.Album: break;
matchResult = IsAlbumAvailable(albums, r.Title, r.ReleaseDate.Year.ToString(), r.ArtistName); case RequestType.Album:
break; matchResult = IsAlbumAvailable(albums, r.Title, r.ReleaseDate.Year.ToString(), r.ArtistName);
default: break;
throw new ArgumentOutOfRangeException(); default:
} throw new ArgumentOutOfRangeException();
}
if (matchResult)
{ if (matchResult)
r.Available = true; {
modifiedModel.Add(r); r.Available = true;
continue; modifiedModel.Add(r);
} continue;
}
Log.Trace("The result from Plex where the title's match was null, so that means the content is not yet in Plex.");
} Log.Trace("The result from Plex where the title's match was null, so that means the content is not yet in Plex.");
}
Log.Trace("Updating the requests now");
Log.Trace("Requests that will be updates:"); Log.Trace("Updating the requests now");
Log.Trace(modifiedModel.SelectMany(x => x.Title).DumpJson()); Log.Trace("Requests that will be updates:");
Log.Trace(modifiedModel.SelectMany(x => x.Title).DumpJson());
if (modifiedModel.Any())
{ if (modifiedModel.Any())
RequestService.BatchUpdate(modifiedModel); {
} RequestService.BatchUpdate(modifiedModel);
} }
}
public List<PlexMovie> GetPlexMovies()
{ public List<PlexMovie> GetPlexMovies()
var movies = new List<PlexMovie>(); {
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries); var movies = new List<PlexMovie>();
if (libs != null) var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
{ if (libs != null)
var movieLibs = libs.Where(x => {
x.Video.Any(y => var movieLibs = libs.Where(x =>
y.Type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase) x.Video.Any(y =>
) y.Type.Equals(PlexMediaType.Movie.ToString(), StringComparison.CurrentCultureIgnoreCase)
).ToArray(); )
).ToArray();
foreach (var lib in movieLibs)
{ foreach (var lib in movieLibs)
movies.AddRange(lib.Video.Select(x => new PlexMovie() // movies are in the Video list {
{ movies.AddRange(lib.Video.Select(x => new PlexMovie() // movies are in the Video list
Title = x.Title, {
ReleaseYear = x.Year Title = x.Title,
})); ReleaseYear = x.Year
} }));
} }
return movies; }
} return movies;
}
public bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year)
{ public bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year)
return plexMovies.Any(x => x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) && x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase)); {
} return plexMovies.Any(x => x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) && x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase));
}
public List<PlexTvShow> GetPlexTvShows()
{ public List<PlexTvShow> GetPlexTvShows()
var shows = new List<PlexTvShow>(); {
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries); var shows = new List<PlexTvShow>();
if (libs != null) var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
{ if (libs != null)
var tvLibs = libs.Where(x => {
x.Directory.Any(y => var tvLibs = libs.Where(x =>
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase) x.Directory.Any(y =>
) y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
).ToArray(); )
).ToArray();
foreach (var lib in tvLibs)
{ foreach (var lib in tvLibs)
shows.AddRange(lib.Directory.Select(x => new PlexTvShow() // shows are in the directory list {
{ shows.AddRange(lib.Directory.Select(x => new PlexTvShow() // shows are in the directory list
Title = x.Title, {
ReleaseYear = x.Year Title = x.Title,
})); ReleaseYear = x.Year
} }));
} }
return shows; }
} return shows;
}
public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year)
{ public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year)
return plexShows.Any(x => {
(x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) || x.Title.StartsWith(title, StringComparison.CurrentCultureIgnoreCase)) && return plexShows.Any(x =>
x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase)); (x.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) || x.Title.StartsWith(title, StringComparison.CurrentCultureIgnoreCase)) &&
} x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase));
}
public List<PlexAlbum> GetPlexAlbums()
{ public List<PlexAlbum> GetPlexAlbums()
var albums = new List<PlexAlbum>(); {
var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries); var albums = new List<PlexAlbum>();
if (libs != null) var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
{ if (libs != null)
var albumLibs = libs.Where(x => {
x.Directory.Any(y => var albumLibs = libs.Where(x =>
y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase) x.Directory.Any(y =>
) y.Type.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)
).ToArray(); )
).ToArray();
foreach (var lib in albumLibs)
{ foreach (var lib in albumLibs)
albums.AddRange(lib.Directory.Select(x => new PlexAlbum() {
{ albums.AddRange(lib.Directory.Select(x => new PlexAlbum()
Title = x.Title, {
ReleaseYear = x.Year, Title = x.Title,
Artist = x.ParentTitle ReleaseYear = x.Year,
})); Artist = x.ParentTitle
} }));
} }
return albums; }
} return albums;
}
public bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist)
{ public bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist)
return plexAlbums.Any(x => {
x.Title.Contains(title) && return plexAlbums.Any(x =>
//x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase) && x.Title.Contains(title) &&
x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase)); //x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase) &&
} x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase));
}
private List<PlexSearch> CachedLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings, bool setCache)
{ private List<PlexSearch> CachedLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings, bool setCache)
Log.Trace("Obtaining library sections from Plex for the following request"); {
Log.Trace("Obtaining library sections from Plex for the following request");
List<PlexSearch> results = new List<PlexSearch>();
List<PlexSearch> results = new List<PlexSearch>();
if (!ValidateSettings(plexSettings, authSettings))
{ if (!ValidateSettings(plexSettings, authSettings))
Log.Warn("The settings are not configured"); {
return results; // don't error out here, just let it go! Log.Warn("The settings are not configured");
} return results; // don't error out here, just let it go!
}
if (setCache)
{ if (setCache)
results = GetLibraries(authSettings, plexSettings); {
Cache.Set(CacheKeys.PlexLibaries, results, 10); results = GetLibraries(authSettings, plexSettings);
} Cache.Set(CacheKeys.PlexLibaries, results, 10);
else }
{ else
results = Cache.GetOrSet(CacheKeys.PlexLibaries, () => { {
return GetLibraries(authSettings, plexSettings); results = Cache.GetOrSet(CacheKeys.PlexLibaries, () => {
}, 10); return GetLibraries(authSettings, plexSettings);
} }, 10);
return results; }
} return results;
}
private List<PlexSearch> GetLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings)
{ private List<PlexSearch> GetLibraries(AuthenticationSettings authSettings, PlexSettings plexSettings)
var sections = PlexApi.GetLibrarySections(authSettings.PlexAuthToken, plexSettings.FullUri); {
var sections = PlexApi.GetLibrarySections(authSettings.PlexAuthToken, plexSettings.FullUri);
List<PlexSearch> libs = new List<PlexSearch>();
if (sections != null) List<PlexSearch> libs = new List<PlexSearch>();
{ if (sections != null)
foreach (var dir in sections.Directories) {
{ foreach (var dir in sections.Directories)
Log.Trace("Obtaining results from Plex for the following library section: {0}", dir.Title); {
var lib = PlexApi.GetLibrary(authSettings.PlexAuthToken, plexSettings.FullUri, dir.Key); Log.Trace("Obtaining results from Plex for the following library section: {0}", dir.Title);
if (lib != null) var lib = PlexApi.GetLibrary(authSettings.PlexAuthToken, plexSettings.FullUri, dir.Key);
{ if (lib != null)
libs.Add(lib); {
} libs.Add(lib);
} }
} }
}
return libs;
} return libs;
}
private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
{ private bool ValidateSettings(PlexSettings plex, AuthenticationSettings auth)
if (plex?.Ip == null || auth?.PlexAuthToken == null) {
{ if (plex?.Ip == null || auth?.PlexAuthToken == null)
Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token."); {
return false; Log.Warn("A setting is null, Ensure Plex is configured correctly, and we have a Plex Auth token.");
} return false;
return true; }
} return true;
} }
public void Execute(IJobExecutionContext context)
{
CheckAndUpdateAll();
}
}
} }

View file

@ -1,78 +1,82 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: PlexAvailabilityChecker.cs // File: PlexAvailabilityChecker.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the // a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including // "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, // without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to // distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to // permit persons to whom the Software is furnished to do so, subject to
// the following conditions: // the following conditions:
// //
// The above copyright notice and this permission notice shall be // The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. // included in all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Linq;
using System;
using NLog; using NLog;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Core; using PlexRequests.Api.Models.SickRage;
using PlexRequests.Core.SettingModels; using PlexRequests.Core;
using PlexRequests.Helpers; using PlexRequests.Core.SettingModels;
using PlexRequests.Services.Interfaces; using PlexRequests.Helpers;
using PlexRequests.Api.Models.Movie; using PlexRequests.Services.Interfaces;
using System.Linq;
using PlexRequests.Api.Models.SickRage; using Quartz;
namespace PlexRequests.Services namespace PlexRequests.Services.Jobs
{ {
public class SickRageCacher : ISickRageCacher public class SickRageCacher : IJob, ISickRageCacher
{ {
public SickRageCacher(ISettingsService<SickRageSettings> srSettings, ISickRageApi srApi, ICacheProvider cache) public SickRageCacher(ISettingsService<SickRageSettings> srSettings, ISickRageApi srApi, ICacheProvider cache)
{ {
SrSettings = srSettings; SrSettings = srSettings;
SrApi = srApi; SrApi = srApi;
Cache = cache; Cache = cache;
} }
private ISettingsService<SickRageSettings> SrSettings { get; } private ISettingsService<SickRageSettings> SrSettings { get; }
private ICacheProvider Cache { get; } private ICacheProvider Cache { get; }
private ISickRageApi SrApi { get; } private ISickRageApi SrApi { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private static Logger Log = LogManager.GetCurrentClassLogger();
public void Queued(long check) public void Queued()
{ {
Log.Trace("This is check no. {0}", check); Log.Trace("Getting the settings");
Log.Trace("Getting the settings");
var settings = SrSettings.GetSettings();
var settings = SrSettings.GetSettings(); if (settings.Enabled)
if (settings.Enabled) {
{ Log.Trace("Getting all shows from SickRage");
Log.Trace("Getting all shows from SickRage"); var movies = SrApi.GetShows(settings.ApiKey, settings.FullUri);
var movies = SrApi.GetShows(settings.ApiKey, settings.FullUri); Cache.Set(CacheKeys.SickRageQueued, movies.Result);
Cache.Set(CacheKeys.SickRageQueued, movies.Result); }
} }
}
// we do not want to set here...
// we do not want to set here... public int[] QueuedIds()
public int[] QueuedIds() {
{ var tv = Cache.Get<SickrageShows>(CacheKeys.SickRageQueued);
var tv = Cache.Get<SickrageShows>(CacheKeys.SickRageQueued); return tv?.data.Values.Select(x => x.tvdbid).ToArray() ?? new int[] { };
return tv?.data.Values.Select(x => x.tvdbid).ToArray() ?? new int[] { }; }
}
} public void Execute(IJobExecutionContext context)
{
Queued();
}
}
} }

View file

@ -1,77 +1,83 @@
#region Copyright #region Copyright
// /************************************************************************ // /************************************************************************
// Copyright (c) 2016 Jamie Rees // Copyright (c) 2016 Jamie Rees
// File: PlexAvailabilityChecker.cs // File: PlexAvailabilityChecker.cs
// Created By: Jamie Rees // Created By: Jamie Rees
// //
// Permission is hereby granted, free of charge, to any person obtaining // Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the // a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including // "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, // without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to // distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to // permit persons to whom the Software is furnished to do so, subject to
// the following conditions: // the following conditions:
// //
// The above copyright notice and this permission notice shall be // The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. // included in all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System.Collections.Generic;
using NLog; using System.Linq;
using PlexRequests.Api.Interfaces; using NLog;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Api.Interfaces;
using PlexRequests.Helpers; using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Services.Interfaces; using PlexRequests.Core;
using System.Linq; using PlexRequests.Core.SettingModels;
using System.Collections.Generic; using PlexRequests.Helpers;
using PlexRequests.Api.Models.Sonarr; using PlexRequests.Services.Interfaces;
namespace PlexRequests.Services using Quartz;
{
public class SonarrCacher : ISonarrCacher namespace PlexRequests.Services.Jobs
{ {
public SonarrCacher(ISettingsService<SonarrSettings> sonarrSettings, ISonarrApi sonarrApi, ICacheProvider cache) public class SonarrCacher : IJob, ISonarrCacher
{ {
SonarrSettings = sonarrSettings; public SonarrCacher(ISettingsService<SonarrSettings> sonarrSettings, ISonarrApi sonarrApi, ICacheProvider cache)
SonarrApi = sonarrApi; {
Cache = cache; SonarrSettings = sonarrSettings;
} SonarrApi = sonarrApi;
Cache = cache;
private ISettingsService<SonarrSettings> SonarrSettings { get; } }
private ICacheProvider Cache { get; }
private ISonarrApi SonarrApi { get; } private ISettingsService<SonarrSettings> SonarrSettings { get; }
private ICacheProvider Cache { get; }
private static Logger Log = LogManager.GetCurrentClassLogger(); private ISonarrApi SonarrApi { get; }
public void Queued(long check) private static Logger Log = LogManager.GetCurrentClassLogger();
{
Log.Trace("This is check no. {0}", check); public void Queued()
Log.Trace("Getting the settings"); {
Log.Trace("Getting the settings");
var settings = SonarrSettings.GetSettings();
if (settings.Enabled) var settings = SonarrSettings.GetSettings();
{ if (settings.Enabled)
Log.Trace("Getting all tv series from Sonarr"); {
var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri); Log.Trace("Getting all tv series from Sonarr");
Cache.Set(CacheKeys.SonarrQueued, series, 10); var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri);
} Cache.Set(CacheKeys.SonarrQueued, series, 10);
} }
}
// we do not want to set here...
public int[] QueuedIds() // we do not want to set here...
{ public int[] QueuedIds()
var series = Cache.Get<List<Series>>(CacheKeys.SonarrQueued); {
return series?.Select(x => x.tvdbId).ToArray() ?? new int[] { }; var series = Cache.Get<List<Series>>(CacheKeys.SonarrQueued);
} return series?.Select(x => x.tvdbId).ToArray() ?? new int[] { };
} }
public void Execute(IJobExecutionContext context)
{
Queued();
}
}
} }

View file

@ -1,102 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: AvailabilityUpdateService.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.Reactive.Linq;
using System.Web.Hosting;
using FluentScheduler;
using Mono.Data.Sqlite;
using NLog;
using PlexRequests.Api;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store;
using PlexRequests.Store.Repository;
using System.Threading.Tasks;
namespace PlexRequests.Services
{
public class MediaCacheService : ITask, IRegisteredObject, IAvailabilityUpdateService
{
public MediaCacheService()
{
var memCache = new MemoryCacheProvider();
var dbConfig = new DbConfiguration(new SqliteFactory());
var repo = new SettingsJsonRepository(dbConfig, memCache);
ConfigurationReader = new ConfigurationReader();
CpCacher = new CouchPotatoCacher(new SettingsServiceV2<CouchPotatoSettings>(repo), new CouchPotatoApi(), memCache);
SonarrCacher = new SonarrCacher(new SettingsServiceV2<SonarrSettings>(repo), new SonarrApi(), memCache);
SickRageCacher = new SickRageCacher(new SettingsServiceV2<SickRageSettings>(repo), new SickrageApi(), memCache);
HostingEnvironment.RegisterObject(this);
}
private static Logger Log = LogManager.GetCurrentClassLogger();
private IConfigurationReader ConfigurationReader { get; }
private ICouchPotatoCacher CpCacher { get; }
private ISonarrCacher SonarrCacher { get; }
private ISickRageCacher SickRageCacher { get; }
private IDisposable CpSubscription { get; set; }
private IDisposable SonarrSubscription { get; set; }
private IDisposable SickRageSubscription { get; set; }
public void Start(Configuration c)
{
CpSubscription?.Dispose();
SonarrSubscription?.Dispose();
SickRageSubscription?.Dispose();
Task.Factory.StartNew(() => CpCacher.Queued(-1));
Task.Factory.StartNew(() => SonarrCacher.Queued(-1));
Task.Factory.StartNew(() => SickRageCacher.Queued(-1));
CpSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(CpCacher.Queued);
SonarrSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(SonarrCacher.Queued);
SickRageSubscription = Observable.Interval(c.Intervals.Notification).Subscribe(SickRageCacher.Queued);
}
public void Execute()
{
Start(ConfigurationReader.Read());
}
public void Stop(bool immediate)
{
HostingEnvironment.UnregisterObject(this);
}
}
public interface ICouchPotatoCacheService
{
void Start(Configuration c);
}
}

View file

@ -35,8 +35,12 @@
<HintPath>..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll</HintPath> <HintPath>..\packages\MimeKit.1.2.22\lib\net45\BouncyCastle.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentScheduler, Version=3.1.46.0, Culture=neutral, PublicKeyToken=b76503528a14ebd1, processorArchitecture=MSIL"> <Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\FluentScheduler.3.1.46\lib\net40\FluentScheduler.dll</HintPath> <HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="MailKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b, processorArchitecture=MSIL"> <Reference Include="MailKit, Version=1.2.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b, processorArchitecture=MSIL">
@ -56,6 +60,10 @@
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath> <HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </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>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@ -84,20 +92,17 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Jobs\CouchPotatoCacher.cs" />
<Compile Include="Jobs\PlexAvailabilityChecker.cs" />
<Compile Include="Jobs\SickRageCacher.cs" />
<Compile Include="Jobs\SonarrCacher.cs" />
<Compile Include="Models\PlexAlbum.cs" /> <Compile Include="Models\PlexAlbum.cs" />
<Compile Include="Models\PlexMovie.cs" /> <Compile Include="Models\PlexMovie.cs" />
<Compile Include="Models\PlexTvShow.cs" /> <Compile Include="Models\PlexTvShow.cs" />
<Compile Include="SickRageCacher.cs" /> <Compile Include="Interfaces\ISickRageCacher.cs" />
<Compile Include="Interfaces\ITvCacher.cs" />
<Compile Include="SonarrCacher.cs" />
<Compile Include="Interfaces\ISonarrCacher.cs" /> <Compile Include="Interfaces\ISonarrCacher.cs" />
<Compile Include="MediaCacheService.cs" />
<Compile Include="AvailabilityUpdateService.cs" />
<Compile Include="Configuration.cs" />
<Compile Include="ConfigurationReader.cs" />
<Compile Include="Interfaces\ICouchPotatoCacher.cs" /> <Compile Include="Interfaces\ICouchPotatoCacher.cs" />
<Compile Include="Interfaces\IAvailabilityChecker.cs" /> <Compile Include="Interfaces\IAvailabilityChecker.cs" />
<Compile Include="Interfaces\IConfigurationReader.cs" />
<Compile Include="Interfaces\IIntervals.cs" /> <Compile Include="Interfaces\IIntervals.cs" />
<Compile Include="Interfaces\INotification.cs" /> <Compile Include="Interfaces\INotification.cs" />
<Compile Include="Interfaces\INotificationService.cs" /> <Compile Include="Interfaces\INotificationService.cs" />
@ -107,11 +112,7 @@
<Compile Include="Notification\NotificationType.cs" /> <Compile Include="Notification\NotificationType.cs" />
<Compile Include="Notification\PushoverNotification.cs" /> <Compile Include="Notification\PushoverNotification.cs" />
<Compile Include="Notification\PushbulletNotification.cs" /> <Compile Include="Notification\PushbulletNotification.cs" />
<Compile Include="CouchPotatoCacher.cs" />
<Compile Include="PlexAvailabilityChecker.cs" />
<Compile Include="PlexType.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UpdateInterval.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />

View file

@ -1,35 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexType.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.Services
{
public enum PlexType
{
Movie,
TvShow,
Music
}
}

View file

@ -1,38 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UpdateInterval.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 PlexRequests.Services.Interfaces;
namespace PlexRequests.Services
{
public class UpdateInterval : IIntervals
{
public TimeSpan Notification => TimeSpan.FromMinutes(10);
}
}

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup></configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>

View file

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="FluentScheduler" version="3.1.46" targetFramework="net452" /> <package id="Common.Logging" version="3.0.0" targetFramework="net452" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net452" />
<package id="MailKit" version="1.2.21" targetFramework="net46" /> <package id="MailKit" version="1.2.21" targetFramework="net46" />
<package id="MimeKit" version="1.2.22" targetFramework="net46" /> <package id="MimeKit" version="1.2.22" targetFramework="net46" />
<package id="NLog" version="4.2.3" targetFramework="net452" /> <package id="NLog" version="4.2.3" targetFramework="net452" />
<package id="Quartz" version="2.3.3" targetFramework="net452" />
<package id="Rx-Core" version="2.2.5" targetFramework="net452" /> <package id="Rx-Core" version="2.2.5" targetFramework="net452" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net452" /> <package id="Rx-Interfaces" version="2.2.5" targetFramework="net452" />
<package id="Rx-Linq" version="2.2.5" targetFramework="net452" /> <package id="Rx-Linq" version="2.2.5" targetFramework="net452" />

View file

@ -52,6 +52,11 @@ using PlexRequests.Store.Repository;
using PlexRequests.UI.Helpers; using PlexRequests.UI.Helpers;
using Nancy.Json; using Nancy.Json;
using PlexRequests.Services.Jobs;
using PlexRequests.UI.Jobs;
using Quartz.Spi;
namespace PlexRequests.UI namespace PlexRequests.UI
{ {
public class Bootstrapper : DefaultNancyBootstrapper public class Bootstrapper : DefaultNancyBootstrapper
@ -90,8 +95,7 @@ namespace PlexRequests.UI
container.Register<ICouchPotatoCacher, CouchPotatoCacher>(); container.Register<ICouchPotatoCacher, CouchPotatoCacher>();
container.Register<ISonarrCacher, SonarrCacher>(); container.Register<ISonarrCacher, SonarrCacher>();
container.Register<ISickRageCacher, SickRageCacher>(); container.Register<ISickRageCacher, SickRageCacher>();
container.Register<IConfigurationReader, ConfigurationReader>(); container.Register<IJobFactory, CustomJobFactory>();
container.Register<IIntervals, UpdateInterval>();
// Api's // Api's
container.Register<ICouchPotatoApi, CouchPotatoApi>(); container.Register<ICouchPotatoApi, CouchPotatoApi>();
@ -105,7 +109,7 @@ namespace PlexRequests.UI
// NotificationService // NotificationService
container.Register<INotificationService, NotificationService>().AsSingleton(); container.Register<INotificationService, NotificationService>().AsSingleton();
JsonSettings.MaxJsonLength = int.MaxValue; JsonSettings.MaxJsonLength = int.MaxValue;
SubscribeAllObservers(container); SubscribeAllObservers(container);

View file

@ -24,11 +24,13 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using Nancy.TinyIoc; using Nancy.TinyIoc;
namespace PlexRequests.UI.Helpers namespace PlexRequests.UI.Helpers
{ {
public class ServiceLocator public class ServiceLocator : IServiceLocator
{ {
static ServiceLocator() static ServiceLocator()
{ {
@ -46,5 +48,16 @@ namespace PlexRequests.UI.Helpers
{ {
return Container?.Resolve<T>(); return Container?.Resolve<T>();
} }
public object Resolve(Type type)
{
return Container.Resolve(type);
}
}
public interface IServiceLocator
{
T Resolve<T>() where T : class;
object Resolve(Type type);
} }
} }

View file

@ -0,0 +1,94 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: CustomJobFactory.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 NLog;
using PlexRequests.UI.Helpers;
using Quartz;
using Quartz.Spi;
namespace PlexRequests.UI.Jobs
{
/// <summary>
/// The custom job factory we are using so we are able to use our IoC container with DI in our Jobs.
/// </summary>
/// <seealso cref="Quartz.Spi.IJobFactory" />
public class CustomJobFactory : IJobFactory
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private IServiceLocator Locator => ServiceLocator.Instance;
/// <summary>
/// Called by the scheduler at the time of the trigger firing, in order to
/// produce a <see cref="T:Quartz.IJob" /> instance on which to call Execute.
/// This will use the <see cref="IServiceLocator"/> to resolve all dependencies of a job.
/// </summary>
/// <param name="bundle">The TriggerFiredBundle from which the <see cref="T:Quartz.IJobDetail" />
/// and other info relating to the trigger firing can be obtained.</param>
/// <param name="scheduler">a handle to the scheduler that is about to execute the job</param>
/// <returns>
/// the newly instantiated Job
/// </returns>
/// <remarks>
/// It should be extremely rare for this method to throw an exception -
/// basically only the case where there is no way at all to instantiate
/// and prepare the Job for execution. When the exception is thrown, the
/// Scheduler will move all triggers associated with the Job into the
/// <see cref="F:Quartz.TriggerState.Error" /> state, which will require human
/// intervention (e.g. an application restart after fixing whatever
/// configuration problem led to the issue with instantiating the Job).
/// </remarks>
/// <throws> SchedulerException if there is a problem instantiating the Job. </throws>
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
var jobDetail = bundle.JobDetail;
var jobType = jobDetail.JobType;
var resolvedType = (IJob)Locator.Resolve(jobType);
return resolvedType;
}
catch (Exception e)
{
Log.Error(e);
throw;
}
}
/// <summary>
/// Allows the job factory to destroy/clean-up the job if needed.
/// </summary>
/// <param name="job"></param>
public void ReturnJob(IJob job)
{
// No need to do anything since our jobs do not implement IDisposable.
}
}
}

View file

@ -1,41 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexRegistry.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 FluentScheduler;
using PlexRequests.Services;
namespace PlexRequests.UI.Jobs
{
public class MediaCacheRegistry : Registry
{
public MediaCacheRegistry()
{
Schedule<MediaCacheService>().ToRunNow();
}
}
}

View file

@ -1,41 +0,0 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: PlexRegistry.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 FluentScheduler;
using PlexRequests.Services;
namespace PlexRequests.UI.Jobs
{
public class PlexRegistry : Registry
{
public PlexRegistry()
{
Schedule<AvailabilityUpdateService>().ToRunNow();
}
}
}

View file

@ -1,23 +0,0 @@
using FluentScheduler;
using Nancy.TinyIoc;
using PlexRequests.Services;
namespace PlexRequests.UI.Jobs
{
public class PlexTaskFactory : ITaskFactory
{
public ITask GetTaskInstance<T>() where T : ITask
{
//typeof(AvailabilityUpdateService);
var container = TinyIoCContainer.Current;
var a= container.Resolve(typeof(T));
object outT;
container.TryResolve(typeof(T), out outT);
return (T)a;
}
}
}

View file

@ -0,0 +1,137 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: Scheduler.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.Services.Jobs;
using Quartz;
using Quartz.Impl;
namespace PlexRequests.UI.Jobs
{
internal sealed class Scheduler
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private readonly ISchedulerFactory _factory;
public Scheduler()
{
_factory = new StdSchedulerFactory();
}
private IEnumerable<IJobDetail> CreateJobs()
{
var jobs = new List<IJobDetail>();
var plex = JobBuilder.Create<PlexAvailabilityChecker>().WithIdentity("PlexAvailabilityChecker", "Plex").Build();
var sickrage = JobBuilder.Create<SickRageCacher>().WithIdentity("SickRageCacher", "Cache").Build();
var sonarr = JobBuilder.Create<SonarrCacher>().WithIdentity("SonarrCacher", "Cache").Build();
var cp = JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build();
jobs.Add(plex);
jobs.Add(sickrage);
jobs.Add(sonarr);
jobs.Add(cp);
return jobs;
}
/// <summary>
/// Starts the scheduler.
/// </summary>
/// <exception cref="System.InvalidProgramException">The jobs do not match the triggers, we should have a trigger per job</exception>
public void StartScheduler()
{
var scheduler = _factory.GetScheduler();
scheduler.JobFactory = new CustomJobFactory();
scheduler.Start();
var jobs = CreateJobs();
var triggers = CreateTriggers();
var jobDetails = jobs as IJobDetail[] ?? jobs.ToArray();
var triggerDetails = triggers as ITrigger[] ?? triggers.ToArray();
if (jobDetails.Length != triggerDetails.Length)
{
Log.Error("The jobs do not match the triggers, we should have a trigger per job");
throw new InvalidProgramException("The jobs do not match the triggers, we should have a trigger per job");
}
for (var i = 0; i < jobDetails.Length; i++)
{
scheduler.ScheduleJob(jobDetails[i], triggerDetails[i]);
}
}
private IEnumerable<ITrigger> CreateTriggers()
{
var triggers = new List<ITrigger>();
var plexAvailabilityChecker =
TriggerBuilder.Create()
.WithIdentity("PlexAvailabilityChecker", "Plex")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInMinutes(10).RepeatForever())
.Build();
var srCacher =
TriggerBuilder.Create()
.WithIdentity("SickRageCacher", "Cache")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInMinutes(10).RepeatForever())
.Build();
var sonarrCacher =
TriggerBuilder.Create()
.WithIdentity("SonarrCacher", "Cache")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInMinutes(10).RepeatForever())
.Build();
var cpCacher =
TriggerBuilder.Create()
.WithIdentity("CouchPotatoCacher", "Cache")
.StartNow()
.WithSimpleSchedule(x => x.WithIntervalInMinutes(10).RepeatForever())
.Build();
triggers.Add(plexAvailabilityChecker);
triggers.Add(srCacher);
triggers.Add(sonarrCacher);
triggers.Add(cpCacher);
return triggers;
}
}
}

View file

@ -57,12 +57,16 @@
</StartupObject> </StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Common.Logging, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath> <HintPath>..\packages\Common.Logging.3.0.0\lib\net40\Common.Logging.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentScheduler, Version=3.1.46.0, Culture=neutral, PublicKeyToken=b76503528a14ebd1, processorArchitecture=MSIL"> <Reference Include="Common.Logging.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
<HintPath>..\packages\FluentScheduler.3.1.46\lib\net40\FluentScheduler.dll</HintPath> <HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dapper, Version=1.40.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
@ -137,6 +141,10 @@
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath> <HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </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>
</Reference>
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll</HintPath> <HintPath>..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -170,7 +178,8 @@
<Compile Include="Helpers\StringHelper.cs" /> <Compile Include="Helpers\StringHelper.cs" />
<Compile Include="Helpers\TvSender.cs" /> <Compile Include="Helpers\TvSender.cs" />
<Compile Include="Helpers\ValidationHelper.cs" /> <Compile Include="Helpers\ValidationHelper.cs" />
<Compile Include="Jobs\MediaCacheRegistry.cs" /> <Compile Include="Jobs\CustomJobFactory.cs" />
<Compile Include="Jobs\Scheduler.cs" />
<Compile Include="Models\DatatablesModel.cs" /> <Compile Include="Models\DatatablesModel.cs" />
<Compile Include="Models\MovieSearchType.cs" /> <Compile Include="Models\MovieSearchType.cs" />
<Compile Include="Models\QualityModel.cs" /> <Compile Include="Models\QualityModel.cs" />
@ -192,8 +201,6 @@
<Content Include="Content\bootstrap.css"> <Content Include="Content\bootstrap.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Compile Include="Jobs\PlexRegistry.cs" />
<Compile Include="Jobs\PlexTaskFactory.cs" />
<Compile Include="Models\JsonResponseModel.cs" /> <Compile Include="Models\JsonResponseModel.cs" />
<Compile Include="Models\PlexAuth.cs" /> <Compile Include="Models\PlexAuth.cs" />
<Compile Include="Models\RequestViewModel.cs" /> <Compile Include="Models\RequestViewModel.cs" />
@ -304,6 +311,9 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="Content\pace.scss" /> <None Include="Content\pace.scss" />
<None Include="job_scheduling_data_2_0.xsd">
<SubType>Designer</SubType>
</None>
<None Include="NLog.xsd"> <None Include="NLog.xsd">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>

View file

@ -40,9 +40,6 @@ using PlexRequests.Store;
using PlexRequests.Store.Repository; using PlexRequests.Store.Repository;
using System.Diagnostics; using System.Diagnostics;
using FluentScheduler;
using PlexRequests.UI.Jobs;
namespace PlexRequests.UI namespace PlexRequests.UI
{ {
class Program class Program
@ -54,7 +51,7 @@ namespace PlexRequests.UI
var port = -1; var port = -1;
if (args.Length > 0) if (args.Length > 0)
{ {
for (int i = 0; i < args.Length; i++) for (var i = 0; i < args.Length; i++)
{ {
var arg = args[i].ToLowerInvariant().Substring(1); var arg = args[i].ToLowerInvariant().Substring(1);
switch (arg) switch (arg)
@ -101,8 +98,6 @@ namespace PlexRequests.UI
{ {
using (WebApp.Start<Startup>(options)) using (WebApp.Start<Startup>(options))
{ {
SetupSchedulers();
Console.WriteLine($"Request Plex is running on the following: http://+:{port}/"); Console.WriteLine($"Request Plex is running on the following: http://+:{port}/");
if (Type.GetType("Mono.Runtime") != null) if (Type.GetType("Mono.Runtime") != null)
@ -165,12 +160,5 @@ namespace PlexRequests.UI
LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level)); LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level));
} }
} }
private static void SetupSchedulers()
{
TaskManager.TaskFactory = new PlexTaskFactory();
TaskManager.Initialize(new PlexRegistry());
TaskManager.Initialize(new MediaCacheRegistry());
}
} }
} }

View file

@ -25,36 +25,33 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using FluentScheduler;
using NLog; using NLog;
using Owin; using Owin;
using PlexRequests.UI.Jobs; using PlexRequests.UI.Jobs;
using TaskFactory = FluentScheduler.TaskFactory;
namespace PlexRequests.UI namespace PlexRequests.UI
{ {
public class Startup public class Startup
{ {
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private static Logger Log = LogManager.GetCurrentClassLogger();
public void Configuration(IAppBuilder app) public void Configuration(IAppBuilder app)
{ {
try try
{ {
app.UseNancy(); app.UseNancy();
var scheduler = new Scheduler();
scheduler.StartScheduler();
} }
catch (Exception exception) catch (Exception exception)
{ {
Log.Fatal(exception); Log.Fatal(exception);
throw; throw;
} }
} }
} }
} }

View file

@ -1,50 +1,50 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<configSections> <configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/> <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup> </sectionGroup>
</configSections> </configSections>
<appSettings> <appSettings>
<add key="ClientSettingsProvider.ServiceUri" value=""/> <add key="ClientSettingsProvider.ServiceUri" value="" />
<add key="webPages:Enabled" value="false"/></appSettings> <add key="webPages:Enabled" value="false" /></appSettings>
<connectionStrings> <connectionStrings>
<add name="Sqlite" connectionString="Data Source=RequestPlex.sqlite" providerName="Mono.Data.Sqlite"/> <add name="Sqlite" connectionString="Data Source=RequestPlex.sqlite" providerName="Mono.Data.Sqlite" />
</connectionStrings> </connectionStrings>
<system.web> <system.web>
<membership defaultProvider="ClientAuthenticationMembershipProvider"> <membership defaultProvider="ClientAuthenticationMembershipProvider">
<providers> <providers>
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri=""/> <add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
</providers> </providers>
</membership> </membership>
<roleManager defaultProvider="ClientRoleProvider" enabled="true"> <roleManager defaultProvider="ClientRoleProvider" enabled="true">
<providers> <providers>
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400"/> <add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
</providers> </providers>
</roleManager> </roleManager>
</system.web> </system.web>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0"/> <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral"/> <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0"/> <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral"/> <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.99.0" newVersion="1.0.99.0"/> <bindingRedirect oldVersion="0.0.0.0-1.0.99.0" newVersion="1.0.99.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<system.web.webPages.razor> <system.web.webPages.razor>
<pages pageBaseType="Nancy.ViewEngines.Razor.NancyRazorViewBase"> <pages pageBaseType="Nancy.ViewEngines.Razor.NancyRazorViewBase">
<namespaces> <namespaces>
<add namespace="Nancy.ViewEngines.Razor"/> <add namespace="Nancy.ViewEngines.Razor" />
</namespaces> </namespaces>
</pages> </pages>
</system.web.webPages.razor><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup></configuration> </system.web.webPages.razor><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /></startup></configuration>

View file

@ -0,0 +1,361 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
targetNamespace="http://quartznet.sourceforge.net/JobSchedulingData"
elementFormDefault="qualified"
version="2.0">
<xs:element name="job-scheduling-data">
<xs:annotation>
<xs:documentation>Root level node</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="pre-processing-commands" type="pre-processing-commandsType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Commands to be executed before scheduling the jobs and triggers in this file.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="processing-directives" type="processing-directivesType" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>Directives to be followed while scheduling the jobs and triggers in this file.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="schedule" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element name="job" type="job-detailType" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="trigger" type="triggerType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="version" type="xs:string">
<xs:annotation>
<xs:documentation>Version of the XML Schema instance</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:complexType name="pre-processing-commandsType">
<xs:sequence maxOccurs="unbounded">
<xs:element name="delete-jobs-in-group" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete all jobs, if any, in the identified group. "*" can be used to identify all groups. Will also result in deleting all triggers related to the jobs.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="delete-triggers-in-group" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete all triggers, if any, in the identified group. "*" can be used to identify all groups. Will also result in deletion of related jobs that are non-durable.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="delete-job" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete the identified job if it exists (will also result in deleting all triggers related to it).</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="delete-trigger" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>Delete the identified trigger if it exists (will also result in deletion of related jobs that are non-durable).</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="processing-directivesType">
<xs:sequence>
<xs:element name="overwrite-existing-data" type="xs:boolean" minOccurs="0" default="true">
<xs:annotation>
<xs:documentation>Whether the existing scheduling data (with same identifiers) will be overwritten. If false, and ignore-duplicates is not false, and jobs or triggers with the same names already exist as those in the file, an error will occur.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="ignore-duplicates" type="xs:boolean" minOccurs="0" default="false">
<xs:annotation>
<xs:documentation>If true (and overwrite-existing-data is false) then any job/triggers encountered in this file that have names that already exist in the scheduler will be ignored, and no error will be produced.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="schedule-trigger-relative-to-replaced-trigger" type="xs:boolean" minOccurs="0" default="false">
<xs:annotation>
<xs:documentation>If true trigger's start time is calculated based on earlier run time instead of fixed value. Trigger's start time must be undefined for this to work.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="job-detailType">
<xs:annotation>
<xs:documentation>Define a JobDetail</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
<xs:element name="description" type="xs:string" minOccurs="0" />
<xs:element name="job-type" type="xs:string" />
<xs:sequence minOccurs="0">
<xs:element name="durable" type="xs:boolean" />
<xs:element name="recover" type="xs:boolean" />
</xs:sequence>
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="job-data-mapType">
<xs:annotation>
<xs:documentation>Define a JobDataMap</xs:documentation>
</xs:annotation>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="entry" type="entryType" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="entryType">
<xs:annotation>
<xs:documentation>Define a JobDataMap entry</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="key" type="xs:string" />
<xs:element name="value" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="triggerType">
<xs:annotation>
<xs:documentation>Define a Trigger</xs:documentation>
</xs:annotation>
<xs:choice>
<xs:element name="simple" type="simpleTriggerType" />
<xs:element name="cron" type="cronTriggerType" />
<xs:element name="calendar-interval" type="calendarIntervalTriggerType" />
</xs:choice>
</xs:complexType>
<xs:complexType name="abstractTriggerType" abstract="true">
<xs:annotation>
<xs:documentation>Common Trigger definitions</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="group" type="xs:string" minOccurs="0" />
<xs:element name="description" type="xs:string" minOccurs="0" />
<xs:element name="job-name" type="xs:string" />
<xs:element name="job-group" type="xs:string" minOccurs="0" />
<xs:element name="priority" type="xs:nonNegativeInteger" minOccurs="0" />
<xs:element name="calendar-name" type="xs:string" minOccurs="0" />
<xs:element name="job-data-map" type="job-data-mapType" minOccurs="0" />
<xs:sequence minOccurs="0">
<xs:choice>
<xs:element name="start-time" type="xs:dateTime" />
<xs:element name="start-time-seconds-in-future" type="xs:nonNegativeInteger" />
</xs:choice>
<xs:element name="end-time" type="xs:dateTime" minOccurs="0" />
</xs:sequence>
</xs:sequence>
</xs:complexType>
<xs:complexType name="simpleTriggerType">
<xs:annotation>
<xs:documentation>Define a SimpleTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="simple-trigger-misfire-instructionType" minOccurs="0" />
<xs:sequence minOccurs="0">
<xs:element name="repeat-count" type="repeat-countType" />
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
</xs:sequence>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="cronTriggerType">
<xs:annotation>
<xs:documentation>Define a CronTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="cron-trigger-misfire-instructionType" minOccurs="0" />
<xs:element name="cron-expression" type="cron-expressionType" />
<xs:element name="time-zone" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="calendarIntervalTriggerType">
<xs:annotation>
<xs:documentation>Define a DateIntervalTrigger</xs:documentation>
</xs:annotation>
<xs:complexContent>
<xs:extension base="abstractTriggerType">
<xs:sequence>
<xs:element name="misfire-instruction" type="date-interval-trigger-misfire-instructionType" minOccurs="0" />
<xs:element name="repeat-interval" type="xs:nonNegativeInteger" />
<xs:element name="repeat-interval-unit" type="interval-unitType" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:simpleType name="cron-expressionType">
<xs:annotation>
<xs:documentation>
Cron expression (see JavaDoc for examples)
Special thanks to Chris Thatcher (thatcher@butterfly.net) for the regular expression!
Regular expressions are not my strong point but I believe this is complete,
with the caveat that order for expressions like 3-0 is not legal but will pass,
and month and day names must be capitalized.
If you want to examine the correctness look for the [\s] to denote the
seperation of individual regular expressions. This is how I break them up visually
to examine them:
SECONDS:
(
((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)
| (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))
| ([\?])
| ([\*])
) [\s]
MINUTES:
(
((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)
| (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))
| ([\?])
| ([\*])
) [\s]
HOURS:
(
((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)
| (([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))
| ([\?])
| ([\*])
) [\s]
DAY OF MONTH:
(
((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)
| (([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)
| (L(-[0-9])?)
| (L(-[1-2][0-9])?)
| (L(-[3][0-1])?)
| (LW)
| ([1-9]W)
| ([1-3][0-9]W)
| ([\?])
| ([\*])
)[\s]
MONTH:
(
((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)
| (([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))
| (((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)
| ((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))
| ([\?])
| ([\*])
)[\s]
DAY OF WEEK:
(
(([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)
| ([1-7]/([1-7]))
| (((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)
| ((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)
| (([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))(L|LW)?)
| (([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)
| ([\?])
| ([\*])
)
YEAR (OPTIONAL):
(
[\s]?
([\*])?
| ((19[7-9][0-9])|(20[0-9][0-9]))?
| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?
| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?
)
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern
value="(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\?])|([\*]))[\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\?])|([\*]))[\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\?])|([\*]))[\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\?])|([\*]))[\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\?])|([\*]))([\s]?(([\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?| (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?| ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="repeat-countType">
<xs:annotation>
<xs:documentation>Number of times to repeat the Trigger (-1 for indefinite)</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:integer">
<xs:minInclusive value="-1" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="simple-trigger-misfire-instructionType">
<xs:annotation>
<xs:documentation>Simple Trigger Misfire Instructions</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="SmartPolicy" />
<xs:pattern value="RescheduleNextWithExistingCount" />
<xs:pattern value="RescheduleNextWithRemainingCount" />
<xs:pattern value="RescheduleNowWithExistingRepeatCount" />
<xs:pattern value="RescheduleNowWithRemainingRepeatCount" />
<xs:pattern value="FireNow" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="cron-trigger-misfire-instructionType">
<xs:annotation>
<xs:documentation>Cron Trigger Misfire Instructions</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="SmartPolicy" />
<xs:pattern value="DoNothing" />
<xs:pattern value="FireOnceNow" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="date-interval-trigger-misfire-instructionType">
<xs:annotation>
<xs:documentation>Date Interval Trigger Misfire Instructions</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="SmartPolicy" />
<xs:pattern value="DoNothing" />
<xs:pattern value="FireOnceNow" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="interval-unitType">
<xs:annotation>
<xs:documentation>Interval Units</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="Day" />
<xs:pattern value="Hour" />
<xs:pattern value="Minute" />
<xs:pattern value="Month" />
<xs:pattern value="Second" />
<xs:pattern value="Week" />
<xs:pattern value="Year" />
</xs:restriction>
</xs:simpleType>
</xs:schema>

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Common.Logging" version="3.0.0" targetFramework="net452" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net452" />
<package id="Dapper" version="1.42" targetFramework="net46" /> <package id="Dapper" version="1.42" targetFramework="net46" />
<package id="FluentScheduler" version="3.1.46" targetFramework="net46" />
<package id="FluentValidation" version="6.2.1.0" targetFramework="net46" /> <package id="FluentValidation" version="6.2.1.0" targetFramework="net46" />
<package id="MarkdownSharp" version="1.13.0.0" targetFramework="net46" /> <package id="MarkdownSharp" version="1.13.0.0" targetFramework="net46" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net452" /> <package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net452" />
@ -22,6 +23,7 @@
<package id="NLog.Config" version="4.2.3" targetFramework="net452" /> <package id="NLog.Config" version="4.2.3" targetFramework="net452" />
<package id="NLog.Schema" version="4.0.0" targetFramework="net452" /> <package id="NLog.Schema" version="4.0.0" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" /> <package id="Owin" version="1.0" targetFramework="net452" />
<package id="Quartz" version="2.3.3" targetFramework="net452" />
<package id="RestSharp" version="105.2.3" targetFramework="net452" /> <package id="RestSharp" version="105.2.3" targetFramework="net452" />
<package id="System.Collections" version="4.0.0" targetFramework="net452" /> <package id="System.Collections" version="4.0.0" targetFramework="net452" />
<package id="System.Data.SQLite.Core" version="1.0.99.0" targetFramework="net46" requireReinstallation="true" /> <package id="System.Data.SQLite.Core" version="1.0.99.0" targetFramework="net46" requireReinstallation="true" />