#region Copyright // /************************************************************************ // Copyright (c) 2016 Jamie Rees // File: ApprovalModule.cs // Created By: Jamie Rees // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Nancy; using Nancy.Security; using NLog; using PlexRequests.Api; using PlexRequests.Api.Interfaces; using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Store; using PlexRequests.UI.Helpers; using PlexRequests.UI.Models; namespace PlexRequests.UI.Modules { public class ApprovalModule : BaseAuthModule { public ApprovalModule(IRequestService service, ISettingsService cpService, ICouchPotatoApi cpApi, ISonarrApi sonarrApi, ISettingsService sonarrSettings, ISickRageApi srApi, ISettingsService srSettings, ISettingsService hpSettings, IHeadphonesApi hpApi, ISettingsService pr) : base("approval", pr) { this.RequiresClaims(UserClaims.Admin); Service = service; CpService = cpService; CpApi = cpApi; SonarrApi = sonarrApi; SonarrSettings = sonarrSettings; SickRageApi = srApi; SickRageSettings = srSettings; HeadphonesSettings = hpSettings; HeadphoneApi = hpApi; Post["/approve"] = parameters => Approve((int)Request.Form.requestid, (string)Request.Form.qualityId); Post["/approveall"] = x => ApproveAll(); Post["/approveallmovies"] = x => ApproveAllMovies(); Post["/approvealltvshows"] = x => ApproveAllTVShows(); } private IRequestService Service { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); private ISettingsService SonarrSettings { get; } private ISettingsService SickRageSettings { get; } private ISettingsService CpService { get; } private ISettingsService HeadphonesSettings { get; } private ISonarrApi SonarrApi { get; } private ISickRageApi SickRageApi { get; } private ICouchPotatoApi CpApi { get; } private IHeadphonesApi HeadphoneApi { get; } /// /// Approves the specified request identifier. /// /// The request identifier. /// private Response Approve(int requestId, string qualityId) { Log.Info("approving request {0}", requestId); // Get the request from the DB var request = Service.Get(requestId); if (request == null) { Log.Warn("Tried approving a request, but the request did not exist in the database, requestId = {0}", requestId); return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." }); } switch (request.Type) { case RequestType.Movie: return RequestMovieAndUpdateStatus(request, qualityId); case RequestType.TvShow: return RequestTvAndUpdateStatus(request, qualityId); case RequestType.Album: return RequestAlbumAndUpdateStatus(request); default: throw new ArgumentOutOfRangeException(nameof(request)); } } private Response RequestTvAndUpdateStatus(RequestedModel request, string qualityId) { var sender = new TvSender(SonarrApi, SickRageApi); var sonarrSettings = SonarrSettings.GetSettings(); if (sonarrSettings.Enabled) { Log.Trace("Sending to Sonarr"); var result = sender.SendToSonarr(sonarrSettings, request, qualityId); Log.Trace("Sonarr Result: "); Log.Trace(result.DumpJson()); if (!string.IsNullOrEmpty(result.title)) { Log.Info("Sent successfully, Approving request now."); request.Approved = true; var requestResult = Service.UpdateRequest(request); Log.Trace("Approval result: {0}", requestResult); if (requestResult) { return Response.AsJson(new JsonResponseModel {Result = true}); } return Response.AsJson(new JsonResponseModel { Result = false, Message = "Updated Sonarr but could not approve it in PlexRequests :(" }); } return Response.AsJson(ValidationHelper.SendSonarrError(result.ErrorMessages)); } var srSettings = SickRageSettings.GetSettings(); if (srSettings.Enabled) { Log.Trace("Sending to SickRage"); var result = sender.SendToSickRage(srSettings, request, qualityId); Log.Trace("SickRage Result: "); Log.Trace(result.DumpJson()); if (result?.result == "success") { Log.Info("Sent successfully, Approving request now."); request.Approved = true; var requestResult = Service.UpdateRequest(request); Log.Trace("Approval result: {0}", requestResult); if (requestResult) { return Response.AsJson(new JsonResponseModel { Result = true }); } return Response.AsJson(new JsonResponseModel { Result = false, Message = "Updated SickRage but could not approve it in PlexRequests :(" }); } return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message != null ? "Message From SickRage: " + result.message : "Could not add the series to SickRage" }); } return Response.AsJson(new JsonResponseModel { Result = false, Message = "SickRage or Sonarr are not set up!" }); } private Response RequestMovieAndUpdateStatus(RequestedModel request, string qualityId) { var cpSettings = CpService.GetSettings(); var cp = new CouchPotatoApi(); Log.Info("Adding movie to CouchPotato : {0}", request.Title); if (!cpSettings.Enabled) { // 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); // Update the record var inserted = Service.UpdateRequest(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 = false, Message = "We could not approve this request. Please try again or check the logs." }); } var result = cp.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) { // Approve it request.Approved = true; // Update the record var inserted = Service.UpdateRequest(request); return Response.AsJson(inserted ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "We could not approve this request. Please try again or check the logs." }); } return Response.AsJson( new { Result = false, Message = "Something went wrong adding the movie to CouchPotato! Please check your settings." }); } private Response RequestAlbumAndUpdateStatus(RequestedModel request) { var hpSettings = HeadphonesSettings.GetSettings(); Log.Info("Adding album to Headphones : {0}", request.Title); if (!hpSettings.Enabled) { // Approve it request.Approved = true; Log.Warn("We approved Album: {0} but could not add it to Headphones because it has not been setup", request.Title); // Update the record var inserted = Service.UpdateRequest(request); return Response.AsJson(inserted ? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to Headphones because it has not been configured." } : new JsonResponseModel { Result = false, Message = "We could not approve this request. Please try again or check the logs." }); } var sender = new HeadphonesSender(HeadphoneApi, hpSettings, Service); var result = sender.AddAlbum(request); return Response.AsJson( new JsonResponseModel { Result = true, Message = "We have sent the approval to Headphones for processing, This can take a few minutes."} ); } private Response ApproveAllMovies() { var requests = Service.GetAll().Where(x => x.CanApprove && x.Type == RequestType.Movie); var requestedModels = requests as RequestedModel[] ?? requests.ToArray(); if (!requestedModels.Any()) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no movie requests to approve. Please refresh." }); } try { return UpdateRequests(requestedModels); } catch (Exception e) { Log.Fatal(e); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" }); } } private Response ApproveAllTVShows() { var requests = Service.GetAll().Where(x => x.CanApprove && x.Type == RequestType.TvShow); var requestedModels = requests as RequestedModel[] ?? requests.ToArray(); if (!requestedModels.Any()) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no tv show requests to approve. Please refresh." }); } try { return UpdateRequests(requestedModels); } catch (Exception e) { Log.Fatal(e); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" }); } } /// /// Approves all. /// /// private Response ApproveAll() { var requests = Service.GetAll().Where(x => x.CanApprove); var requestedModels = requests as RequestedModel[] ?? requests.ToArray(); if (!requestedModels.Any()) { return Response.AsJson(new JsonResponseModel { Result = false, Message = "There are no requests to approve. Please refresh." }); } try { return UpdateRequests(requestedModels); } catch (Exception e) { Log.Fatal(e); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" }); } } private Response UpdateRequests(RequestedModel[] requestedModels) { var cpSettings = CpService.GetSettings(); var updatedRequests = new List(); foreach (var r in requestedModels) { if (r.Type == RequestType.Movie) { 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); } } if (r.Type == RequestType.TvShow) { var sender = new TvSender(SonarrApi, SickRageApi); var sr = SickRageSettings.GetSettings(); var sonarr = SonarrSettings.GetSettings(); if (sr.Enabled) { var res = sender.SendToSickRage(sr, r); if (res?.result == "success") { r.Approved = true; updatedRequests.Add(r); } else { Log.Error("Could not approve and send the TV {0} to SickRage!", r.Title); Log.Error("SickRage Message: {0}", res?.message); } } if (sonarr.Enabled) { var res = sender.SendToSonarr(sonarr, r); if (!string.IsNullOrEmpty(res?.title)) { r.Approved = true; updatedRequests.Add(r); } else { Log.Error("Could not approve and send the TV {0} to Sonarr!", r.Title); res?.ErrorMessages.ForEach(x => Log.Error("Error messages: {0}", x)); } } } } try { var result = Service.BatchUpdate(updatedRequests); return Response.AsJson(result ? new JsonResponseModel { Result = true } : new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." }); } catch (Exception e) { Log.Fatal(e); return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something bad happened, please check the logs!" }); } } 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; } } }