diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs index ad989d1e7..89af627de 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotato.cs @@ -1,32 +1,51 @@ -using System; +using System.Collections.Generic; using NLog; using NzbDrone.Core.Configuration; using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.ThingiProvider; namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato { public class TorrentPotato : TorrentIndexerBase { public override string Name => "TorrentPotato"; - public override string[] IndexerUrls => new[] { "http://127.0.0.1" }; + public override string[] IndexerUrls => new[] { "" }; public override string Description => "A JSON based torrent provider previously developed for CouchPotato"; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override TimeSpan RateLimit => TimeSpan.FromSeconds(2); + public override IndexerCapabilities Capabilities => SetCapabilities(); public TorrentPotato(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) : base(httpClient, eventAggregator, indexerStatusService, configService, logger) { } - private IndexerDefinition GetDefinition(string name, TorrentPotatoSettings settings) + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new TorrentPotatoRequestGenerator(Settings); + } + + public override IParseIndexerResponse GetParser() + { + return new TorrentPotatoParser(); + } + + public override IEnumerable DefaultDefinitions + { + get + { + yield return GetDefinition("TorrentPotato", "A JSON based torrent provider previously developed for CouchPotato", "http://127.0.0.1"); + } + } + + private IndexerDefinition GetDefinition(string name, string description, string baseUrl) { return new IndexerDefinition { Enable = true, Name = name, + Description = description, Implementation = GetType().Name, - Settings = settings, + Settings = new TorrentPotatoSettings { BaseUrl = baseUrl }, Protocol = DownloadProtocol.Torrent, SupportsRss = SupportsRss, SupportsSearch = SupportsSearch, @@ -35,14 +54,19 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato }; } - public override IIndexerRequestGenerator GetRequestGenerator() + private IndexerCapabilities SetCapabilities() { - return new TorrentPotatoRequestGenerator() { Settings = Settings }; - } + var caps = new IndexerCapabilities + { + MovieSearchParams = new List + { + MovieSearchParam.Q, MovieSearchParam.ImdbId + } + }; - public override IParseIndexerResponse GetParser() - { - return new TorrentPotatoParser(); + caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.Movies); + + return caps; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoParser.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoParser.cs index 4b8a03666..268c1e92b 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoParser.cs @@ -1,52 +1,62 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Text.RegularExpressions; using NzbDrone.Common.Http; using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato { public class TorrentPotatoParser : IParseIndexerResponse { - private static readonly Regex RegexGuid = new Regex(@"^magnet:\?xt=urn:btih:([a-f0-9]+)", RegexOptions.Compiled); + private static readonly Regex RegexGuid = new (@"^magnet:\?xt=urn:btih:([a-f0-9]+)", RegexOptions.Compiled); public IList ParseResponse(IndexerResponse indexerResponse) { - var results = new List(); + var releaseInfos = new List(); - switch (indexerResponse.HttpResponse.StatusCode) + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { - default: - if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) - { - throw new IndexerException(indexerResponse, "Indexer API call returned an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode); - } - - break; + throw new IndexerException(indexerResponse, "Indexer API call returned an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode); } var jsonResponse = new HttpResponse(indexerResponse.HttpResponse); + if (jsonResponse.Resource?.error != null) + { + throw new IndexerException(indexerResponse, "Indexer API call returned an error [{0}]", jsonResponse.Resource.error); + } + + if (jsonResponse.Resource?.results == null) + { + return releaseInfos; + } + foreach (var torrent in jsonResponse.Resource.results) { var torrentInfo = new TorrentInfo { Guid = GetGuid(torrent), Title = torrent.release_name, - Size = (long)torrent.size * 1000 * 1000, + Categories = new List { NewznabStandardCategory.Movies }, + Size = torrent.size * 1000 * 1000, DownloadUrl = torrent.download_url, InfoUrl = torrent.details_url, + ImdbId = ParseUtil.GetImdbId(torrent.imdb_id).GetValueOrDefault(), PublishDate = torrent.publish_date.ToUniversalTime(), Seeders = torrent.seeders, Peers = torrent.leechers + torrent.seeders }; - results.Add(torrentInfo); + releaseInfos.Add(torrentInfo); } - return results; + return releaseInfos + .OrderByDescending(o => o.PublishDate) + .ToArray(); } public Action, DateTime?> CookiesUpdater { get; set; } @@ -57,12 +67,10 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato if (match.Success) { - return string.Format("potato-{0}", match.Groups[1].Value); - } - else - { - return string.Format("potato-{0}", torrent.download_url); + return $"potato-{match.Groups[1].Value}"; } + + return $"potato-{torrent.download_url}"; } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs index 922bf5d43..465437b0f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoRequestGenerator.cs @@ -8,70 +8,19 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato { public class TorrentPotatoRequestGenerator : IIndexerRequestGenerator { - public TorrentPotatoSettings Settings { get; set; } + private readonly TorrentPotatoSettings _settings; - public virtual IndexerPageableRequestChain GetRecentRequests() + public TorrentPotatoRequestGenerator(TorrentPotatoSettings settings) { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests("list", null, null)); - - return pageableRequests; - } - - private IEnumerable GetPagedRequests(string mode, int? tvdbId, string query, params object[] args) - { - var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl) - .Accept(HttpAccept.Json); - - requestBuilder.AddQueryParam("passkey", Settings.Passkey); - if (!string.IsNullOrWhiteSpace(Settings.User)) - { - requestBuilder.AddQueryParam("user", Settings.User); - } - else - { - requestBuilder.AddQueryParam("user", ""); - } - - requestBuilder.AddQueryParam("search", "-"); - - yield return new IndexerRequest(requestBuilder.Build()); - } - - private IEnumerable GetMovieRequest(MovieSearchCriteria searchCriteria) - { - var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl) - .Accept(HttpAccept.Json); - - requestBuilder.AddQueryParam("passkey", Settings.Passkey); - - if (!string.IsNullOrWhiteSpace(Settings.User)) - { - requestBuilder.AddQueryParam("user", Settings.User); - } - else - { - requestBuilder.AddQueryParam("user", ""); - } - - if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace()) - { - requestBuilder.AddQueryParam("imdbid", searchCriteria.ImdbId); - } - else if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace()) - { - //TODO: Hack for now - requestBuilder.AddQueryParam("search", $"{searchCriteria.SearchTerm}"); - } - - yield return new IndexerRequest(requestBuilder.Build()); + _settings = settings; } public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) { var pageableRequests = new IndexerPageableRequestChain(); - pageableRequests.Add(GetMovieRequest(searchCriteria)); + + pageableRequests.Add(BuildRequest(searchCriteria.SearchTerm, searchCriteria.ImdbId)); + return pageableRequests; } @@ -92,7 +41,29 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) { - return new IndexerPageableRequestChain(); + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(BuildRequest(searchCriteria.SearchTerm)); + + return pageableRequests; + } + + private IEnumerable BuildRequest(string searchTerm, string imdbId = null) + { + var requestBuilder = new HttpRequestBuilder(_settings.BaseUrl) + .Accept(HttpAccept.Json); + + requestBuilder.AddQueryParam("passkey", _settings.Passkey); + requestBuilder.AddQueryParam("user", _settings.User.IsNotNullOrWhiteSpace() ? _settings.User : ""); + + if (imdbId.IsNotNullOrWhiteSpace()) + { + requestBuilder.AddQueryParam("imdbid", imdbId); + } + + requestBuilder.AddQueryParam("search", searchTerm.IsNotNullOrWhiteSpace() ? $"{searchTerm}" : " "); + + yield return new IndexerRequest(requestBuilder.Build()); } public Func> GetCookies { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoResponse.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoResponse.cs index b199157d8..c41dd148a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoResponse.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoResponse.cs @@ -4,8 +4,9 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato { public class TorrentPotatoResponse { - public Result[] results { get; set; } public int total_results { get; set; } + public Result[] results { get; set; } + public string error { get; set; } } public class Result @@ -14,9 +15,10 @@ namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato public string torrent_id { get; set; } public string details_url { get; set; } public string download_url { get; set; } + public string imdb_id { get; set; } public bool freeleech { get; set; } public string type { get; set; } - public int size { get; set; } + public long size { get; set; } public int leechers { get; set; } public int seeders { get; set; } public DateTime publish_date { get; set; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs index 7f7e7fb23..5c7d3808f 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentPotato/TorrentPotatoSettings.cs @@ -1,30 +1,42 @@ using FluentValidation; using NzbDrone.Core.Annotations; -using NzbDrone.Core.Indexers.Settings; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Indexers.Definitions.TorrentPotato { - public class TorrentPotatoSettingsValidator : NoAuthSettingsValidator + public class TorrentPotatoSettingsValidator : AbstractValidator { public TorrentPotatoSettingsValidator() { RuleFor(c => c.User).NotEmpty(); RuleFor(c => c.Passkey).NotEmpty(); + + RuleFor(c => c.BaseUrl).ValidRootUrl(); + RuleFor(c => c.BaseSettings).SetValidator(new IndexerCommonSettingsValidator()); + RuleFor(c => c.TorrentBaseSettings).SetValidator(new IndexerTorrentSettingsValidator()); } } - public class TorrentPotatoSettings : NoAuthTorrentBaseSettings + public class TorrentPotatoSettings : IIndexerSettings { private static readonly TorrentPotatoSettingsValidator Validator = new (); + [FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato API")] + public string BaseUrl { get; set; } + [FieldDefinition(2, Label = "Username", HelpText = "Indexer Username", Privacy = PrivacyLevel.UserName)] public string User { get; set; } - [FieldDefinition(3, Label = "Passkey", HelpText = "Indexer Password", Privacy = PrivacyLevel.Password, Type = FieldType.Password)] + [FieldDefinition(3, Label = "Passkey", HelpText = "Indexer Passkey", Privacy = PrivacyLevel.Password, Type = FieldType.Password)] public string Passkey { get; set; } - public override NzbDroneValidationResult Validate() + [FieldDefinition(20)] + public IndexerBaseSettings BaseSettings { get; set; } = new (); + + [FieldDefinition(21)] + public IndexerTorrentBaseSettings TorrentBaseSettings { get; set; } = new (); + + public virtual NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate(this)); }