From c35f1212fbf9ae56d2ddccd7916bcf3487c8e3b5 Mon Sep 17 00:00:00 2001 From: Qstick Date: Tue, 20 Dec 2022 21:57:17 -0600 Subject: [PATCH] New: (Indexer) Torrent Bytes --- .../Indexers/Definitions/TorrentBytes.cs | 330 ++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs diff --git a/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs b/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs new file mode 100644 index 000000000..3107041e1 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/TorrentBytes.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using AngleSharp.Html.Parser; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Indexers.Settings; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Indexers.Definitions +{ + public class TorrentBytes : TorrentIndexerBase + { + public override string Name => "Torrent Bytes"; + public override string[] IndexerUrls => new string[] { "https://www.torrentbytes.net/" }; + private string LoginUrl => Settings.BaseUrl + "takelogin.php"; + public override string Description => "A decade of TorrentBytes"; + public override string Language => "en-US"; + public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1"); + public override DownloadProtocol Protocol => DownloadProtocol.Torrent; + public override IndexerPrivacy Privacy => IndexerPrivacy.Private; + public override IndexerCapabilities Capabilities => SetCapabilities(); + + public TorrentBytes(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, eventAggregator, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new TorrentBytesRequestGenerator() { Settings = Settings, Capabilities = Capabilities }; + } + + public override IParseIndexerResponse GetParser() + { + return new TorrentBytesParser(Settings, Capabilities.Categories); + } + + protected override async Task DoLogin() + { + var requestBuilder = new HttpRequestBuilder(LoginUrl) + { + LogResponseContent = true, + AllowAutoRedirect = true + }; + + requestBuilder.Method = HttpMethod.Post; + requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); + + var cookies = Cookies; + + Cookies = null; + var authLoginRequest = requestBuilder + .AddFormParameter("username", Settings.Username) + .AddFormParameter("password", Settings.Password) + .AddFormParameter("returnto", "/") + .AddFormParameter("login", "Log in!") + .SetHeader("Content-Type", "multipart/form-data") + .Build(); + + var headers = new NameValueCollection + { + { "Referer", LoginUrl } + }; + + authLoginRequest.Headers.Add(headers); + + var response = await ExecuteAuth(authLoginRequest); + + if (CheckIfLoginNeeded(response)) + { + var parser = new HtmlParser(); + var dom = parser.ParseDocument(response.Content); + var errorMessage = dom.QuerySelector("td.embedded").TextContent.Trim(); + + throw new IndexerAuthException(errorMessage); + } + + cookies = response.GetCookies(); + UpdateCookies(cookies, DateTime.Now + TimeSpan.FromDays(30)); + + _logger.Debug("TorrentBytes authentication succeeded."); + } + + protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) + { + if (!httpResponse.Content.Contains("my.php")) + { + return true; + } + + return false; + } + + private IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId + }, + MovieSearchParams = new List + { + MovieSearchParam.Q, MovieSearchParam.ImdbId + }, + MusicSearchParams = new List + { + MusicSearchParam.Q + } + }; + + caps.Categories.AddCategoryMapping(23, NewznabStandardCategory.TVAnime, "Anime"); + caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.PCMac, "Apple/All"); + caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.PC, "Apps/misc"); + caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.PC, "Apps/PC"); + caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.TVForeign, "Foreign Titles"); + caps.Categories.AddCategoryMapping(50, NewznabStandardCategory.Console, "Games/Consoles"); + caps.Categories.AddCategoryMapping(42, NewznabStandardCategory.PCGames, "Games/Pack"); + caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCGames, "Games/PC"); + caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.PC, "Linux/All"); + caps.Categories.AddCategoryMapping(31, NewznabStandardCategory.OtherMisc, "Misc"); + caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.MoviesDVD, "Movies/DVD-R"); + caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.MoviesBluRay, "Movies/Full Blu-ray"); + caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.MoviesHD, "Movies/HD"); + caps.Categories.AddCategoryMapping(40, NewznabStandardCategory.Movies, "Movies/Pack"); + caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.MoviesSD, "Movies/SD"); + caps.Categories.AddCategoryMapping(49, NewznabStandardCategory.MoviesUHD, "Movies/UHD"); + caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.Audio, "Music/DVDR"); + caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.AudioLossless, "Music/Flac"); + caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.AudioMP3, "Music/MP3"); + caps.Categories.AddCategoryMapping(43, NewznabStandardCategory.Audio, "Music/Pack"); + caps.Categories.AddCategoryMapping(34, NewznabStandardCategory.AudioVideo, "Music/Videos"); + caps.Categories.AddCategoryMapping(45, NewznabStandardCategory.MoviesBluRay, "NonScene/BRrip"); + caps.Categories.AddCategoryMapping(46, NewznabStandardCategory.MoviesHD, "NonScene/x264"); + caps.Categories.AddCategoryMapping(44, NewznabStandardCategory.MoviesSD, "NonScene/Xvid"); + caps.Categories.AddCategoryMapping(37, NewznabStandardCategory.TVHD, "TV/BRrip"); + caps.Categories.AddCategoryMapping(38, NewznabStandardCategory.TVHD, "TV/HD"); + caps.Categories.AddCategoryMapping(41, NewznabStandardCategory.TV, "TV/Pack"); + caps.Categories.AddCategoryMapping(33, NewznabStandardCategory.TVSD, "TV/SD"); + caps.Categories.AddCategoryMapping(32, NewznabStandardCategory.TVUHD, "TV/UHD"); + caps.Categories.AddCategoryMapping(39, NewznabStandardCategory.XXXx264, "XXX/HD"); + caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.XXXImageSet, "XXX/IMGSET"); + caps.Categories.AddCategoryMapping(21, NewznabStandardCategory.XXXPack, "XXX/Pack"); + caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.XXXXviD, "XXX/SD"); + caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.XXX, "XXX/Web"); + + return caps; + } + } + + public class TorrentBytesRequestGenerator : IIndexerRequestGenerator + { + public UserPassTorrentBaseSettings Settings { get; set; } + public IndexerCapabilities Capabilities { get; set; } + + public TorrentBytesRequestGenerator() + { + } + + private IEnumerable GetPagedRequests(string term, int[] categories, string imdbId = null) + { + var searchUrl = string.Format("{0}/browse.php", Settings.BaseUrl.TrimEnd('/')); + + var qc = new NameValueCollection + { + { "incldead", "1" } + }; + + if (imdbId.IsNotNullOrWhiteSpace()) + { + qc.Add("search", imdbId); + qc.Add("sc", "2"); // search in description + } + else + { + qc.Add("search", term); + qc.Add("sc", "1"); // search in title + } + + foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories)) + { + qc.Add("c" + cat, "1"); + } + + searchUrl = searchUrl + "?" + qc.GetQueryString(); + + var request = new IndexerRequest(searchUrl, HttpAccept.Html); + + yield return request; + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + public Func> GetCookies { get; set; } + public Action, DateTime?> CookiesUpdater { get; set; } + } + + public class TorrentBytesParser : IParseIndexerResponse + { + private readonly UserPassTorrentBaseSettings _settings; + private readonly IndexerCapabilitiesCategories _categories; + + public TorrentBytesParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) + { + _settings = settings; + _categories = categories; + } + + public IList ParseResponse(IndexerResponse indexerResponse) + { + var torrentInfos = new List(); + + var parser = new HtmlParser(); + var doc = parser.ParseDocument(indexerResponse.Content); + var rows = doc.QuerySelectorAll("table > tbody:has(tr > td.colhead) > tr:not(:has(td.colhead))"); + foreach (var row in rows) + { + var release = new TorrentInfo(); + var link = row.QuerySelector("td:nth-of-type(2) a:nth-of-type(2)"); + release.Guid = _settings.BaseUrl + link.GetAttribute("href"); + release.InfoUrl = release.Guid; + release.Title = link.GetAttribute("title"); + + // There isn't a title attribute if the release name isn't truncated. + if (string.IsNullOrWhiteSpace(release.Title)) + { + release.Title = link.FirstChild.TextContent.Trim(); + } + + release.Description = release.Title; + + // If we search an get no results, we still get a table just with no info. + if (string.IsNullOrWhiteSpace(release.Title)) + { + break; + } + + // Check if the release has been assigned a category + var qCat = row.QuerySelector("td:nth-of-type(1) a"); + if (qCat != null) + { + var cat = qCat.GetAttribute("href").Substring(15); + release.Categories = _categories.MapTrackerCatToNewznab(cat); + } + + var qLink = row.QuerySelector("td:nth-of-type(2) a"); + release.DownloadUrl = _settings.BaseUrl + qLink.GetAttribute("href"); + + var added = row.QuerySelector("td:nth-of-type(5)").TextContent.Trim(); + release.PublishDate = DateTime.ParseExact(added, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); + + var sizeStr = row.QuerySelector("td:nth-of-type(7)").TextContent.Trim(); + release.Size = ParseUtil.GetBytes(sizeStr); + release.Seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent.Trim()); + release.Peers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent.Trim()) + + release.Seeders; + + var files = row.QuerySelector("td:nth-child(3)").TextContent; + release.Files = ParseUtil.CoerceInt(files); + + var grabs = row.QuerySelector("td:nth-child(8)").TextContent; + if (grabs != "----") + { + release.Grabs = ParseUtil.CoerceInt(grabs); + } + + release.DownloadVolumeFactor = row.QuerySelector("font[color=\"green\"]:contains(\"F\"):contains(\"L\")") != null ? 0 : 1; + release.UploadVolumeFactor = 1; + + torrentInfos.Add(release); + } + + return torrentInfos.ToArray(); + } + + public Action, DateTime?> CookiesUpdater { get; set; } + } +}