From 72e6d6626943766d23d397de929a09d8cb48289e Mon Sep 17 00:00:00 2001 From: Bogdan Date: Sun, 23 Jul 2023 01:33:08 +0300 Subject: [PATCH] New: (Apps) Add force sync indexers for applications --- frontend/src/App/AppRoutes.js | 4 +- frontend/src/App/State/SettingsAppState.ts | 4 +- frontend/src/Indexer/Index/IndexerIndex.tsx | 21 ++-- .../Applications/ApplicationSettings.js | 103 ------------------ .../Applications/ApplicationSettings.tsx | 102 +++++++++++++++++ .../ApplicationSettingsConnector.js | 35 ------ .../Applications/ApplicationBase.cs | 2 +- .../ApplicationIndexerSyncCommand.cs | 7 ++ .../Applications/ApplicationService.cs | 8 +- .../Applications/IApplication.cs | 2 +- .../LazyLibrarian/LazyLibrarian.cs | 8 +- .../Applications/Lidarr/Lidarr.cs | 27 +++-- .../Applications/Lidarr/LidarrIndexer.cs | 7 +- src/NzbDrone.Core/Applications/Mylar/Mylar.cs | 8 +- .../Applications/Radarr/Radarr.cs | 25 +++-- .../Applications/Radarr/RadarrIndexer.cs | 7 +- .../Applications/Readarr/Readarr.cs | 29 +++-- .../Applications/Readarr/ReadarrIndexer.cs | 7 +- .../Applications/Sonarr/Sonarr.cs | 27 +++-- .../Applications/Sonarr/SonarrIndexer.cs | 7 +- .../Applications/Whisparr/Whisparr.cs | 29 +++-- .../Applications/Whisparr/WhisparrIndexer.cs | 7 +- .../Indexers/IndexerStatusController.cs | 2 +- 23 files changed, 251 insertions(+), 227 deletions(-) delete mode 100644 frontend/src/Settings/Applications/ApplicationSettings.js create mode 100644 frontend/src/Settings/Applications/ApplicationSettings.tsx delete mode 100644 frontend/src/Settings/Applications/ApplicationSettingsConnector.js diff --git a/frontend/src/App/AppRoutes.js b/frontend/src/App/AppRoutes.js index 8bf97249c..f7a578da2 100644 --- a/frontend/src/App/AppRoutes.js +++ b/frontend/src/App/AppRoutes.js @@ -7,7 +7,7 @@ import HistoryConnector from 'History/HistoryConnector'; import IndexerIndex from 'Indexer/Index/IndexerIndex'; import IndexerStats from 'Indexer/Stats/IndexerStats'; import SearchIndexConnector from 'Search/SearchIndexConnector'; -import ApplicationSettingsConnector from 'Settings/Applications/ApplicationSettingsConnector'; +import ApplicationSettings from 'Settings/Applications/ApplicationSettings'; import DevelopmentSettingsConnector from 'Settings/Development/DevelopmentSettingsConnector'; import DownloadClientSettingsConnector from 'Settings/DownloadClients/DownloadClientSettingsConnector'; import GeneralSettingsConnector from 'Settings/General/GeneralSettingsConnector'; @@ -98,7 +98,7 @@ function AppRoutes(props) { , AppSectionDeleteState, - AppSectionSaveState {} + AppSectionSaveState { + isTestingAll: boolean; +} export interface DownloadClientAppState extends AppSectionState, diff --git a/frontend/src/Indexer/Index/IndexerIndex.tsx b/frontend/src/Indexer/Index/IndexerIndex.tsx index 93bb2cd5e..644c81493 100644 --- a/frontend/src/Indexer/Index/IndexerIndex.tsx +++ b/frontend/src/Indexer/Index/IndexerIndex.tsx @@ -45,9 +45,7 @@ import IndexerIndexTable from './Table/IndexerIndexTable'; import IndexerIndexTableOptions from './Table/IndexerIndexTableOptions'; import styles from './IndexerIndex.css'; -function getViewComponent() { - return IndexerIndexTable; -} +const getViewComponent = () => IndexerIndexTable; interface IndexerIndexProps { initialScrollTop?: number; @@ -84,14 +82,6 @@ const IndexerIndex = withScrollPosition((props: IndexerIndexProps) => { ); const [isSelectMode, setIsSelectMode] = useState(false); - const onAppIndexerSyncPress = useCallback(() => { - dispatch( - executeCommand({ - name: APP_INDEXER_SYNC, - }) - ); - }, [dispatch]); - const onAddIndexerPress = useCallback(() => { setIsAddIndexerModalOpen(true); }, [setIsAddIndexerModalOpen]); @@ -108,6 +98,15 @@ const IndexerIndex = withScrollPosition((props: IndexerIndexProps) => { setIsEditIndexerModalOpen(false); }, [setIsEditIndexerModalOpen]); + const onAppIndexerSyncPress = useCallback(() => { + dispatch( + executeCommand({ + name: APP_INDEXER_SYNC, + forceSync: true, + }) + ); + }, [dispatch]); + const onTestAllPress = useCallback(() => { dispatch(testAllIndexers()); }, [dispatch]); diff --git a/frontend/src/Settings/Applications/ApplicationSettings.js b/frontend/src/Settings/Applications/ApplicationSettings.js deleted file mode 100644 index d06b7fb65..000000000 --- a/frontend/src/Settings/Applications/ApplicationSettings.js +++ /dev/null @@ -1,103 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component, Fragment } from 'react'; -import PageContent from 'Components/Page/PageContent'; -import PageContentBody from 'Components/Page/PageContentBody'; -import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; -import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; -import { icons } from 'Helpers/Props'; -import AppProfilesConnector from 'Settings/Profiles/App/AppProfilesConnector'; -import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; -import translate from 'Utilities/String/translate'; -import ApplicationsConnector from './Applications/ApplicationsConnector'; -import ManageApplicationsModal from './Applications/Manage/ManageApplicationsModal'; - -class ApplicationSettings extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - isManageApplicationsOpen: false - }; - } - - // - // Listeners - - onManageApplicationsPress = () => { - this.setState({ isManageApplicationsOpen: true }); - }; - - onManageApplicationsModalClose = () => { - this.setState({ isManageApplicationsOpen: false }); - }; - - // - // Render - - render() { - const { - isTestingAll, - isSyncingIndexers, - onTestAllPress, - onAppIndexerSyncPress - } = this.props; - - const { isManageApplicationsOpen } = this.state; - - return ( - - - - - - - - - - - } - /> - - - - - - - - - ); - } -} - -ApplicationSettings.propTypes = { - isTestingAll: PropTypes.bool.isRequired, - isSyncingIndexers: PropTypes.bool.isRequired, - onTestAllPress: PropTypes.func.isRequired, - onAppIndexerSyncPress: PropTypes.func.isRequired -}; - -export default ApplicationSettings; diff --git a/frontend/src/Settings/Applications/ApplicationSettings.tsx b/frontend/src/Settings/Applications/ApplicationSettings.tsx new file mode 100644 index 000000000..c35d55e2d --- /dev/null +++ b/frontend/src/Settings/Applications/ApplicationSettings.tsx @@ -0,0 +1,102 @@ +import React, { Fragment, useCallback, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import AppState from 'App/State/AppState'; +import { APP_INDEXER_SYNC } from 'Commands/commandNames'; +import PageContent from 'Components/Page/PageContent'; +import PageContentBody from 'Components/Page/PageContentBody'; +import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; +import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; +import { icons } from 'Helpers/Props'; +import AppProfilesConnector from 'Settings/Profiles/App/AppProfilesConnector'; +import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; +import { executeCommand } from 'Store/Actions/commandActions'; +import { testAllApplications } from 'Store/Actions/Settings/applications'; +import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; +import translate from 'Utilities/String/translate'; +import ApplicationsConnector from './Applications/ApplicationsConnector'; +import ManageApplicationsModal from './Applications/Manage/ManageApplicationsModal'; + +function ApplicationSettings() { + const isSyncingIndexers = useSelector( + createCommandExecutingSelector(APP_INDEXER_SYNC) + ); + const isTestingAll = useSelector( + (state: AppState) => state.settings.applications.isTestingAll + ); + const dispatch = useDispatch(); + + const [isManageApplicationsOpen, setIsManageApplicationsOpen] = + useState(false); + + const onManageApplicationsPress = useCallback(() => { + setIsManageApplicationsOpen(true); + }, [setIsManageApplicationsOpen]); + + const onManageApplicationsModalClose = useCallback(() => { + setIsManageApplicationsOpen(false); + }, [setIsManageApplicationsOpen]); + + const onAppIndexerSyncPress = useCallback(() => { + dispatch( + executeCommand({ + name: APP_INDEXER_SYNC, + forceSync: true, + }) + ); + }, [dispatch]); + + const onTestAllPress = useCallback(() => { + dispatch(testAllApplications()); + }, [dispatch]); + + return ( + + + + + + + + + + + } + /> + + + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore */} + + {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} + {/* @ts-ignore */} + + + + + + ); +} + +export default ApplicationSettings; diff --git a/frontend/src/Settings/Applications/ApplicationSettingsConnector.js b/frontend/src/Settings/Applications/ApplicationSettingsConnector.js deleted file mode 100644 index aece6e91f..000000000 --- a/frontend/src/Settings/Applications/ApplicationSettingsConnector.js +++ /dev/null @@ -1,35 +0,0 @@ -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import * as commandNames from 'Commands/commandNames'; -import { executeCommand } from 'Store/Actions/commandActions'; -import { testAllApplications } from 'Store/Actions/settingsActions'; -import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector'; -import ApplicationSettings from './ApplicationSettings'; - -function createMapStateToProps() { - return createSelector( - (state) => state.settings.applications.isTestingAll, - createCommandExecutingSelector(commandNames.APP_INDEXER_SYNC), - (isTestingAll, isSyncingIndexers) => { - return { - isTestingAll, - isSyncingIndexers - }; - } - ); -} - -function mapDispatchToProps(dispatch, props) { - return { - onTestAllPress() { - dispatch(testAllApplications()); - }, - onAppIndexerSyncPress() { - dispatch(executeCommand({ - name: commandNames.APP_INDEXER_SYNC - })); - } - }; -} - -export default connect(createMapStateToProps, mapDispatchToProps)(ApplicationSettings); diff --git a/src/NzbDrone.Core/Applications/ApplicationBase.cs b/src/NzbDrone.Core/Applications/ApplicationBase.cs index 9cba791e6..e055d37aa 100644 --- a/src/NzbDrone.Core/Applications/ApplicationBase.cs +++ b/src/NzbDrone.Core/Applications/ApplicationBase.cs @@ -54,7 +54,7 @@ namespace NzbDrone.Core.Applications } public abstract void AddIndexer(IndexerDefinition indexer); - public abstract void UpdateIndexer(IndexerDefinition indexer); + public abstract void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false); public abstract void RemoveIndexer(int indexerId); public abstract List GetIndexerMappings(); diff --git a/src/NzbDrone.Core/Applications/ApplicationIndexerSyncCommand.cs b/src/NzbDrone.Core/Applications/ApplicationIndexerSyncCommand.cs index ad9023993..50318ecbd 100644 --- a/src/NzbDrone.Core/Applications/ApplicationIndexerSyncCommand.cs +++ b/src/NzbDrone.Core/Applications/ApplicationIndexerSyncCommand.cs @@ -4,6 +4,13 @@ namespace NzbDrone.Core.Applications { public class ApplicationIndexerSyncCommand : Command { + public bool ForceSync { get; set; } + + public ApplicationIndexerSyncCommand() + { + ForceSync = false; + } + public override bool SendUpdatesToClient => true; public override string CompletionMessage => null; diff --git a/src/NzbDrone.Core/Applications/ApplicationService.cs b/src/NzbDrone.Core/Applications/ApplicationService.cs index 5d9e868cf..419008f7f 100644 --- a/src/NzbDrone.Core/Applications/ApplicationService.cs +++ b/src/NzbDrone.Core/Applications/ApplicationService.cs @@ -104,7 +104,7 @@ namespace NzbDrone.Core.Applications var indexers = _indexerFactory.AllProviders().Select(i => (IndexerDefinition)i.Definition).ToList(); - SyncIndexers(enabledApps, indexers, true); + SyncIndexers(enabledApps, indexers, true, true); } public void HandleAsync(ProviderBulkUpdatedEvent message) @@ -122,10 +122,10 @@ namespace NzbDrone.Core.Applications var indexers = _indexerFactory.AllProviders().Select(i => (IndexerDefinition)i.Definition).ToList(); - SyncIndexers(enabledApps, indexers, true); + SyncIndexers(enabledApps, indexers, true, message.ForceSync); } - private void SyncIndexers(List applications, List indexers, bool removeRemote = false) + private void SyncIndexers(List applications, List indexers, bool removeRemote = false, bool forceSync = false) { foreach (var app in applications) { @@ -165,7 +165,7 @@ namespace NzbDrone.Core.Applications { if (((ApplicationDefinition)app.Definition).SyncLevel == ApplicationSyncLevel.FullSync && ShouldHandleIndexer(app.Definition, indexer)) { - ExecuteAction(a => a.UpdateIndexer(definition), app); + ExecuteAction(a => a.UpdateIndexer(definition, forceSync), app); } } else diff --git a/src/NzbDrone.Core/Applications/IApplication.cs b/src/NzbDrone.Core/Applications/IApplication.cs index 5dd4572f5..599010ee1 100644 --- a/src/NzbDrone.Core/Applications/IApplication.cs +++ b/src/NzbDrone.Core/Applications/IApplication.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.Applications public interface IApplication : IProvider { void AddIndexer(IndexerDefinition indexer); - void UpdateIndexer(IndexerDefinition indexer); + void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false); void RemoveIndexer(int indexerId); List GetIndexerMappings(); } diff --git a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs index 7ed59061c..47890594e 100644 --- a/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs +++ b/src/NzbDrone.Core/Applications/LazyLibrarian/LazyLibrarian.cs @@ -103,7 +103,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -118,10 +118,12 @@ namespace NzbDrone.Core.Applications.LazyLibrarian if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} found", remoteIndexer.Name); - if (!lazyLibrarianIndexer.Equals(remoteIndexer)) + if (!lazyLibrarianIndexer.Equals(remoteIndexer) || forceSync) { + _logger.Debug("Syncing remote indexer with current settings"); + _lazyLibrarianV1Proxy.UpdateIndexer(lazyLibrarianIndexer, Settings); indexerMapping.RemoteIndexerName = $"{lazyLibrarianIndexer.Type},{lazyLibrarianIndexer.Altername}"; _appIndexerMapService.Update(indexerMapping); diff --git a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs index 68137dc4d..84e6c4f72 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/Lidarr.cs @@ -96,15 +96,20 @@ namespace NzbDrone.Core.Applications.Lidarr foreach (var indexer in indexers) { - if ((string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value == _configFileProvider.ApiKey) + var baseUrl = (string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl")?.Value ?? string.Empty; + + if (!baseUrl.StartsWith(Settings.ProwlarrUrl.TrimEnd('/')) && + (string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value != _configFileProvider.ApiKey) { - var match = AppIndexerRegex.Match((string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value); + continue; + } - if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) - { - // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance - mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); - } + var match = AppIndexerRegex.Match(baseUrl); + + if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) + { + // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance + mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); } } @@ -150,7 +155,7 @@ namespace NzbDrone.Core.Applications.Lidarr } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -163,10 +168,12 @@ namespace NzbDrone.Core.Applications.Lidarr if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} [{1}] found", remoteIndexer.Name, remoteIndexer.Id); - if (!lidarrIndexer.Equals(remoteIndexer)) + if (!lidarrIndexer.Equals(remoteIndexer) || forceSync) { + _logger.Debug("Syncing remote indexer with current settings"); + if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { // Retain user fields not-affiliated with Prowlarr diff --git a/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs b/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs index 7ac02fd45..45221126e 100644 --- a/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Lidarr/LidarrIndexer.cs @@ -29,9 +29,12 @@ namespace NzbDrone.Core.Applications.Lidarr } var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; - var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value); + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var otherApiKey = (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var apiKeyCompare = apiKey == otherApiKey || otherApiKey == "********"; + var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var apiPathCompare = apiPath.Equals(otherApiPath); @@ -59,7 +62,7 @@ namespace NzbDrone.Core.Applications.Lidarr other.Implementation == Implementation && other.Priority == Priority && other.Id == Id && - apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare; + apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare; } } } diff --git a/src/NzbDrone.Core/Applications/Mylar/Mylar.cs b/src/NzbDrone.Core/Applications/Mylar/Mylar.cs index 7d84feae1..45b5667e7 100644 --- a/src/NzbDrone.Core/Applications/Mylar/Mylar.cs +++ b/src/NzbDrone.Core/Applications/Mylar/Mylar.cs @@ -103,7 +103,7 @@ namespace NzbDrone.Core.Applications.Mylar } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -118,10 +118,12 @@ namespace NzbDrone.Core.Applications.Mylar if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} found", remoteIndexer.Name); - if (!mylarIndexer.Equals(remoteIndexer)) + if (!mylarIndexer.Equals(remoteIndexer) || forceSync) { + _logger.Debug("Syncing remote indexer with current settings"); + _mylarV3Proxy.UpdateIndexer(mylarIndexer, Settings); indexerMapping.RemoteIndexerName = $"{mylarIndexer.Type},{mylarIndexer.Altername}"; _appIndexerMapService.Update(indexerMapping); diff --git a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs index a3d63317f..7ab8bf0ab 100644 --- a/src/NzbDrone.Core/Applications/Radarr/Radarr.cs +++ b/src/NzbDrone.Core/Applications/Radarr/Radarr.cs @@ -96,15 +96,20 @@ namespace NzbDrone.Core.Applications.Radarr foreach (var indexer in indexers) { - if ((string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value == _configFileProvider.ApiKey) + var baseUrl = (string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl")?.Value ?? string.Empty; + + if (!baseUrl.StartsWith(Settings.ProwlarrUrl.TrimEnd('/')) && + (string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value != _configFileProvider.ApiKey) { - var match = AppIndexerRegex.Match((string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value); + continue; + } - if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) - { - // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance - mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); - } + var match = AppIndexerRegex.Match(baseUrl); + + if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) + { + // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance + mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); } } @@ -150,7 +155,7 @@ namespace NzbDrone.Core.Applications.Radarr } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -163,9 +168,9 @@ namespace NzbDrone.Core.Applications.Radarr if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} [{1}] found", remoteIndexer.Name, remoteIndexer.Id); - if (!radarrIndexer.Equals(remoteIndexer)) + if (!radarrIndexer.Equals(remoteIndexer) || forceSync) { if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { diff --git a/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs b/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs index 38082724e..d68dda0ad 100644 --- a/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Radarr/RadarrIndexer.cs @@ -29,9 +29,12 @@ namespace NzbDrone.Core.Applications.Radarr } var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; - var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value); + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var otherApiKey = (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var apiKeyCompare = apiKey == otherApiKey || otherApiKey == "********"; + var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var apiPathCompare = apiPath.Equals(otherApiPath); @@ -55,7 +58,7 @@ namespace NzbDrone.Core.Applications.Radarr other.Implementation == Implementation && other.Priority == Priority && other.Id == Id && - apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare; + apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare; } } } diff --git a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs index 0860c4c6f..a75ccacc3 100644 --- a/src/NzbDrone.Core/Applications/Readarr/Readarr.cs +++ b/src/NzbDrone.Core/Applications/Readarr/Readarr.cs @@ -90,21 +90,26 @@ namespace NzbDrone.Core.Applications.Readarr public override List GetIndexerMappings() { var indexers = _readarrV1Proxy.GetIndexers(Settings) - .Where(i => i.Implementation == "Newznab" || i.Implementation == "Torznab"); + .Where(i => i.Implementation is "Newznab" or "Torznab"); var mappings = new List(); foreach (var indexer in indexers) { - if ((string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value == _configFileProvider.ApiKey) + var baseUrl = (string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl")?.Value ?? string.Empty; + + if (!baseUrl.StartsWith(Settings.ProwlarrUrl.TrimEnd('/')) && + (string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value != _configFileProvider.ApiKey) { - var match = AppIndexerRegex.Match((string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value); + continue; + } - if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) - { - // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance - mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); - } + var match = AppIndexerRegex.Match(baseUrl); + + if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) + { + // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance + mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); } } @@ -150,7 +155,7 @@ namespace NzbDrone.Core.Applications.Readarr } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -163,10 +168,12 @@ namespace NzbDrone.Core.Applications.Readarr if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} [{1}] found", remoteIndexer.Name, remoteIndexer.Id); - if (!readarrIndexer.Equals(remoteIndexer)) + if (!readarrIndexer.Equals(remoteIndexer) || forceSync) { + _logger.Debug("Syncing remote indexer with current settings"); + if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { // Retain user fields not-affiliated with Prowlarr diff --git a/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs b/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs index e4683ae01..ca98f5700 100644 --- a/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Readarr/ReadarrIndexer.cs @@ -28,9 +28,12 @@ namespace NzbDrone.Core.Applications.Readarr } var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; - var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value); + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var otherApiKey = (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var apiKeyCompare = apiKey == otherApiKey || otherApiKey == "********"; + var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var apiPathCompare = apiPath.Equals(otherApiPath); @@ -58,7 +61,7 @@ namespace NzbDrone.Core.Applications.Readarr other.Implementation == Implementation && other.Priority == Priority && other.Id == Id && - apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare; + apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && discographySeedTimeCompare; } } } diff --git a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs index a894b9d1c..4805cf5bd 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/Sonarr.cs @@ -100,15 +100,20 @@ namespace NzbDrone.Core.Applications.Sonarr foreach (var indexer in indexers) { - if ((string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value == _configFileProvider.ApiKey) + var baseUrl = (string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl")?.Value ?? string.Empty; + + if (!baseUrl.StartsWith(Settings.ProwlarrUrl.TrimEnd('/')) && + (string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value != _configFileProvider.ApiKey) { - var match = AppIndexerRegex.Match((string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value); + continue; + } - if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) - { - // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance - mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); - } + var match = AppIndexerRegex.Match(baseUrl); + + if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) + { + // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance + mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); } } @@ -155,7 +160,7 @@ namespace NzbDrone.Core.Applications.Sonarr } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -168,10 +173,12 @@ namespace NzbDrone.Core.Applications.Sonarr if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} [{1}] found", remoteIndexer.Name, remoteIndexer.Id); - if (!sonarrIndexer.Equals(remoteIndexer)) + if (!sonarrIndexer.Equals(remoteIndexer) || forceSync) { + _logger.Debug("Syncing remote indexer with current settings"); + if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any() || indexer.Capabilities.Categories.SupportedCategories(Settings.AnimeSyncCategories.ToArray()).Any()) { // Retain user fields not-affiliated with Prowlarr diff --git a/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs b/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs index 03c4376c4..8b0b6a561 100644 --- a/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Sonarr/SonarrIndexer.cs @@ -30,10 +30,13 @@ namespace NzbDrone.Core.Applications.Sonarr } var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; - var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value); var animeCats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "animeCategories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value); + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var otherApiKey = (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var apiKeyCompare = apiKey == otherApiKey || otherApiKey == "********"; + var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var apiPathCompare = apiPath.Equals(otherApiPath); @@ -65,7 +68,7 @@ namespace NzbDrone.Core.Applications.Sonarr other.Implementation == Implementation && other.Priority == Priority && other.Id == Id && - apiKey && apiPathCompare && baseUrl && cats && animeCats && animeStandardFormatSearchCompare && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare; + apiKeyCompare && apiPathCompare && baseUrl && cats && animeCats && animeStandardFormatSearchCompare && minimumSeedersCompare && seedRatioCompare && seedTimeCompare && seasonSeedTimeCompare; } } } diff --git a/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs b/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs index 96076e61f..855692d07 100644 --- a/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs +++ b/src/NzbDrone.Core/Applications/Whisparr/Whisparr.cs @@ -90,21 +90,26 @@ namespace NzbDrone.Core.Applications.Whisparr public override List GetIndexerMappings() { var indexers = _whisparrV3Proxy.GetIndexers(Settings) - .Where(i => i.Implementation == "Newznab" || i.Implementation == "Torznab"); + .Where(i => i.Implementation is "Newznab" or "Torznab"); var mappings = new List(); foreach (var indexer in indexers) { - if ((string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value == _configFileProvider.ApiKey) + var baseUrl = (string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl")?.Value ?? string.Empty; + + if (!baseUrl.StartsWith(Settings.ProwlarrUrl.TrimEnd('/')) && + (string)indexer.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value != _configFileProvider.ApiKey) { - var match = AppIndexerRegex.Match((string)indexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value); + continue; + } - if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) - { - // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance - mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); - } + var match = AppIndexerRegex.Match(baseUrl); + + if (match.Groups["indexer"].Success && int.TryParse(match.Groups["indexer"].Value, out var indexerId)) + { + // Add parsed mapping if it's mapped to a Indexer in this Prowlarr instance + mappings.Add(new AppIndexerMap { IndexerId = indexerId, RemoteIndexerId = indexer.Id }); } } @@ -150,7 +155,7 @@ namespace NzbDrone.Core.Applications.Whisparr } } - public override void UpdateIndexer(IndexerDefinition indexer) + public override void UpdateIndexer(IndexerDefinition indexer, bool forceSync = false) { _logger.Debug("Updating indexer {0} [{1}]", indexer.Name, indexer.Id); @@ -163,10 +168,12 @@ namespace NzbDrone.Core.Applications.Whisparr if (remoteIndexer != null) { - _logger.Debug("Remote indexer found, syncing with current settings"); + _logger.Debug("Remote indexer {0} [{1}] found", remoteIndexer.Name, remoteIndexer.Id); - if (!whisparrIndexer.Equals(remoteIndexer)) + if (!whisparrIndexer.Equals(remoteIndexer) || forceSync) { + _logger.Debug("Syncing remote indexer with current settings"); + if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any()) { // Retain user fields not-affiliated with Prowlarr diff --git a/src/NzbDrone.Core/Applications/Whisparr/WhisparrIndexer.cs b/src/NzbDrone.Core/Applications/Whisparr/WhisparrIndexer.cs index b1d720360..4cbd2d3ff 100644 --- a/src/NzbDrone.Core/Applications/Whisparr/WhisparrIndexer.cs +++ b/src/NzbDrone.Core/Applications/Whisparr/WhisparrIndexer.cs @@ -28,9 +28,12 @@ namespace NzbDrone.Core.Applications.Whisparr } var baseUrl = (string)Fields.FirstOrDefault(x => x.Name == "baseUrl").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value; - var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey").Value == (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey").Value; var cats = JToken.DeepEquals((JArray)Fields.FirstOrDefault(x => x.Name == "categories").Value, (JArray)other.Fields.FirstOrDefault(x => x.Name == "categories").Value); + var apiKey = (string)Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var otherApiKey = (string)other.Fields.FirstOrDefault(x => x.Name == "apiKey")?.Value; + var apiKeyCompare = apiKey == otherApiKey || otherApiKey == "********"; + var apiPath = Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var otherApiPath = other.Fields.FirstOrDefault(x => x.Name == "apiPath")?.Value == null ? null : other.Fields.FirstOrDefault(x => x.Name == "apiPath").Value; var apiPathCompare = apiPath.Equals(otherApiPath); @@ -54,7 +57,7 @@ namespace NzbDrone.Core.Applications.Whisparr other.Implementation == Implementation && other.Priority == Priority && other.Id == Id && - apiKey && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare; + apiKeyCompare && apiPathCompare && baseUrl && cats && minimumSeedersCompare && seedRatioCompare && seedTimeCompare; } } } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerStatusController.cs b/src/Prowlarr.Api.V1/Indexers/IndexerStatusController.cs index 566be187f..97ebe606d 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerStatusController.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerStatusController.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; @@ -7,7 +8,6 @@ using NzbDrone.Core.ThingiProvider.Events; using NzbDrone.SignalR; using Prowlarr.Http; using Prowlarr.Http.REST; -using NotImplementedException = System.NotImplementedException; namespace Prowlarr.Api.V1.Indexers {