diff --git a/frontend/src/Settings/Applications/Applications/Application.js b/frontend/src/Settings/Applications/Applications/Application.js index 4573d8357..2d8280617 100644 --- a/frontend/src/Settings/Applications/Applications/Application.js +++ b/frontend/src/Settings/Applications/Applications/Application.js @@ -71,7 +71,7 @@ class Application extends Component { { syncLevel === 'addOnly' && } diff --git a/frontend/src/Settings/Applications/Applications/EditApplicationModalContent.js b/frontend/src/Settings/Applications/Applications/EditApplicationModalContent.js index 4893b5af1..5acc84309 100644 --- a/frontend/src/Settings/Applications/Applications/EditApplicationModalContent.js +++ b/frontend/src/Settings/Applications/Applications/EditApplicationModalContent.js @@ -19,7 +19,7 @@ import styles from './EditApplicationModalContent.css'; const syncLevelOptions = [ { key: 'disabled', value: 'Disabled' }, - { key: 'addOnly', value: 'Add Only' }, + { key: 'addOnly', value: 'Add and Remove Only' }, { key: 'fullSync', value: 'Full Sync' } ]; diff --git a/src/NzbDrone.Core/Applications/ApplicationService.cs b/src/NzbDrone.Core/Applications/ApplicationService.cs index 720c35b25..de15de6c0 100644 --- a/src/NzbDrone.Core/Applications/ApplicationService.cs +++ b/src/NzbDrone.Core/Applications/ApplicationService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using NLog; using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Indexers; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; @@ -15,6 +16,7 @@ namespace NzbDrone.Core.Applications IHandleAsync>, IHandleAsync>, IHandleAsync>, + IHandleAsync, IExecute { private readonly IApplicationFactory _applicationsFactory; @@ -40,8 +42,9 @@ namespace NzbDrone.Core.Applications if (appDefinition.Enable) { var app = _applicationsFactory.GetInstance(appDefinition); + var indexers = _indexerFactory.Enabled().Select(i => (IndexerDefinition)i.Definition).ToList(); - SyncIndexers(new List { app }); + SyncIndexers(new List { app }, indexers); } } @@ -57,8 +60,7 @@ namespace NzbDrone.Core.Applications public void HandleAsync(ProviderDeletedEvent message) { - var enabledApps = _applicationsFactory.SyncEnabled() - .Where(n => ((ApplicationDefinition)n.Definition).SyncLevel == ApplicationSyncLevel.FullSync); + var enabledApps = _applicationsFactory.SyncEnabled(); foreach (var app in enabledApps) { @@ -69,39 +71,54 @@ namespace NzbDrone.Core.Applications public void HandleAsync(ProviderUpdatedEvent message) { var enabledApps = _applicationsFactory.SyncEnabled() - .Where(n => ((ApplicationDefinition)n.Definition).SyncLevel == ApplicationSyncLevel.FullSync); + .Where(n => ((ApplicationDefinition)n.Definition).SyncLevel == ApplicationSyncLevel.FullSync) + .ToList(); - foreach (var app in enabledApps) - { - ExecuteAction(a => a.UpdateIndexer((IndexerDefinition)message.Definition), app); - } + SyncIndexers(enabledApps, new List { (IndexerDefinition)message.Definition }); } - public void Execute(ApplicationIndexerSyncCommand message) + public void HandleAsync(ApiKeyChangedEvent message) { var enabledApps = _applicationsFactory.SyncEnabled(); - SyncIndexers(enabledApps); + var indexers = _indexerFactory.AllProviders().Select(i => (IndexerDefinition)i.Definition).ToList(); + + SyncIndexers(enabledApps, indexers); } - private void SyncIndexers(List applications) + public void Execute(ApplicationIndexerSyncCommand message) { - var indexers = _indexerFactory.Enabled(); + var enabledApps = _applicationsFactory.SyncEnabled(); + var indexers = _indexerFactory.AllProviders().Select(i => (IndexerDefinition)i.Definition).ToList(); + + SyncIndexers(enabledApps, indexers); + } + + private void SyncIndexers(List applications, List indexers) + { foreach (var app in applications) { var indexerMappings = _appIndexerMapService.GetMappingsForApp(app.Definition.Id); foreach (var indexer in indexers) { - if (indexerMappings.Any(x => x.IndexerId == indexer.Definition.Id)) + var definition = indexer; + + if (indexerMappings.Any(x => x.IndexerId == definition.Id)) { - continue; + if (((ApplicationDefinition)app.Definition).SyncLevel == ApplicationSyncLevel.FullSync) + { + ExecuteAction(a => a.UpdateIndexer(definition), app); + } + } + else + { + if (indexer.Enable) + { + ExecuteAction(a => a.AddIndexer(definition), app); + } } - - var definition = (IndexerDefinition)indexer.Definition; - - ExecuteAction(a => a.AddIndexer(definition), app); } } } @@ -156,7 +173,7 @@ namespace NzbDrone.Core.Applications catch (Exception ex) { _applicationStatusService.RecordFailure(application.Definition.Id); - _logger.Error(ex, "An error occurred while talking to application."); + _logger.Error(ex, "An error occurred while talking to remote application."); } } } diff --git a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs index aa6c1c819..9e7cba5f4 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs @@ -40,11 +40,7 @@ namespace NzbDrone.Core.Applications.Lidarr { if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { - var schema = _schemaCache.Get(Definition.Settings.ToJson(), () => _lidarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); - var newznab = schema.Where(i => i.Implementation == "Newznab").First(); - var torznab = schema.Where(i => i.Implementation == "Torznab").First(); - - var lidarrIndexer = BuildLidarrIndexer(indexer, indexer.Protocol == DownloadProtocol.Usenet ? newznab : torznab); + var lidarrIndexer = BuildLidarrIndexer(indexer, indexer.Protocol); var remoteIndexer = _lidarrV1Proxy.AddIndexer(lidarrIndexer, Settings); _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = remoteIndexer.Id }); @@ -67,19 +63,50 @@ namespace NzbDrone.Core.Applications.Lidarr public override void UpdateIndexer(IndexerDefinition indexer) { - //Use the Id mapping here to delete the correct indexer - throw new System.NotImplementedException(); + _logger.Debug("Updating indexer {0}[{1}]", indexer.Name, indexer.Id); + + var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); + var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); + + var readarrIndexer = BuildLidarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0); + + var remoteIndexer = _lidarrV1Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings); + + if (remoteIndexer != null) + { + _logger.Debug("Remote indexer found, syncing with current settings"); + + if (!readarrIndexer.Equals(remoteIndexer)) + { + _lidarrV1Proxy.UpdateIndexer(readarrIndexer, Settings); + } + } + else + { + _logger.Debug("Remote indexer not found, re-adding indexer to Lidarr"); + readarrIndexer.Id = 0; + + var newRemoteIndexer = _lidarrV1Proxy.AddIndexer(readarrIndexer, Settings); + _appIndexerMapService.Delete(indexerMapping.Id); + _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = newRemoteIndexer.Id }); + } } - private LidarrIndexer BuildLidarrIndexer(IndexerDefinition indexer, LidarrIndexer schema) + private LidarrIndexer BuildLidarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0) { + var schemas = _schemaCache.Get(Definition.Settings.ToJson(), () => _lidarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); + var newznab = schemas.Where(i => i.Implementation == "Newznab").First(); + var torznab = schemas.Where(i => i.Implementation == "Torznab").First(); + + var schema = protocol == DownloadProtocol.Usenet ? newznab : torznab; + var lidarrIndexer = new LidarrIndexer { Id = 0, Name = $"{indexer.Name} (Prowlarr)", - EnableRss = true, - EnableAutomaticSearch = true, - EnableInteractiveSearch = true, + EnableRss = indexer.Enable, + EnableAutomaticSearch = indexer.Enable, + EnableInteractiveSearch = indexer.Enable, Priority = indexer.Priority, Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab", ConfigContract = schema.ConfigContract, diff --git a/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs b/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs index 3d4d9f25f..ef92105fe 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace NzbDrone.Core.Applications.Lidarr { @@ -16,5 +17,26 @@ namespace NzbDrone.Core.Applications.Lidarr public string InfoLink { get; set; } public HashSet Tags { get; set; } public List Fields { get; set; } + + public bool Equals(LidarrIndexer other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; + var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; + + return other.EnableRss == EnableRss && + other.EnableAutomaticSearch == EnableAutomaticSearch && + other.EnableInteractiveSearch == EnableAutomaticSearch && + other.Name == Name && + other.Implementation == Implementation && + other.Priority == Priority && + other.Id == Id && + apiKey && apiPath && baseUrl; + } } } diff --git a/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs b/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs index adc72a12a..a0cacc605 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/LidarrV1Proxy.cs @@ -13,8 +13,10 @@ namespace NzbDrone.Core.Applications.Lidarr { LidarrIndexer AddIndexer(LidarrIndexer indexer, LidarrSettings settings); List GetIndexers(LidarrSettings settings); + LidarrIndexer GetIndexer(int indexerId, LidarrSettings settings); List GetIndexerSchema(LidarrSettings settings); void RemoveIndexer(int indexerId, LidarrSettings settings); + LidarrIndexer UpdateIndexer(LidarrIndexer indexer, LidarrSettings settings); ValidationFailure Test(LidarrSettings settings); } @@ -41,6 +43,24 @@ namespace NzbDrone.Core.Applications.Lidarr return Execute>(request); } + public LidarrIndexer GetIndexer(int indexerId, LidarrSettings settings) + { + try + { + var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.GET); + return Execute(request); + } + catch (HttpException ex) + { + if (ex.Response.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + return null; + } + public void RemoveIndexer(int indexerId, LidarrSettings settings) { var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.DELETE); @@ -62,6 +82,15 @@ namespace NzbDrone.Core.Applications.Lidarr return Execute(request); } + public LidarrIndexer UpdateIndexer(LidarrIndexer indexer, LidarrSettings settings) + { + var request = BuildRequest(settings, $"/api/v1/indexer/{indexer.Id}", HttpMethod.PUT); + + request.SetContent(indexer.ToJson()); + + return Execute(request); + } + public ValidationFailure Test(LidarrSettings settings) { try diff --git a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs index 3e9225e87..21ffa7f74 100644 --- a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs +++ b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs @@ -40,11 +40,7 @@ namespace NzbDrone.Core.Applications.Radarr { if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { - var schema = _schemaCache.Get(Definition.Settings.ToJson(), () => _radarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); - var newznab = schema.Where(i => i.Implementation == "Newznab").First(); - var torznab = schema.Where(i => i.Implementation == "Torznab").First(); - - var radarrIndexer = BuildRadarrIndexer(indexer, indexer.Protocol == DownloadProtocol.Usenet ? newznab : torznab); + var radarrIndexer = BuildRadarrIndexer(indexer, indexer.Protocol); var remoteIndexer = _radarrV3Proxy.AddIndexer(radarrIndexer, Settings); _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = remoteIndexer.Id }); @@ -67,19 +63,50 @@ namespace NzbDrone.Core.Applications.Radarr public override void UpdateIndexer(IndexerDefinition indexer) { - //Use the Id mapping here to delete the correct indexer - throw new System.NotImplementedException(); + _logger.Debug("Updating indexer {0}[{1}]", indexer.Name, indexer.Id); + + var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); + var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); + + var radarrIndexer = BuildRadarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0); + + var remoteIndexer = _radarrV3Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings); + + if (remoteIndexer != null) + { + _logger.Debug("Remote indexer found, syncing with current settings"); + + if (!radarrIndexer.Equals(remoteIndexer)) + { + _radarrV3Proxy.UpdateIndexer(radarrIndexer, Settings); + } + } + else + { + _logger.Debug("Remote indexer not found, re-adding indexer to Radarr"); + radarrIndexer.Id = 0; + + var newRemoteIndexer = _radarrV3Proxy.AddIndexer(radarrIndexer, Settings); + _appIndexerMapService.Delete(indexerMapping.Id); + _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = newRemoteIndexer.Id }); + } } - private RadarrIndexer BuildRadarrIndexer(IndexerDefinition indexer, RadarrIndexer schema) + private RadarrIndexer BuildRadarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0) { + var schemas = _schemaCache.Get(Definition.Settings.ToJson(), () => _radarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); + var newznab = schemas.Where(i => i.Implementation == "Newznab").First(); + var torznab = schemas.Where(i => i.Implementation == "Torznab").First(); + + var schema = protocol == DownloadProtocol.Usenet ? newznab : torznab; + var radarrIndexer = new RadarrIndexer { - Id = 0, + Id = id, Name = $"{indexer.Name} (Prowlarr)", - EnableRss = true, - EnableAutomaticSearch = true, - EnableInteractiveSearch = true, + EnableRss = indexer.Enable, + EnableAutomaticSearch = indexer.Enable, + EnableInteractiveSearch = indexer.Enable, Priority = indexer.Priority, Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab", ConfigContract = schema.ConfigContract, diff --git a/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs b/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs index 20c5d0567..0d5367152 100644 --- a/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace NzbDrone.Core.Applications.Radarr { @@ -16,5 +17,26 @@ namespace NzbDrone.Core.Applications.Radarr public string InfoLink { get; set; } public HashSet Tags { get; set; } public List Fields { get; set; } + + public bool Equals(RadarrIndexer other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; + var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; + + return other.EnableRss == EnableRss && + other.EnableAutomaticSearch == EnableAutomaticSearch && + other.EnableInteractiveSearch == EnableAutomaticSearch && + other.Name == Name && + other.Implementation == Implementation && + other.Priority == Priority && + other.Id == Id && + apiKey && apiPath && baseUrl; + } } } diff --git a/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs b/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs index 54aef898d..a2f7087e5 100644 --- a/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs +++ b/src/NzbDrone.Core/Applications/Radarr/RadarrV3Proxy.cs @@ -13,8 +13,10 @@ namespace NzbDrone.Core.Applications.Radarr { RadarrIndexer AddIndexer(RadarrIndexer indexer, RadarrSettings settings); List GetIndexers(RadarrSettings settings); + RadarrIndexer GetIndexer(int indexerId, RadarrSettings settings); List GetIndexerSchema(RadarrSettings settings); void RemoveIndexer(int indexerId, RadarrSettings settings); + RadarrIndexer UpdateIndexer(RadarrIndexer indexer, RadarrSettings settings); ValidationFailure Test(RadarrSettings settings); } @@ -41,6 +43,24 @@ namespace NzbDrone.Core.Applications.Radarr return Execute>(request); } + public RadarrIndexer GetIndexer(int indexerId, RadarrSettings settings) + { + try + { + var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.GET); + return Execute(request); + } + catch (HttpException ex) + { + if (ex.Response.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + return null; + } + public void RemoveIndexer(int indexerId, RadarrSettings settings) { var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.DELETE); @@ -62,6 +82,15 @@ namespace NzbDrone.Core.Applications.Radarr return Execute(request); } + public RadarrIndexer UpdateIndexer(RadarrIndexer indexer, RadarrSettings settings) + { + var request = BuildRequest(settings, $"/api/v3/indexer/{indexer.Id}", HttpMethod.PUT); + + request.SetContent(indexer.ToJson()); + + return Execute(request); + } + public ValidationFailure Test(RadarrSettings settings) { try diff --git a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs index 45839d424..2a19d7414 100644 --- a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs +++ b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs @@ -40,11 +40,7 @@ namespace NzbDrone.Core.Applications.Readarr { if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { - var schema = _schemaCache.Get(Definition.Settings.ToJson(), () => _readarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); - var newznab = schema.Where(i => i.Implementation == "Newznab").First(); - var torznab = schema.Where(i => i.Implementation == "Torznab").First(); - - var readarrIndexer = BuildReadarrIndexer(indexer, indexer.Protocol == DownloadProtocol.Usenet ? newznab : torznab); + var readarrIndexer = BuildReadarrIndexer(indexer, indexer.Protocol); var remoteIndexer = _readarrV1Proxy.AddIndexer(readarrIndexer, Settings); _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = remoteIndexer.Id }); @@ -67,19 +63,50 @@ namespace NzbDrone.Core.Applications.Readarr public override void UpdateIndexer(IndexerDefinition indexer) { - //Use the Id mapping here to delete the correct indexer - throw new System.NotImplementedException(); + _logger.Debug("Updating indexer {0}[{1}]", indexer.Name, indexer.Id); + + var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); + var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); + + var readarrIndexer = BuildReadarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0); + + var remoteIndexer = _readarrV1Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings); + + if (remoteIndexer != null) + { + _logger.Debug("Remote indexer found, syncing with current settings"); + + if (!readarrIndexer.Equals(remoteIndexer)) + { + _readarrV1Proxy.UpdateIndexer(readarrIndexer, Settings); + } + } + else + { + _logger.Debug("Remote indexer not found, re-adding indexer to Readarr"); + readarrIndexer.Id = 0; + + var newRemoteIndexer = _readarrV1Proxy.AddIndexer(readarrIndexer, Settings); + _appIndexerMapService.Delete(indexerMapping.Id); + _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = newRemoteIndexer.Id }); + } } - private ReadarrIndexer BuildReadarrIndexer(IndexerDefinition indexer, ReadarrIndexer schema) + private ReadarrIndexer BuildReadarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0) { + var schemas = _schemaCache.Get(Definition.Settings.ToJson(), () => _readarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); + var newznab = schemas.Where(i => i.Implementation == "Newznab").First(); + var torznab = schemas.Where(i => i.Implementation == "Torznab").First(); + + var schema = protocol == DownloadProtocol.Usenet ? newznab : torznab; + var readarrIndexer = new ReadarrIndexer { Id = 0, Name = $"{indexer.Name} (Prowlarr)", - EnableRss = true, - EnableAutomaticSearch = true, - EnableInteractiveSearch = true, + EnableRss = indexer.Enable, + EnableAutomaticSearch = indexer.Enable, + EnableInteractiveSearch = indexer.Enable, Priority = indexer.Priority, Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab", ConfigContract = schema.ConfigContract, diff --git a/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs b/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs index b9ac674a0..360147c96 100644 --- a/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace NzbDrone.Core.Applications.Readarr { @@ -16,5 +17,26 @@ namespace NzbDrone.Core.Applications.Readarr public string InfoLink { get; set; } public HashSet Tags { get; set; } public List Fields { get; set; } + + public bool Equals(ReadarrIndexer other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; + var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; + + return other.EnableRss == EnableRss && + other.EnableAutomaticSearch == EnableAutomaticSearch && + other.EnableInteractiveSearch == EnableAutomaticSearch && + other.Name == Name && + other.Implementation == Implementation && + other.Priority == Priority && + other.Id == Id && + apiKey && apiPath && baseUrl; + } } } diff --git a/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs b/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs index f39c4f34b..eaffae394 100644 --- a/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs +++ b/src/NzbDrone.Core/Applications/Readarr/ReadarrV1Proxy.cs @@ -13,8 +13,10 @@ namespace NzbDrone.Core.Applications.Readarr { ReadarrIndexer AddIndexer(ReadarrIndexer indexer, ReadarrSettings settings); List GetIndexers(ReadarrSettings settings); + ReadarrIndexer GetIndexer(int indexerId, ReadarrSettings settings); List GetIndexerSchema(ReadarrSettings settings); void RemoveIndexer(int indexerId, ReadarrSettings settings); + ReadarrIndexer UpdateIndexer(ReadarrIndexer indexer, ReadarrSettings settings); ValidationFailure Test(ReadarrSettings settings); } @@ -41,6 +43,24 @@ namespace NzbDrone.Core.Applications.Readarr return Execute>(request); } + public ReadarrIndexer GetIndexer(int indexerId, ReadarrSettings settings) + { + try + { + var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.GET); + return Execute(request); + } + catch (HttpException ex) + { + if (ex.Response.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + return null; + } + public void RemoveIndexer(int indexerId, ReadarrSettings settings) { var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.DELETE); @@ -62,6 +82,15 @@ namespace NzbDrone.Core.Applications.Readarr return Execute(request); } + public ReadarrIndexer UpdateIndexer(ReadarrIndexer indexer, ReadarrSettings settings) + { + var request = BuildRequest(settings, $"/api/v1/indexer/{indexer.Id}", HttpMethod.PUT); + + request.SetContent(indexer.ToJson()); + + return Execute(request); + } + public ValidationFailure Test(ReadarrSettings settings) { try diff --git a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs index 35b14077f..86b7bc582 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs @@ -40,11 +40,7 @@ namespace NzbDrone.Core.Applications.Sonarr { if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { - var schema = _schemaCache.Get(Definition.Settings.ToJson(), () => _sonarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); - var newznab = schema.Where(i => i.Implementation == "Newznab").First(); - var torznab = schema.Where(i => i.Implementation == "Torznab").First(); - - var sonarrIndexer = BuildSonarrIndexer(indexer, indexer.Protocol == DownloadProtocol.Usenet ? newznab : torznab); + var sonarrIndexer = BuildSonarrIndexer(indexer, indexer.Protocol); var remoteIndexer = _sonarrV3Proxy.AddIndexer(sonarrIndexer, Settings); _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = remoteIndexer.Id }); @@ -67,19 +63,50 @@ namespace NzbDrone.Core.Applications.Sonarr public override void UpdateIndexer(IndexerDefinition indexer) { - //Use the Id mapping here to delete the correct indexer - throw new System.NotImplementedException(); + _logger.Debug("Updating indexer {0}[{1}]", indexer.Name, indexer.Id); + + var appMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id); + var indexerMapping = appMappings.FirstOrDefault(m => m.IndexerId == indexer.Id); + + var sonarrIndexer = BuildSonarrIndexer(indexer, indexer.Protocol, indexerMapping?.RemoteIndexerId ?? 0); + + var remoteIndexer = _sonarrV3Proxy.GetIndexer(indexerMapping.RemoteIndexerId, Settings); + + if (remoteIndexer != null) + { + _logger.Debug("Remote indexer found, syncing with current settings"); + + if (!sonarrIndexer.Equals(remoteIndexer)) + { + _sonarrV3Proxy.UpdateIndexer(sonarrIndexer, Settings); + } + } + else + { + _logger.Debug("Remote indexer not found, re-adding indexer to Sonarr"); + sonarrIndexer.Id = 0; + + var newRemoteIndexer = _sonarrV3Proxy.AddIndexer(sonarrIndexer, Settings); + _appIndexerMapService.Delete(indexerMapping.Id); + _appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerId = newRemoteIndexer.Id }); + } } - private SonarrIndexer BuildSonarrIndexer(IndexerDefinition indexer, SonarrIndexer schema) + private SonarrIndexer BuildSonarrIndexer(IndexerDefinition indexer, DownloadProtocol protocol, int id = 0) { + var schemas = _schemaCache.Get(Definition.Settings.ToJson(), () => _sonarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7)); + var newznab = schemas.Where(i => i.Implementation == "Newznab").First(); + var torznab = schemas.Where(i => i.Implementation == "Torznab").First(); + + var schema = protocol == DownloadProtocol.Usenet ? newznab : torznab; + var sonarrIndexer = new SonarrIndexer { Id = 0, Name = $"{indexer.Name} (Prowlarr)", - EnableRss = true, - EnableAutomaticSearch = true, - EnableInteractiveSearch = true, + EnableRss = indexer.Enable, + EnableAutomaticSearch = indexer.Enable, + EnableInteractiveSearch = indexer.Enable, Priority = indexer.Priority, Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab", ConfigContract = schema.ConfigContract, diff --git a/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs b/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs index 0f7af2a28..840a4430f 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace NzbDrone.Core.Applications.Sonarr { @@ -16,5 +17,26 @@ namespace NzbDrone.Core.Applications.Sonarr public string InfoLink { get; set; } public HashSet Tags { get; set; } public List Fields { get; set; } + + public bool Equals(SonarrIndexer other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; + var apiPath = (string)Fields.FirstOrDefault(x => x.Name == "apiPath").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; + + return other.EnableRss == EnableRss && + other.EnableAutomaticSearch == EnableAutomaticSearch && + other.EnableInteractiveSearch == EnableAutomaticSearch && + other.Name == Name && + other.Implementation == Implementation && + other.Priority == Priority && + other.Id == Id && + apiKey && apiPath && baseUrl; + } } } diff --git a/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs b/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs index fd0731a63..2d8779db1 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/SonarrV3Proxy.cs @@ -13,8 +13,10 @@ namespace NzbDrone.Core.Applications.Sonarr { SonarrIndexer AddIndexer(SonarrIndexer indexer, SonarrSettings settings); List GetIndexers(SonarrSettings settings); + SonarrIndexer GetIndexer(int indexerId, SonarrSettings settings); List GetIndexerSchema(SonarrSettings settings); void RemoveIndexer(int indexerId, SonarrSettings settings); + SonarrIndexer UpdateIndexer(SonarrIndexer indexer, SonarrSettings settings); ValidationFailure Test(SonarrSettings settings); } @@ -41,6 +43,24 @@ namespace NzbDrone.Core.Applications.Sonarr return Execute>(request); } + public SonarrIndexer GetIndexer(int indexerId, SonarrSettings settings) + { + try + { + var request = BuildRequest(settings, $"/api/v1/indexer/{indexerId}", HttpMethod.GET); + return Execute(request); + } + catch (HttpException ex) + { + if (ex.Response.StatusCode != HttpStatusCode.NotFound) + { + throw; + } + } + + return null; + } + public void RemoveIndexer(int indexerId, SonarrSettings settings) { var request = BuildRequest(settings, $"/api/v3/indexer/{indexerId}", HttpMethod.DELETE); @@ -62,6 +82,15 @@ namespace NzbDrone.Core.Applications.Sonarr return Execute(request); } + public SonarrIndexer UpdateIndexer(SonarrIndexer indexer, SonarrSettings settings) + { + var request = BuildRequest(settings, $"/api/v3/indexer/{indexer.Id}", HttpMethod.PUT); + + request.SetContent(indexer.ToJson()); + + return Execute(request); + } + public ValidationFailure Test(SonarrSettings settings) { try diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index 2c9e9340c..45f2366b4 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -370,6 +370,7 @@ namespace NzbDrone.Core.Configuration public void Execute(ResetApiKeyCommand message) { SetValue("ApiKey", GenerateApiKey()); + _eventAggregator.PublishEvent(new ApiKeyChangedEvent()); } } } diff --git a/src/NzbDrone.Core/Configuration/Events/ApiKeyChangedEvent.cs b/src/NzbDrone.Core/Configuration/Events/ApiKeyChangedEvent.cs new file mode 100644 index 000000000..86e79bb41 --- /dev/null +++ b/src/NzbDrone.Core/Configuration/Events/ApiKeyChangedEvent.cs @@ -0,0 +1,8 @@ +using NzbDrone.Common.Messaging; + +namespace NzbDrone.Core.Configuration.Events +{ + public class ApiKeyChangedEvent : IEvent + { + } +} diff --git a/src/NzbDrone.Core/Indexers/IndexerFactory.cs b/src/NzbDrone.Core/Indexers/IndexerFactory.cs index ecec57ff0..429910e82 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFactory.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFactory.cs @@ -15,6 +15,7 @@ namespace NzbDrone.Core.Indexers public interface IIndexerFactory : IProviderFactory { List Enabled(bool filterBlockedIndexers = true); + List AllProviders(bool filterBlockedIndexers = true); void DeleteIndexers(List indexerIds); } @@ -188,6 +189,18 @@ namespace NzbDrone.Core.Indexers return enabledIndexers.ToList(); } + public List AllProviders(bool filterBlockedIndexers = true) + { + var enabledIndexers = All().Select(GetInstance); + + if (filterBlockedIndexers) + { + return FilterBlockedIndexers(enabledIndexers).ToList(); + } + + return enabledIndexers.ToList(); + } + private IEnumerable FilterBlockedIndexers(IEnumerable indexers) { var blockedIndexers = _indexerStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v); diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 28755adf1..6edff7f27 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -3,11 +3,11 @@ "AcceptConfirmationModal": "Accept Confirmation Modal", "Actions": "Actions", "Added": "Added", + "AddedToDownloadClient": "Release added to client", "AddIndexer": "Add Indexer", "AddingTag": "Adding tag", "AddNewIndexer": "Add New Indexer", "AddToDownloadClient": "Add release to download client", - "AddedToDownloadClient": "Release added to client", "Age": "Age", "All": "All", "AllIndexersHiddenDueToFilter": "All indexers are hidden due to applied filter.", @@ -66,6 +66,8 @@ "DBMigration": "DB Migration", "DelayProfile": "Delay Profile", "Delete": "Delete", + "DeleteApplication": "Delete Application", + "DeleteApplicationMessageText": "Are you sure you want to delete the application '{0}'?", "DeleteBackup": "Delete Backup", "DeleteBackupMessageText": "Are you sure you want to delete the backup '{0}'?", "DeleteIndexer": "Delete Indexer",