Finished the main part of #844 just need testing

This commit is contained in:
Jamie.Rees 2016-12-29 09:37:00 +00:00
commit 0811a89c86
26 changed files with 820 additions and 91 deletions

View file

@ -33,6 +33,7 @@ using System.Linq;
using NLog;
using Ombi.Core;
using Ombi.Core.SettingModels;
using Ombi.Services.Interfaces;
using Ombi.Services.Jobs;
using Ombi.UI.Helpers;
using Quartz;
@ -68,6 +69,7 @@ namespace Ombi.UI.Jobs
JobBuilder.Create<SickRageCacher>().WithIdentity("SickRageCacher", "Cache").Build(),
JobBuilder.Create<SonarrCacher>().WithIdentity("SonarrCacher", "Cache").Build(),
JobBuilder.Create<CouchPotatoCacher>().WithIdentity("CouchPotatoCacher", "Cache").Build(),
JobBuilder.Create<WatcherCacher>().WithIdentity("WatcherCacher", "Cache").Build(),
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
@ -117,6 +119,10 @@ namespace Ombi.UI.Jobs
{
s.CouchPotatoCacher = 60;
}
if (s.WatcherCacher == 0)
{
s.WatcherCacher = 60;
}
if (s.PlexAvailabilityChecker == 0)
{
s.PlexAvailabilityChecker = 60;
@ -208,6 +214,13 @@ namespace Ombi.UI.Jobs
.WithSimpleSchedule(x => x.WithIntervalInMinutes(s.CouchPotatoCacher).RepeatForever())
.Build();
var watcherCacher =
TriggerBuilder.Create()
.WithIdentity("WatcherCacher", "Cache")
.StartAt(DateBuilder.FutureDate(4, IntervalUnit.Minute))
.WithSimpleSchedule(x => x.WithIntervalInMinutes(s.WatcherCacher).RepeatForever())
.Build();
var storeBackup =
TriggerBuilder.Create()
.WithIdentity("StoreBackup", "Database")
@ -258,6 +271,7 @@ namespace Ombi.UI.Jobs
triggers.Add(srCacher);
triggers.Add(sonarrCacher);
triggers.Add(cpCacher);
triggers.Add(watcherCacher);
triggers.Add(storeBackup);
triggers.Add(storeCleanup);
triggers.Add(userRequestLimiter);

View file

@ -75,6 +75,7 @@ namespace Ombi.UI.Modules.Admin
private ISettingsService<PushoverNotificationSettings> PushoverService { get; }
private ISettingsService<HeadphonesSettings> HeadphonesService { get; }
private ISettingsService<NewletterSettings> NewsLetterService { get; }
private ISettingsService<WatcherSettings> WatcherSettings { get; }
private ISettingsService<LogSettings> LogService { get; }
private IPlexApi PlexApi { get; }
private ISonarrApi SonarrApi { get; }
@ -116,7 +117,8 @@ namespace Ombi.UI.Modules.Admin
ICacheProvider cache, ISettingsService<SlackNotificationSettings> slackSettings,
ISlackApi slackApi, ISettingsService<LandingPageSettings> lp,
ISettingsService<ScheduledJobsSettings> scheduler, IJobRecord rec, IAnalytics analytics,
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded
ISettingsService<NotificationSettingsV2> notifyService, IRecentlyAdded recentlyAdded,
ISettingsService<WatcherSettings> watcherSettings
, ISecurityExtensions security) : base("admin", prService, security)
{
PrService = prService;
@ -147,6 +149,7 @@ namespace Ombi.UI.Modules.Admin
Analytics = analytics;
NotifySettings = notifyService;
RecentlyAdded = recentlyAdded;
WatcherSettings = watcherSettings;
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
@ -374,6 +377,18 @@ namespace Ombi.UI.Modules.Admin
return Response.AsJson(valid.SendJsonError());
}
var watcherSettings = WatcherSettings.GetSettings();
if (watcherSettings.Enabled)
{
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = "Cannot have Watcher and CouchPotato both enabled."
});
}
couchPotatoSettings.ApiKey = couchPotatoSettings.ApiKey.Trim();
var result = CpService.SaveSettings(couchPotatoSettings);
return Response.AsJson(result

View file

@ -0,0 +1,107 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SystemStatusModule.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.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using MarkdownSharp;
using Nancy;
using Nancy.ModelBinding;
using Nancy.Responses.Negotiation;
using Nancy.Validation;
using Ombi.Core;
using Ombi.Core.SettingModels;
using Ombi.Core.StatusChecker;
using Ombi.Helpers;
using Ombi.Helpers.Analytics;
using Ombi.Helpers.Permissions;
using Ombi.UI.Helpers;
using Ombi.UI.Models;
using Action = Ombi.Helpers.Analytics.Action;
using ISecurityExtensions = Ombi.Core.ISecurityExtensions;
namespace Ombi.UI.Modules.Admin
{
public class IntegrationModule : BaseModule
{
public IntegrationModule(ISettingsService<PlexRequestSettings> settingsService, ISettingsService<WatcherSettings> watcher,
ISettingsService<CouchPotatoSettings> cp,ISecurityExtensions security, IAnalytics a) : base("admin", settingsService, security)
{
WatcherSettings = watcher;
Analytics = a;
Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx);
Get["/watcher", true] = async (x, ct) => await Watcher();
Post["/watcher", true] = async (x, ct) => await SaveWatcher();
}
private ISettingsService<WatcherSettings> WatcherSettings { get; }
private ISettingsService<CouchPotatoSettings> CpSettings { get; }
private IAnalytics Analytics { get; }
private async Task<Negotiator> Watcher()
{
var settings = await WatcherSettings.GetSettingsAsync();
return View["Watcher", settings];
}
private async Task<Response> SaveWatcher()
{
var settings = this.Bind<WatcherSettings>();
var valid = this.Validate(settings);
if (!valid.IsValid)
{
return Response.AsJson(valid.SendJsonError());
}
var cpSettings = await CpSettings.GetSettingsAsync().ConfigureAwait(false);
if (cpSettings.Enabled)
{
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = "Cannot have Watcher and CouchPotato both enabled."
});
}
settings.ApiKey = settings.ApiKey.Trim();
var result = await WatcherSettings.SaveSettingsAsync(settings);
return Response.AsJson(result
? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Watcher!" }
: new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." });
}
}
}

View file

@ -45,7 +45,8 @@ namespace Ombi.UI.Modules
{
public ApplicationTesterModule(ICouchPotatoApi cpApi, ISonarrApi sonarrApi, IPlexApi plexApi,
ISickRageApi srApi, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security) : base("test", pr, security)
ISickRageApi srApi, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ISecurityExtensions security,
IWatcherApi watcherApi) : base("test", pr, security)
{
this.RequiresAuthentication();
@ -54,6 +55,7 @@ namespace Ombi.UI.Modules
PlexApi = plexApi;
SickRageApi = srApi;
HeadphonesApi = hpApi;
WatcherApi = watcherApi;
Post["/cp"] = _ => CouchPotatoTest();
Post["/sonarr"] = _ => SonarrTest();
@ -61,6 +63,7 @@ namespace Ombi.UI.Modules
Post["/sickrage"] = _ => SickRageTest();
Post["/headphones"] = _ => HeadphonesTest();
Post["/plexdb"] = _ => TestPlexDb();
Post["/watcher"] = _ => WatcherTest();
}
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
@ -69,6 +72,7 @@ namespace Ombi.UI.Modules
private IPlexApi PlexApi { get; }
private ISickRageApi SickRageApi { get; }
private IHeadphonesApi HeadphonesApi { get; }
private IWatcherApi WatcherApi { get; }
private Response CouchPotatoTest()
{
@ -86,7 +90,7 @@ namespace Ombi.UI.Modules
: Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not connect to CouchPotato, please check your settings." });
}
catch (Exception e) // Exceptions are expected if we cannot connect so we will just log and swallow them.
catch (Exception e) // Exceptions are expected if we cannot connect so we will just log and swallow them.
{
Log.Warn("Exception thrown when attempting to get CP's status: ");
Log.Warn(e);
@ -99,6 +103,35 @@ namespace Ombi.UI.Modules
}
}
private Response WatcherTest()
{
var settings = this.Bind<WatcherSettings>();
var valid = this.Validate(settings);
if (!valid.IsValid)
{
return Response.AsJson(valid.SendJsonError());
}
try
{
var status = WatcherApi.ListMovies(settings.ApiKey, settings.FullUri);
return !status.Error
? Response.AsJson(new JsonResponseModel { Result = true, Message = "Connected to Watcher successfully!" })
: Response.AsJson(new JsonResponseModel { Result = false, Message = $"Could not connect to Watcher, Error: {status.ErrorMessage}" });
}
catch (Exception e) // Exceptions are expected if we cannot connect so we will just log and swallow them.
{
Log.Warn("Exception thrown when attempting to test Watcher ");
Log.Warn(e);
var message = $"Could not connect to Watcher, please check your settings. <strong>Exception Message:</strong> {e.Message}";
if (e.InnerException != null)
{
message = $"Could not connect to Watcher, please check your settings. <strong>Exception Message:</strong> {e.InnerException.Message}";
}
return Response.AsJson(new JsonResponseModel { Result = false, Message = message });
}
}
private Response SonarrTest()
{
var sonarrSettings = this.Bind<SonarrSettings>();

View file

@ -47,17 +47,15 @@ namespace Ombi.UI.Modules
public class ApprovalModule : BaseAuthModule
{
public ApprovalModule(IRequestService service, ISettingsService<CouchPotatoSettings> cpService, ICouchPotatoApi cpApi, ISonarrApi sonarrApi,
public ApprovalModule(IRequestService service, ISonarrApi sonarrApi,
ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings,
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi hpApi, ISettingsService<PlexRequestSettings> pr, ITransientFaultQueue faultQueue
, ISecurityExtensions security) : base("approval", pr, security)
, ISecurityExtensions security, IMovieSender movieSender) : base("approval", pr, security)
{
Before += (ctx) => Security.AdminLoginRedirect(ctx, Permissions.Administrator,Permissions.ManageRequests);
Service = service;
CpService = cpService;
CpApi = cpApi;
SonarrApi = sonarrApi;
SonarrSettings = sonarrSettings;
SickRageApi = srApi;
@ -65,6 +63,7 @@ namespace Ombi.UI.Modules
HeadphonesSettings = hpSettings;
HeadphoneApi = hpApi;
FaultQueue = faultQueue;
MovieSender = movieSender;
Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
Post["/deny", true] = async (x, ct) => await DenyRequest((int)Request.Form.requestid, (string)Request.Form.reason);
@ -77,15 +76,14 @@ namespace Ombi.UI.Modules
}
private IRequestService Service { get; }
private IMovieSender MovieSender { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
private ISettingsService<SonarrSettings> SonarrSettings { get; }
private ISettingsService<SickRageSettings> SickRageSettings { get; }
private ISettingsService<CouchPotatoSettings> CpService { get; }
private ISettingsService<HeadphonesSettings> HeadphonesSettings { get; }
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickRageApi { get; }
private ICouchPotatoApi CpApi { get; }
private IHeadphonesApi HeadphoneApi { get; }
private ITransientFaultQueue FaultQueue { get; }
@ -186,19 +184,19 @@ namespace Ombi.UI.Modules
private async Task<Response> RequestMovieAndUpdateStatus(RequestedModel request, string qualityId)
{
var cpSettings = await CpService.GetSettingsAsync();
Log.Info("Adding movie to CouchPotato : {0}", request.Title);
if (!cpSettings.Enabled)
var result = await MovieSender.Send(request, qualityId);
if (!result.MovieSendingEnabled)
{
// Approve it
request.Approved = true;
Log.Warn("We approved movie: {0} but could not add it to CouchPotato because it has not been setup", request.Title);
Log.Warn("We approved movie: {0} but could not add it to CouchPotato/Watcher because it has not been setup", request.Title);
// Update the record
var inserted = await Service.UpdateRequestAsync(request);
return Response.AsJson(inserted
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to CouchPotato because it has not been configured." }
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to CouchPotato/Watcher because it has not been configured." }
: new JsonResponseModel
{
Result = false,
@ -206,9 +204,7 @@ namespace Ombi.UI.Modules
});
}
var result = CpApi.AddMovie(request.ImdbId, cpSettings.ApiKey, request.Title, cpSettings.FullUri, string.IsNullOrEmpty(qualityId) ? cpSettings.ProfileId : qualityId);
Log.Trace("Adding movie to CP result {0}", result);
if (result)
if (result.Result)
{
// Approve it
request.Approved = true;
@ -230,7 +226,7 @@ namespace Ombi.UI.Modules
{
Result = false,
Message =
"Something went wrong adding the movie to CouchPotato! Please check your settings."
"Something went wrong adding the movie! Please check your settings."
});
}
@ -415,26 +411,23 @@ namespace Ombi.UI.Modules
private async Task<Response> UpdateRequestsAsync(RequestedModel[] requestedModels)
{
var cpSettings = await CpService.GetSettingsAsync();
var updatedRequests = new List<RequestedModel>();
foreach (var r in requestedModels)
{
if (r.Type == RequestType.Movie)
{
if (cpSettings.Enabled)
var movieResult = await MovieSender.Send(r);
if (movieResult.Result)
{
var res = SendMovie(cpSettings, r, CpApi);
if (res)
{
r.Approved = true;
updatedRequests.Add(r);
}
else
{
Log.Error("Could not approve and send the movie {0} to couch potato!", r.Title);
}
r.Approved = true;
updatedRequests.Add(r);
}
else
{
Log.Error("Could not approve and send the movie {0} to couch potato!", r.Title);
}
if(!movieResult.MovieSendingEnabled)
{
r.Approved = true;
updatedRequests.Add(r);
@ -512,13 +505,5 @@ namespace Ombi.UI.Modules
: Response.AsJson(new JsonResponseModel { Result = false, Message = "An error happened, could not update the DB" });
}
private bool SendMovie(CouchPotatoSettings settings, RequestedModel r, ICouchPotatoApi cp)
{
Log.Info("Adding movie to CP : {0}", r.Title);
var result = cp.AddMovie(r.ImdbId, settings.ApiKey, r.Title, settings.FullUri, settings.ProfileId);
Log.Trace("Adding movie to CP result {0}", result);
return result;
}
}
}

View file

@ -66,22 +66,22 @@ namespace Ombi.UI.Modules
{
public class SearchModule : BaseAuthModule
{
public SearchModule(ICacheProvider cache, ISettingsService<CouchPotatoSettings> cpSettings,
public SearchModule(ICacheProvider cache,
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker checker,
IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings,
ISettingsService<SickRageSettings> sickRageService, ICouchPotatoApi cpApi, ISickRageApi srApi,
ISettingsService<SickRageSettings> sickRageService, ISickRageApi srApi,
INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi,
ISettingsService<HeadphonesSettings> hpService,
ICouchPotatoCacher cpCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi,
ICouchPotatoCacher cpCacher, IWatcherCacher watcherCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi,
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth,
IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue, IRepository<PlexContent> content, ISecurityExtensions security)
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue, IRepository<PlexContent> content,
ISecurityExtensions security, IMovieSender movieSender)
: base("search", prSettings, security)
{
Auth = auth;
PlexService = plexService;
PlexApi = plexApi;
CpService = cpSettings;
PrService = prSettings;
MovieApi = new TheMovieDbApi();
Cache = cache;
@ -92,7 +92,6 @@ namespace Ombi.UI.Modules
RequestService = request;
SonarrApi = sonarrApi;
SonarrService = sonarrSettings;
CouchPotatoApi = cpApi;
SickRageService = sickRageService;
SickrageApi = srApi;
NotificationService = notify;
@ -107,7 +106,8 @@ namespace Ombi.UI.Modules
FaultQueue = tfQueue;
TvApi = new TvMazeApi();
PlexContentRepository = content;
MovieSender = movieSender;
WatcherCacher = watcherCacher;
Get["SearchIndex", "/", true] = async (x, ct) => await RequestLoad();
@ -128,20 +128,19 @@ namespace Ombi.UI.Modules
Get["/seasons"] = x => GetSeasons();
Get["/episodes", true] = async (x, ct) => await GetEpisodes();
}
private IWatcherCacher WatcherCacher { get; }
private IMovieSender MovieSender { get; }
private IRepository<PlexContent> PlexContentRepository { get; }
private TvMazeApi TvApi { get; }
private IPlexApi PlexApi { get; }
private TheMovieDbApi MovieApi { get; }
private INotificationService NotificationService { get; }
private ICouchPotatoApi CouchPotatoApi { get; }
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickrageApi { get; }
private IRequestService RequestService { get; }
private ICacheProvider Cache { get; }
private ISettingsService<AuthenticationSettings> Auth { get; }
private ISettingsService<PlexSettings> PlexService { get; }
private ISettingsService<CouchPotatoSettings> CpService { get; }
private ISettingsService<PlexRequestSettings> PrService { get; }
private ISettingsService<SonarrSettings> SonarrService { get; }
private ISettingsService<SickRageSettings> SickRageService { get; }
@ -236,9 +235,9 @@ namespace Ombi.UI.Modules
var cpCached = CpCacher.QueuedIds();
var watcherCached = WatcherCacher.QueuedIds();
var content = PlexContentRepository.GetAll();
var plexMovies = Checker.GetPlexMovies(content);
var settings = await PrService.GetSettingsAsync();
var viewMovies = new List<SearchMovieViewModel>();
var counter = 0;
foreach (var movie in apiMovies)
@ -290,6 +289,10 @@ namespace Ombi.UI.Modules
{
viewMovie.Requested = true;
}
else if(watcherCached.Contains(imdbId) && canSee) // compare to the watcher db
{
viewMovie.Requested = true;
}
viewMovies.Add(viewMovie);
}
@ -564,30 +567,25 @@ namespace Ombi.UI.Modules
{
if (ShouldAutoApprove(RequestType.Movie, settings, Username))
{
var cpSettings = await CpService.GetSettingsAsync();
model.Approved = true;
if (cpSettings.Enabled)
{
Log.Info("Adding movie to CP (No approval required)");
var result = CouchPotatoApi.AddMovie(model.ImdbId, cpSettings.ApiKey, model.Title,
cpSettings.FullUri, cpSettings.ProfileId);
Log.Debug("Adding movie to CP result {0}", result);
if (result)
{
return
await
AddRequest(model, settings,
$"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
return Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_CouchPotatoError
});
var result = await MovieSender.Send(model);
if (result.Result)
{
return await AddRequest(model, settings,
$"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
model.Approved = true;
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
if (!result.MovieSendingEnabled)
{
model.Approved = true;
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
return Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_CouchPotatoError
});
}

View file

@ -43,6 +43,7 @@ namespace Ombi.UI.NinjectModules
{
Bind<IAvailabilityChecker>().To<PlexAvailabilityChecker>();
Bind<ICouchPotatoCacher>().To<CouchPotatoCacher>();
Bind<IWatcherCacher>().To<IWatcherCacher>();
Bind<ISonarrCacher>().To<SonarrCacher>();
Bind<ISickRageCacher>().To<SickRageCacher>();
Bind<IRecentlyAdded>().To<RecentlyAdded>();

View file

@ -251,6 +251,7 @@
<Compile Include="Models\UserManagement\UserUpdateViewModel.cs" />
<Compile Include="Modules\Admin\AboutModule.cs" />
<Compile Include="Modules\Admin\CustomizationModule.cs" />
<Compile Include="Modules\Admin\IntegrationModule.cs" />
<Compile Include="Modules\Admin\UserManagementSettingsModule.cs" />
<Compile Include="Modules\Admin\FaultQueueModule.cs" />
<Compile Include="Modules\Admin\SystemStatusModule.cs" />
@ -282,6 +283,7 @@
</Compile>
<Compile Include="Start\StartupOptions.cs" />
<Compile Include="Start\UpdateValue.cs" />
<Compile Include="Validators\WatcherValidator.cs" />
<Compile Include="Validators\SlackSettingsValidator.cs" />
<Compile Include="Validators\UserViewModelValidator.cs" />
<Compile Include="Validators\HeadphonesValidator.cs" />
@ -761,6 +763,9 @@
<Content Include="Views\About\About.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Views\Integration\Watcher.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Views\Admin\NewsletterSettings.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View file

@ -0,0 +1,43 @@

#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SonarrValidator.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 FluentValidation;
using Ombi.Core.SettingModels;
namespace Ombi.UI.Validators
{
public class WatcherValidator : AbstractValidator<WatcherSettings>
{
public WatcherValidator()
{
RuleFor(request => request.Ip).NotEmpty().WithMessage("You must specify a IP/Host name.");
RuleFor(request => request.Port).NotEmpty().WithMessage("You must specify a Port.");
RuleFor(request => request.ApiKey).NotEmpty().WithMessage("You must specify a Api Key.");
}
}
}

View file

@ -0,0 +1,133 @@
@using Ombi.UI.Helpers
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<Ombi.Core.SettingModels.WatcherSettings>
@Html.Partial("Shared/Partial/_Sidebar")
@{
int port;
if (Model.Port == 0)
{
port = 9090;
}
else
{
port = Model.Port;
}
}
<div class="col-sm-8 col-sm-push-1">
<form class="form-horizontal" method="POST" id="mainForm">
<fieldset>
<legend>Watcher Settings</legend>
@Html.Checkbox(Model.Enabled, "Enabled", "Enabled")
<div class="form-group">
<label for="Ip" class="control-label">Watcher Hostname or IP</label>
<div class="">
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" value="@Model.Ip">
</div>
</div>
<div class="form-group">
<label for="portNumber" class="control-label">Port</label>
<div class="">
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="@port">
</div>
</div>
<div class="form-group">
<label for="ApiKey" class="control-label">Watcher API Key</label>
<div>
<input type="text" class="form-control form-control-custom " id="ApiKey" name="ApiKey" value="@Model.ApiKey">
</div>
</div>
@Html.Checkbox(Model.Ssl, "Ssl", "SSL")
<div class="form-group">
<label for="SubDir" class="control-label">Watcher Base Url</label>
<div>
<input type="text" class="form-control form-control-custom " id="SubDir" name="SubDir" value="@Model.SubDir">
</div>
</div>
<br />
<div class="form-group">
<div>
<button id="testWatcher" type="submit" class="btn btn-primary-outline">Test Connectivity <div id="spinner"> </div></button>
</div>
</div>
<div class="form-group">
<div>
<button id="save" type="submit" class="btn btn-primary-outline">Submit</button>
</div>
</div>
</fieldset>
</form>
</div>
<script>
$(function () {
var baseUrl = '@Html.GetBaseUrl()';
$('#testWatcher').click(function (e) {
e.preventDefault();
var $form = $("#mainForm");
var url = createBaseUrl(baseUrl, "/test/watcher");
$('#spinner').attr("class", "fa fa-spinner fa-spin");
$.ajax({
type: $form.prop("method"),
url: url,
data: $form.serialize(),
dataType: "json",
success: function (response) {
console.log(response);
if (response.result === true) {
$('#spinner').attr("class", "fa fa-check");
generateNotify(response.message, "success");
} else {
generateNotify(response.message, "warning");
$('#spinner').attr("class", "fa fa-times");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
$('#spinner').attr("class", "fa fa-times");
}
});
});
$('#save').click(function (e) {
e.preventDefault();
var $form = $("#mainForm");
var data = $form.serialize();
$.ajax({
type: $form.prop("method"),
data: data,
url: $form.prop("action"),
dataType: "json",
success: function (response) {
if (response.result === true) {
generateNotify(response.message, "success");
} else {
generateNotify(response.message, "warning");
}
},
error: function (e) {
console.log(e);
generateNotify("Something went wrong!", "danger");
}
});
});
});
</script>

View file

@ -9,6 +9,7 @@
@Html.GetSidebarUrl(Context, "/admin/usermanagementsettings", "User Management Settings")
@Html.GetSidebarUrl(Context, "/admin/plex", "Plex")
@Html.GetSidebarUrl(Context, "/admin/couchpotato", "CouchPotato")
@Html.GetSidebarUrl(Context, "/admin/watcher", "Watcher")
@Html.GetSidebarUrl(Context, "/admin/sonarr", "Sonarr")
@Html.GetSidebarUrl(Context, "/admin/sickrage", "SickRage")
@Html.GetSidebarUrl(Context, "/admin/headphones", "Headphones (Beta)")