|
|
|
@ -1,14 +1,13 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Specialized;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using FluentValidation;
|
|
|
|
|
using FluentValidation.Results;
|
|
|
|
|
using NLog;
|
|
|
|
|
using NzbDrone.Common.Extensions;
|
|
|
|
|
using NzbDrone.Common.Http;
|
|
|
|
|
using NzbDrone.Common.Serializer;
|
|
|
|
|
using NzbDrone.Core.Annotations;
|
|
|
|
|
using NzbDrone.Core.Configuration;
|
|
|
|
|
using NzbDrone.Core.Indexers.Exceptions;
|
|
|
|
@ -25,21 +24,25 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
public class Redacted : TorrentIndexerBase<RedactedSettings>
|
|
|
|
|
{
|
|
|
|
|
public override string Name => "Redacted";
|
|
|
|
|
public override string[] IndexerUrls => new string[] { "https://redacted.ch/" };
|
|
|
|
|
public override string[] IndexerUrls => new[] { "https://redacted.ch/" };
|
|
|
|
|
public override string Description => "REDActed (Aka.PassTheHeadPhones) is one of the most well-known music trackers.";
|
|
|
|
|
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
|
|
|
|
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
|
|
|
|
public override IndexerCapabilities Capabilities => SetCapabilities();
|
|
|
|
|
public override bool SupportsRedirect => true;
|
|
|
|
|
|
|
|
|
|
public Redacted(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
|
|
|
|
public Redacted(IIndexerHttpClient httpClient,
|
|
|
|
|
IEventAggregator eventAggregator,
|
|
|
|
|
IIndexerStatusService indexerStatusService,
|
|
|
|
|
IConfigService configService,
|
|
|
|
|
Logger logger)
|
|
|
|
|
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override IIndexerRequestGenerator GetRequestGenerator()
|
|
|
|
|
{
|
|
|
|
|
return new RedactedRequestGenerator() { Settings = Settings, Capabilities = Capabilities, HttpClient = _httpClient };
|
|
|
|
|
return new RedactedRequestGenerator(Settings, Capabilities);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override IParseIndexerResponse GetParser()
|
|
|
|
@ -51,22 +54,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
{
|
|
|
|
|
var caps = new IndexerCapabilities
|
|
|
|
|
{
|
|
|
|
|
TvSearchParams = new List<TvSearchParam>
|
|
|
|
|
{
|
|
|
|
|
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
|
|
|
|
},
|
|
|
|
|
MovieSearchParams = new List<MovieSearchParam>
|
|
|
|
|
{
|
|
|
|
|
MovieSearchParam.Q
|
|
|
|
|
},
|
|
|
|
|
MusicSearchParams = new List<MusicSearchParam>
|
|
|
|
|
{
|
|
|
|
|
MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album, MusicSearchParam.Year
|
|
|
|
|
},
|
|
|
|
|
BookSearchParams = new List<BookSearchParam>
|
|
|
|
|
{
|
|
|
|
|
BookSearchParam.Q
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
BookSearchParam.Q
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio, "Music");
|
|
|
|
@ -105,21 +100,39 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
|
|
|
|
|
public class RedactedRequestGenerator : IIndexerRequestGenerator
|
|
|
|
|
{
|
|
|
|
|
public RedactedSettings Settings { get; set; }
|
|
|
|
|
public IndexerCapabilities Capabilities { get; set; }
|
|
|
|
|
private readonly RedactedSettings _settings;
|
|
|
|
|
private readonly IndexerCapabilities _capabilities;
|
|
|
|
|
|
|
|
|
|
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
|
|
|
|
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
|
|
|
|
public IIndexerHttpClient HttpClient { get; set; }
|
|
|
|
|
|
|
|
|
|
public RedactedRequestGenerator()
|
|
|
|
|
public RedactedRequestGenerator(RedactedSettings settings, IndexerCapabilities capabilities)
|
|
|
|
|
{
|
|
|
|
|
_settings = settings;
|
|
|
|
|
_capabilities = capabilities;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
|
|
|
|
{
|
|
|
|
|
var pageableRequests = new IndexerPageableRequestChain();
|
|
|
|
|
var parameters = new NameValueCollection();
|
|
|
|
|
|
|
|
|
|
pageableRequests.Add(GetRequest(string.Format("&artistname={0}&groupname={1}", searchCriteria.Artist, searchCriteria.Album)));
|
|
|
|
|
if (searchCriteria.Artist.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
parameters.Add("artistname", searchCriteria.Artist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (searchCriteria.Album.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
parameters.Add("groupname", searchCriteria.Album);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (searchCriteria.Year.HasValue)
|
|
|
|
|
{
|
|
|
|
|
parameters.Add("year", searchCriteria.Year.ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
|
|
|
|
|
|
|
|
|
return pageableRequests;
|
|
|
|
|
}
|
|
|
|
@ -127,8 +140,9 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
|
|
|
|
{
|
|
|
|
|
var pageableRequests = new IndexerPageableRequestChain();
|
|
|
|
|
var parameters = new NameValueCollection();
|
|
|
|
|
|
|
|
|
|
pageableRequests.Add(GetRequest(searchCriteria.SanitizedSearchTerm));
|
|
|
|
|
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
|
|
|
|
|
|
|
|
|
return pageableRequests;
|
|
|
|
|
}
|
|
|
|
@ -146,26 +160,43 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
|
|
|
|
{
|
|
|
|
|
var pageableRequests = new IndexerPageableRequestChain();
|
|
|
|
|
var parameters = new NameValueCollection();
|
|
|
|
|
|
|
|
|
|
pageableRequests.Add(GetRequest(searchCriteria.SanitizedSearchTerm));
|
|
|
|
|
pageableRequests.Add(GetRequest(searchCriteria, parameters));
|
|
|
|
|
|
|
|
|
|
return pageableRequests;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
|
|
|
|
private IEnumerable<IndexerRequest> GetRequest(SearchCriteriaBase searchCriteria, NameValueCollection parameters)
|
|
|
|
|
{
|
|
|
|
|
var req = RequestBuilder()
|
|
|
|
|
.Resource($"ajax.php?action=browse&searchstr={searchParameters}")
|
|
|
|
|
var term = searchCriteria.SanitizedSearchTerm.Trim();
|
|
|
|
|
|
|
|
|
|
parameters.Add("action", "browse");
|
|
|
|
|
parameters.Add("order_by", "time");
|
|
|
|
|
parameters.Add("order_way", "desc");
|
|
|
|
|
parameters.Add("searchstr", term);
|
|
|
|
|
|
|
|
|
|
var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories);
|
|
|
|
|
if (queryCats.Any())
|
|
|
|
|
{
|
|
|
|
|
foreach (var cat in queryCats)
|
|
|
|
|
{
|
|
|
|
|
parameters.Add($"filter_cat[{cat}]", "1");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var request = RequestBuilder()
|
|
|
|
|
.Resource($"/ajax.php?{parameters.GetQueryString()}")
|
|
|
|
|
.Build();
|
|
|
|
|
|
|
|
|
|
yield return new IndexerRequest(req);
|
|
|
|
|
yield return new IndexerRequest(request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private HttpRequestBuilder RequestBuilder()
|
|
|
|
|
{
|
|
|
|
|
return new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
|
|
|
|
|
return new HttpRequestBuilder($"{_settings.BaseUrl.TrimEnd('/')}")
|
|
|
|
|
.Accept(HttpAccept.Json)
|
|
|
|
|
.SetHeader("Authorization", Settings.Apikey);
|
|
|
|
|
.SetHeader("Authorization", _settings.Apikey);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -210,24 +241,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
foreach (var torrent in result.Torrents)
|
|
|
|
|
{
|
|
|
|
|
var id = torrent.TorrentId;
|
|
|
|
|
var artist = WebUtility.HtmlDecode(result.Artist);
|
|
|
|
|
var album = WebUtility.HtmlDecode(result.GroupName);
|
|
|
|
|
|
|
|
|
|
var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
|
|
|
|
|
if (torrent.HasCue)
|
|
|
|
|
{
|
|
|
|
|
title += " [Cue]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var title = GetTitle(result, torrent);
|
|
|
|
|
var infoUrl = GetInfoUrl(result.GroupId, id);
|
|
|
|
|
|
|
|
|
|
GazelleInfo release = new GazelleInfo()
|
|
|
|
|
var release = new GazelleInfo
|
|
|
|
|
{
|
|
|
|
|
Guid = infoUrl,
|
|
|
|
|
|
|
|
|
|
// Splice Title from info to avoid calling API again for every torrent.
|
|
|
|
|
Title = WebUtility.HtmlDecode(title),
|
|
|
|
|
|
|
|
|
|
Container = torrent.Encoding,
|
|
|
|
|
Codec = torrent.Format,
|
|
|
|
|
Size = long.Parse(torrent.Size),
|
|
|
|
@ -264,7 +285,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
var id = result.TorrentId;
|
|
|
|
|
var infoUrl = GetInfoUrl(result.GroupId, id);
|
|
|
|
|
|
|
|
|
|
GazelleInfo release = new GazelleInfo()
|
|
|
|
|
var release = new GazelleInfo
|
|
|
|
|
{
|
|
|
|
|
Guid = infoUrl,
|
|
|
|
|
Title = WebUtility.HtmlDecode(result.GroupName),
|
|
|
|
@ -302,6 +323,30 @@ namespace NzbDrone.Core.Indexers.Definitions
|
|
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetTitle(GazelleRelease result, GazelleTorrent torrent)
|
|
|
|
|
{
|
|
|
|
|
var title = $"{result.Artist} - {result.GroupName} [{result.GroupYear}]";
|
|
|
|
|
|
|
|
|
|
if (result.ReleaseType.IsNotNullOrWhiteSpace() && result.ReleaseType != "Unknown")
|
|
|
|
|
{
|
|
|
|
|
title += " [" + result.ReleaseType + "]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (torrent.RemasterTitle.IsNotNullOrWhiteSpace())
|
|
|
|
|
{
|
|
|
|
|
title += $" [{$"{torrent.RemasterTitle} {torrent.RemasterYear}".Trim()}]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
title += $" [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
|
|
|
|
|
|
|
|
|
|
if (torrent.HasCue)
|
|
|
|
|
{
|
|
|
|
|
title += " [Cue]";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return title;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetDownloadUrl(int torrentId, bool canUseToken)
|
|
|
|
|
{
|
|
|
|
|
// AuthKey is required but not checked, just pass in a dummy variable
|
|
|
|
|