using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text.RegularExpressions; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Indexers.Exceptions; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.BroadcastheNet { public class BroadcastheNetParser : IParseIndexerResponse { private static readonly Regex RegexProtocol = new("^https?:", RegexOptions.Compiled); public Action, DateTime?> CookiesUpdater { get; set; } private readonly IndexerCapabilitiesCategories _categories; public BroadcastheNetParser(IndexerCapabilitiesCategories categories) { _categories = categories; } public IList ParseResponse(IndexerResponse indexerResponse) { var releaseInfos = new List(); var indexerHttpResponse = indexerResponse.HttpResponse; switch (indexerHttpResponse.StatusCode) { case HttpStatusCode.Unauthorized: throw new IndexerAuthException("API Key invalid or not authorized"); case HttpStatusCode.NotFound: throw new IndexerException(indexerResponse, "Indexer API call returned NotFound, the Indexer API may have changed."); case HttpStatusCode.ServiceUnavailable: throw new RequestLimitReachedException(indexerResponse, "Cannot do more than 150 API requests per hour."); default: if (indexerHttpResponse.StatusCode != HttpStatusCode.OK) { throw new IndexerException(indexerResponse, "Indexer API call returned an unexpected StatusCode [{0}]", indexerHttpResponse.StatusCode); } break; } if (indexerHttpResponse.Headers.ContentType != null && indexerHttpResponse.Headers.ContentType.Contains("text/html")) { throw new IndexerException(indexerResponse, "Indexer responded with html content. Site is likely blocked or unavailable."); } if (indexerResponse.Content.ContainsIgnoreCase("Call Limit Exceeded")) { throw new RequestLimitReachedException(indexerResponse, "Cannot do more than 150 API requests per hour."); } if (indexerResponse.Content == "Query execution was interrupted") { throw new IndexerException(indexerResponse, "Indexer API returned an internal server error"); } var jsonResponse = new HttpResponse>(indexerHttpResponse).Resource; if (jsonResponse.Error != null || jsonResponse.Result == null) { throw new IndexerException(indexerResponse, "Indexer API call returned an error [{0}]", jsonResponse.Error); } if (jsonResponse.Result.Results == 0 || jsonResponse.Result?.Torrents?.Values == null) { return releaseInfos; } var protocol = indexerResponse.HttpRequest.Url.Scheme + ":"; foreach (var torrent in jsonResponse.Result.Torrents.Values) { var flags = new HashSet(); if (torrent.Origin.ToUpperInvariant() == "INTERNAL") { flags.Add(IndexerFlag.Internal); } var releaseInfo = new TorrentInfo { Guid = $"BTN-{torrent.TorrentID}", InfoUrl = $"{protocol}//broadcasthe.net/torrents.php?id={torrent.GroupID}&torrentid={torrent.TorrentID}", DownloadUrl = RegexProtocol.Replace(torrent.DownloadURL, protocol), Title = GetTitle(torrent), Categories = _categories.MapTrackerCatToNewznab(torrent.Resolution), InfoHash = torrent.InfoHash, Size = torrent.Size, Grabs = torrent.Snatched, Seeders = torrent.Seeders, Peers = torrent.Leechers + torrent.Seeders, PublishDate = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).ToUniversalTime().AddSeconds(torrent.Time), Origin = torrent.Origin, Source = torrent.Source, Container = torrent.Container, Codec = torrent.Codec, Resolution = torrent.Resolution, Scene = torrent.Origin.ToUpperInvariant() == "SCENE", IndexerFlags = flags, DownloadVolumeFactor = 0, UploadVolumeFactor = 1, MinimumRatio = 1, MinimumSeedTime = torrent.Category.ToUpperInvariant() == "SEASON" ? 432000 : 86400, // 120 hours for seasons and 24 hours for episodes }; if (torrent.TvdbID is > 0) { releaseInfo.TvdbId = torrent.TvdbID.Value; } if (torrent.TvrageID is > 0) { releaseInfo.TvRageId = torrent.TvrageID.Value; } if (torrent.ImdbID.IsNotNullOrWhiteSpace() && int.TryParse(torrent.ImdbID, out var imdbId)) { releaseInfo.ImdbId = imdbId; } // Default to TV if category could not be mapped if (releaseInfo.Categories == null || !releaseInfo.Categories.Any()) { releaseInfo.Categories = new List { NewznabStandardCategory.TV }; } releaseInfos.Add(releaseInfo); } return releaseInfos; } private static string GetTitle(BroadcastheNetTorrent torrent) { var releaseName = torrent.ReleaseName.Replace("\\", ""); if (torrent.Container.ToUpperInvariant() is "M2TS" or "ISO") { releaseName = Regex.Replace(releaseName, @"\b(H\.?265)\b", "HEVC", RegexOptions.Compiled); releaseName = Regex.Replace(releaseName, @"\b(H\.?264)\b", "AVC", RegexOptions.Compiled); } return releaseName; } } }