New: Support Indexer Grab Redirects

pull/18/head
Qstick 3 years ago
parent a676eaeda5
commit df8ef83e40

1
.gitignore vendored

@ -12,6 +12,7 @@ src/**/[Oo]bj/
*.sln.docstates
.vs/
.vscode/
.idea/
# Build results
*_i.c

@ -63,6 +63,7 @@ class IndexerIndexRow extends Component {
name,
baseUrl,
enable,
redirect,
tags,
protocol,
privacy,
@ -114,6 +115,7 @@ class IndexerIndexRow extends Component {
key={column.name}
className={styles[column.name]}
enabled={enable}
redirect={redirect}
status={status}
longDateFormat={longDateFormat}
timeFormat={timeFormat}
@ -258,6 +260,7 @@ IndexerIndexRow.propTypes = {
priority: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
enable: PropTypes.bool.isRequired,
redirect: PropTypes.bool.isRequired,
status: PropTypes.object,
capabilities: PropTypes.object.isRequired,
added: PropTypes.string.isRequired,

@ -10,6 +10,7 @@ function IndexerStatusCell(props) {
const {
className,
enabled,
redirect,
status,
longDateFormat,
timeFormat,
@ -17,6 +18,9 @@ function IndexerStatusCell(props) {
...otherProps
} = props;
const enableKind = redirect ? kinds.WARNING : kinds.SUCCESS;
const enableTitle = redirect ? 'Indexer is Enabled, Redirect is Enabled' : 'Indexer is Enabled';
return (
<Component
className={className}
@ -25,9 +29,9 @@ function IndexerStatusCell(props) {
{
<Icon
className={styles.statusIcon}
kind={enabled ? kinds.SUCCESS : kinds.DEFAULT}
kind={enabled ? enableKind : kinds.DEFAULT}
name={enabled ? icons.CHECK : icons.BLACKLIST}
title={enabled ? 'Indexer is Enabled' : 'Indexer is Disabled'}
title={enabled ? enableTitle : 'Indexer is Disabled'}
/>
}
{
@ -46,6 +50,7 @@ function IndexerStatusCell(props) {
IndexerStatusCell.propTypes = {
className: PropTypes.string.isRequired,
enabled: PropTypes.bool.isRequired,
redirect: PropTypes.bool.isRequired,
status: PropTypes.object,
longDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,

@ -39,7 +39,9 @@ function EditIndexerModalContent(props) {
implementationName,
name,
enable,
redirect,
supportsRss,
supportsRedirect,
fields,
priority
} = item;
@ -90,6 +92,19 @@ function EditIndexerModalContent(props) {
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('Redirect')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="redirect"
helpText={'Redirect incoming download requests for indexer instead of Proxying using Prowlarr'}
isDisabled={!supportsRedirect.value}
{...redirect}
onChange={onInputChange}
/>
</FormGroup>
{
fields.map((field) => {
return (

@ -0,0 +1,30 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(3)]
public class IndexerProps : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Indexers")
.AddColumn("Redirect").AsBoolean().NotNullable().WithDefaultValue(false);
Create.TableForModel("DownloadClients")
.WithColumn("Enable").AsBoolean().NotNullable()
.WithColumn("Name").AsString().NotNullable()
.WithColumn("Implementation").AsString().NotNullable()
.WithColumn("Settings").AsString().NotNullable()
.WithColumn("ConfigContract").AsString().NotNullable()
.WithColumn("Priority").AsInt32().WithDefaultValue(1);
Create.TableForModel("DownloadClientStatus")
.WithColumn("ProviderId").AsInt32().NotNullable().Unique()
.WithColumn("InitialFailure").AsDateTime().Nullable()
.WithColumn("MostRecentFailure").AsDateTime().Nullable()
.WithColumn("EscalationLevel").AsInt32().NotNullable()
.WithColumn("DisabledTill").AsDateTime().Nullable();
}
}
}

@ -45,6 +45,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(i => i.Privacy)
.Ignore(i => i.SupportsRss)
.Ignore(i => i.SupportsSearch)
.Ignore(i => i.SupportsRedirect)
.Ignore(i => i.Capabilities)
.Ignore(d => d.Tags);

@ -112,6 +112,8 @@ namespace NzbDrone.Core.History
history.Data.Add("Successful", message.Successful.ToString());
history.Data.Add("Source", message.Source ?? string.Empty);
history.Data.Add("GrabMethod", message.Redirect ? "Proxy" : "Redirect");
history.Data.Add("Title", message.Title);
_historyRepository.Insert(history);
}

@ -78,6 +78,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
Privacy = definition.Type == "private" ? IndexerPrivacy.Private : IndexerPrivacy.Public,
SupportsRss = SupportsRss,
SupportsSearch = SupportsSearch,
SupportsRedirect = SupportsRedirect,
Capabilities = new IndexerCapabilities(),
ExtraFields = settings
};

@ -223,7 +223,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (setting.Type != "password")
{
_logger.Debug($"{name} got value {value.ToJson()}");
_logger.Trace($"{name} got value {value.ToJson()}");
}
if (setting.Type == "text" || setting.Type == "password")
@ -236,7 +236,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
else if (setting.Type == "select")
{
_logger.Debug($"Setting options: {setting.Options.ToJson()}");
_logger.Trace($"Setting options: {setting.Options.ToJson()}");
var sorted = setting.Options.OrderBy(x => x.Key).ToList();
var selected = sorted[(int)(long)value];

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Indexers.Newznab
public override string Name => "Newznab";
public override string BaseUrl => GetBaseUrlFromSettings();
public override bool FollowRedirect => true;
public override bool SupportsRedirect => true;
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@ -113,6 +114,7 @@ namespace NzbDrone.Core.Indexers.Newznab
Privacy = IndexerPrivacy.Private,
SupportsRss = SupportsRss,
SupportsSearch = SupportsSearch,
SupportsRedirect = SupportsRedirect,
Capabilities = Capabilities
};
}

@ -29,7 +29,8 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
Settings = settings,
Protocol = DownloadProtocol.Torrent,
SupportsRss = SupportsRss,
SupportsSearch = SupportsSearch
SupportsSearch = SupportsSearch,
SupportsRedirect = SupportsRedirect
};
}

@ -64,6 +64,7 @@ namespace NzbDrone.Core.Indexers.Torznab
Protocol = DownloadProtocol.Usenet,
SupportsRss = SupportsRss,
SupportsSearch = SupportsSearch,
SupportsRedirect = SupportsRedirect,
Capabilities = new IndexerCapabilities()
};
}

@ -12,7 +12,8 @@ namespace NzbDrone.Core.Indexers
{
public interface IDownloadService
{
byte[] DownloadReport(string link, int indexerId, string source);
byte[] DownloadReport(string link, int indexerId, string source, string title);
void RecordRedirect(string link, int indexerId, string source, string title);
}
public class DownloadService : IDownloadService
@ -36,7 +37,7 @@ namespace NzbDrone.Core.Indexers
_logger = logger;
}
public byte[] DownloadReport(string link, int indexerId, string source)
public byte[] DownloadReport(string link, int indexerId, string source, string title)
{
var url = new HttpUri(link);
@ -59,7 +60,7 @@ namespace NzbDrone.Core.Indexers
catch (ReleaseUnavailableException)
{
_logger.Trace("Release {0} no longer available on indexer.", link);
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source));
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, title));
throw;
}
catch (ReleaseDownloadException ex)
@ -74,12 +75,17 @@ namespace NzbDrone.Core.Indexers
_indexerStatusService.RecordFailure(indexerId);
}
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source));
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, title));
throw;
}
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source));
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, success, source, title));
return downloadedBytes;
}
public void RecordRedirect(string link, int indexerId, string source, string title)
{
_eventAggregator.PublishEvent(new IndexerDownloadEvent(indexerId, true, source, title, true));
}
}
}

@ -7,12 +7,16 @@ namespace NzbDrone.Core.Indexers.Events
public int IndexerId { get; set; }
public bool Successful { get; set; }
public string Source { get; set; }
public string Title { get; set; }
public bool Redirect { get; set; }
public IndexerDownloadEvent(int indexerId, bool successful, string source)
public IndexerDownloadEvent(int indexerId, bool successful, string source, string title, bool redirect = false)
{
IndexerId = indexerId;
Successful = successful;
Source = source;
Title = title;
Redirect = redirect;
}
}
}

@ -26,6 +26,7 @@ namespace NzbDrone.Core.Indexers
public override bool SupportsRss => true;
public override bool SupportsSearch => true;
public override bool SupportsRedirect => false;
public override bool FollowRedirect => false;
public override IndexerCapabilities Capabilities { get; protected set; }

@ -8,6 +8,7 @@ namespace NzbDrone.Core.Indexers
{
bool SupportsRss { get; }
bool SupportsSearch { get; }
bool SupportsRedirect { get; }
IndexerCapabilities Capabilities { get; }
string BaseUrl { get; }

@ -25,9 +25,11 @@ namespace NzbDrone.Core.Indexers
public abstract DownloadProtocol Protocol { get; }
public abstract IndexerPrivacy Privacy { get; }
public int Priority { get; set; }
public bool Redirect { get; set; }
public abstract bool SupportsRss { get; }
public abstract bool SupportsSearch { get; }
public abstract bool SupportsRedirect { get; }
public abstract IndexerCapabilities Capabilities { get; protected set; }
public IndexerBase(IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)

@ -12,8 +12,10 @@ namespace NzbDrone.Core.Indexers
public IndexerPrivacy Privacy { get; set; }
public bool SupportsRss { get; set; }
public bool SupportsSearch { get; set; }
public bool SupportsRedirect { get; set; }
public IndexerCapabilities Capabilities { get; set; }
public int Priority { get; set; } = 25;
public bool Redirect { get; set; }
public DateTime Added { get; set; }
public IndexerStatus Status { get; set; }

@ -156,6 +156,7 @@ namespace NzbDrone.Core.Indexers
definition.Protocol = provider.Protocol;
definition.SupportsRss = provider.SupportsRss;
definition.SupportsSearch = provider.SupportsSearch;
definition.SupportsRedirect = provider.SupportsRedirect;
//We want to use the definition Caps and Privacy for Cardigann instead of the provider.
if (definition.Implementation != typeof(Cardigann.Cardigann).Name)

@ -4,6 +4,7 @@ using System.Net;
using System.Text;
using Nancy;
using Nancy.ModelBinding;
using Nancy.Responses;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch;
@ -107,17 +108,26 @@ namespace Prowlarr.Api.V1.Indexers
throw new BadRequestException("Invalid Prowlarr link");
}
file = WebUtility.UrlDecode(file);
if (indexer == null)
{
throw new NotFoundException("Indexer Not Found");
}
var indexerInstance = _indexerFactory.GetInstance(indexer);
var source = UserAgentParser.ParseSource(Request.Headers.UserAgent);
var unprotectedlLink = _downloadMappingService.ConvertToNormalLink((string)link.Value);
// If Indexer is set to download via Redirect then just redirect to the link
if (indexer.SupportsRedirect && indexer.Redirect)
{
_downloadService.RecordRedirect(unprotectedlLink, id, source, file);
return Response.AsRedirect(unprotectedlLink, RedirectResponse.RedirectType.Temporary);
}
var downloadBytes = Array.Empty<byte>();
downloadBytes = _downloadService.DownloadReport(_downloadMappingService.ConvertToNormalLink(link), id, source);
downloadBytes = _downloadService.DownloadReport(unprotectedlLink, id, source, file);
// handle magnet URLs
if (downloadBytes.Length >= 7
@ -130,7 +140,7 @@ namespace Prowlarr.Api.V1.Indexers
&& downloadBytes[6] == 0x3a)
{
var magnetUrl = Encoding.UTF8.GetString(downloadBytes);
return Response.AsRedirect(magnetUrl);
return Response.AsRedirect(magnetUrl, RedirectResponse.RedirectType.Temporary);
}
var contentType = indexer.Protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb";

@ -14,8 +14,10 @@ namespace Prowlarr.Api.V1.Indexers
{
public string BaseUrl { get; set; }
public bool Enable { get; set; }
public bool Redirect { get; set; }
public bool SupportsRss { get; set; }
public bool SupportsSearch { get; set; }
public bool SupportsRedirect { get; set; }
public DownloadProtocol Protocol { get; set; }
public IndexerPrivacy Privacy { get; set; }
public IndexerCapabilityResource Capabilities { get; set; }
@ -61,8 +63,10 @@ namespace Prowlarr.Api.V1.Indexers
resource.BaseUrl = definition.BaseUrl;
resource.Enable = definition.Enable;
resource.Redirect = definition.Redirect;
resource.SupportsRss = definition.SupportsRss;
resource.SupportsSearch = definition.SupportsSearch;
resource.SupportsRedirect = definition.SupportsRedirect;
resource.Capabilities = definition.Capabilities.ToResource();
resource.Protocol = definition.Protocol;
resource.Privacy = definition.Privacy;
@ -100,6 +104,7 @@ namespace Prowlarr.Api.V1.Indexers
}
definition.Enable = resource.Enable;
definition.Redirect = resource.Redirect;
definition.BaseUrl = resource.BaseUrl;
definition.Priority = resource.Priority;
definition.Privacy = resource.Privacy;

Loading…
Cancel
Save