diff --git a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs index e00708195..795aeb566 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Redacted.cs @@ -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 { 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.Q, TvSearchParam.Season, TvSearchParam.Ep - }, - MovieSearchParams = new List - { - MovieSearchParam.Q - }, MusicSearchParams = new List - { - MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year - }, + { + MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album, MusicSearchParam.Year + }, BookSearchParams = new List - { - 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> GetCookies { get; set; } public Action, 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 GetRequest(string searchParameters) + private IEnumerable 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