Cleanup Search UI, Newznab Caps API

This commit is contained in:
Qstick
2020-10-20 13:46:58 -04:00
parent f290afa68c
commit 84cbfe870f
105 changed files with 562 additions and 791 deletions
@@ -0,0 +1,30 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.Indexers.HDBits
{
public class HDBits : HttpIndexerBase<HDBitsSettings>
{
public override string Name => "HDBits";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override int PageSize => 30;
public HDBits(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new HDBitsRequestGenerator() { Settings = Settings };
}
public override IParseIndexerResponse GetParser()
{
return new HDBitsParser(Settings);
}
}
}
@@ -0,0 +1,132 @@
using System;
using Newtonsoft.Json;
namespace NzbDrone.Core.Indexers.HDBits
{
public class TorrentQuery
{
[JsonProperty(Required = Required.Always)]
public string Username { get; set; }
[JsonProperty(Required = Required.Always)]
public string Passkey { get; set; }
public string Hash { get; set; }
public string Search { get; set; }
public int[] Category { get; set; }
public int[] Codec { get; set; }
public int[] Medium { get; set; }
public int? Origin { get; set; }
[JsonProperty(PropertyName = "imdb")]
public ImdbInfo ImdbInfo { get; set; }
[JsonProperty(PropertyName = "tvdb")]
public TvdbInfo TvdbInfo { get; set; }
[JsonProperty(PropertyName = "file_in_torrent")]
public string FileInTorrent { get; set; }
[JsonProperty(PropertyName = "snatched_only")]
public bool? SnatchedOnly { get; set; }
public int? Limit { get; set; }
public int? Page { get; set; }
public TorrentQuery Clone()
{
return MemberwiseClone() as TorrentQuery;
}
}
public class HDBitsResponse
{
[JsonProperty(Required = Required.Always)]
public StatusCode Status { get; set; }
public string Message { get; set; }
public object Data { get; set; }
}
public class TorrentQueryResponse
{
public string Id { get; set; }
public string Hash { get; set; }
public int Leechers { get; set; }
public int Seeders { get; set; }
public string Name { get; set; }
[JsonProperty(PropertyName = "times_completed")]
public uint TimesCompleted { get; set; }
public long Size { get; set; }
[JsonProperty(PropertyName = "utadded")]
public long UtAdded { get; set; }
public DateTime Added { get; set; }
public uint Comments { get; set; }
[JsonProperty(PropertyName = "numfiles")]
public uint NumFiles { get; set; }
[JsonProperty(PropertyName = "filename")]
public string FileName { get; set; }
[JsonProperty(PropertyName = "freeleech")]
public string FreeLeech { get; set; }
[JsonProperty(PropertyName = "type_category")]
public int TypeCategory { get; set; }
[JsonProperty(PropertyName = "type_codec")]
public int TypeCodec { get; set; }
[JsonProperty(PropertyName = "type_medium")]
public int TypeMedium { get; set; }
[JsonProperty(PropertyName = "type_origin")]
public int TypeOrigin { get; set; }
[JsonProperty(PropertyName = "imdb")]
public ImdbInfo ImdbInfo { get; set; }
[JsonProperty(PropertyName = "tvdb")]
public TvdbInfo TvdbInfo { get; set; }
}
public class ImdbInfo
{
public int Id { get; set; }
public string EnglishTitle { get; set; }
public string OriginalTitle { get; set; }
public int? Year { get; set; }
public string[] Genres { get; set; }
public float? Rating { get; set; }
}
public class TvdbInfo
{
public int? Id { get; set; }
public int? Season { get; set; }
public int? Episode { get; set; }
}
public enum StatusCode
{
Success = 0,
Failure = 1,
SslRequired = 2,
JsonMalformed = 3,
AuthDataMissing = 4,
AuthFailed = 5,
MissingRequiredParameters = 6,
InvalidParameter = 7,
ImdbImportFail = 8,
ImdbTvNotAllowed = 9
}
}
@@ -0,0 +1,9 @@
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.HDBits
{
public class HDBitsInfo : TorrentInfo
{
public bool? Internal { get; set; }
}
}
@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.HDBits
{
public class HDBitsParser : IParseIndexerResponse
{
private readonly HDBitsSettings _settings;
public HDBitsParser(HDBitsSettings settings)
{
_settings = settings;
}
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
throw new IndexerException(indexerResponse,
"Unexpected response status {0} code from API request",
indexerResponse.HttpResponse.StatusCode);
}
var jsonResponse = JsonConvert.DeserializeObject<HDBitsResponse>(indexerResponse.Content);
if (jsonResponse.Status != StatusCode.Success)
{
throw new IndexerException(indexerResponse,
"HDBits API request returned status code {0}: {1}",
jsonResponse.Status,
jsonResponse.Message ?? string.Empty);
}
var responseData = jsonResponse.Data as JArray;
if (responseData == null)
{
throw new IndexerException(indexerResponse,
"Indexer API call response missing result data");
}
var queryResults = responseData.ToObject<TorrentQueryResponse[]>();
foreach (var result in queryResults)
{
var id = result.Id;
var internalRelease = result.TypeOrigin == 1 ? true : false;
IndexerFlags flags = 0;
if (result.FreeLeech == "yes")
{
flags |= IndexerFlags.G_Freeleech;
}
if (internalRelease)
{
flags |= IndexerFlags.HDB_Internal;
}
torrentInfos.Add(new HDBitsInfo()
{
Guid = string.Format("HDBits-{0}", id),
Title = result.Name,
Size = result.Size,
InfoHash = result.Hash,
DownloadUrl = GetDownloadUrl(id),
InfoUrl = GetInfoUrl(id),
Seeders = result.Seeders,
Peers = result.Leechers + result.Seeders,
PublishDate = result.Added.ToUniversalTime(),
Internal = internalRelease,
ImdbId = result.ImdbInfo?.Id ?? 0,
IndexerFlags = flags
});
}
return torrentInfos.ToArray();
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
private string GetDownloadUrl(string torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/download.php")
.AddQueryParam("id", torrentId)
.AddQueryParam("passkey", _settings.ApiKey);
return url.FullUri;
}
private string GetInfoUrl(string torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/details.php")
.AddQueryParam("id", torrentId);
return url.FullUri;
}
}
}
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.IndexerSearch.Definitions;
namespace NzbDrone.Core.Indexers.HDBits
{
public class HDBitsRequestGenerator : IIndexerRequestGenerator
{
public HDBitsSettings Settings { get; set; }
public virtual IndexerPageableRequestChain GetRecentRequests()
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(new TorrentQuery()));
return pageableRequests;
}
public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
var query = new TorrentQuery();
if (TryAddSearchParameters(query, searchCriteria))
{
pageableRequests.Add(GetRequest(query));
}
return pageableRequests;
}
private bool TryAddSearchParameters(TorrentQuery query, SearchCriteriaBase searchCriteria)
{
if (searchCriteria.ImdbId.IsNullOrWhiteSpace())
{
return false;
}
var imdbId = int.Parse(searchCriteria.ImdbId.Substring(2));
if (imdbId != 0)
{
query.ImdbInfo = query.ImdbInfo ?? new ImdbInfo();
query.ImdbInfo.Id = imdbId;
return true;
}
return false;
}
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
private IEnumerable<IndexerRequest> GetRequest(TorrentQuery query)
{
var request = new HttpRequestBuilder(Settings.BaseUrl)
.Resource("/api/torrents")
.Build();
request.Method = HttpMethod.POST;
const string appJson = "application/json";
request.Headers.Accept = appJson;
request.Headers.ContentType = appJson;
query.Username = Settings.Username;
query.Passkey = Settings.ApiKey;
query.Category = Settings.Categories.ToArray();
query.Codec = Settings.Codecs.ToArray();
query.Medium = Settings.Mediums.ToArray();
request.SetContent(query.ToJson());
yield return new IndexerRequest(request);
}
}
}
@@ -0,0 +1,97 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.HDBits
{
public class HDBitsSettingsValidator : AbstractValidator<HDBitsSettings>
{
public HDBitsSettingsValidator()
{
RuleFor(c => c.BaseUrl).ValidRootUrl();
RuleFor(c => c.ApiKey).NotEmpty();
}
}
public class HDBitsSettings : ITorrentIndexerSettings
{
private static readonly HDBitsSettingsValidator Validator = new HDBitsSettingsValidator();
public HDBitsSettings()
{
BaseUrl = "https://hdbits.org";
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
Categories = new int[] { (int)HdBitsCategory.Movie };
Codecs = System.Array.Empty<int>();
Mediums = System.Array.Empty<int>();
MultiLanguages = new List<int>();
RequiredFlags = new List<int>();
}
[FieldDefinition(0, Label = "Username", Privacy = PrivacyLevel.UserName)]
public string Username { get; set; }
[FieldDefinition(1, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)]
public IEnumerable<int> MultiLanguages { get; set; }
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your API key will be sent to that host.")]
public string BaseUrl { get; set; }
[FieldDefinition(4, Label = "Categories", Type = FieldType.TagSelect, SelectOptions = typeof(HdBitsCategory), Advanced = true, HelpText = "Options: Movie, TV, Documentary, Music, Sport, Audio, XXX, MiscDemo. If unspecified, all options are used.")]
public IEnumerable<int> Categories { get; set; }
[FieldDefinition(5, Label = "Codecs", Type = FieldType.TagSelect, SelectOptions = typeof(HdBitsCodec), Advanced = true, HelpText = "Options: h264, Mpeg2, VC1, Xvid. If unspecified, all options are used.")]
public IEnumerable<int> Codecs { get; set; }
[FieldDefinition(6, Label = "Mediums", Type = FieldType.TagSelect, SelectOptions = typeof(HdBitsMedium), Advanced = true, HelpText = "Options: BluRay, Encode, Capture, Remux, WebDL. If unspecified, all options are used.")]
public IEnumerable<int> Mediums { get; set; }
[FieldDefinition(7, Type = FieldType.Number, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
public int MinimumSeeders { get; set; }
[FieldDefinition(8, Type = FieldType.TagSelect, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", HelpLink = "https://github.com/Prowlarr/Prowlarr/wiki/Indexer-Flags#1-required-flags", Advanced = true)]
public IEnumerable<int> RequiredFlags { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
public enum HdBitsCategory
{
Movie = 1,
Tv = 2,
Documentary = 3,
Music = 4,
Sport = 5,
Audio = 6,
Xxx = 7,
MiscDemo = 8
}
public enum HdBitsCodec
{
H264 = 1,
Mpeg2 = 2,
Vc1 = 3,
Xvid = 4,
HEVC = 5
}
public enum HdBitsMedium
{
Bluray = 1,
Encode = 3,
Capture = 4,
Remux = 5,
WebDl = 6
}
}