New: Add download client per indexer setting

pull/1763/head
ricci2511 12 months ago committed by Bogdan
parent 252740519f
commit 826828e8ec

@ -0,0 +1,98 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchDownloadClients } from 'Store/Actions/settingsActions';
import sortByName from 'Utilities/Array/sortByName';
import EnhancedSelectInput from './EnhancedSelectInput';
function createMapStateToProps() {
return createSelector(
(state) => state.settings.downloadClients,
(state, { includeAny }) => includeAny,
(state, { protocol }) => protocol,
(downloadClients, includeAny, protocolFilter) => {
const {
isFetching,
isPopulated,
error,
items
} = downloadClients;
const values = items
.filter((downloadClient) => downloadClient.protocol === protocolFilter)
.sort(sortByName)
.map((downloadClient) => ({
key: downloadClient.id,
value: downloadClient.name
}));
if (includeAny) {
values.unshift({
key: 0,
value: '(Any)'
});
}
return {
isFetching,
isPopulated,
error,
values
};
}
);
}
const mapDispatchToProps = {
dispatchFetchDownloadClients: fetchDownloadClients
};
class DownloadClientSelectInputConnector extends Component {
//
// Lifecycle
componentDidMount() {
if (!this.props.isPopulated) {
this.props.dispatchFetchDownloadClients();
}
}
//
// Listeners
onChange = ({ name, value }) => {
this.props.onChange({ name, value: parseInt(value) });
};
//
// Render
render() {
return (
<EnhancedSelectInput
{...this.props}
onChange={this.onChange}
/>
);
}
}
DownloadClientSelectInputConnector.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
includeAny: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
dispatchFetchDownloadClients: PropTypes.func.isRequired
};
DownloadClientSelectInputConnector.defaultProps = {
includeAny: false,
protocol: 'torrent'
};
export default connect(createMapStateToProps, mapDispatchToProps)(DownloadClientSelectInputConnector);

@ -10,6 +10,7 @@ import CaptchaInputConnector from './CaptchaInputConnector';
import CardigannCaptchaInputConnector from './CardigannCaptchaInputConnector';
import CheckInput from './CheckInput';
import DeviceInputConnector from './DeviceInputConnector';
import DownloadClientSelectInputConnector from './DownloadClientSelectInputConnector';
import EnhancedSelectInput from './EnhancedSelectInput';
import EnhancedSelectInputConnector from './EnhancedSelectInputConnector';
import FormInputHelpText from './FormInputHelpText';
@ -72,6 +73,9 @@ function getComponent(type) {
case inputTypes.CATEGORY_SELECT:
return NewznabCategorySelectInputConnector;
case inputTypes.DOWNLOAD_CLIENT_SELECT:
return DownloadClientSelectInputConnector;
case inputTypes.INDEXER_FLAGS_SELECT:
return IndexerFlagsSelectInputConnector;

@ -9,6 +9,7 @@ export const KEY_VALUE_LIST = 'keyValueList';
export const INFO = 'info';
export const MOVIE_MONITORED_SELECT = 'movieMonitoredSelect';
export const CATEGORY_SELECT = 'newznabCategorySelect';
export const DOWNLOAD_CLIENT_SELECT = 'downloadClientSelect';
export const NUMBER = 'number';
export const OAUTH = 'oauth';
export const PASSWORD = 'password';

@ -48,7 +48,9 @@ function EditIndexerModalContent(props) {
appProfileId,
tags,
fields,
priority
priority,
protocol,
downloadClientId
} = item;
const indexerDisplayName = implementationName === definitionName ? implementationName : `${implementationName} (${definitionName})`;
@ -156,6 +158,23 @@ function EditIndexerModalContent(props) {
/>
</FormGroup>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('DownloadClient')}</FormLabel>
<FormInputGroup
type={inputTypes.DOWNLOAD_CLIENT_SELECT}
name="downloadClientId"
helpText={translate('IndexerDownloadClientHelpText')}
{...downloadClientId}
includeAny={true}
protocol={protocol.value}
onChange={onInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Tags')}</FormLabel>

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(035)]
public class download_client_per_indexer : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Indexers").AddColumn("DownloadClientId").AsInt32().WithDefaultValue(0);
}
}
}

@ -2,13 +2,14 @@
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Core.Download
{
public interface IProvideDownloadClient
{
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol);
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol, int indexerId = 0);
IEnumerable<IDownloadClient> GetDownloadClients();
IDownloadClient Get(int id);
}
@ -18,17 +19,23 @@ namespace NzbDrone.Core.Download
private readonly Logger _logger;
private readonly IDownloadClientFactory _downloadClientFactory;
private readonly IDownloadClientStatusService _downloadClientStatusService;
private readonly IIndexerFactory _indexerFactory;
private readonly ICached<int> _lastUsedDownloadClient;
public DownloadClientProvider(IDownloadClientStatusService downloadClientStatusService, IDownloadClientFactory downloadClientFactory, ICacheManager cacheManager, Logger logger)
public DownloadClientProvider(IDownloadClientStatusService downloadClientStatusService,
IDownloadClientFactory downloadClientFactory,
IIndexerFactory indexerFactory,
ICacheManager cacheManager,
Logger logger)
{
_logger = logger;
_downloadClientFactory = downloadClientFactory;
_downloadClientStatusService = downloadClientStatusService;
_indexerFactory = indexerFactory;
_lastUsedDownloadClient = cacheManager.GetCache<int>(GetType(), "lastDownloadClientId");
}
public IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol)
public IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol, int indexerId = 0)
{
var availableProviders = _downloadClientFactory.GetAvailableProviders().Where(v => v.Protocol == downloadProtocol).ToList();
@ -37,6 +44,23 @@ namespace NzbDrone.Core.Download
return null;
}
if (indexerId > 0)
{
var indexer = _indexerFactory.Find(indexerId);
if (indexer is { DownloadClientId: > 0 })
{
var client = availableProviders.SingleOrDefault(d => d.Definition.Id == indexer.DownloadClientId);
if (client == null)
{
throw new DownloadClientUnavailableException("Indexer specified download client is not available");
}
return client;
}
}
var blockedProviders = new HashSet<int>(_downloadClientStatusService.GetBlockedProviders().Select(v => v.ProviderId));
if (blockedProviders.Any())
@ -54,7 +78,7 @@ namespace NzbDrone.Core.Download
}
// Use the first priority clients first
availableProviders = availableProviders.GroupBy(v => (v.Definition as DownloadClientDefinition).Priority)
availableProviders = availableProviders.GroupBy(v => ((DownloadClientDefinition)v.Definition).Priority)
.OrderBy(v => v.Key)
.First().OrderBy(v => v.Definition.Id).ToList();

@ -16,7 +16,7 @@ namespace NzbDrone.Core.Download
{
public interface IDownloadService
{
Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect);
Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect, int? downloadClientId);
Task<byte[]> DownloadReport(string link, int indexerId, string source, string host, string title);
void RecordRedirect(string link, int indexerId, string source, string host, string title);
}
@ -48,10 +48,18 @@ namespace NzbDrone.Core.Download
_logger = logger;
}
public async Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect)
public async Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect, int? downloadClientId)
{
var downloadClient = downloadClientId.HasValue
? _downloadClientProvider.Get(downloadClientId.Value)
: _downloadClientProvider.GetDownloadClient(release.DownloadProtocol, release.IndexerId);
await SendReportToClient(release, source, host, redirect, downloadClient);
}
private async Task SendReportToClient(ReleaseInfo release, string source, string host, bool redirect, IDownloadClient downloadClient)
{
var downloadTitle = release.Title;
var downloadClient = _downloadClientProvider.GetDownloadClient(release.DownloadProtocol);
if (downloadClient == null)
{

@ -24,6 +24,7 @@ namespace NzbDrone.Core.Indexers
public IndexerCapabilities Capabilities { get; set; }
public int Priority { get; set; } = 25;
public bool Redirect { get; set; }
public int DownloadClientId { get; set; }
public DateTime Added { get; set; }
public int AppProfileId { get; set; }
public LazyLoaded<AppSyncProfile> AppProfile { get; set; }

@ -204,6 +204,7 @@
"IndexerCategories": "Indexer Categories",
"IndexerDetails": "Indexer Details",
"IndexerDisabled": "Indexer Disabled",
"IndexerDownloadClientHelpText": "Specify which download client is used for grabs made within Prowlarr from this indexer",
"IndexerFailureRate": "Indexer Failure Rate",
"IndexerFlags": "Indexer Flags",
"IndexerHealthCheckNoIndexers": "No indexers enabled, Prowlarr will not return search results",

@ -30,6 +30,7 @@ namespace Prowlarr.Api.V1.Indexers
public IndexerPrivacy Privacy { get; set; }
public IndexerCapabilityResource Capabilities { get; set; }
public int Priority { get; set; }
public int DownloadClientId { get; set; }
public DateTime Added { get; set; }
public IndexerStatusResource Status { get; set; }
public string SortName { get; set; }
@ -96,6 +97,7 @@ namespace Prowlarr.Api.V1.Indexers
resource.Protocol = definition.Protocol;
resource.Privacy = definition.Privacy;
resource.Priority = definition.Priority;
resource.DownloadClientId = definition.DownloadClientId;
resource.Added = definition.Added;
resource.SortName = definition.Name.NormalizeTitle();
@ -142,6 +144,7 @@ namespace Prowlarr.Api.V1.Indexers
definition.IndexerUrls = resource.IndexerUrls;
definition.Priority = resource.Priority;
definition.Privacy = resource.Privacy;
definition.DownloadClientId = resource.DownloadClientId;
definition.Added = resource.Added;
return definition;

@ -73,7 +73,7 @@ namespace Prowlarr.Api.V1.Search
try
{
_downloadService.SendReportToClient(releaseInfo, source, host, indexerDef.Redirect);
_downloadService.SendReportToClient(releaseInfo, source, host, indexerDef.Redirect, null).GetAwaiter().GetResult();
}
catch (ReleaseDownloadException ex)
{
@ -106,7 +106,7 @@ namespace Prowlarr.Api.V1.Search
try
{
_downloadService.SendReportToClient(releaseInfo, source, host, indexerDef.Redirect);
_downloadService.SendReportToClient(releaseInfo, source, host, indexerDef.Redirect, null).GetAwaiter().GetResult();
}
catch (ReleaseDownloadException ex)
{

Loading…
Cancel
Save