using System; using System.Collections.Generic; using System.Net; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions.HDBits { public class HDBitsParser : IParseIndexerResponse { private readonly HDBitsSettings _settings; private readonly IndexerCapabilitiesCategories _categories; private readonly List _halfLeechMediums = new () { (int)HdBitsMedium.Bluray, (int)HdBitsMedium.Remux, (int)HdBitsMedium.Capture }; public HDBitsParser(HDBitsSettings settings, IndexerCapabilitiesCategories categories) { _settings = settings; _categories = categories; } public IList ParseResponse(IndexerResponse indexerResponse) { var indexerHttpResponse = indexerResponse.HttpResponse; if (indexerHttpResponse.StatusCode == HttpStatusCode.Forbidden) { throw new RequestLimitReachedException(indexerResponse, "HDBits Query Limit Reached. Please try again later."); } if (indexerHttpResponse.StatusCode != HttpStatusCode.OK) { throw new IndexerException(indexerResponse, "Unexpected response status {0} code from indexer request", indexerHttpResponse.StatusCode); } var jsonResponse = JsonConvert.DeserializeObject(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); } if (jsonResponse.Data is not JArray responseData) { throw new IndexerException(indexerResponse, "Indexer API call response missing result data"); } var releaseInfos = new List(); var queryResults = responseData.ToObject(); foreach (var result in queryResults) { // Skip non-freeleech results when freeleech only is set if (_settings.FreeleechOnly && result.FreeLeech != "yes") { continue; } var id = result.Id; releaseInfos.Add(new TorrentInfo { Guid = $"HDBits-{id}", Title = GetTitle(result), Size = result.Size, Categories = _categories.MapTrackerCatToNewznab(result.TypeCategory.ToString()), InfoHash = result.Hash, DownloadUrl = GetDownloadUrl(id), InfoUrl = GetInfoUrl(id), Seeders = result.Seeders, Grabs = (int)result.TimesCompleted, Files = (int)result.NumFiles, Peers = result.Leechers + result.Seeders, PublishDate = result.Added.ToUniversalTime(), Year = result.ImdbInfo?.Year ?? 0, ImdbId = result.ImdbInfo?.Id ?? 0, TvdbId = result.TvdbInfo?.Id ?? 0, DownloadVolumeFactor = GetDownloadVolumeFactor(result), UploadVolumeFactor = GetUploadVolumeFactor(result), IndexerFlags = GetIndexerFlags(result) }); } return releaseInfos.ToArray(); } private string GetTitle(TorrentQueryResponse item) { // Use release name for XXX content and full discs return item.TypeCategory != 7 && item.TypeMedium != 1 && _settings.UseFilenames && item.FileName.IsNotNullOrWhiteSpace() ? item.FileName.Replace(".torrent", "", StringComparison.InvariantCultureIgnoreCase) : item.Name; } private static HashSet GetIndexerFlags(TorrentQueryResponse item) { var flags = new HashSet(); if (item.TypeOrigin == 1) { flags.Add(IndexerFlag.Internal); } if (item.TypeExclusive == 1) { flags.Add(IndexerFlag.Exclusive); } return flags; } private double GetDownloadVolumeFactor(TorrentQueryResponse item) { if (item.FreeLeech == "yes") { return 0; } // 100% Neutral Leech: all XXX content. if (item.TypeCategory == 7) { return 0; } // 50% Free Leech: all full discs, remuxes, captures and all internal encodes, also all TV and Documentary content. if (_halfLeechMediums.Contains(item.TypeMedium) || item.TypeOrigin == 1 || item.TypeCategory is 2 or 3) { return 0.5; } return 1; } private static double GetUploadVolumeFactor(TorrentQueryResponse item) { // 100% Neutral Leech: all XXX content. return item.TypeCategory == 7 ? 0 : 1; } 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; } public Action, DateTime?> CookiesUpdater { get; set; } } }