New: Move newznab endpoint to /{id}/api

pull/49/head
ta264 3 years ago committed by Qstick
parent 283d273802
commit 76c3283c05

@ -59,7 +59,7 @@ function IndexerInfoModalContent(props) {
<DescriptionListItemTitle>{protocol === 'usenet' ? 'Newznab' : 'Torznab'} Url</DescriptionListItemTitle> <DescriptionListItemTitle>{protocol === 'usenet' ? 'Newznab' : 'Torznab'} Url</DescriptionListItemTitle>
<DescriptionListItemDescription> <DescriptionListItemDescription>
{`${window.Prowlarr.apiRoot}/indexer/${id}/newznab`} {`${window.Prowlarr.apiRoot}/${id}/api`}
</DescriptionListItemDescription> </DescriptionListItemDescription>
</DescriptionList> </DescriptionList>

@ -14,7 +14,7 @@ namespace NzbDrone.Core.Applications
protected readonly IAppIndexerMapService _appIndexerMapService; protected readonly IAppIndexerMapService _appIndexerMapService;
protected readonly Logger _logger; protected readonly Logger _logger;
protected static readonly Regex AppIndexerRegex = new Regex(@"api\/v\d*\/indexer\/(?<indexer>\d*)", protected static readonly Regex AppIndexerRegex = new Regex(@"(?<indexer>\d*)/api",
RegexOptions.IgnoreCase | RegexOptions.Compiled); RegexOptions.IgnoreCase | RegexOptions.Compiled);
public abstract string Name { get; } public abstract string Name { get; }

@ -139,8 +139,8 @@ namespace NzbDrone.Core.Applications.Lidarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); lidarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

@ -139,8 +139,8 @@ namespace NzbDrone.Core.Applications.Radarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; radarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); radarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

@ -139,8 +139,8 @@ namespace NzbDrone.Core.Applications.Readarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; readarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); readarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

@ -139,8 +139,8 @@ namespace NzbDrone.Core.Applications.Sonarr
Fields = schema.Fields, Fields = schema.Fields,
}; };
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/api/v1/indexer/{indexer.Id}/"; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "baseUrl").Value = $"{Settings.ProwlarrUrl}/{indexer.Id}/";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/newznab"; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiPath").Value = "/api";
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey; sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "apiKey").Value = _configFileProvider.ApiKey;
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "categories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));
sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray())); sonarrIndexer.Fields.FirstOrDefault(x => x.Name == "animeCategories").Value = JArray.FromObject(indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()));

@ -37,7 +37,7 @@ namespace NzbDrone.Core.Indexers
var encryptedLink = _protectionService.Protect(link.ToString()); var encryptedLink = _protectionService.Protect(link.ToString());
var encodedLink = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(encryptedLink)); var encodedLink = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(encryptedLink));
var urlEncodedFile = WebUtility.UrlEncode(file); var urlEncodedFile = WebUtility.UrlEncode(file);
var proxyLink = $"{serverUrl}{urlBase}/api/v1/indexer/{indexerId}/download?apikey={_configFileProvider.ApiKey}&link={encodedLink}&file={urlEncodedFile}"; var proxyLink = $"{serverUrl}{urlBase}/{indexerId}/download?apikey={_configFileProvider.ApiKey}&link={encodedLink}&file={urlEncodedFile}";
return new Uri(proxyLink); return new Uri(proxyLink);
} }

@ -1,35 +1,14 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Parser;
using Prowlarr.Http; using Prowlarr.Http;
using Prowlarr.Http.Extensions;
using Prowlarr.Http.REST;
namespace Prowlarr.Api.V1.Indexers namespace Prowlarr.Api.V1.Indexers
{ {
[V1ApiController] [V1ApiController]
public class IndexerController : ProviderControllerBase<IndexerResource, IIndexer, IndexerDefinition> public class IndexerController : ProviderControllerBase<IndexerResource, IIndexer, IndexerDefinition>
{ {
private IIndexerFactory _indexerFactory { get; set; } public IndexerController(IndexerFactory indexerFactory, IndexerResourceMapper resourceMapper)
private ISearchForNzb _nzbSearchService { get; set; }
private IDownloadMappingService _downloadMappingService { get; set; }
private IDownloadService _downloadService { get; set; }
public IndexerController(IndexerFactory indexerFactory, ISearchForNzb nzbSearchService, IDownloadMappingService downloadMappingService, IDownloadService downloadService, IndexerResourceMapper resourceMapper)
: base(indexerFactory, "indexer", resourceMapper) : base(indexerFactory, "indexer", resourceMapper)
{ {
_indexerFactory = indexerFactory;
_nzbSearchService = nzbSearchService;
_downloadMappingService = downloadMappingService;
_downloadService = downloadService;
} }
protected override void Validate(IndexerDefinition definition, bool includeWarnings) protected override void Validate(IndexerDefinition definition, bool includeWarnings)
@ -41,102 +20,5 @@ namespace Prowlarr.Api.V1.Indexers
base.Validate(definition, includeWarnings); base.Validate(definition, includeWarnings);
} }
[HttpGet("{id:int}/newznab")]
public async Task<IActionResult> GetNewznabResponse(int id, [FromQuery] NewznabRequest request)
{
var requestType = request.t;
request.source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
request.server = Request.GetServerUrl();
if (requestType.IsNullOrWhiteSpace())
{
throw new BadRequestException("Missing Function Parameter");
}
var indexer = _indexerFactory.Get(id);
if (indexer == null)
{
throw new NotFoundException("Indexer Not Found");
}
var indexerInstance = _indexerFactory.GetInstance(indexer);
switch (requestType)
{
case "caps":
var caps = indexerInstance.GetCapabilities();
return Content(caps.ToXml(), "application/rss+xml");
case "search":
case "tvsearch":
case "music":
case "book":
case "movie":
var results = await _nzbSearchService.Search(request, new List<int> { indexer.Id }, false);
foreach (var result in results.Releases)
{
result.DownloadUrl = _downloadMappingService.ConvertToProxyLink(new Uri(result.DownloadUrl), request.server, indexer.Id, result.Title).ToString();
}
return Content(results.ToXml(indexerInstance.Protocol), "application/rss+xml");
default:
throw new BadRequestException("Function Not Available");
}
}
[HttpGet("{id:int}/download")]
public async Task<object> GetDownload(int id, string link, string file)
{
var indexerDef = _indexerFactory.Get(id);
var indexer = _indexerFactory.GetInstance(indexerDef);
if (link.IsNullOrWhiteSpace() || file.IsNullOrWhiteSpace())
{
throw new BadRequestException("Invalid Prowlarr link");
}
file = WebUtility.UrlDecode(file);
if (indexer == null)
{
throw new NotFoundException("Indexer Not Found");
}
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
var unprotectedlLink = _downloadMappingService.ConvertToNormalLink(link);
// If Indexer is set to download via Redirect then just redirect to the link
if (indexer.SupportsRedirect && indexerDef.Redirect)
{
_downloadService.RecordRedirect(unprotectedlLink, id, source, file);
return RedirectPermanent(unprotectedlLink);
}
var downloadBytes = Array.Empty<byte>();
downloadBytes = await _downloadService.DownloadReport(unprotectedlLink, id, source, file);
// handle magnet URLs
if (downloadBytes.Length >= 7
&& downloadBytes[0] == 0x6d
&& downloadBytes[1] == 0x61
&& downloadBytes[2] == 0x67
&& downloadBytes[3] == 0x6e
&& downloadBytes[4] == 0x65
&& downloadBytes[5] == 0x74
&& downloadBytes[6] == 0x3a)
{
var magnetUrl = Encoding.UTF8.GetString(downloadBytes);
return RedirectPermanent(magnetUrl);
}
var contentType = indexer.Protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb";
var extension = indexer.Protocol == DownloadProtocol.Torrent ? "torrent" : "nzb";
var filename = $"{file}.{extension}";
return File(downloadBytes, contentType, filename);
}
} }
} }

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Parser;
using Prowlarr.Http.Extensions;
using Prowlarr.Http.REST;
namespace NzbDrone.Api.V1.Indexers
{
[Route("")]
[EnableCors("ApiCorsPolicy")]
[ApiController]
public class NewznabController : Controller
{
private IIndexerFactory _indexerFactory { get; set; }
private ISearchForNzb _nzbSearchService { get; set; }
private IDownloadMappingService _downloadMappingService { get; set; }
private IDownloadService _downloadService { get; set; }
public NewznabController(IndexerFactory indexerFactory,
ISearchForNzb nzbSearchService,
IDownloadMappingService downloadMappingService,
IDownloadService downloadService)
{
_indexerFactory = indexerFactory;
_nzbSearchService = nzbSearchService;
_downloadMappingService = downloadMappingService;
_downloadService = downloadService;
}
[HttpGet("{id:int}/api")]
public async Task<IActionResult> GetNewznabResponse(int id, [FromQuery] NewznabRequest request)
{
var requestType = request.t;
request.source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
request.server = Request.GetServerUrl();
if (requestType.IsNullOrWhiteSpace())
{
throw new BadRequestException("Missing Function Parameter");
}
var indexer = _indexerFactory.Get(id);
if (indexer == null)
{
throw new NotFoundException("Indexer Not Found");
}
var indexerInstance = _indexerFactory.GetInstance(indexer);
switch (requestType)
{
case "caps":
var caps = indexerInstance.GetCapabilities();
return Content(caps.ToXml(), "application/rss+xml");
case "search":
case "tvsearch":
case "music":
case "book":
case "movie":
var results = await _nzbSearchService.Search(request, new List<int> { indexer.Id }, false);
foreach (var result in results.Releases)
{
result.DownloadUrl = _downloadMappingService.ConvertToProxyLink(new Uri(result.DownloadUrl), request.server, indexer.Id, result.Title).ToString();
}
return Content(results.ToXml(indexerInstance.Protocol), "application/rss+xml");
default:
throw new BadRequestException("Function Not Available");
}
}
[HttpGet("{id:int}/download")]
public async Task<object> GetDownload(int id, string link, string file)
{
var indexerDef = _indexerFactory.Get(id);
var indexer = _indexerFactory.GetInstance(indexerDef);
if (link.IsNullOrWhiteSpace() || file.IsNullOrWhiteSpace())
{
throw new BadRequestException("Invalid Prowlarr link");
}
file = WebUtility.UrlDecode(file);
if (indexer == null)
{
throw new NotFoundException("Indexer Not Found");
}
var source = UserAgentParser.ParseSource(Request.Headers["User-Agent"]);
var unprotectedlLink = _downloadMappingService.ConvertToNormalLink(link);
// If Indexer is set to download via Redirect then just redirect to the link
if (indexer.SupportsRedirect && indexerDef.Redirect)
{
_downloadService.RecordRedirect(unprotectedlLink, id, source, file);
return RedirectPermanent(unprotectedlLink);
}
var downloadBytes = Array.Empty<byte>();
downloadBytes = await _downloadService.DownloadReport(unprotectedlLink, id, source, file);
// handle magnet URLs
if (downloadBytes.Length >= 7
&& downloadBytes[0] == 0x6d
&& downloadBytes[1] == 0x61
&& downloadBytes[2] == 0x67
&& downloadBytes[3] == 0x6e
&& downloadBytes[4] == 0x65
&& downloadBytes[5] == 0x74
&& downloadBytes[6] == 0x3a)
{
var magnetUrl = Encoding.UTF8.GetString(downloadBytes);
return RedirectPermanent(magnetUrl);
}
var contentType = indexer.Protocol == DownloadProtocol.Torrent ? "application/x-bittorrent" : "application/x-nzb";
var extension = indexer.Protocol == DownloadProtocol.Torrent ? "torrent" : "nzb";
var filename = $"{file}.{extension}";
return File(downloadBytes, contentType, filename);
}
}
}
Loading…
Cancel
Save