using System; using System.Collections.Generic; using System.Linq; using System.Net; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Definitions.Gazelle; public class GazelleParser : IParseIndexerResponse { protected GazelleSettings Settings { get; } protected IndexerCapabilities Capabilities { get; } public GazelleParser(GazelleSettings settings, IndexerCapabilities capabilities) { Settings = settings; Capabilities = capabilities; } public Action, DateTime?> CookiesUpdater { get; set; } public virtual IList ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List(); if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { // Remove cookie cache CookiesUpdater(null, null); throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from indexer request"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) { // Remove cookie cache CookiesUpdater(null, null); throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from indexer request, expected {HttpAccept.Json.Value}"); } var jsonResponse = new HttpResponse(indexerResponse.HttpResponse); if (jsonResponse.Resource.Status != "success" || jsonResponse.Resource.Status.IsNullOrWhiteSpace() || jsonResponse.Resource.Response == null) { return torrentInfos; } foreach (var result in jsonResponse.Resource.Response.Results) { var posterUrl = GetPosterUrl(result.Cover); if (result.Torrents != null) { foreach (var torrent in result.Torrents) { // skip releases that cannot be used with freeleech tokens when the option is enabled if (Settings.UseFreeleechToken && !torrent.CanUseToken) { continue; } var id = torrent.TorrentId; var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]"; if (torrent.HasCue) { title += " [Cue]"; } var infoUrl = GetInfoUrl(result.GroupId, id); var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, DownloadUrl = GetDownloadUrl(id, !torrent.IsFreeLeech && !torrent.IsNeutralLeech && !torrent.IsPersonalFreeLeech), Title = WebUtility.HtmlDecode(title), Container = torrent.Encoding, Files = torrent.FileCount, Grabs = torrent.Snatches, Codec = torrent.Format, Size = long.Parse(torrent.Size), Seeders = int.Parse(torrent.Seeders), Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders), PublishDate = torrent.Time.ToUniversalTime(), Scene = torrent.Scene, PosterUrl = posterUrl, DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1, UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1 }; var category = torrent.Category; if (category == null || category.Contains("Select Category")) { release.Categories = Capabilities.Categories.MapTrackerCatToNewznab("1"); } else { release.Categories = Capabilities.Categories.MapTrackerCatDescToNewznab(category); } torrentInfos.Add(release); } } else { // skip releases that cannot be used with freeleech tokens when the option is enabled if (Settings.UseFreeleechToken && !result.CanUseToken) { continue; } var id = result.TorrentId; var groupName = WebUtility.HtmlDecode(result.GroupName); var infoUrl = GetInfoUrl(result.GroupId, id); var release = new TorrentInfo { Guid = infoUrl, InfoUrl = infoUrl, DownloadUrl = GetDownloadUrl(id, !result.IsFreeLeech && !result.IsNeutralLeech && !result.IsPersonalFreeLeech), Title = groupName, Size = long.Parse(result.Size), Seeders = int.Parse(result.Seeders), Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders), Files = result.FileCount, Grabs = result.Snatches, PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime), PosterUrl = posterUrl, DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1, UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1 }; var category = result.Category; if (category == null || category.Contains("Select Category")) { release.Categories = Capabilities.Categories.MapTrackerCatToNewznab("1"); } else { release.Categories = Capabilities.Categories.MapTrackerCatDescToNewznab(category); } torrentInfos.Add(release); } } // order by date return torrentInfos .OrderByDescending(o => o.PublishDate) .ToArray(); } protected virtual string GetDownloadUrl(int torrentId, bool canUseToken) { var url = new HttpUri(Settings.BaseUrl) .CombinePath("/torrents.php") .AddQueryParam("action", "download") .AddQueryParam("id", torrentId); if (Settings.UseFreeleechToken) { url = url.AddQueryParam("usetoken", "1"); } return url.FullUri; } protected virtual string GetPosterUrl(string cover) { if (!string.IsNullOrEmpty(cover)) { return cover.StartsWith("http") ? new HttpUri(cover).FullUri : new HttpUri(Settings.BaseUrl).CombinePath(cover).FullUri; } return null; } protected virtual string GetInfoUrl(string groupId, int torrentId) { var url = new HttpUri(Settings.BaseUrl) .CombinePath("/torrents.php") .AddQueryParam("id", groupId) .AddQueryParam("torrentid", torrentId); return url.FullUri; } }