From 1c6e75a41332924c6bc1c8b52af195e5ce25226a Mon Sep 17 00:00:00 2001 From: "Jamie.Rees" Date: Mon, 23 Jan 2017 13:57:52 +0000 Subject: [PATCH 1/3] Started to add the specify Sonarr root folders. --- Ombi.Api.Interfaces/ISonarrApi.cs | 4 +- Ombi.Api.Models/Ombi.Api.Models.csproj | 1 + Ombi.Api.Models/Sonarr/SonarrRootFolder.cs | 35 ++++ Ombi.Api/SonarrApi.cs | 20 ++- Ombi.Core/CacheKeys.cs | 1 + Ombi.Core/SettingModels/SonarrSettings.cs | 3 +- Ombi.Core/TvSender.cs | 19 +- Ombi.Core/TvSenderOld.cs | 21 ++- .../SonarrSettingsDataProvider.cs | 1 + Ombi.UI/Models/RequestViewModel.cs | 1 + Ombi.UI/Models/RootFolderModel.cs | 36 ++++ Ombi.UI/Modules/Admin/IntegrationModule.cs | 22 ++- Ombi.UI/Modules/ApprovalModule.cs | 8 +- Ombi.UI/Modules/RequestsModule.cs | 36 +++- Ombi.UI/Ombi.UI.csproj | 1 + Ombi.UI/Views/Admin/Sonarr.cshtml | 166 ++++++++++++++---- 16 files changed, 316 insertions(+), 59 deletions(-) create mode 100644 Ombi.Api.Models/Sonarr/SonarrRootFolder.cs create mode 100644 Ombi.UI/Models/RootFolderModel.cs diff --git a/Ombi.Api.Interfaces/ISonarrApi.cs b/Ombi.Api.Interfaces/ISonarrApi.cs index 203020737..881f469eb 100644 --- a/Ombi.Api.Interfaces/ISonarrApi.cs +++ b/Ombi.Api.Interfaces/ISonarrApi.cs @@ -36,14 +36,16 @@ namespace Ombi.Api.Interfaces List GetProfiles(string apiKey, Uri baseUrl); SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, + int rootFolderId, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false); SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, - int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, + int rootFolderId, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false); SystemStatus SystemStatus(string apiKey, Uri baseUrl); + List GetRootFolders(string apiKey, Uri baseUrl); List GetSeries(string apiKey, Uri baseUrl); Series GetSeries(string seriesId, string apiKey, Uri baseUrl); diff --git a/Ombi.Api.Models/Ombi.Api.Models.csproj b/Ombi.Api.Models/Ombi.Api.Models.csproj index 003b6d465..c180f5624 100644 --- a/Ombi.Api.Models/Ombi.Api.Models.csproj +++ b/Ombi.Api.Models/Ombi.Api.Models.csproj @@ -101,6 +101,7 @@ + diff --git a/Ombi.Api.Models/Sonarr/SonarrRootFolder.cs b/Ombi.Api.Models/Sonarr/SonarrRootFolder.cs new file mode 100644 index 000000000..d506feba6 --- /dev/null +++ b/Ombi.Api.Models/Sonarr/SonarrRootFolder.cs @@ -0,0 +1,35 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: SonarrRootFolder.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 Ombi.Api.Models.Sonarr +{ + public class SonarrRootFolder + { + public int id { get; set; } + public string path { get; set; } + public long freespace { get; set; } + } +} \ No newline at end of file diff --git a/Ombi.Api/SonarrApi.cs b/Ombi.Api/SonarrApi.cs index 7c80883dc..c4c448628 100644 --- a/Ombi.Api/SonarrApi.cs +++ b/Ombi.Api/SonarrApi.cs @@ -62,7 +62,23 @@ namespace Ombi.Api return obj; } - public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) + public List GetRootFolders(string apiKey, Uri baseUrl) + { + var request = new RestRequest { Resource = "/api/rootfolder", Method = Method.GET }; + + request.AddHeader("X-Api-Key", apiKey); + var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetRootFolders for Sonarr, Retrying {0}", timespan), new TimeSpan[] { + TimeSpan.FromSeconds (2), + TimeSpan.FromSeconds(5), + TimeSpan.FromSeconds(10) + }); + + var obj = policy.Execute(() => Api.ExecuteJson>(request, baseUrl)); + + return obj; + } + + public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int rootFolderId, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) { Log.Debug("Adding series {0}", title); Log.Debug("Seasons = {0}, out of {1} seasons", seasons.DumpJson(), seasonCount); @@ -132,7 +148,7 @@ namespace Ombi.Api return result; } - public SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) + public SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int rootFolderId, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) { var request = new RestRequest { diff --git a/Ombi.Core/CacheKeys.cs b/Ombi.Core/CacheKeys.cs index 889ac6df7..39ed7a71b 100644 --- a/Ombi.Core/CacheKeys.cs +++ b/Ombi.Core/CacheKeys.cs @@ -47,5 +47,6 @@ namespace Ombi.Core public const string WatcherQueued = nameof(WatcherQueued); public const string GetPlexRequestSettings = nameof(GetPlexRequestSettings); public const string LastestProductVersion = nameof(LastestProductVersion); + public const string SonarrRootFolders = nameof(SonarrRootFolders); } } \ No newline at end of file diff --git a/Ombi.Core/SettingModels/SonarrSettings.cs b/Ombi.Core/SettingModels/SonarrSettings.cs index 31f96b3e6..ee309e376 100644 --- a/Ombi.Core/SettingModels/SonarrSettings.cs +++ b/Ombi.Core/SettingModels/SonarrSettings.cs @@ -33,6 +33,7 @@ namespace Ombi.Core.SettingModels public string QualityProfile { get; set; } public bool SeasonFolders { get; set; } public string RootPath { get; set; } - + public string RootFolder { get; set; } + } } \ No newline at end of file diff --git a/Ombi.Core/TvSender.cs b/Ombi.Core/TvSender.cs index 1e97e3550..34be1bb76 100644 --- a/Ombi.Core/TvSender.cs +++ b/Ombi.Core/TvSender.cs @@ -51,7 +51,7 @@ namespace Ombi.Core public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model) { - return await SendToSonarr(sonarrSettings, model, string.Empty); + return await SendToSonarr(sonarrSettings, model, string.Empty, string.Empty); } /// @@ -61,7 +61,7 @@ namespace Ombi.Core /// /// /// - public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId) + public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId, string rootFolderId) { var qualityProfile = 0; var episodeRequest = model.Episodes.Any(); @@ -82,6 +82,17 @@ namespace Ombi.Core var latest = model.SeasonsRequested?.Equals("Latest", StringComparison.CurrentCultureIgnoreCase); var specificSeasonRequest = model.SeasonList?.Any(); + var rootFolder = 0; + if (!string.IsNullOrEmpty(rootFolderId)) + { + int.TryParse(qualityId, out rootFolder); + } + + if (rootFolder <= 0) + { + int.TryParse(sonarrSettings.RootFolder, out rootFolder); + } + if (episodeRequest) { // Does series exist? @@ -96,7 +107,7 @@ namespace Ombi.Core // Series doesn't exist, need to add it as unmonitored. var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, new int[0], sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, rootFolder, new int[0], sonarrSettings.ApiKey, sonarrSettings.FullUri, false)); @@ -125,7 +136,7 @@ namespace Ombi.Core { // Set the series as monitored with a season count as 0 so it doesn't search for anything SonarrApi.AddSeriesNew(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, sonarrSettings.RootPath, rootFolder, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey, sonarrSettings.FullUri); await Task.Delay(TimeSpan.FromSeconds(1)); diff --git a/Ombi.Core/TvSenderOld.cs b/Ombi.Core/TvSenderOld.cs index 6594fe5b8..5edbda02c 100644 --- a/Ombi.Core/TvSenderOld.cs +++ b/Ombi.Core/TvSenderOld.cs @@ -49,12 +49,12 @@ namespace Ombi.Core private ISickRageApi SickrageApi { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); - public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model) + public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string rootFolderId = null) { - return await SendToSonarr(sonarrSettings, model, string.Empty); + return await SendToSonarr(sonarrSettings, model, string.Empty, rootFolderId); } - public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId) + public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId, string rootFolderId) { var qualityProfile = 0; var episodeRequest = model.Episodes.Any(); @@ -67,6 +67,17 @@ namespace Ombi.Core { int.TryParse(sonarrSettings.QualityProfile, out qualityProfile); } + var rootFolder = 0; + if (!string.IsNullOrEmpty(rootFolderId)) + { + int.TryParse(qualityId, out rootFolder); + } + + if (rootFolder <= 0) + { + int.TryParse(sonarrSettings.RootFolder, out rootFolder); + } + var series = await GetSonarrSeries(sonarrSettings, model.ProviderId); @@ -84,7 +95,7 @@ namespace Ombi.Core // Series doesn't exist, need to add it as unmonitored. var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, new int[0], sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, sonarrSettings.RootPath, rootFolder, 0, new int[0], sonarrSettings.ApiKey, sonarrSettings.FullUri, false)); @@ -156,7 +167,7 @@ namespace Ombi.Core var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, sonarrSettings.RootPath, rootFolder, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey, sonarrSettings.FullUri, true, true); return result; diff --git a/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs b/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs index 694973e6a..ba4b727bd 100644 --- a/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs +++ b/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs @@ -53,6 +53,7 @@ namespace Ombi.UI.ModelDataProviders with.Property(x => x.SeasonFolders).Description("Sonarr's season folders").Required(false); + with.Property(x => x.RootFolder).Description("Sonarr's root folder").Required(true); with.Property(x => x.RootPath).Description("Sonarr's root path").Required(false); }); } diff --git a/Ombi.UI/Models/RequestViewModel.cs b/Ombi.UI/Models/RequestViewModel.cs index b6db27bca..cbe59eea4 100644 --- a/Ombi.UI/Models/RequestViewModel.cs +++ b/Ombi.UI/Models/RequestViewModel.cs @@ -58,5 +58,6 @@ namespace Ombi.UI.Models public Store.EpisodesModel[] Episodes { get; set; } public bool Denied { get; set; } public string DeniedReason { get; set; } + public RootFolderModel[] RootFolders { get; set; } } } diff --git a/Ombi.UI/Models/RootFolderModel.cs b/Ombi.UI/Models/RootFolderModel.cs new file mode 100644 index 000000000..9d15024fe --- /dev/null +++ b/Ombi.UI/Models/RootFolderModel.cs @@ -0,0 +1,36 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: RootFolderModel.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 Ombi.UI.Models +{ + + public class RootFolderModel + { + public string Id { get; set; } + public string Path { get; set; } + public long FreeSpace { get; set; } + } +} \ No newline at end of file diff --git a/Ombi.UI/Modules/Admin/IntegrationModule.cs b/Ombi.UI/Modules/Admin/IntegrationModule.cs index 1322e7c64..36ec5fcc9 100644 --- a/Ombi.UI/Modules/Admin/IntegrationModule.cs +++ b/Ombi.UI/Modules/Admin/IntegrationModule.cs @@ -54,7 +54,7 @@ namespace Ombi.UI.Modules.Admin { public IntegrationModule(ISettingsService settingsService, ISettingsService watcher, ISettingsService cp,ISecurityExtensions security, IAnalytics a, ISettingsService radarrSettings, - ICacheProvider cache, IRadarrApi radarrApi) : base("admin", settingsService, security) + ICacheProvider cache, IRadarrApi radarrApi, ISonarrApi sonarrApi) : base("admin", settingsService, security) { WatcherSettings = watcher; @@ -63,9 +63,13 @@ namespace Ombi.UI.Modules.Admin Cache = cache; RadarrApi = radarrApi; RadarrSettings = radarrSettings; + SonarrApi = sonarrApi; Before += (ctx) => Security.AdminLoginRedirect(Permissions.Administrator, ctx); + + Post["/sonarrrootfolders"] = _ => GetSonarrRootFolders(); + Get["/watcher", true] = async (x, ct) => await Watcher(); Post["/watcher", true] = async (x, ct) => await SaveWatcher(); @@ -82,6 +86,7 @@ namespace Ombi.UI.Modules.Admin private IRadarrApi RadarrApi { get; } private ICacheProvider Cache { get; } private IAnalytics Analytics { get; } + private ISonarrApi SonarrApi { get; } private async Task Watcher() { @@ -182,5 +187,20 @@ namespace Ombi.UI.Modules.Admin return Response.AsJson(profiles); } + private Response GetSonarrRootFolders() + { + var settings = this.Bind(); + + var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + + // set the cache + if (rootFolders != null) + { + Cache.Set(CacheKeys.SonarrRootFolders, rootFolders); + } + + return Response.AsJson(rootFolders); + } + } } \ No newline at end of file diff --git a/Ombi.UI/Modules/ApprovalModule.cs b/Ombi.UI/Modules/ApprovalModule.cs index 9781b79a9..0f92abfb3 100644 --- a/Ombi.UI/Modules/ApprovalModule.cs +++ b/Ombi.UI/Modules/ApprovalModule.cs @@ -65,7 +65,7 @@ namespace Ombi.UI.Modules FaultQueue = faultQueue; MovieSender = movieSender; - Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId); + Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId, (string)Request.Form.rootFolderId ?? null); Post["/deny", true] = async (x, ct) => await DenyRequest((int)Request.Form.requestid, (string)Request.Form.reason); Post["/approveall", true] = async (x, ct) => await ApproveAll(); Post["/approveallmovies", true] = async (x, ct) => await ApproveAllMovies(); @@ -92,7 +92,7 @@ namespace Ombi.UI.Modules /// /// The request identifier. /// - private async Task Approve(int requestId, string qualityId) + private async Task Approve(int requestId, string qualityId, string rootFolderId = null) { Log.Info("approving request {0}", requestId); @@ -110,7 +110,7 @@ namespace Ombi.UI.Modules case RequestType.Movie: return await RequestMovieAndUpdateStatus(request, qualityId); case RequestType.TvShow: - return await RequestTvAndUpdateStatus(request, qualityId); + return await RequestTvAndUpdateStatus(request, qualityId, rootFolderId); case RequestType.Album: return await RequestAlbumAndUpdateStatus(request); default: @@ -118,7 +118,7 @@ namespace Ombi.UI.Modules } } - private async Task RequestTvAndUpdateStatus(RequestedModel request, string qualityId) + private async Task RequestTvAndUpdateStatus(RequestedModel request, string qualityId, string rootFolderId) { var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back diff --git a/Ombi.UI/Modules/RequestsModule.cs b/Ombi.UI/Modules/RequestsModule.cs index 4faefa206..5030a3686 100644 --- a/Ombi.UI/Modules/RequestsModule.cs +++ b/Ombi.UI/Modules/RequestsModule.cs @@ -160,6 +160,37 @@ namespace Ombi.UI.Modules } } + IEnumerable rootFolders = new List(); + if (IsAdmin) + { + try + { + var sonarrSettings = await SonarrSettings.GetSettingsAsync(); + if (sonarrSettings.Enabled) + { + var result = Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () => + { + return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri)); + }); + rootFolders = result.Result.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path }).ToList(); + } + // @TODO Sick Rage Root Folders + //else + //{ + // var sickRageSettings = await SickRageSettings.GetSettingsAsync(); + // if (sickRageSettings.Enabled) + // { + // qualities = sickRageSettings.Qualities.Select(x => new QualityModel { Id = x.Key, Name = x.Value }).ToList(); + // } + //} + } + catch (Exception e) + { + Log.Info(e); + } + + } + var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests); var viewModel = dbMovies.Select(movie => new RequestViewModel @@ -185,7 +216,8 @@ namespace Ombi.UI.Modules IssueId = movie.IssueId, Denied = movie.Denied, DeniedReason = movie.DeniedReason, - Qualities = qualities.ToArray() + Qualities = qualities.ToArray(), + RootFolders = rootFolders.ToArray(), }).ToList(); return Response.AsJson(viewModel); @@ -235,7 +267,7 @@ namespace Ombi.UI.Modules } - + var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests); var viewModel = dbTv.Select(tv => new RequestViewModel diff --git a/Ombi.UI/Ombi.UI.csproj b/Ombi.UI/Ombi.UI.csproj index 22dd6175d..3d51318d0 100644 --- a/Ombi.UI/Ombi.UI.csproj +++ b/Ombi.UI/Ombi.UI.csproj @@ -250,6 +250,7 @@ + diff --git a/Ombi.UI/Views/Admin/Sonarr.cshtml b/Ombi.UI/Views/Admin/Sonarr.cshtml index f2b4ca49a..7048a4152 100644 --- a/Ombi.UI/Views/Admin/Sonarr.cshtml +++ b/Ombi.UI/Views/Admin/Sonarr.cshtml @@ -17,14 +17,14 @@ Sonarr Settings
- @if (Model.Enabled) - { - - } - else - { - - } + @if (Model.Enabled) + { + + } + else + { + + }
@@ -51,14 +51,14 @@
- @if (Model.Ssl) - { - - } - else - { - - } + @if (Model.Ssl) + { + + } + else + { + + }
@@ -69,7 +69,7 @@
- +
@@ -80,32 +80,47 @@
-
- - + + +
+ +
+
+ +
+ +
+ @*
+ +
+ + +
+
*@ +
- @if (Model.SeasonFolders) - { - - } - else - { - - } - + @if (Model.SeasonFolders) + { + + } + else + { + + } +
- +
@@ -161,6 +176,39 @@ } + @if (!string.IsNullOrEmpty(Model.RootFolder)) + { + + + console.log('Hit root folders..'); + + var rootFolderSelected = @Model.RootFolder; + if (!rootFolderSelected) { + return; + } + var $form = $("#mainForm"); + $.ajax({ + type: $form.prop("method"), + data: $form.serialize(), + url: "sonarrrootfolders", + dataType: "json", + success: function(response) { + response.forEach(function(result) { + if (result.id == rootFolderSelected) { + $("#selectRootFolder").append(""); + } else { + $("#selectRootFolder").append(""); + } + }); + }, + error: function(e) { + console.log(e); + generateNotify("Something went wrong!", "danger"); + } + }); + + } + $('#save').click(function(e) { e.preventDefault(); @@ -201,17 +249,17 @@ e.preventDefault(); if (!$('#Ip').val()) { generateNotify("Please enter a valid IP/Hostname.", "warning"); - $('#getSpinner').attr("class", "fa fa-times"); + $('#getSpinner').attr("class", "fa fa-times"); return; } if (!$('#portNumber').val()) { generateNotify("Please enter a valid Port Number.", "warning"); - $('#getSpinner').attr("class", "fa fa-times"); + $('#getSpinner').attr("class", "fa fa-times"); return; } if (!$('#ApiKey').val()) { generateNotify("Please enter a valid ApiKey.", "warning"); - $('#getSpinner').attr("class", "fa fa-times"); + $('#getSpinner').attr("class", "fa fa-times"); return; } var $form = $("#mainForm"); @@ -222,18 +270,58 @@ dataType: "json", success: function (response) { response.forEach(function (result) { - $('#getSpinner').attr("class", "fa fa-check"); + $('#getSpinner').attr("class", "fa fa-check"); $("#select").append(""); }); }, error: function (e) { console.log(e); - $('#getSpinner').attr("class", "fa fa-times"); + $('#getSpinner').attr("class", "fa fa-times"); generateNotify("Something went wrong!", "danger"); } }); }); + $('#getRootFolders').click(function (e) { + + $('#getRootFolderSpinner').attr("class", "fa fa-spinner fa-spin"); + e.preventDefault(); + if (!$('#Ip').val()) { + generateNotify("Please enter a valid IP/Hostname.", "warning"); + $('#getRootFolderSpinner').attr("class", "fa fa-times"); + return; + } + if (!$('#portNumber').val()) { + generateNotify("Please enter a valid Port Number.", "warning"); + $('#getRootFolderSpinner').attr("class", "fa fa-times"); + return; + } + if (!$('#ApiKey').val()) { + generateNotify("Please enter a valid ApiKey.", "warning"); + $('#getRootFolderSpinner').attr("class", "fa fa-times"); + return; + } + var $form = $("#mainForm"); + $.ajax({ + type: $form.prop("method"), + data: $form.serialize(), + url: "sonarrrootfolders", + dataType: "json", + success: function (response) { + response.forEach(function (result) { + $('#getRootFolderSpinner').attr("class", "fa fa-check"); + $("#selectRootFolder").append(""); + }); + }, + error: function (e) { + console.log(e); + $('#getRootFolderSpinner').attr("class", "fa fa-times"); + generateNotify("Something went wrong!", "danger"); + } + }); + }); + + var base = '@Html.GetBaseUrl()'; $('#testSonarr').click(function (e) { @@ -245,7 +333,7 @@ var data = $form.serialize(); data = data + "&qualityProfile=" + qualityProfile; - + var url = createBaseUrl(base, '/test/sonarr'); $.ajax({ type: $form.prop("method"), @@ -256,16 +344,16 @@ console.log(response); if (response.result === true) { generateNotify(response.message, "success"); - $('#spinner').attr("class", "fa fa-check"); + $('#spinner').attr("class", "fa fa-check"); } else { generateNotify(response.message, "warning"); - $('#spinner').attr("class", "fa fa-times"); + $('#spinner').attr("class", "fa fa-times"); } }, error: function (e) { console.log(e); generateNotify("Something went wrong!", "danger"); - $('#spinner').attr("class", "fa fa-times"); + $('#spinner').attr("class", "fa fa-times"); } }); }); From 9121af004278b7753dc2fc52405a5ced3b634a92 Mon Sep 17 00:00:00 2001 From: "Jamie.Rees" Date: Mon, 23 Jan 2017 13:58:41 +0000 Subject: [PATCH 2/3] Fixed tests --- Ombi.UI.Tests/TvSenderTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Ombi.UI.Tests/TvSenderTests.cs b/Ombi.UI.Tests/TvSenderTests.cs index 1f6d337da..8a5191c12 100644 --- a/Ombi.UI.Tests/TvSenderTests.cs +++ b/Ombi.UI.Tests/TvSenderTests.cs @@ -79,6 +79,7 @@ namespace Ombi.UI.Tests It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -99,6 +100,7 @@ namespace Ombi.UI.Tests It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -128,7 +130,7 @@ namespace Ombi.UI.Tests var model = F.Build().With(x => x.ProviderId, 1) .With(x => x.Episodes, episodes).Create(); - var result = await Sender.SendToSonarr(GetSonarrSettings(), model, "2"); + var result = await Sender.SendToSonarr(GetSonarrSettings(), model, "2", string.Empty); Assert.That(result, Is.EqualTo(seriesResult)); SonarrMock.Verify(x => x.AddSeries(It.IsAny(), @@ -137,6 +139,7 @@ namespace Ombi.UI.Tests It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), From 7fc26df59967547de9ec544819937e2014365492 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 23 Jan 2017 22:50:54 +0000 Subject: [PATCH 3/3] Finished #535 #445 #170 --- Ombi.Api.Interfaces/ISonarrApi.cs | 3 +- Ombi.Api/SonarrApi.cs | 4 +- Ombi.Core/SettingModels/SonarrSettings.cs | 2 - Ombi.Core/TvSender.cs | 39 ++++--- Ombi.Core/TvSenderOld.cs | 41 ++++--- Ombi.Services/Jobs/FaultQueueHandler.cs | 7 +- Ombi.Services/Jobs/PlexAvailabilityChecker.cs | 1 + Ombi.Store/RequestedModel.cs | 7 ++ Ombi.UI.Tests/TvSenderTests.cs | 14 +-- Ombi.UI/Content/requests.js | 53 +++++++++ .../SonarrSettingsDataProvider.cs | 3 +- Ombi.UI/Models/RequestViewModel.cs | 2 + Ombi.UI/Modules/ApprovalModule.cs | 16 +-- Ombi.UI/Modules/RequestsModule.cs | 105 +++++++++++------- Ombi.UI/Modules/SearchModule.cs | 2 +- Ombi.UI/Ombi.UI.csproj | 2 +- Ombi.UI/Views/Admin/Sonarr.cshtml | 62 ++++++----- .../{Admin => Integration}/Radarr.cshtml | 0 Ombi.UI/Views/Requests/Index.cshtml | 27 +++++ 19 files changed, 262 insertions(+), 128 deletions(-) rename Ombi.UI/Views/{Admin => Integration}/Radarr.cshtml (100%) diff --git a/Ombi.Api.Interfaces/ISonarrApi.cs b/Ombi.Api.Interfaces/ISonarrApi.cs index 881f469eb..bce750901 100644 --- a/Ombi.Api.Interfaces/ISonarrApi.cs +++ b/Ombi.Api.Interfaces/ISonarrApi.cs @@ -36,12 +36,11 @@ namespace Ombi.Api.Interfaces List GetProfiles(string apiKey, Uri baseUrl); SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, - int rootFolderId, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false); SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, - int rootFolderId, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, + int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false); SystemStatus SystemStatus(string apiKey, Uri baseUrl); diff --git a/Ombi.Api/SonarrApi.cs b/Ombi.Api/SonarrApi.cs index c4c448628..2485150b8 100644 --- a/Ombi.Api/SonarrApi.cs +++ b/Ombi.Api/SonarrApi.cs @@ -78,7 +78,7 @@ namespace Ombi.Api return obj; } - public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int rootFolderId, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) + public SonarrAddSeries AddSeries(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int seasonCount, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) { Log.Debug("Adding series {0}", title); Log.Debug("Seasons = {0}, out of {1} seasons", seasons.DumpJson(), seasonCount); @@ -148,7 +148,7 @@ namespace Ombi.Api return result; } - public SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int rootFolderId, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) + public SonarrAddSeries AddSeriesNew(int tvdbId, string title, int qualityId, bool seasonFolders, string rootPath, int[] seasons, string apiKey, Uri baseUrl, bool monitor = true, bool searchForMissingEpisodes = false) { var request = new RestRequest { diff --git a/Ombi.Core/SettingModels/SonarrSettings.cs b/Ombi.Core/SettingModels/SonarrSettings.cs index ee309e376..ec2edb49a 100644 --- a/Ombi.Core/SettingModels/SonarrSettings.cs +++ b/Ombi.Core/SettingModels/SonarrSettings.cs @@ -33,7 +33,5 @@ namespace Ombi.Core.SettingModels public string QualityProfile { get; set; } public bool SeasonFolders { get; set; } public string RootPath { get; set; } - public string RootFolder { get; set; } - } } \ No newline at end of file diff --git a/Ombi.Core/TvSender.cs b/Ombi.Core/TvSender.cs index 34be1bb76..14146c17f 100644 --- a/Ombi.Core/TvSender.cs +++ b/Ombi.Core/TvSender.cs @@ -34,24 +34,27 @@ using Ombi.Api.Interfaces; using Ombi.Api.Models.SickRage; using Ombi.Api.Models.Sonarr; using Ombi.Core.SettingModels; +using Ombi.Helpers; using Ombi.Store; namespace Ombi.Core { public class TvSender { - public TvSender(ISonarrApi sonarrApi, ISickRageApi srApi) + public TvSender(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache) { SonarrApi = sonarrApi; SickrageApi = srApi; + Cache = cache; } private ISonarrApi SonarrApi { get; } private ISickRageApi SickrageApi { get; } + private ICacheProvider Cache { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model) { - return await SendToSonarr(sonarrSettings, model, string.Empty, string.Empty); + return await SendToSonarr(sonarrSettings, model, string.Empty); } /// @@ -61,7 +64,7 @@ namespace Ombi.Core /// /// /// - public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId, string rootFolderId) + public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId) { var qualityProfile = 0; var episodeRequest = model.Episodes.Any(); @@ -82,16 +85,7 @@ namespace Ombi.Core var latest = model.SeasonsRequested?.Equals("Latest", StringComparison.CurrentCultureIgnoreCase); var specificSeasonRequest = model.SeasonList?.Any(); - var rootFolder = 0; - if (!string.IsNullOrEmpty(rootFolderId)) - { - int.TryParse(qualityId, out rootFolder); - } - - if (rootFolder <= 0) - { - int.TryParse(sonarrSettings.RootFolder, out rootFolder); - } + var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.RootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings); if (episodeRequest) { @@ -107,7 +101,7 @@ namespace Ombi.Core // Series doesn't exist, need to add it as unmonitored. var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, 0, rootFolder, new int[0], sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, rootFolderPath, 0, new int[0], sonarrSettings.ApiKey, sonarrSettings.FullUri, false)); @@ -136,7 +130,7 @@ namespace Ombi.Core { // Set the series as monitored with a season count as 0 so it doesn't search for anything SonarrApi.AddSeriesNew(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, rootFolder, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, rootFolderPath, new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13}, sonarrSettings.ApiKey, sonarrSettings.FullUri); await Task.Delay(TimeSpan.FromSeconds(1)); @@ -383,5 +377,20 @@ namespace Ombi.Core return selectedSeries; } + + private async Task GetRootPath(int pathId, SonarrSettings sonarrSettings) + { + var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () => + { + return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri)); + }); + + foreach (var r in rootFoldersResult.Where(r => r.id == pathId)) + { + return r.path; + } + return string.Empty; + } + } } \ No newline at end of file diff --git a/Ombi.Core/TvSenderOld.cs b/Ombi.Core/TvSenderOld.cs index 5edbda02c..6d819753e 100644 --- a/Ombi.Core/TvSenderOld.cs +++ b/Ombi.Core/TvSenderOld.cs @@ -34,27 +34,30 @@ using Ombi.Api.Interfaces; using Ombi.Api.Models.SickRage; using Ombi.Api.Models.Sonarr; using Ombi.Core.SettingModels; +using Ombi.Helpers; using Ombi.Store; namespace Ombi.Core { public class TvSenderOld { - public TvSenderOld(ISonarrApi sonarrApi, ISickRageApi srApi) + public TvSenderOld(ISonarrApi sonarrApi, ISickRageApi srApi, ICacheProvider cache) { SonarrApi = sonarrApi; SickrageApi = srApi; + Cache = cache; } private ISonarrApi SonarrApi { get; } private ISickRageApi SickrageApi { get; } + private ICacheProvider Cache { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); - public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string rootFolderId = null) + public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model) { - return await SendToSonarr(sonarrSettings, model, string.Empty, rootFolderId); + return await SendToSonarr(sonarrSettings, model, string.Empty); } - public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId, string rootFolderId) + public async Task SendToSonarr(SonarrSettings sonarrSettings, RequestedModel model, string qualityId) { var qualityProfile = 0; var episodeRequest = model.Episodes.Any(); @@ -67,16 +70,7 @@ namespace Ombi.Core { int.TryParse(sonarrSettings.QualityProfile, out qualityProfile); } - var rootFolder = 0; - if (!string.IsNullOrEmpty(rootFolderId)) - { - int.TryParse(qualityId, out rootFolder); - } - - if (rootFolder <= 0) - { - int.TryParse(sonarrSettings.RootFolder, out rootFolder); - } + var rootFolderPath = model.RootFolderSelected <= 0 ? sonarrSettings.RootPath : await GetRootPath(model.RootFolderSelected, sonarrSettings); var series = await GetSonarrSeries(sonarrSettings, model.ProviderId); @@ -95,7 +89,7 @@ namespace Ombi.Core // Series doesn't exist, need to add it as unmonitored. var addResult = await Task.Run(() => SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, rootFolder, 0, new int[0], sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, rootFolderPath, 0, new int[0], sonarrSettings.ApiKey, sonarrSettings.FullUri, false)); @@ -167,7 +161,7 @@ namespace Ombi.Core var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, - sonarrSettings.SeasonFolders, sonarrSettings.RootPath, rootFolder, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey, + sonarrSettings.SeasonFolders, rootFolderPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey, sonarrSettings.FullUri, true, true); return result; @@ -309,5 +303,20 @@ namespace Ombi.Core return selectedSeries; } + + + private async Task GetRootPath(int pathId, SonarrSettings sonarrSettings) + { + var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () => + { + return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri)); + }); + + foreach (var r in rootFoldersResult.Where(r => r.id == pathId)) + { + return r.path; + } + return string.Empty; + } } } \ No newline at end of file diff --git a/Ombi.Services/Jobs/FaultQueueHandler.cs b/Ombi.Services/Jobs/FaultQueueHandler.cs index 61d3a4674..09c311225 100644 --- a/Ombi.Services/Jobs/FaultQueueHandler.cs +++ b/Ombi.Services/Jobs/FaultQueueHandler.cs @@ -53,7 +53,7 @@ namespace Ombi.Services.Jobs ISickRageApi srApi, ISettingsService sonarrSettings, ISettingsService srSettings, ICouchPotatoApi cpApi, ISettingsService cpsettings, IRequestService requestService, ISettingsService hpSettings, IHeadphonesApi headphonesApi, ISettingsService prSettings, - ISecurityExtensions security, IMovieSender movieSender) + ISecurityExtensions security, IMovieSender movieSender, ICacheProvider cache) { Record = record; Repo = repo; @@ -71,6 +71,8 @@ namespace Ombi.Services.Jobs Security = security; PrSettings = prSettings.GetSettings(); MovieSender = movieSender; + + Cache = cache; } private IMovieSender MovieSender { get; } @@ -78,6 +80,7 @@ namespace Ombi.Services.Jobs private IJobRecord Record { get; } private ISonarrApi SonarrApi { get; } private ISickRageApi SrApi { get; } + private ICacheProvider Cache { get; } private ICouchPotatoApi CpApi { get; } private IHeadphonesApi HpApi { get; } private IRequestService RequestService { get; } @@ -163,7 +166,7 @@ namespace Ombi.Services.Jobs try { - var sender = new TvSenderOld(SonarrApi, SrApi); + var sender = new TvSenderOld(SonarrApi, SrApi, Cache); if (sonarr.Enabled) { var task = sender.SendToSonarr(sonarr, tvModel, sonarr.QualityProfile); diff --git a/Ombi.Services/Jobs/PlexAvailabilityChecker.cs b/Ombi.Services/Jobs/PlexAvailabilityChecker.cs index a58b5841c..936b128dc 100644 --- a/Ombi.Services/Jobs/PlexAvailabilityChecker.cs +++ b/Ombi.Services/Jobs/PlexAvailabilityChecker.cs @@ -79,6 +79,7 @@ namespace Ombi.Services.Jobs public void CheckAndUpdateAll() { + var plexSettings = Plex.GetSettings(); if (!ValidateSettings(plexSettings)) diff --git a/Ombi.Store/RequestedModel.cs b/Ombi.Store/RequestedModel.cs index ffd416966..89c30e21e 100644 --- a/Ombi.Store/RequestedModel.cs +++ b/Ombi.Store/RequestedModel.cs @@ -46,6 +46,13 @@ namespace Ombi.Store public List Episodes { get; set; } public bool Denied { get; set; } public string DeniedReason { get; set; } + /// + /// For TV Shows with a custom root folder + /// + /// + /// The root folder selected. + /// + public int RootFolderSelected { get; set; } [JsonIgnore] public List AllUsers diff --git a/Ombi.UI.Tests/TvSenderTests.cs b/Ombi.UI.Tests/TvSenderTests.cs index 8a5191c12..1a766ad67 100644 --- a/Ombi.UI.Tests/TvSenderTests.cs +++ b/Ombi.UI.Tests/TvSenderTests.cs @@ -35,6 +35,7 @@ using Ombi.Api.Interfaces; using Ombi.Api.Models.Sonarr; using Ombi.Core; using Ombi.Core.SettingModels; +using Ombi.Helpers; using Ombi.Store; using Ploeh.AutoFixture; @@ -49,6 +50,7 @@ namespace Ombi.UI.Tests private TvSender Sender { get; set; } private Fixture F { get; set; } + private Mock Cache { get; set; } [SetUp] public void Setup() @@ -56,7 +58,8 @@ namespace Ombi.UI.Tests F = new Fixture(); SonarrMock = new Mock(); SickrageMock = new Mock(); - Sender = new TvSender(SonarrMock.Object, SickrageMock.Object); + Cache = new Mock(); + Sender = new TvSender(SonarrMock.Object, SickrageMock.Object, Cache.Object); } [Test] @@ -66,7 +69,7 @@ namespace Ombi.UI.Tests var seriesResult = new SonarrAddSeries() { title = "ABC"}; SonarrMock.Setup(x => x.GetSeries(It.IsAny(), It.IsAny())).Returns(F.Build().With(x => x.tvdbId, 1).With(x => x.title, "ABC").CreateMany().ToList()); - Sender = new TvSender(SonarrMock.Object, SickrageMock.Object); + Sender = new TvSender(SonarrMock.Object, SickrageMock.Object, Cache.Object); var request = new RequestedModel {SeasonsRequested = "All", ProviderId = 1, Title = "ABC"}; @@ -79,7 +82,6 @@ namespace Ombi.UI.Tests It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -100,7 +102,6 @@ namespace Ombi.UI.Tests It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -118,7 +119,7 @@ namespace Ombi.UI.Tests SonarrMock.Setup(x => x.GetEpisodes(It.IsAny(), It.IsAny(), It.IsAny())).Returns(F.CreateMany()); - Sender = new TvSender(SonarrMock.Object, SickrageMock.Object); + Sender = new TvSender(SonarrMock.Object, SickrageMock.Object, Cache.Object); var episodes = new List { new EpisodesModel @@ -130,7 +131,7 @@ namespace Ombi.UI.Tests var model = F.Build().With(x => x.ProviderId, 1) .With(x => x.Episodes, episodes).Create(); - var result = await Sender.SendToSonarr(GetSonarrSettings(), model, "2", string.Empty); + var result = await Sender.SendToSonarr(GetSonarrSettings(), model, "2"); Assert.That(result, Is.EqualTo(seriesResult)); SonarrMock.Verify(x => x.AddSeries(It.IsAny(), @@ -139,7 +140,6 @@ namespace Ombi.UI.Tests It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), diff --git a/Ombi.UI/Content/requests.js b/Ombi.UI/Content/requests.js index 7928f39aa..1d2ad987d 100644 --- a/Ombi.UI/Content/requests.js +++ b/Ombi.UI/Content/requests.js @@ -559,6 +559,25 @@ $(document).on("click", ".approve-with-quality", function (e) { }); +// Change root folder +$(document).on("click", ".change-root-folder", function (e) { + e.preventDefault(); + var $this = $(this); + var $button = $this.parents('.btn-split').children('.change').first(); + var rootFolderId = e.target.id + var $form = $this.parents('form').first(); + + if ($button.text() === " Loading...") { + return; + } + + loadingButton($button.attr('id'), "success"); + + changeRootFolder($form, rootFolderId, function () { + }); + +}); + // Change Availability $(document).on("click", ".change", function (e) { @@ -638,6 +657,37 @@ function approveRequest($form, qualityId, successCallback) { }); } +function changeRootFolder($form, rootFolderId, successCallback) { + + var formData = $form.serialize(); + if (rootFolderId) formData += ("&rootFolderId=" + rootFolderId); + + $.ajax({ + type: $form.prop('method'), + url: $form.prop('action'), + data: formData, + dataType: "json", + success: function (response) { + + if (checkJsonResponse(response)) { + if (response.message) { + generateNotify(response.message, "success"); + } else { + generateNotify("Success! Changed Root Path.", "success"); + } + + if (successCallback) { + successCallback(); + } + } + }, + error: function (e) { + console.log(e); + generateNotify("Something went wrong!", "danger"); + } + }); +} + function denyRequest($form, successCallback) { var formData = $form.serialize(); @@ -808,6 +858,9 @@ function buildRequestContext(result, type) { musicBrainzId: result.musicBrainzId, denied: result.denied, deniedReason: result.deniedReason, + hasRootFolders: result.hasRootFolders, + rootFolders: result.rootFolders, + currentRootPath : result.currentRootPath }; return context; diff --git a/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs b/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs index ba4b727bd..7f3474ff8 100644 --- a/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs +++ b/Ombi.UI/ModelDataProviders/SonarrSettingsDataProvider.cs @@ -52,8 +52,7 @@ namespace Ombi.UI.ModelDataProviders with.Property(x => x.QualityProfile).Description("Sonarr's quality profile").Required(true); with.Property(x => x.SeasonFolders).Description("Sonarr's season folders").Required(false); - - with.Property(x => x.RootFolder).Description("Sonarr's root folder").Required(true); + with.Property(x => x.RootPath).Description("Sonarr's root path").Required(false); }); } diff --git a/Ombi.UI/Models/RequestViewModel.cs b/Ombi.UI/Models/RequestViewModel.cs index cbe59eea4..b31237bdc 100644 --- a/Ombi.UI/Models/RequestViewModel.cs +++ b/Ombi.UI/Models/RequestViewModel.cs @@ -59,5 +59,7 @@ namespace Ombi.UI.Models public bool Denied { get; set; } public string DeniedReason { get; set; } public RootFolderModel[] RootFolders { get; set; } + public bool HasRootFolders { get; set; } + public string CurrentRootPath { get; set; } } } diff --git a/Ombi.UI/Modules/ApprovalModule.cs b/Ombi.UI/Modules/ApprovalModule.cs index 0f92abfb3..667b3e843 100644 --- a/Ombi.UI/Modules/ApprovalModule.cs +++ b/Ombi.UI/Modules/ApprovalModule.cs @@ -50,7 +50,7 @@ namespace Ombi.UI.Modules public ApprovalModule(IRequestService service, ISonarrApi sonarrApi, ISettingsService sonarrSettings, ISickRageApi srApi, ISettingsService srSettings, ISettingsService hpSettings, IHeadphonesApi hpApi, ISettingsService pr, ITransientFaultQueue faultQueue - , ISecurityExtensions security, IMovieSender movieSender) : base("approval", pr, security) + , ISecurityExtensions security, IMovieSender movieSender, ICacheProvider cache) : base("approval", pr, security) { Before += (ctx) => Security.AdminLoginRedirect(ctx, Permissions.Administrator,Permissions.ManageRequests); @@ -64,8 +64,9 @@ namespace Ombi.UI.Modules HeadphoneApi = hpApi; FaultQueue = faultQueue; MovieSender = movieSender; + Cache = cache; - Post["/approve", true] = async (x, ct) => await Approve((int)Request.Form.requestid, (string)Request.Form.qualityId, (string)Request.Form.rootFolderId ?? null); + 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); Post["/approveall", true] = async (x, ct) => await ApproveAll(); Post["/approveallmovies", true] = async (x, ct) => await ApproveAllMovies(); @@ -86,13 +87,14 @@ namespace Ombi.UI.Modules private ISickRageApi SickRageApi { get; } private IHeadphonesApi HeadphoneApi { get; } private ITransientFaultQueue FaultQueue { get; } + private ICacheProvider Cache { get; } /// /// Approves the specified request identifier. /// /// The request identifier. /// - private async Task Approve(int requestId, string qualityId, string rootFolderId = null) + private async Task Approve(int requestId, string qualityId) { Log.Info("approving request {0}", requestId); @@ -110,7 +112,7 @@ namespace Ombi.UI.Modules case RequestType.Movie: return await RequestMovieAndUpdateStatus(request, qualityId); case RequestType.TvShow: - return await RequestTvAndUpdateStatus(request, qualityId, rootFolderId); + return await RequestTvAndUpdateStatus(request, qualityId); case RequestType.Album: return await RequestAlbumAndUpdateStatus(request); default: @@ -118,9 +120,9 @@ namespace Ombi.UI.Modules } } - private async Task RequestTvAndUpdateStatus(RequestedModel request, string qualityId, string rootFolderId) + private async Task RequestTvAndUpdateStatus(RequestedModel request, string qualityId) { - var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back + var sender = new TvSenderOld(SonarrApi, SickRageApi, Cache); // TODO put back var sonarrSettings = await SonarrSettings.GetSettingsAsync(); if (sonarrSettings.Enabled) @@ -435,7 +437,7 @@ namespace Ombi.UI.Modules } if (r.Type == RequestType.TvShow) { - var sender = new TvSenderOld(SonarrApi, SickRageApi); // TODO put back + var sender = new TvSenderOld(SonarrApi, SickRageApi, Cache); // TODO put back var sr = await SickRageSettings.GetSettingsAsync(); var sonarr = await SonarrSettings.GetSettingsAsync(); if (sr.Enabled) diff --git a/Ombi.UI/Modules/RequestsModule.cs b/Ombi.UI/Modules/RequestsModule.cs index 5030a3686..72fbba0f9 100644 --- a/Ombi.UI/Modules/RequestsModule.cs +++ b/Ombi.UI/Modules/RequestsModule.cs @@ -96,6 +96,8 @@ namespace Ombi.UI.Modules Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available); + Post["/changeRootFolder", true] = async (x, ct) => await ChangeRootFolder((int) Request.Form.requestId, (int) Request.Form.rootFolderId); + Get["/UpdateFilters", true] = async (x, ct) => await GetFilterAndSortSettings(); } @@ -160,38 +162,7 @@ namespace Ombi.UI.Modules } } - IEnumerable rootFolders = new List(); - if (IsAdmin) - { - try - { - var sonarrSettings = await SonarrSettings.GetSettingsAsync(); - if (sonarrSettings.Enabled) - { - var result = Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () => - { - return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri)); - }); - rootFolders = result.Result.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path }).ToList(); - } - // @TODO Sick Rage Root Folders - //else - //{ - // var sickRageSettings = await SickRageSettings.GetSettingsAsync(); - // if (sickRageSettings.Enabled) - // { - // qualities = sickRageSettings.Qualities.Select(x => new QualityModel { Id = x.Key, Name = x.Value }).ToList(); - // } - //} - } - catch (Exception e) - { - Log.Info(e); - } - - } - - + var canManageRequest = Security.HasAnyPermissions(User, Permissions.Administrator, Permissions.ManageRequests); var viewModel = dbMovies.Select(movie => new RequestViewModel { @@ -217,7 +188,6 @@ namespace Ombi.UI.Modules Denied = movie.Denied, DeniedReason = movie.DeniedReason, Qualities = qualities.ToArray(), - RootFolders = rootFolders.ToArray(), }).ToList(); return Response.AsJson(viewModel); @@ -225,32 +195,39 @@ namespace Ombi.UI.Modules private async Task GetTvShows() { - var settingsTask = PrSettings.GetSettingsAsync(); - var requests = await Service.GetAllAsync(); requests = requests.Where(x => x.Type == RequestType.TvShow); var dbTv = requests; - var settings = await settingsTask; if (Security.HasPermissions(User, Permissions.UsersCanViewOnlyOwnRequests) && !IsAdmin) { dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList(); } IEnumerable qualities = new List(); + IEnumerable rootFolders = new List(); + + var sonarrSettings = await SonarrSettings.GetSettingsAsync(); if (IsAdmin) { try { - var sonarrSettings = await SonarrSettings.GetSettingsAsync(); if (sonarrSettings.Enabled) { - var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () => + var result = await Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () => { return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri)); }); - qualities = result.Result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList(); - } + qualities = result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList(); + + + var rootFoldersResult =await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () => + { + return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri)); + }); + + rootFolders = rootFoldersResult.Select(x => new RootFolderModel { Id = x.id.ToString(), Path = x.path, FreeSpace = x.freespace}).ToList(); + } else { var sickRageSettings = await SickRageSettings.GetSettingsAsync(); @@ -296,11 +273,28 @@ namespace Ombi.UI.Modules TvSeriesRequestType = tv.SeasonsRequested, Qualities = qualities.ToArray(), Episodes = tv.Episodes.ToArray(), + RootFolders = rootFolders.ToArray(), + HasRootFolders = rootFolders.Any(), + CurrentRootPath = GetRootPath(tv.RootFolderSelected, sonarrSettings).Result }).ToList(); return Response.AsJson(viewModel); } + private async Task GetRootPath(int pathId, SonarrSettings sonarrSettings) + { + var rootFoldersResult = await Cache.GetOrSetAsync(CacheKeys.SonarrRootFolders, async () => + { + return await Task.Run(() => SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri)); + }); + + foreach (var r in rootFoldersResult.Where(r => r.id == pathId)) + { + return r.path; + } + return string.Empty; + } + private async Task GetAlbumRequests() { var settings = PrSettings.GetSettings(); @@ -464,5 +458,34 @@ namespace Ombi.UI.Modules return Response.AsJson(vm); } - } + + private async Task ChangeRootFolder(int id, int rootFolderId) + { + // Get all root folders + var settings = await SonarrSettings.GetSettingsAsync(); + var rootFolders = SonarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + + // Get Request + var allRequests = await Service.GetAllAsync(); + var request = allRequests.FirstOrDefault(x => x.Id == id); + + if (request == null) + { + return Response.AsJson(new JsonResponseModel {Result = false}); + } + + foreach (var folder in rootFolders) + { + if (folder.id.Equals(rootFolderId)) + { + request.RootFolderSelected = folder.id; + break; + } + } + + await Service.UpdateRequestAsync(request); + + return Response.AsJson(new JsonResponseModel {Result = true}); + } + } } diff --git a/Ombi.UI/Modules/SearchModule.cs b/Ombi.UI/Modules/SearchModule.cs index 63cc4c03c..75c6352e4 100644 --- a/Ombi.UI/Modules/SearchModule.cs +++ b/Ombi.UI/Modules/SearchModule.cs @@ -1107,7 +1107,7 @@ namespace Ombi.UI.Modules { model.Approved = true; var s = await sonarrSettings; - var sender = new TvSenderOld(SonarrApi, SickrageApi); // TODO put back + var sender = new TvSenderOld(SonarrApi, SickrageApi, Cache); // TODO put back if (s.Enabled) { var result = await sender.SendToSonarr(s, model); diff --git a/Ombi.UI/Ombi.UI.csproj b/Ombi.UI/Ombi.UI.csproj index 3d51318d0..25745546a 100644 --- a/Ombi.UI/Ombi.UI.csproj +++ b/Ombi.UI/Ombi.UI.csproj @@ -798,7 +798,7 @@ Always - + Always diff --git a/Ombi.UI/Views/Admin/Sonarr.cshtml b/Ombi.UI/Views/Admin/Sonarr.cshtml index 7048a4152..025059e94 100644 --- a/Ombi.UI/Views/Admin/Sonarr.cshtml +++ b/Ombi.UI/Views/Admin/Sonarr.cshtml @@ -81,13 +81,13 @@
- +
- +
@@ -176,37 +176,38 @@ } - @if (!string.IsNullOrEmpty(Model.RootFolder)) + @if (!string.IsNullOrEmpty(Model.RootPath)) { - console.log('Hit root folders..'); + console.log('Hit root folders..'); - var rootFolderSelected = @Model.RootFolder; - if (!rootFolderSelected) { - return; - } - var $form = $("#mainForm"); - $.ajax({ - type: $form.prop("method"), - data: $form.serialize(), - url: "sonarrrootfolders", - dataType: "json", - success: function(response) { - response.forEach(function(result) { - if (result.id == rootFolderSelected) { - $("#selectRootFolder").append(""); - } else { - $("#selectRootFolder").append(""); - } - }); - }, - error: function(e) { - console.log(e); - generateNotify("Something went wrong!", "danger"); - } - }); - + var rootFolderSelected = @Model.RootPath; + if (!rootFolderSelected) { + return; + } + var $form = $("#mainForm"); + $.ajax({ + type: $form.prop("method"), + data: $form.serialize(), + url: "sonarrrootfolders", + dataType: "json", + success: function(response) { + response.forEach(function(result) { + $('#selectedRootFolder').html(""); + if (result.id == rootFolderSelected) { + $("#selectRootFolder").append(""); + } else { + $("#selectRootFolder").append(""); + } + }); + }, + error: function(e) { + console.log(e); + generateNotify("Something went wrong!", "danger"); + } + }); + } @@ -218,11 +219,12 @@ return; } var qualityProfile = $("#profiles option:selected").val(); + var rootFolder = $("#rootFolders option:selected").val(); var $form = $("#mainForm"); var data = $form.serialize(); - data = data + "&qualityProfile=" + qualityProfile; + data = data + "&qualityProfile=" + qualityProfile + "&rootPath=" + rootFolder; $.ajax({ type: $form.prop("method"), diff --git a/Ombi.UI/Views/Admin/Radarr.cshtml b/Ombi.UI/Views/Integration/Radarr.cshtml similarity index 100% rename from Ombi.UI/Views/Admin/Radarr.cshtml rename to Ombi.UI/Views/Integration/Radarr.cshtml diff --git a/Ombi.UI/Views/Requests/Index.cshtml b/Ombi.UI/Views/Requests/Index.cshtml index 0447e25fa..7199253c7 100644 --- a/Ombi.UI/Views/Requests/Index.cshtml +++ b/Ombi.UI/Views/Requests/Index.cshtml @@ -244,6 +244,11 @@
@UI.Requests_RequestedBy: {{requestedUsers}}
{{/if}}
@UI.Requests_RequestedDate: {{requestedDate}}
+ {{#if admin}} + {{#if currentRootPath}} +
Root Path: {{currentRootPath}}
+ {{/if}} + {{/if}}
{{#if_eq issueId 0}} @*Nothing*@ @@ -275,6 +280,28 @@ {{/if_eq}} + + +
+ + {{#if_eq hasRootFolders true}} +
+ + + +
+ {{/if_eq}} +
+ + + {{#unless denied}}