mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
New: Application Status Warnings
This commit is contained in:
@@ -61,9 +61,7 @@ class MovieIndexRow extends Component {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
enableRss,
|
||||
enableAutomaticSearch,
|
||||
enableInteractiveSearch,
|
||||
enable,
|
||||
tags,
|
||||
protocol,
|
||||
privacy,
|
||||
@@ -114,7 +112,7 @@ class MovieIndexRow extends Component {
|
||||
<IndexerStatusCell
|
||||
key={column.name}
|
||||
className={styles[column.name]}
|
||||
enabled={enableRss || enableAutomaticSearch || enableInteractiveSearch}
|
||||
enabled={enable}
|
||||
status={status}
|
||||
longDateFormat={longDateFormat}
|
||||
timeFormat={timeFormat}
|
||||
@@ -255,9 +253,7 @@ MovieIndexRow.propTypes = {
|
||||
privacy: PropTypes.string.isRequired,
|
||||
priority: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
enableRss: PropTypes.bool.isRequired,
|
||||
enableAutomaticSearch: PropTypes.bool.isRequired,
|
||||
enableInteractiveSearch: PropTypes.bool.isRequired,
|
||||
enable: PropTypes.bool.isRequired,
|
||||
status: PropTypes.object,
|
||||
capabilities: PropTypes.object.isRequired,
|
||||
added: PropTypes.string.isRequired,
|
||||
|
@@ -38,11 +38,8 @@ function EditIndexerModalContent(props) {
|
||||
id,
|
||||
implementationName,
|
||||
name,
|
||||
enableRss,
|
||||
enableAutomaticSearch,
|
||||
enableInteractiveSearch,
|
||||
enable,
|
||||
supportsRss,
|
||||
supportsSearch,
|
||||
fields,
|
||||
priority
|
||||
} = item;
|
||||
@@ -81,42 +78,14 @@ function EditIndexerModalContent(props) {
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('EnableRSS')}</FormLabel>
|
||||
<FormLabel>{translate('Enable')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableRss"
|
||||
name="enable"
|
||||
helpTextWarning={supportsRss.value ? undefined : translate('RSSIsNotSupportedWithThisIndexer')}
|
||||
isDisabled={!supportsRss.value}
|
||||
{...enableRss}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('EnableAutomaticSearch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableAutomaticSearch"
|
||||
helpText={supportsSearch.value ? translate('EnableAutomaticSearchHelpText') : undefined}
|
||||
helpTextWarning={supportsSearch.value ? undefined : translate('EnableAutomaticSearchHelpTextWarning')}
|
||||
isDisabled={!supportsSearch.value}
|
||||
{...enableAutomaticSearch}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('EnableInteractiveSearch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="enableInteractiveSearch"
|
||||
helpText={supportsSearch.value ? translate('EnableInteractiveSearchHelpText') : undefined}
|
||||
helpTextWarning={supportsSearch.value ? undefined : translate('EnableInteractiveSearchHelpTextWarning')}
|
||||
isDisabled={!supportsSearch.value}
|
||||
{...enableInteractiveSearch}
|
||||
{...enable}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
@@ -65,11 +65,8 @@ class Indexer extends Component {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
enableRss,
|
||||
enableAutomaticSearch,
|
||||
enableInteractiveSearch,
|
||||
enable,
|
||||
supportsRss,
|
||||
supportsSearch,
|
||||
priority,
|
||||
showPriority
|
||||
} = this.props;
|
||||
@@ -96,26 +93,12 @@ class Indexer extends Component {
|
||||
<div className={styles.enabled}>
|
||||
|
||||
{
|
||||
supportsRss && enableRss &&
|
||||
supportsRss && enable &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
RSS
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsSearch && enableAutomaticSearch &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('AutomaticSearch')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
supportsSearch && enableInteractiveSearch &&
|
||||
<Label kind={kinds.SUCCESS}>
|
||||
{translate('InteractiveSearch')}
|
||||
</Label>
|
||||
}
|
||||
|
||||
{
|
||||
showPriority &&
|
||||
<Label kind={kinds.DEFAULT}>
|
||||
@@ -123,7 +106,7 @@ class Indexer extends Component {
|
||||
</Label>
|
||||
}
|
||||
{
|
||||
!enableRss && !enableAutomaticSearch && !enableInteractiveSearch &&
|
||||
!enable &&
|
||||
<Label
|
||||
kind={kinds.DISABLED}
|
||||
outline={true}
|
||||
@@ -157,9 +140,7 @@ class Indexer extends Component {
|
||||
Indexer.propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
enableRss: PropTypes.bool.isRequired,
|
||||
enableAutomaticSearch: PropTypes.bool.isRequired,
|
||||
enableInteractiveSearch: PropTypes.bool.isRequired,
|
||||
enable: PropTypes.bool.isRequired,
|
||||
supportsRss: PropTypes.bool.isRequired,
|
||||
supportsSearch: PropTypes.bool.isRequired,
|
||||
onCloneIndexerPress: PropTypes.func.isRequired,
|
||||
|
@@ -142,9 +142,7 @@ export const reducers = createHandleActions({
|
||||
|
||||
[SELECT_INDEXER_SCHEMA]: (state, { payload }) => {
|
||||
return selectSchema(state, payload, (selectedSchema) => {
|
||||
selectedSchema.enableRss = selectedSchema.supportsRss;
|
||||
selectedSchema.enableAutomaticSearch = selectedSchema.supportsSearch;
|
||||
selectedSchema.enableInteractiveSearch = selectedSchema.supportsSearch;
|
||||
selectedSchema.enable = selectedSchema.supportsRss;
|
||||
|
||||
return selectedSchema;
|
||||
});
|
||||
|
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Applications;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class ApplicationStatusCheckFixture : CoreTest<ApplicationStatusCheck>
|
||||
{
|
||||
private List<IApplication> _applications = new List<IApplication>();
|
||||
private List<ApplicationStatus> _blockedApplications = new List<ApplicationStatus>();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Mocker.GetMock<IApplicationFactory>()
|
||||
.Setup(v => v.GetAvailableProviders())
|
||||
.Returns(_applications);
|
||||
|
||||
Mocker.GetMock<IApplicationStatusService>()
|
||||
.Setup(v => v.GetBlockedProviders())
|
||||
.Returns(_blockedApplications);
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Some Warning Message");
|
||||
}
|
||||
|
||||
private Mock<IApplication> GivenIndexer(int i, double backoffHours, double failureHours)
|
||||
{
|
||||
var id = i;
|
||||
|
||||
var mockIndexer = new Mock<IApplication>();
|
||||
mockIndexer.SetupGet(s => s.Definition).Returns(new ApplicationDefinition { Id = id });
|
||||
|
||||
_applications.Add(mockIndexer.Object);
|
||||
|
||||
if (backoffHours != 0.0)
|
||||
{
|
||||
_blockedApplications.Add(new ApplicationStatus
|
||||
{
|
||||
ProviderId = id,
|
||||
InitialFailure = DateTime.UtcNow.AddHours(-failureHours),
|
||||
MostRecentFailure = DateTime.UtcNow.AddHours(-0.1),
|
||||
EscalationLevel = 5,
|
||||
DisabledTill = DateTime.UtcNow.AddHours(backoffHours)
|
||||
});
|
||||
}
|
||||
|
||||
return mockIndexer;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_return_error_when_no_indexers()
|
||||
{
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_if_indexer_unavailable()
|
||||
{
|
||||
GivenIndexer(1, 2.0, 4.0);
|
||||
GivenIndexer(2, 0.0, 0.0);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_if_all_indexers_unavailable()
|
||||
{
|
||||
GivenIndexer(1, 2.0, 4.0);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_if_few_indexers_unavailable()
|
||||
{
|
||||
GivenIndexer(1, 2.0, 4.0);
|
||||
GivenIndexer(2, 2.0, 4.0);
|
||||
GivenIndexer(3, 0.0, 0.0);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
.Returns(new List<IIndexer>());
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.RssEnabled(It.IsAny<bool>()))
|
||||
.Setup(s => s.Enabled(It.IsAny<bool>()))
|
||||
.Returns(new List<IIndexer>());
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
@@ -43,14 +43,14 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
private void GivenRssEnabled()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.RssEnabled(It.IsAny<bool>()))
|
||||
.Setup(s => s.Enabled(It.IsAny<bool>()))
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
}
|
||||
|
||||
private void GivenRssFiltered()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.RssEnabled(false))
|
||||
.Setup(s => s.Enabled(false))
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
|
@@ -1,135 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class IndexerSearchCheckFixture : CoreTest<IndexerSearchCheck>
|
||||
{
|
||||
private Mock<IIndexer> _indexerMock;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.GetAvailableProviders())
|
||||
.Returns(new List<IIndexer>());
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.AutomaticSearchEnabled(It.IsAny<bool>()))
|
||||
.Returns(new List<IIndexer>());
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.InteractiveSearchEnabled(It.IsAny<bool>()))
|
||||
.Returns(new List<IIndexer>());
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Some Warning Message");
|
||||
}
|
||||
|
||||
private void GivenIndexer(bool supportsRss, bool supportsSearch)
|
||||
{
|
||||
_indexerMock = Mocker.GetMock<IIndexer>();
|
||||
_indexerMock.SetupGet(s => s.SupportsRss).Returns(supportsRss);
|
||||
_indexerMock.SetupGet(s => s.SupportsSearch).Returns(supportsSearch);
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.GetAvailableProviders())
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
}
|
||||
|
||||
private void GivenAutomaticSearchEnabled()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.AutomaticSearchEnabled(It.IsAny<bool>()))
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
}
|
||||
|
||||
private void GivenInteractiveSearchEnabled()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.InteractiveSearchEnabled(It.IsAny<bool>()))
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
}
|
||||
|
||||
private void GivenSearchFiltered()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.AutomaticSearchEnabled(false))
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(s => s.InteractiveSearchEnabled(false))
|
||||
.Returns(new List<IIndexer> { _indexerMock.Object });
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("recent indexer errors");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_when_no_indexer_present()
|
||||
{
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_when_no_search_supported_indexer_present()
|
||||
{
|
||||
GivenIndexer(true, false);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_ok_when_automatic_and__search_is_enabled()
|
||||
{
|
||||
GivenIndexer(false, true);
|
||||
GivenAutomaticSearchEnabled();
|
||||
GivenInteractiveSearchEnabled();
|
||||
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_when_only_automatic_search_is_enabled()
|
||||
{
|
||||
GivenIndexer(false, true);
|
||||
GivenAutomaticSearchEnabled();
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_when_only_interactive_search_is_enabled()
|
||||
{
|
||||
GivenIndexer(false, true);
|
||||
GivenInteractiveSearchEnabled();
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_if_search_is_supported_but_disabled()
|
||||
{
|
||||
GivenIndexer(false, true);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_filter_warning_if_search_is_enabled_but_filtered()
|
||||
{
|
||||
GivenIndexer(false, true);
|
||||
GivenSearchFiltered();
|
||||
|
||||
Subject.Check().ShouldBeWarning("recent indexer errors");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -8,13 +9,53 @@ namespace NzbDrone.Core.Applications
|
||||
{
|
||||
public interface IApplicationFactory : IProviderFactory<IApplication, ApplicationDefinition>
|
||||
{
|
||||
List<IApplication> SyncEnabled(bool filterBlockedIndexers = true);
|
||||
}
|
||||
|
||||
public class ApplicationFactory : ProviderFactory<IApplication, ApplicationDefinition>, IApplicationFactory
|
||||
{
|
||||
public ApplicationFactory(IApplicationsRepository providerRepository, IEnumerable<IApplication> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
||||
private readonly IApplicationStatusService _applicationStatusService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ApplicationFactory(IApplicationStatusService applicationStatusService,
|
||||
IApplicationsRepository providerRepository,
|
||||
IEnumerable<IApplication> providers,
|
||||
IContainer container,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||
{
|
||||
_applicationStatusService = applicationStatusService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<IApplication> SyncEnabled(bool filterBlockedClients = true)
|
||||
{
|
||||
var enabledClients = GetAvailableProviders().Where(n => ((ApplicationDefinition)n.Definition).Enable);
|
||||
|
||||
if (filterBlockedClients)
|
||||
{
|
||||
return FilterBlockedApplications(enabledClients).ToList();
|
||||
}
|
||||
|
||||
return enabledClients.ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<IApplication> FilterBlockedApplications(IEnumerable<IApplication> applications)
|
||||
{
|
||||
var blockedApplications = _applicationStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
|
||||
|
||||
foreach (var application in applications)
|
||||
{
|
||||
ApplicationStatus blockedApplicationStatus;
|
||||
if (blockedApplications.TryGetValue(application.Definition.Id, out blockedApplicationStatus))
|
||||
{
|
||||
_logger.Debug("Temporarily ignoring application {0} till {1} due to recent failures.", application.Definition.Name, blockedApplicationStatus.DisabledTill.Value.ToLocalTime());
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return application;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -14,11 +17,13 @@ namespace NzbDrone.Core.Applications
|
||||
IExecute<ApplicationIndexerSyncCommand>
|
||||
{
|
||||
private readonly IApplicationFactory _applicationsFactory;
|
||||
private readonly IApplicationStatusService _applicationStatusService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ApplicationService(IApplicationFactory applicationsFactory, Logger logger)
|
||||
public ApplicationService(IApplicationFactory applicationsFactory, IApplicationStatusService applicationStatusService, Logger logger)
|
||||
{
|
||||
_applicationsFactory = applicationsFactory;
|
||||
_applicationStatusService = applicationStatusService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -31,51 +36,103 @@ namespace NzbDrone.Core.Applications
|
||||
{
|
||||
var app = _applicationsFactory.GetInstance(appDefinition);
|
||||
|
||||
app.SyncIndexers();
|
||||
ExecuteAction(a => a.SyncIndexers(), app);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAsync(ProviderAddedEvent<IIndexer> message)
|
||||
{
|
||||
var enabledApps = _applicationsFactory.GetAvailableProviders()
|
||||
.Where(n => ((ApplicationDefinition)n.Definition).Enable);
|
||||
var enabledApps = _applicationsFactory.SyncEnabled();
|
||||
|
||||
foreach (var app in enabledApps)
|
||||
{
|
||||
app.AddIndexer((IndexerDefinition)message.Definition);
|
||||
ExecuteAction(a => a.AddIndexer((IndexerDefinition)message.Definition), app);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAsync(ProviderDeletedEvent<IIndexer> message)
|
||||
{
|
||||
var enabledApps = _applicationsFactory.GetAvailableProviders()
|
||||
var enabledApps = _applicationsFactory.SyncEnabled()
|
||||
.Where(n => ((ApplicationDefinition)n.Definition).SyncLevel == ApplicationSyncLevel.FullSync);
|
||||
|
||||
foreach (var app in enabledApps)
|
||||
{
|
||||
app.RemoveIndexer(message.ProviderId);
|
||||
ExecuteAction(a => a.RemoveIndexer(message.ProviderId), app);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleAsync(ProviderUpdatedEvent<IIndexer> message)
|
||||
{
|
||||
var enabledApps = _applicationsFactory.GetAvailableProviders()
|
||||
var enabledApps = _applicationsFactory.SyncEnabled()
|
||||
.Where(n => ((ApplicationDefinition)n.Definition).SyncLevel == ApplicationSyncLevel.FullSync);
|
||||
|
||||
foreach (var app in enabledApps)
|
||||
{
|
||||
app.UpdateIndexer((IndexerDefinition)message.Definition);
|
||||
ExecuteAction(a => a.UpdateIndexer((IndexerDefinition)message.Definition), app);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(ApplicationIndexerSyncCommand message)
|
||||
{
|
||||
var enabledApps = _applicationsFactory.GetAvailableProviders()
|
||||
.Where(n => ((ApplicationDefinition)n.Definition).Enable);
|
||||
var enabledApps = _applicationsFactory.SyncEnabled();
|
||||
|
||||
foreach (var app in enabledApps)
|
||||
{
|
||||
app.SyncIndexers();
|
||||
ExecuteAction(a => a.SyncIndexers(), app);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteAction(Action<IApplication> applicationAction, IApplication application)
|
||||
{
|
||||
try
|
||||
{
|
||||
applicationAction(application);
|
||||
_applicationStatusService.RecordSuccess(application.Definition.Id);
|
||||
}
|
||||
catch (WebException webException)
|
||||
{
|
||||
if (webException.Status == WebExceptionStatus.NameResolutionFailure ||
|
||||
webException.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
_applicationStatusService.RecordConnectionFailure(application.Definition.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
_applicationStatusService.RecordFailure(application.Definition.Id);
|
||||
}
|
||||
|
||||
if (webException.Message.Contains("502") || webException.Message.Contains("503") ||
|
||||
webException.Message.Contains("timed out"))
|
||||
{
|
||||
_logger.Warn("{0} server is currently unavailable. {1}", this, webException.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn("{0} {1}", this, webException.Message);
|
||||
}
|
||||
}
|
||||
catch (TooManyRequestsException ex)
|
||||
{
|
||||
if (ex.RetryAfter != TimeSpan.Zero)
|
||||
{
|
||||
_applicationStatusService.RecordFailure(application.Definition.Id, ex.RetryAfter);
|
||||
}
|
||||
else
|
||||
{
|
||||
_applicationStatusService.RecordFailure(application.Definition.Id, TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
_logger.Warn("API Request Limit reached for {0}", this);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_applicationStatusService.RecordFailure(application.Definition.Id);
|
||||
_logger.Warn("{0} {1}", this, ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_applicationStatusService.RecordFailure(application.Definition.Id);
|
||||
_logger.Error(ex, "An error occurred while talking to application.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
src/NzbDrone.Core/Applications/ApplicationStatus.cs
Normal file
8
src/NzbDrone.Core/Applications/ApplicationStatus.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using NzbDrone.Core.ThingiProvider.Status;
|
||||
|
||||
namespace NzbDrone.Core.Applications
|
||||
{
|
||||
public class ApplicationStatus : ProviderStatusBase
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider.Status;
|
||||
|
||||
namespace NzbDrone.Core.Applications
|
||||
{
|
||||
public interface IApplicationStatusRepository : IProviderStatusRepository<ApplicationStatus>
|
||||
{
|
||||
}
|
||||
|
||||
public class ApplicationStatusRepository : ProviderStatusRepository<ApplicationStatus>, IApplicationStatusRepository
|
||||
{
|
||||
public ApplicationStatusRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
22
src/NzbDrone.Core/Applications/ApplicationStatusService.cs
Normal file
22
src/NzbDrone.Core/Applications/ApplicationStatusService.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider.Status;
|
||||
|
||||
namespace NzbDrone.Core.Applications
|
||||
{
|
||||
public interface IApplicationStatusService : IProviderStatusServiceBase<ApplicationStatus>
|
||||
{
|
||||
}
|
||||
|
||||
public class ApplicationStatusService : ProviderStatusServiceBase<IApplication, ApplicationStatus>, IApplicationStatusService
|
||||
{
|
||||
public ApplicationStatusService(IApplicationStatusRepository providerStatusRepository, IEventAggregator eventAggregator, IRuntimeInfo runtimeInfo, Logger logger)
|
||||
: base(providerStatusRepository, eventAggregator, runtimeInfo, logger)
|
||||
{
|
||||
MinimumTimeSinceInitialFailure = TimeSpan.FromMinutes(5);
|
||||
MaximumEscalationLevel = 5;
|
||||
}
|
||||
}
|
||||
}
|
@@ -76,7 +76,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
var indexers = _lidarrV1Proxy.GetIndexers(Settings);
|
||||
|
||||
//Pull all local indexers (TODO only those that support movie categories.)
|
||||
var prowlarrIndexers = _indexerFactory.GetAvailableProviders();
|
||||
var prowlarrIndexers = _indexerFactory.Enabled();
|
||||
|
||||
//Pull mapping so we can check the mapping to see what already exists.
|
||||
var indexerMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
@@ -107,9 +107,9 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
Id = 0,
|
||||
Name = $"{indexer.Name} (Prowlarr)",
|
||||
EnableRss = indexer.EnableRss,
|
||||
EnableAutomaticSearch = indexer.EnableAutomaticSearch,
|
||||
EnableInteractiveSearch = indexer.EnableInteractiveSearch,
|
||||
EnableRss = true,
|
||||
EnableAutomaticSearch = true,
|
||||
EnableInteractiveSearch = true,
|
||||
Priority = indexer.Priority,
|
||||
Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab",
|
||||
ConfigContract = schema.ConfigContract,
|
||||
|
@@ -76,7 +76,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
var indexers = _radarrV3Proxy.GetIndexers(Settings);
|
||||
|
||||
//Pull all local indexers (TODO only those that support movie categories.)
|
||||
var prowlarrIndexers = _indexerFactory.GetAvailableProviders();
|
||||
var prowlarrIndexers = _indexerFactory.Enabled();
|
||||
|
||||
//Pull mapping so we can check the mapping to see what already exists.
|
||||
var indexerMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
@@ -107,9 +107,9 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
Id = 0,
|
||||
Name = $"{indexer.Name} (Prowlarr)",
|
||||
EnableRss = indexer.EnableRss,
|
||||
EnableAutomaticSearch = indexer.EnableAutomaticSearch,
|
||||
EnableInteractiveSearch = indexer.EnableInteractiveSearch,
|
||||
EnableRss = true,
|
||||
EnableAutomaticSearch = true,
|
||||
EnableInteractiveSearch = true,
|
||||
Priority = indexer.Priority,
|
||||
Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab",
|
||||
ConfigContract = schema.ConfigContract,
|
||||
|
@@ -76,7 +76,7 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
var indexers = _readarrV1Proxy.GetIndexers(Settings);
|
||||
|
||||
//Pull all local indexers (TODO only those that support movie categories.)
|
||||
var prowlarrIndexers = _indexerFactory.GetAvailableProviders();
|
||||
var prowlarrIndexers = _indexerFactory.Enabled();
|
||||
|
||||
//Pull mapping so we can check the mapping to see what already exists.
|
||||
var indexerMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
@@ -107,9 +107,9 @@ namespace NzbDrone.Core.Applications.Readarr
|
||||
{
|
||||
Id = 0,
|
||||
Name = $"{indexer.Name} (Prowlarr)",
|
||||
EnableRss = indexer.EnableRss,
|
||||
EnableAutomaticSearch = indexer.EnableAutomaticSearch,
|
||||
EnableInteractiveSearch = indexer.EnableInteractiveSearch,
|
||||
EnableRss = true,
|
||||
EnableAutomaticSearch = true,
|
||||
EnableInteractiveSearch = true,
|
||||
Priority = indexer.Priority,
|
||||
Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab",
|
||||
ConfigContract = schema.ConfigContract,
|
||||
|
@@ -76,7 +76,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
var indexers = _sonarrV3Proxy.GetIndexers(Settings);
|
||||
|
||||
//Pull all local indexers (TODO only those that support movie categories.)
|
||||
var prowlarrIndexers = _indexerFactory.GetAvailableProviders();
|
||||
var prowlarrIndexers = _indexerFactory.Enabled();
|
||||
|
||||
//Pull mapping so we can check the mapping to see what already exists.
|
||||
var indexerMappings = _appIndexerMapService.GetMappingsForApp(Definition.Id);
|
||||
@@ -107,9 +107,9 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
Id = 0,
|
||||
Name = $"{indexer.Name} (Prowlarr)",
|
||||
EnableRss = indexer.EnableRss,
|
||||
EnableAutomaticSearch = indexer.EnableAutomaticSearch,
|
||||
EnableInteractiveSearch = indexer.EnableInteractiveSearch,
|
||||
EnableRss = true,
|
||||
EnableAutomaticSearch = true,
|
||||
EnableInteractiveSearch = true,
|
||||
Priority = indexer.Priority,
|
||||
Implementation = indexer.Protocol == DownloadProtocol.Usenet ? "Newznab" : "Torznab",
|
||||
ConfigContract = schema.ConfigContract,
|
||||
|
@@ -0,0 +1,24 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(2)]
|
||||
public class ApplicationStatus : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Delete.Column("EnableAutomaticSearch").FromTable("Indexers");
|
||||
Delete.Column("EnableInteractiveSearch").FromTable("Indexers");
|
||||
|
||||
Rename.Column("EnableRss").OnTable("Indexers").To("Enable");
|
||||
|
||||
Create.TableForModel("ApplicationStatus")
|
||||
.WithColumn("ProviderId").AsInt32().NotNullable().Unique()
|
||||
.WithColumn("InitialFailure").AsDateTime().Nullable()
|
||||
.WithColumn("MostRecentFailure").AsDateTime().Nullable()
|
||||
.WithColumn("EscalationLevel").AsInt32().NotNullable()
|
||||
.WithColumn("DisabledTill").AsDateTime().Nullable();
|
||||
}
|
||||
}
|
||||
}
|
@@ -40,7 +40,6 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
Mapper.Entity<IndexerDefinition>("Indexers").RegisterModel()
|
||||
.Ignore(x => x.ImplementationName)
|
||||
.Ignore(i => i.Enable)
|
||||
.Ignore(i => i.Protocol)
|
||||
.Ignore(i => i.Privacy)
|
||||
.Ignore(i => i.SupportsRss)
|
||||
@@ -69,6 +68,8 @@ namespace NzbDrone.Core.Datastore
|
||||
|
||||
Mapper.Entity<IndexerStatus>("IndexerStatus").RegisterModel();
|
||||
|
||||
Mapper.Entity<ApplicationStatus>("ApplicationStatus").RegisterModel();
|
||||
|
||||
Mapper.Entity<CustomFilter>("CustomFilters").RegisterModel();
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Applications;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IApplication>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IApplication>))]
|
||||
[CheckOn(typeof(ProviderStatusChangedEvent<IApplication>))]
|
||||
public class ApplicationStatusCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IApplicationFactory _providerFactory;
|
||||
private readonly IApplicationStatusService _providerStatusService;
|
||||
|
||||
public ApplicationStatusCheck(IApplicationFactory providerFactory, IApplicationStatusService providerStatusService, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_providerFactory = providerFactory;
|
||||
_providerStatusService = providerStatusService;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var enabledProviders = _providerFactory.GetAvailableProviders();
|
||||
var backOffProviders = enabledProviders.Join(_providerStatusService.GetBlockedProviders(),
|
||||
i => i.Definition.Id,
|
||||
s => s.ProviderId,
|
||||
(i, s) => new { Provider = i, Status = s })
|
||||
.Where(p => p.Status.InitialFailure.HasValue &&
|
||||
p.Status.InitialFailure.Value.After(
|
||||
DateTime.UtcNow.AddHours(-6)))
|
||||
.ToList();
|
||||
|
||||
if (backOffProviders.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
|
||||
if (backOffProviders.Count == enabledProviders.Count)
|
||||
{
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Error,
|
||||
_localizationService.GetLocalizedString("ApplicationStatusCheckAllClientMessage"),
|
||||
"#applications-are-unavailable-due-to-failures");
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType(),
|
||||
HealthCheckResult.Warning,
|
||||
string.Format(_localizationService.GetLocalizedString("ApplicationStatusCheckSingleClientMessage"),
|
||||
string.Join(", ", backOffProviders.Select(v => v.Provider.Definition.Name))),
|
||||
"#applications-are-unavailable-due-to-failures");
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,14 +21,14 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var enabled = _indexerFactory.RssEnabled(false);
|
||||
var enabled = _indexerFactory.Enabled(false);
|
||||
|
||||
if (enabled.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Error, _localizationService.GetLocalizedString("IndexerRssHealthCheckNoIndexers"));
|
||||
}
|
||||
|
||||
var active = _indexerFactory.RssEnabled(true);
|
||||
var active = _indexerFactory.Enabled(true);
|
||||
|
||||
if (active.Empty())
|
||||
{
|
||||
|
@@ -1,48 +0,0 @@
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.ThingiProvider.Events;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
|
||||
[CheckOn(typeof(ProviderStatusChangedEvent<IIndexer>))]
|
||||
public class IndexerSearchCheck : HealthCheckBase
|
||||
{
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public IndexerSearchCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
|
||||
: base(localizationService)
|
||||
{
|
||||
_indexerFactory = indexerFactory;
|
||||
}
|
||||
|
||||
public override HealthCheck Check()
|
||||
{
|
||||
var automaticSearchEnabled = _indexerFactory.AutomaticSearchEnabled(false);
|
||||
|
||||
if (automaticSearchEnabled.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerSearchCheckNoAutomaticMessage"));
|
||||
}
|
||||
|
||||
var interactiveSearchEnabled = _indexerFactory.InteractiveSearchEnabled(false);
|
||||
|
||||
if (interactiveSearchEnabled.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerSearchCheckNoInteractiveMessage"));
|
||||
}
|
||||
|
||||
var active = _indexerFactory.AutomaticSearchEnabled(true);
|
||||
|
||||
if (active.Empty())
|
||||
{
|
||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, _localizationService.GetLocalizedString("IndexerSearchCheckNoAvailableIndexersMessage"));
|
||||
}
|
||||
|
||||
return new HealthCheck(GetType());
|
||||
}
|
||||
}
|
||||
}
|
@@ -129,9 +129,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
|
||||
private List<ReleaseInfo> Dispatch(Func<IIndexer, IndexerPageableQueryResult> searchAction, SearchCriteriaBase criteriaBase)
|
||||
{
|
||||
var indexers = criteriaBase.InteractiveSearch ?
|
||||
_indexerFactory.InteractiveSearchEnabled() :
|
||||
_indexerFactory.AutomaticSearchEnabled();
|
||||
var indexers = _indexerFactory.GetAvailableProviders();
|
||||
|
||||
if (criteriaBase.IndexerIds != null && criteriaBase.IndexerIds.Count > 0)
|
||||
{
|
||||
|
@@ -63,9 +63,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
return new IndexerDefinition
|
||||
{
|
||||
EnableRss = false,
|
||||
EnableAutomaticSearch = false,
|
||||
EnableInteractiveSearch = false,
|
||||
Enable = true,
|
||||
Name = definition.Name,
|
||||
Implementation = GetType().Name,
|
||||
Settings = new CardigannSettings { DefinitionFile = definition.File },
|
||||
|
@@ -91,9 +91,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
return new IndexerDefinition
|
||||
{
|
||||
EnableRss = false,
|
||||
EnableAutomaticSearch = false,
|
||||
EnableInteractiveSearch = false,
|
||||
Enable = true,
|
||||
Name = name,
|
||||
Implementation = GetType().Name,
|
||||
Settings = settings,
|
||||
|
@@ -23,9 +23,7 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
|
||||
{
|
||||
return new IndexerDefinition
|
||||
{
|
||||
EnableRss = false,
|
||||
EnableAutomaticSearch = false,
|
||||
EnableInteractiveSearch = false,
|
||||
Enable = true,
|
||||
Name = name,
|
||||
Implementation = GetType().Name,
|
||||
Settings = settings,
|
||||
|
@@ -57,9 +57,7 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
{
|
||||
return new IndexerDefinition
|
||||
{
|
||||
EnableRss = false,
|
||||
EnableAutomaticSearch = false,
|
||||
EnableInteractiveSearch = false,
|
||||
Enable = true,
|
||||
Name = name,
|
||||
Implementation = GetType().Name,
|
||||
Settings = settings,
|
||||
|
@@ -50,9 +50,7 @@ namespace NzbDrone.Core.Indexers
|
||||
yield return new IndexerDefinition
|
||||
{
|
||||
Name = GetType().Name,
|
||||
EnableRss = config.Validate().IsValid && SupportsRss,
|
||||
EnableAutomaticSearch = config.Validate().IsValid && SupportsSearch,
|
||||
EnableInteractiveSearch = config.Validate().IsValid && SupportsSearch,
|
||||
Enable = config.Validate().IsValid && SupportsRss,
|
||||
Implementation = GetType().Name,
|
||||
Settings = config
|
||||
};
|
||||
|
@@ -7,9 +7,6 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public class IndexerDefinition : ProviderDefinition
|
||||
{
|
||||
public bool EnableRss { get; set; }
|
||||
public bool EnableAutomaticSearch { get; set; }
|
||||
public bool EnableInteractiveSearch { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public IndexerPrivacy Privacy { get; set; }
|
||||
public bool SupportsRss { get; set; }
|
||||
@@ -18,8 +15,6 @@ namespace NzbDrone.Core.Indexers
|
||||
public int Priority { get; set; } = 25;
|
||||
public DateTime Added { get; set; }
|
||||
|
||||
public override bool Enable => EnableRss || EnableAutomaticSearch || EnableInteractiveSearch;
|
||||
|
||||
public IndexerStatus Status { get; set; }
|
||||
|
||||
public List<SettingsField> ExtraFields { get; set; } = new List<SettingsField>();
|
||||
|
@@ -14,9 +14,7 @@ namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IIndexerFactory : IProviderFactory<IIndexer, IndexerDefinition>
|
||||
{
|
||||
List<IIndexer> RssEnabled(bool filterBlockedIndexers = true);
|
||||
List<IIndexer> AutomaticSearchEnabled(bool filterBlockedIndexers = true);
|
||||
List<IIndexer> InteractiveSearchEnabled(bool filterBlockedIndexers = true);
|
||||
List<IIndexer> Enabled(bool filterBlockedIndexers = true);
|
||||
void DeleteIndexers(List<int> indexerIds);
|
||||
}
|
||||
|
||||
@@ -165,33 +163,9 @@ namespace NzbDrone.Core.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
public List<IIndexer> RssEnabled(bool filterBlockedIndexers = true)
|
||||
public List<IIndexer> Enabled(bool filterBlockedIndexers = true)
|
||||
{
|
||||
var enabledIndexers = GetAvailableProviders().Where(n => ((IndexerDefinition)n.Definition).EnableRss);
|
||||
|
||||
if (filterBlockedIndexers)
|
||||
{
|
||||
return FilterBlockedIndexers(enabledIndexers).ToList();
|
||||
}
|
||||
|
||||
return enabledIndexers.ToList();
|
||||
}
|
||||
|
||||
public List<IIndexer> AutomaticSearchEnabled(bool filterBlockedIndexers = true)
|
||||
{
|
||||
var enabledIndexers = GetAvailableProviders().Where(n => ((IndexerDefinition)n.Definition).EnableAutomaticSearch);
|
||||
|
||||
if (filterBlockedIndexers)
|
||||
{
|
||||
return FilterBlockedIndexers(enabledIndexers).ToList();
|
||||
}
|
||||
|
||||
return enabledIndexers.ToList();
|
||||
}
|
||||
|
||||
public List<IIndexer> InteractiveSearchEnabled(bool filterBlockedIndexers = true)
|
||||
{
|
||||
var enabledIndexers = GetAvailableProviders().Where(n => ((IndexerDefinition)n.Definition).EnableInteractiveSearch);
|
||||
var enabledIndexers = GetAvailableProviders().Where(n => ((IndexerDefinition)n.Definition).Enable);
|
||||
|
||||
if (filterBlockedIndexers)
|
||||
{
|
||||
|
@@ -35,6 +35,8 @@
|
||||
"ApiKey": "API Key",
|
||||
"AppDataDirectory": "AppData directory",
|
||||
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
||||
"ApplicationStatusCheckAllClientMessage": "All applications are unavailable due to failures",
|
||||
"ApplicationStatusCheckSingleClientMessage": "Applications unavailable due to failures: {0}",
|
||||
"Apply": "Apply",
|
||||
"ApplyTags": "Apply Tags",
|
||||
"ApplyTagsHelpTexts1": "How to apply tags to the selected movies",
|
||||
@@ -185,8 +187,6 @@
|
||||
"DownloadClients": "Download Clients",
|
||||
"DownloadClientSettings": "Download Client Settings",
|
||||
"DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings",
|
||||
"DownloadClientStatusCheckAllClientMessage": "All download clients are unavailable due to failures",
|
||||
"DownloadClientStatusCheckSingleClientMessage": "Download clients unavailable due to failures: {0}",
|
||||
"DownloadClientUnavailable": "Download client is unavailable",
|
||||
"Downloaded": "Downloaded",
|
||||
"DownloadedAndMonitored": "Downloaded and Monitored",
|
||||
|
@@ -16,7 +16,7 @@ namespace NzbDrone.Integration.Test.ApiTests
|
||||
|
||||
indexers.Should().NotBeEmpty();
|
||||
indexers.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name));
|
||||
indexers.Where(c => c.ConfigContract == typeof(NullConfig).Name).Should().OnlyContain(c => c.EnableRss);
|
||||
indexers.Where(c => c.ConfigContract == typeof(NullConfig).Name).Should().OnlyContain(c => c.Enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,9 +36,7 @@ namespace NzbDrone.Integration.Test
|
||||
{
|
||||
Indexers.Post(new Prowlarr.Api.V1.Indexers.IndexerResource
|
||||
{
|
||||
EnableRss = false,
|
||||
EnableInteractiveSearch = false,
|
||||
EnableAutomaticSearch = false,
|
||||
Enable = false,
|
||||
ConfigContract = nameof(FileListSettings),
|
||||
Implementation = nameof(FileList),
|
||||
Name = "NewznabTest",
|
||||
|
@@ -10,9 +10,7 @@ namespace Prowlarr.Api.V1.Indexers
|
||||
{
|
||||
public class IndexerResource : ProviderResource
|
||||
{
|
||||
public bool EnableRss { get; set; }
|
||||
public bool EnableAutomaticSearch { get; set; }
|
||||
public bool EnableInteractiveSearch { get; set; }
|
||||
public bool Enable { get; set; }
|
||||
public bool SupportsRss { get; set; }
|
||||
public bool SupportsSearch { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
@@ -51,9 +49,7 @@ namespace Prowlarr.Api.V1.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
resource.EnableRss = definition.EnableRss;
|
||||
resource.EnableAutomaticSearch = definition.EnableAutomaticSearch;
|
||||
resource.EnableInteractiveSearch = definition.EnableInteractiveSearch;
|
||||
resource.Enable = definition.Enable;
|
||||
resource.SupportsRss = definition.SupportsRss;
|
||||
resource.SupportsSearch = definition.SupportsSearch;
|
||||
resource.Capabilities = definition.Capabilities.ToResource();
|
||||
@@ -88,9 +84,7 @@ namespace Prowlarr.Api.V1.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
definition.EnableRss = resource.EnableRss;
|
||||
definition.EnableAutomaticSearch = resource.EnableAutomaticSearch;
|
||||
definition.EnableInteractiveSearch = resource.EnableInteractiveSearch;
|
||||
definition.Enable = resource.Enable;
|
||||
definition.Priority = resource.Priority;
|
||||
definition.Privacy = resource.Privacy;
|
||||
definition.Added = resource.Added;
|
||||
|
Reference in New Issue
Block a user