diff --git a/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs b/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs deleted file mode 100644 index 78465e54b..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/Anthelion.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -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 -{ - [Obsolete("Moved to YML for Cardigann")] - public class Anthelion : TorrentIndexerBase - { - public override string Name => "Anthelion"; - public override string[] IndexerUrls => new string[] { "https://anthelion.me/" }; - private string LoginUrl => Settings.BaseUrl + "login.php"; - public override string Description => "A movies tracker"; - public override string Language => "en-US"; - public override Encoding Encoding => Encoding.UTF8; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override IndexerCapabilities Capabilities => SetCapabilities(); - - public Anthelion(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new AnthelionRequestGenerator() { Settings = Settings, Capabilities = Capabilities }; - } - - public override IParseIndexerResponse GetParser() - { - return new AnthelionParser(Settings, Capabilities.Categories); - } - - protected override async Task DoLogin() - { - var requestBuilder = new HttpRequestBuilder(LoginUrl) - { - LogResponseContent = true, - AllowAutoRedirect = true, - Method = HttpMethod.Post - }; - - var cookies = Cookies; - Cookies = null; - - var authLoginRequest = requestBuilder - .AddFormParameter("username", Settings.Username) - .AddFormParameter("password", Settings.Password) - .AddFormParameter("keeplogged", "1") - .AddFormParameter("login", "Log+In!") - .SetHeader("Content-Type", "application/x-www-form-urlencoded") - .SetHeader("Referer", LoginUrl) - .Build(); - - var response = await ExecuteAuth(authLoginRequest); - - if (CheckIfLoginNeeded(response)) - { - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(response.Content); - var errorMessage = dom.QuerySelector("form#loginform")?.TextContent.Trim(); - - throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report."); - } - - cookies = response.GetCookies(); - UpdateCookies(cookies, DateTime.Now.AddDays(30)); - - _logger.Debug("Anthelion authentication succeeded."); - } - - protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) - { - return !httpResponse.Content.Contains("logout.php"); - } - - private IndexerCapabilities SetCapabilities() - { - var caps = new IndexerCapabilities - { - TvSearchParams = new List - { - TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep - }, - MovieSearchParams = new List - { - MovieSearchParam.Q - } - }; - - caps.Categories.AddCategoryMapping("1", NewznabStandardCategory.Movies, "Film/Feature"); - caps.Categories.AddCategoryMapping("2", NewznabStandardCategory.Movies, "Film/Short"); - caps.Categories.AddCategoryMapping("3", NewznabStandardCategory.TV, "TV/Miniseries"); - caps.Categories.AddCategoryMapping("4", NewznabStandardCategory.Other, "Other"); - - return caps; - } - } - - public class AnthelionRequestGenerator : IIndexerRequestGenerator - { - public UserPassTorrentBaseSettings Settings { get; set; } - public IndexerCapabilities Capabilities { get; set; } - - private IEnumerable GetPagedRequests(string term, int[] categories, string imdbId = null) - { - var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/')); - - // TODO: IMDB search is available but it requires to parse the details page - var qc = new NameValueCollection - { - { "order_by", "time" }, - { "order_way", "desc" }, - { "action", "basic" }, - { "searchsubmit", "1" }, - { "searchstr", imdbId.IsNotNullOrWhiteSpace() ? imdbId : term.Replace(".", " ") } - }; - - var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories); - - foreach (var cat in catList) - { - qc.Add($"filter_cat[{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(); - - 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 AnthelionParser : IParseIndexerResponse - { - private readonly UserPassTorrentBaseSettings _settings; - private readonly IndexerCapabilitiesCategories _categories; - - public AnthelionParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) - { - _settings = settings; - _categories = categories; - } - - public IList ParseResponse(IndexerResponse indexerResponse) - { - var torrentInfos = new List(); - - var parser = new HtmlParser(); - using var doc = parser.ParseDocument(indexerResponse.Content); - var rows = doc.QuerySelectorAll("table.torrent_table > tbody > tr.torrent"); - foreach (var row in rows) - { - var qDetailsLink = row.QuerySelector("a.torrent_name"); - var year = qDetailsLink.NextSibling.TextContent.Replace("[", "").Replace("]", "").Trim(); - var tags = row.QuerySelector("div.torrent_info").FirstChild.TextContent.Replace(" / ", " ").Trim(); - var title = $"{qDetailsLink.TextContent} {year} {tags}"; - var description = row.QuerySelector("div.tags").TextContent.Trim(); - var details = _settings.BaseUrl + qDetailsLink.GetAttribute("href"); - var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last(); - var link = _settings.BaseUrl + "torrents.php?action=download&id=" + torrentId; - var posterStr = qDetailsLink.GetAttribute("data-cover"); - var poster = !string.IsNullOrWhiteSpace(posterStr) ? posterStr : null; - - var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent); - var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent); - var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent); - var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent); - var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent); - var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent); - - var dlVolumeFactor = row.QuerySelector("strong.tl_free") != null ? 0 : 1; - - var cat = row.QuerySelector("td.cats_col > div").GetAttribute("class").Replace("tooltip cats_", ""); - var category = new List - { - cat switch - { - "featurefilm" => NewznabStandardCategory.Movies, - "shortfilm" => NewznabStandardCategory.Movies, - "miniseries" => NewznabStandardCategory.TV, - "other" => NewznabStandardCategory.Other, - _ => throw new Exception($"Unknown category: {cat}") - } - }; - - // TODO: TMDb is also available - var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]"); - var imdb = qImdb != null ? ParseUtil.GetImdbId(qImdb.GetAttribute("href").Split('/').Last()) : null; - - var release = new TorrentInfo - { - MinimumRatio = 1, - MinimumSeedTime = 259200, - Description = description, - Title = title, - PublishDate = publishDate, - Categories = category, - DownloadUrl = link, - InfoUrl = details, - PosterUrl = poster, - Guid = link, - ImdbId = imdb.GetValueOrDefault(), - Seeders = seeders, - Peers = leechers + seeders, - Size = size, - Grabs = grabs, - Files = files, - DownloadVolumeFactor = dlVolumeFactor, - UploadVolumeFactor = 1 - }; - - torrentInfos.Add(release); - } - - return torrentInfos.ToArray(); - } - - public Action, DateTime?> CookiesUpdater { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/AudioBookBay.cs b/src/NzbDrone.Core/Indexers/Definitions/AudioBookBay.cs deleted file mode 100644 index 8f08fe857..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/AudioBookBay.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using AngleSharp.Html.Dom; -using AngleSharp.Html.Parser; -using NLog; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Http; -using NzbDrone.Core.Configuration; -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; - -[Obsolete("User Agent blocked")] -public class AudioBookBay : TorrentIndexerBase -{ - public override string Name => "AudioBook Bay"; - public override string[] IndexerUrls => new[] - { - "https://audiobookbay.is/" - }; - public override string[] LegacyUrls => new[] - { - "https://audiobookbay.la/", - "http://audiobookbay.net/", - "https://audiobookbay.unblockit.tv/", - "http://audiobookbay.nl/", - "http://audiobookbay.ws/", - "https://audiobookbay.unblockit.how/", - "https://audiobookbay.unblockit.cam/", - "https://audiobookbay.unblockit.biz/", - "https://audiobookbay.unblockit.day/", - "https://audiobookbay.unblockit.llc/", - "https://audiobookbay.unblockit.blue/", - "https://audiobookbay.unblockit.name/", - "http://audiobookbay.fi/", - "http://audiobookbay.se/", - "http://audiobookbayabb.com/", - "https://audiobookbay.unblockit.ist/", - "https://audiobookbay.unblockit.bet/", - "https://audiobookbay.unblockit.cat/", - "https://audiobookbay.unblockit.nz/", - "https://audiobookbay.fi/", - "https://audiobookbay.unblockit.page/", - "https://audiobookbay.unblockit.pet/", - "https://audiobookbay.unblockit.ink/", - "https://audiobookbay.unblockit.bio/", // error 502 - "https://audiobookbay.li/", - "https://audiobookbay.se/" // redirects to .is but has invalid CA - }; - public override string Description => "AudioBook Bay (ABB) is a public Torrent Tracker for AUDIOBOOKS"; - public override string Language => "en-US"; - public override IndexerPrivacy Privacy => IndexerPrivacy.Public; - public override int PageSize => 15; - public override TimeSpan RateLimit => TimeSpan.FromSeconds(5); - public override IndexerCapabilities Capabilities => SetCapabilities(); - - public AudioBookBay(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new AudioBookBayRequestGenerator(Settings); - } - - public override IParseIndexerResponse GetParser() - { - return new AudioBookBayParser(Settings, Capabilities.Categories); - } - - public override async Task Download(Uri link) - { - var request = new HttpRequestBuilder(link.ToString()) - .SetCookies(GetCookies() ?? new Dictionary()) - .Accept(HttpAccept.Html) - .Build(); - - var response = await _httpClient.ExecuteProxiedAsync(request, Definition); - - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(response.Content); - - var hash = dom.QuerySelector("td:contains(\"Info Hash:\") ~ td")?.TextContent.Trim(); - if (hash == null) - { - throw new Exception($"Failed to fetch hash from {link}"); - } - - var title = dom.QuerySelector("div.postTitle h1")?.TextContent.Trim(); - if (title == null) - { - throw new Exception($"Failed to fetch title from {link}"); - } - - title = StringUtil.MakeValidFileName(title, '_', false); - - var magnet = MagnetLinkBuilder.BuildPublicMagnetLink(hash, title); - - return await base.Download(new Uri(magnet)); - } - - private IndexerCapabilities SetCapabilities() - { - var caps = new IndexerCapabilities - { - BookSearchParams = new List - { - BookSearchParam.Q - } - }; - - caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.AudioAudiobook); - - return caps; - } -} - -public class AudioBookBayRequestGenerator : IIndexerRequestGenerator -{ - private readonly NoAuthTorrentBaseSettings _settings; - - public AudioBookBayRequestGenerator(NoAuthTorrentBaseSettings settings) - { - _settings = settings; - } - - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}")); - - return pageableRequests; - } - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}")); - - return pageableRequests; - } - - private IEnumerable GetPagedRequests(string term) - { - var searchUrl = _settings.BaseUrl; - - var parameters = new NameValueCollection(); - - term = Regex.Replace(term, @"[\W]+", " ").Trim(); - - if (term.IsNotNullOrWhiteSpace()) - { - parameters.Set("s", term); - parameters.Set("tt", "1"); - } - - if (parameters.Count > 0) - { - searchUrl += $"?{parameters.GetQueryString()}"; - } - - yield return new IndexerRequest(new UriBuilder(searchUrl) { Path = "/" }.Uri.AbsoluteUri, HttpAccept.Html); - } - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } -} - -public class AudioBookBayParser : IParseIndexerResponse -{ - private readonly NoAuthTorrentBaseSettings _settings; - private readonly IndexerCapabilitiesCategories _categories; - - public AudioBookBayParser(NoAuthTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) - { - _settings = settings; - _categories = categories; - } - - public IList ParseResponse(IndexerResponse indexerResponse) - { - var releaseInfos = new List(); - - using var doc = ParseHtmlDocument(indexerResponse.Content); - - var rows = doc.QuerySelectorAll("div.post:has(div[class=\"postTitle\"])"); - foreach (var row in rows) - { - var infoUrl = _settings.BaseUrl + row.QuerySelector("div.postTitle h2 a")?.GetAttribute("href")?.Trim().TrimStart('/'); - - var title = row.QuerySelector("div.postTitle")?.TextContent.Trim(); - - var infoString = row.QuerySelector("div.postContent")?.TextContent.Trim() ?? string.Empty; - - var matchFormat = Regex.Match(infoString, @"Format: (.+) \/", RegexOptions.IgnoreCase); - if (matchFormat.Groups[1].Success && matchFormat.Groups[1].Value.Length > 0 && matchFormat.Groups[1].Value != "?") - { - title += $" [{matchFormat.Groups[1].Value.Trim()}]"; - } - - var matchBitrate = Regex.Match(infoString, @"Bitrate: (.+)File", RegexOptions.IgnoreCase); - if (matchBitrate.Groups[1].Success && matchBitrate.Groups[1].Value.Length > 0 && matchBitrate.Groups[1].Value != "?") - { - title += $" [{matchBitrate.Groups[1].Value.Trim()}]"; - } - - var matchSize = Regex.Match(infoString, @"File Size: (.+?)s?$", RegexOptions.IgnoreCase); - var size = matchSize.Groups[1].Success ? ParseUtil.GetBytes(matchSize.Groups[1].Value) : 0; - - var matchDateAdded = Regex.Match(infoString, @"Posted: (\d{1,2} \D{3} \d{4})", RegexOptions.IgnoreCase); - var publishDate = matchDateAdded.Groups[1].Success && DateTime.TryParseExact(matchDateAdded.Groups[1].Value, "d MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsedDate) ? parsedDate : DateTime.Now; - - var postInfo = row.QuerySelector("div.postInfo")?.FirstChild?.TextContent.Trim().Replace("\xA0", ";") ?? string.Empty; - var matchCategory = Regex.Match(postInfo, @"Category: (.+)$", RegexOptions.IgnoreCase); - var genres = matchCategory.Groups[1].Success ? matchCategory.Groups[1].Value.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList() : new List(); - - var release = new TorrentInfo - { - Guid = infoUrl, - InfoUrl = infoUrl, - DownloadUrl = infoUrl, - Title = CleanTitle(title), - Categories = new List { NewznabStandardCategory.AudioAudiobook }, - Size = size, - Seeders = 1, - Peers = 1, - PublishDate = publishDate, - DownloadVolumeFactor = 0, - UploadVolumeFactor = 1, - Genres = genres - }; - - var cover = row.QuerySelector("img[src]")?.GetAttribute("src")?.Trim(); - if (!string.IsNullOrEmpty(cover)) - { - release.PosterUrl = cover.StartsWith("http") ? cover : _settings.BaseUrl + cover; - } - - releaseInfos.Add(release); - } - - return releaseInfos; - } - - private static IHtmlDocument ParseHtmlDocument(string response) - { - var parser = new HtmlParser(); - var doc = parser.ParseDocument(response); - - var hidden = doc.QuerySelectorAll("div.post.re-ab"); - foreach (var element in hidden) - { - var body = doc.CreateElement("div"); - body.ClassList.Add("post"); - body.InnerHtml = Encoding.UTF8.GetString(Convert.FromBase64String(element.TextContent)); - element.Parent.ReplaceChild(body, element); - } - - return doc; - } - - private static string CleanTitle(string title) - { - title = Regex.Replace(title, @"[\u0000-\u0008\u000A-\u001F\u0100-\uFFFF]", string.Empty, RegexOptions.Compiled); - title = Regex.Replace(title, @"\s+", " ", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - return title.Trim(); - } - - public Action, DateTime?> CookiesUpdater { get; set; } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/BB.cs b/src/NzbDrone.Core/Indexers/Definitions/BB.cs deleted file mode 100644 index a2439873f..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/BB.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Net.Http; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using AngleSharp.Dom; -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 -{ - [Obsolete("Site has shutdown")] - public class BB : TorrentIndexerBase - { - public override string Name => "BB"; - public override string[] IndexerUrls => new[] { Base64Extensions.FromBase64("aHR0cHM6Ly9iYWNvbmJpdHMub3JnLw==") }; - private string LoginUrl => Settings.BaseUrl + "login.php"; - public override string Description => "BB is a Private Torrent Tracker for 0DAY / GENERAL"; - public override string Language => "en-US"; - public override Encoding Encoding => Encoding.UTF8; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override IndexerCapabilities Capabilities => SetCapabilities(); - - public BB(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new BBRequestGenerator { Settings = Settings, Capabilities = Capabilities }; - } - - public override IParseIndexerResponse GetParser() - { - return new BBParser(Settings, Capabilities.Categories); - } - - protected override async Task DoLogin() - { - var requestBuilder = new HttpRequestBuilder(LoginUrl) - { - LogResponseContent = true, - AllowAutoRedirect = true, - Method = HttpMethod.Post - }; - - var cookies = Cookies; - Cookies = null; - - var authLoginRequest = requestBuilder - .AddFormParameter("username", Settings.Username) - .AddFormParameter("password", Settings.Password) - .AddFormParameter("keeplogged", "1") - .AddFormParameter("login", "Log+In!") - .SetHeader("Content-Type", "application/x-www-form-urlencoded") - .SetHeader("Referer", LoginUrl) - .Build(); - - var response = await ExecuteAuth(authLoginRequest); - - if (CheckIfLoginNeeded(response)) - { - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(response.Content); - var messageEl = dom.QuerySelectorAll("#loginform"); - var messages = new List(); - for (var i = 0; i < 13; i++) - { - var child = messageEl[0].ChildNodes[i]; - messages.Add(child.Text().Trim()); - } - - var message = string.Join(" ", messages); - - throw new IndexerAuthException(message); - } - - cookies = response.GetCookies(); - UpdateCookies(cookies, DateTime.Now.AddDays(30)); - - _logger.Debug("BB authentication succeeded."); - } - - protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) - { - return !httpResponse.Content.Contains("logout.php"); - } - - private IndexerCapabilities SetCapabilities() - { - var caps = new IndexerCapabilities - { - TvSearchParams = new List - { - TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep - }, - MovieSearchParams = new List - { - MovieSearchParam.Q - }, - MusicSearchParams = new List - { - MusicSearchParam.Q - }, - BookSearchParams = new List - { - BookSearchParam.Q - } - }; - - caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio); - caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.AudioMP3); - caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.AudioLossless); - caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC); - caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.BooksEBook); - caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.AudioAudiobook); - caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other); - caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.BooksMags); - caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.BooksComics); - caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.TVAnime); - caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.Movies); - caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TVHD); - caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TVSD); - caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TV); - caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.PCGames); - caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.Console); - caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.Other); - caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.Other); - - return caps; - } - } - - public class BBRequestGenerator : IIndexerRequestGenerator - { - public UserPassTorrentBaseSettings Settings { get; set; } - public IndexerCapabilities Capabilities { get; set; } - - private IEnumerable GetPagedRequests(string term, int[] categories) - { - var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/')); - - // TODO: IMDB search is available but it requires to parse the details page - var qc = new NameValueCollection - { - { "order_by", "s3" }, - { "order_way", "desc" }, - { "disablegrouping", "1" }, - { "searchtags", "" }, - { "tags_type", "0" }, - { "action", "basic" }, - { "searchstr", term.Replace(".", " ") } - }; - - var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories); - - foreach (var cat in catList) - { - qc.Add($"filter_cat[{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)); - - 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)); - - return pageableRequests; - } - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); - - 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 BBParser : IParseIndexerResponse - { - private readonly UserPassTorrentBaseSettings _settings; - private readonly IndexerCapabilitiesCategories _categories; - - public BBParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) - { - _settings = settings; - _categories = categories; - } - - public IList ParseResponse(IndexerResponse indexerResponse) - { - var torrentInfos = new List(); - - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(indexerResponse.Content); - var rows = dom.QuerySelectorAll("#torrent_table > tbody > tr.torrent"); - - foreach (var row in rows) - { - var release = new TorrentInfo(); - - release.MinimumRatio = 1; - release.MinimumSeedTime = 172800; // 48 hours - - var catStr = row.Children[0].FirstElementChild.GetAttribute("href").Split(new[] { '[', ']' })[1]; - release.Categories = _categories.MapTrackerCatToNewznab(catStr); - - var qDetails = row.Children[1].QuerySelector("a[title='View Torrent']"); - release.InfoUrl = _settings.BaseUrl + qDetails.GetAttribute("href"); - release.Guid = release.InfoUrl; - - var qDownload = row.Children[1].QuerySelector("a[title='Download']"); - release.DownloadUrl = _settings.BaseUrl + qDownload.GetAttribute("href"); - - var dateStr = row.Children[3].TextContent.Trim().Replace(" and", ""); - release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr); - - var sizeStr = row.Children[4].TextContent; - release.Size = ParseUtil.GetBytes(sizeStr); - - release.Files = ParseUtil.CoerceInt(row.Children[2].TextContent.Trim()); - release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent.Trim()); - release.Peers = ParseUtil.CoerceInt(row.Children[8].TextContent.Trim()) + release.Seeders; - - var grabs = row.QuerySelector("td:nth-child(6)").TextContent; - release.Grabs = ParseUtil.CoerceInt(grabs); - - if (row.QuerySelector("strong:contains(\"Freeleech!\")") != null) - { - release.DownloadVolumeFactor = 0; - } - else - { - release.DownloadVolumeFactor = 1; - } - - release.UploadVolumeFactor = 1; - - var title = row.QuerySelector("td:nth-child(2)"); - foreach (var element in title.QuerySelectorAll("span, strong, div, br")) - { - element.Remove(); - } - - release.Title = ParseUtil.NormalizeMultiSpaces(title.TextContent.Replace(" - ]", "]")); - - //change "Season #" to "S##" for TV shows - if (catStr == "10") - { - release.Title = Regex.Replace(release.Title, - @"Season (\d+)", - m => string.Format("S{0:00}", - int.Parse(m.Groups[1].Value))); - } - - torrentInfos.Add(release); - } - - return torrentInfos.ToArray(); - } - - public Action, DateTime?> CookiesUpdater { get; set; } - } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/MoreThanTV.cs b/src/NzbDrone.Core/Indexers/Definitions/MoreThanTV.cs deleted file mode 100644 index 9b4d10fd5..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/MoreThanTV.cs +++ /dev/null @@ -1,310 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using AngleSharp.Dom; -using AngleSharp.Html.Dom; -using AngleSharp.Html.Parser; -using NLog; -using NzbDrone.Common.Http; -using NzbDrone.Core.Configuration; -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; - -[Obsolete("Converted to Torznab")] -public class MoreThanTV : TorrentIndexerBase -{ - public override string Name => "MoreThanTV"; - public override string[] IndexerUrls => new[] { "https://www.morethantv.me/" }; - public override string Description => "Private torrent tracker for TV / MOVIES"; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override IndexerCapabilities Capabilities => SetCapabilities(); - public override bool FollowRedirect => true; - - public MoreThanTV(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - => new MoreThanTVRequestGenerator(Settings, Capabilities); - - public override IParseIndexerResponse GetParser() - => new MoreThanTVParser - { - Settings = Settings - }; - - private IndexerCapabilities SetCapabilities() - { - var caps = new IndexerCapabilities - { - TvSearchParams = new List - { - TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep - }, - MovieSearchParams = new List - { - MovieSearchParam.Q - } - }; - - caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies"); - caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV"); - - return caps; - } - - protected override IDictionary GetCookies() - { - return CookieUtil.CookieHeaderToDictionary(Settings.Cookie); - } -} - -public class MoreThanTVRequestGenerator : IIndexerRequestGenerator -{ - private CookieTorrentBaseSettings Settings { get; } - private IndexerCapabilities Capabilities { get; } - - private NameValueCollection BrowserHeaders { get; } - - public MoreThanTVRequestGenerator(CookieTorrentBaseSettings settings, IndexerCapabilities capabilities) - { - Settings = settings; - Capabilities = capabilities; - BrowserHeaders = new NameValueCollection() - { - { "referer", settings.BaseUrl }, - { "Upgrade-Insecure-Requests", "1" }, - { "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36" } - }; - } - - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) - => PerformRequest(searchCriteria); - - public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) - => PerformRequest(searchCriteria); - - public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) - => PerformRequest(searchCriteria); - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - => PerformRequest(searchCriteria); - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - => PerformRequest(searchCriteria); - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } - - private IndexerPageableRequestChain PerformRequest(SearchCriteriaBase query) - { - var chain = new IndexerPageableRequestChain(); - - var requests = new List { new (new HttpRequest(GetTorrentSearchUrl(query)) { Headers = new HttpHeader(BrowserHeaders), AllowAutoRedirect = true }) }; - - if (query is TvSearchCriteria tvSearchCriteria) - { - // Always search for torrent groups (complete seasons) too - var seasonRegex = new Regex(@".*\s[Ss]{1}\d{2}([Ee]{1}\d{2,3})?$", RegexOptions.Compiled); - var seasonMatch = seasonRegex.Match(query.SanitizedSearchTerm); - if (seasonMatch.Success) - { - var seasonReplaceRegex = new Regex(@"[Ss]{1}\d{2}([Ee]{1}\d{2,3})?", RegexOptions.Compiled); - var newSearchQuery = seasonReplaceRegex.Replace(query.SanitizedSearchTerm, $"Season {tvSearchCriteria.Season}"); - requests.Add(new IndexerRequest(new HttpRequest(GetTorrentSearchUrl(query, newSearchQuery)) { Headers = new HttpHeader(BrowserHeaders), AllowAutoRedirect = true })); - } - } - - chain.Add(requests); - - return chain; - } - - private string GetTorrentSearchUrl(SearchCriteriaBase query, string overrideSearchTerm = null) - { - var qc = new NameValueCollection - { - { "action", "advanced" }, - { "sizetype", "gb" }, - { "sizerange", "0.01" }, - { "title", overrideSearchTerm ?? GetSearchString(query.SanitizedSearchTerm) } - }; - - switch (query) - { - case MovieSearchCriteria: - qc.Add("filter_cat[1]", "1"); // HD Movies - qc.Add("filter_cat[2]", "1"); // SD Movies - break; - case TvSearchCriteria: - qc.Add("filter_cat[3]", "1"); // HD Episode - qc.Add("filter_cat[4]", "1"); // SD Episode - qc.Add("filter_cat[5]", "1"); // HD Season - qc.Add("filter_cat[6]", "1"); // SD Season - break; - } - - return $"{Settings.BaseUrl}torrents/browse?{qc.GetQueryString()}"; - } - - private string GetSearchString(string input) - { - input = input.Replace("Marvels", "Marvel"); // strip 's for better results - var regex = new Regex(@"(S\d{2})$", RegexOptions.Compiled); - return regex.Replace(input, "$1*"); // If we're just seaching for a season (no episode) append an * to include all episodes of that season. - } -} - -public class MoreThanTVParser : IParseIndexerResponse -{ - public CookieTorrentBaseSettings Settings { get; init; } - - public IList ParseResponse(IndexerResponse indexerResponse) - { - var releases = new List(); - - try - { - var parser = new HtmlParser(); - using var document = parser.ParseDocument(indexerResponse.Content); - var torrents = document.QuerySelectorAll("#torrent_table > tbody > tr.torrent"); - var movies = new[] { "movie" }; - var tv = new[] { "season", "episode" }; - - // Loop through all torrents checking for groups - foreach (var torrent in torrents) - { - // Parse required data - var downloadAnchor = torrent.QuerySelector("span a[href^=\"/torrents.php?action=download\"]"); - - if (downloadAnchor == null) - { - continue; - } - - var title = downloadAnchor.ParentElement.ParentElement.ParentElement.QuerySelector("a[class=\"overlay_torrent\"]").TextContent.Trim(); - title = CleanUpTitle(title); - - var category = torrent.QuerySelector(".cats_col div").GetAttribute("title"); - - // default to Other - var indexerCategory = NewznabStandardCategory.Other; - - if (movies.Any(category.Contains)) - { - indexerCategory = NewznabStandardCategory.Movies; - } - else if (tv.Any(category.Contains)) - { - indexerCategory = NewznabStandardCategory.TV; - } - - releases.Add(GetReleaseInfo(torrent, downloadAnchor, title, indexerCategory)); - } - - return releases; - } - catch (Exception ex) - { - throw new Exception("Error while parsing torrent response", ex); - } - } - - /// - /// Gather Release info from torrent table. Target using css - /// - /// - /// - /// - /// - /// - private ReleaseInfo GetReleaseInfo(IElement row, IElement downloadAnchor, string title, IndexerCategory category) - { - // count from bottom - const int FILES_COL = 7; - /*const int COMMENTS_COL = 7;*/ - const int DATE_COL = 6; - const int FILESIZE_COL = 5; - const int SNATCHED_COL = 4; - const int SEEDS_COL = 3; - const int LEECHERS_COL = 2; - /*const int USER_COL = 1;*/ - - var downloadAnchorHref = (downloadAnchor as IHtmlAnchorElement).Href; - var queryParams = HttpUtility.ParseQueryString(downloadAnchorHref, Encoding.UTF8); - var torrentId = queryParams["id"]; - - var qFiles = row.QuerySelector("td:nth-last-child(" + FILES_COL + ")").TextContent; - - var fileCount = ParseUtil.CoerceInt(qFiles); - var qPublishDate = row.QuerySelector("td:nth-last-child(" + DATE_COL + ") .time").Attributes["title"].Value; - var publishDate = DateTime.ParseExact(qPublishDate, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); - var qPoster = row.QuerySelector("div.tp-banner img")?.GetAttribute("src"); - var poster = (qPoster != null && !qPoster.Contains("caticons")) ? qPoster : null; - var description = row.QuerySelector("div.tags")?.TextContent.Trim(); - var fileSize = row.QuerySelector("td:nth-last-child(" + FILESIZE_COL + ")").TextContent.Trim(); - var snatched = row.QuerySelector("td:nth-last-child(" + SNATCHED_COL + ")").TextContent.Trim(); - var seeds = row.QuerySelector("td:nth-last-child(" + SEEDS_COL + ")").TextContent.Trim(); - var leechs = row.QuerySelector("td:nth-last-child(" + LEECHERS_COL + ")").TextContent.Trim(); - - if (fileSize.Length <= 0 || snatched.Length <= 0 || seeds.Length <= 0 || leechs.Length <= 0) - { - // Size (xx.xx GB[ (Max)]) Snatches (xx) Seeders (xx) Leechers (xx) - throw new Exception($"We expected 4 torrent datas."); - } - - var detailUrl = $"{Settings.BaseUrl}details.php"; - - var size = ParseUtil.GetBytes(fileSize); - var grabs = int.Parse(snatched, NumberStyles.AllowThousands, CultureInfo.InvariantCulture); - var seeders = int.Parse(seeds, NumberStyles.AllowThousands, CultureInfo.InvariantCulture); - var leechers = int.Parse(leechs, NumberStyles.AllowThousands, CultureInfo.InvariantCulture); - var detailsUrl = $"{detailUrl}?torrentid={torrentId}"; - var downloadUrl = $"{detailUrl}?action=download&id={torrentId}"; - var categories = new List { category }; - - return new TorrentInfo - { - Title = title, - Categories = categories, - DownloadUrl = downloadUrl, - PublishDate = publishDate, - PosterUrl = poster, - Description = description, - Seeders = seeders, - Peers = seeders + leechers, - Files = fileCount, - Size = size, - Grabs = grabs, - Guid = downloadUrl, - InfoUrl = detailsUrl, - DownloadVolumeFactor = 0, // ratioless tracker - UploadVolumeFactor = 1 - }; - } - - /// - /// Clean Up any title stuff - /// - /// - /// - private string CleanUpTitle(string title) - { - return title - .Replace(".", " ") - .Replace("4K", "2160p"); // sonarr cleanup - } - - public Action, DateTime?> CookiesUpdater { get; set; } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/PirateTheNet.cs b/src/NzbDrone.Core/Indexers/Definitions/PirateTheNet.cs deleted file mode 100644 index 53adf8e30..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/PirateTheNet.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using AngleSharp.Html.Parser; -using Newtonsoft.Json; -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; -[Obsolete("PirateTheNet has shutdown 2023-10-14")] -public class PirateTheNet : TorrentIndexerBase -{ - public override string Name => "PirateTheNet"; - public override string[] IndexerUrls => new[] { "https://piratethenet.org/" }; - public override string[] LegacyUrls => new[] { "http://piratethenet.org/" }; - public override string Description => "PirateTheNet (PTN) is a ratioless movie tracker."; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override IndexerCapabilities Capabilities => SetCapabilities(); - private string LoginUrl => Settings.BaseUrl + "takelogin.php"; - private string CaptchaUrl => Settings.BaseUrl + "simpleCaptcha.php?numImages=1"; - - public PirateTheNet(IIndexerHttpClient httpClient, - IEventAggregator eventAggregator, - IIndexerStatusService indexerStatusService, - IConfigService configService, - Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new PirateTheNetRequestGenerator(Settings, Capabilities); - } - - public override IParseIndexerResponse GetParser() - { - return new PirateTheNetParser(Settings, Capabilities.Categories); - } - - protected override async Task DoLogin() - { - var captchaPage = await ExecuteAuth(new HttpRequest(CaptchaUrl)); - - var captchaResponse = JsonConvert.DeserializeAnonymousType(captchaPage.Content, new - { - images = new[] { new { hash = string.Empty } } - }); - - var requestBuilder = new HttpRequestBuilder(LoginUrl) - { - LogResponseContent = true, - AllowAutoRedirect = true, - Method = HttpMethod.Post - }; - - var authLoginRequest = requestBuilder - .SetCookies(captchaPage.GetCookies()) - .AddFormParameter("username", Settings.Username) - .AddFormParameter("password", Settings.Password) - .AddFormParameter("captchaSelection", captchaResponse.images[0].hash) - .SetHeader("Content-Type", "application/x-www-form-urlencoded") - .SetHeader("Referer", LoginUrl) - .Build(); - - var response = await ExecuteAuth(authLoginRequest); - - if (CheckIfLoginNeeded(response)) - { - throw new IndexerAuthException("Login Failed."); - } - - var cookies = response.GetCookies(); - UpdateCookies(cookies, DateTime.Now.AddDays(30)); - - _logger.Debug("Authentication succeeded."); - } - - protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) - { - return !httpResponse.Content.Contains("logout.php"); - } - - private IndexerCapabilities SetCapabilities() - { - var caps = new IndexerCapabilities - { - MovieSearchParams = new List - { - MovieSearchParam.Q, MovieSearchParam.ImdbId - } - }; - - caps.Categories.AddCategoryMapping("1080P", NewznabStandardCategory.MoviesHD, "1080P"); - caps.Categories.AddCategoryMapping("2160P", NewznabStandardCategory.MoviesHD, "2160P"); - caps.Categories.AddCategoryMapping("720P", NewznabStandardCategory.MoviesHD, "720P"); - caps.Categories.AddCategoryMapping("BDRip", NewznabStandardCategory.MoviesSD, "BDRip"); - caps.Categories.AddCategoryMapping("BluRay", NewznabStandardCategory.MoviesBluRay, "BluRay"); - caps.Categories.AddCategoryMapping("BRRip", NewznabStandardCategory.MoviesSD, "BRRip"); - caps.Categories.AddCategoryMapping("DVDR", NewznabStandardCategory.MoviesDVD, "DVDR"); - caps.Categories.AddCategoryMapping("DVDRip", NewznabStandardCategory.MoviesSD, "DVDRip"); - caps.Categories.AddCategoryMapping("FLAC", NewznabStandardCategory.AudioLossless, "FLAC OST"); - caps.Categories.AddCategoryMapping("MP3", NewznabStandardCategory.AudioMP3, "MP3 OST"); - caps.Categories.AddCategoryMapping("MP4", NewznabStandardCategory.MoviesOther, "MP4"); - caps.Categories.AddCategoryMapping("Packs", NewznabStandardCategory.MoviesOther, "Packs"); - caps.Categories.AddCategoryMapping("R5", NewznabStandardCategory.MoviesDVD, "R5 / SCR"); - caps.Categories.AddCategoryMapping("Remux", NewznabStandardCategory.MoviesOther, "Remux"); - caps.Categories.AddCategoryMapping("TVRip", NewznabStandardCategory.MoviesOther, "TVRip"); - caps.Categories.AddCategoryMapping("WebRip", NewznabStandardCategory.MoviesWEBDL, "WebRip"); - - return caps; - } -} - -public class PirateTheNetRequestGenerator : IIndexerRequestGenerator -{ - private readonly UserPassTorrentBaseSettings _settings; - private readonly IndexerCapabilities _capabilities; - - public PirateTheNetRequestGenerator(UserPassTorrentBaseSettings settings, IndexerCapabilities capabilities) - { - _settings = settings; - _capabilities = capabilities; - } - - public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories, searchCriteria.FullImdbId)); - - return pageableRequests; - } - - public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories)); - - return pageableRequests; - } - - private IEnumerable GetPagedRequests(string term, int[] categories, string imdbId = null) - { - var parameters = new NameValueCollection - { - { "action", "torrentstable" }, - { "viewtype", "0" }, - { "visiblecategories", "Action,Adventure,Animation,Biography,Comedy,Crime,Documentary,Drama,Family,Fantasy,History,Horror,Kids,Music,Mystery,Packs,Romance,Sci-Fi,Short,Sports,Thriller,War,Western" }, - { "page", "1" }, - { "visibility", "showall" }, - { "compression", "showall" }, - { "sort", "added" }, - { "order", "DESC" }, - { "titleonly", "true" }, - { "packs", "showall" }, - { "bookmarks", "showall" }, - { "subscriptions", "showall" }, - { "skw", "showall" } - }; - - if (imdbId.IsNotNullOrWhiteSpace()) - { - parameters.Set("advancedsearchparameters", $"[imdb={imdbId}]"); - } - else if (term.IsNotNullOrWhiteSpace()) - { - parameters.Set("searchstring", term); - } - - var queryCats = _capabilities.Categories.MapTorznabCapsToTrackers(categories); - if (queryCats.Any()) - { - parameters.Set("hiddenqualities", string.Join(",", queryCats)); - } - - var searchUrl = _settings.BaseUrl + "torrentsutils.php"; - - if (parameters.Count > 0) - { - searchUrl += $"?{parameters.GetQueryString()}"; - } - - var request = new IndexerRequest(searchUrl, HttpAccept.Html); - - yield return request; - } - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } -} - -public class PirateTheNetParser : IParseIndexerResponse -{ - private readonly UserPassTorrentBaseSettings _settings; - private readonly IndexerCapabilitiesCategories _categories; - - public PirateTheNetParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) - { - _settings = settings; - _categories = categories; - } - - public IList ParseResponse(IndexerResponse indexerResponse) - { - var releaseInfos = new List(); - - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(indexerResponse.Content); - - var rows = dom.QuerySelectorAll("table.main > tbody > tr"); - foreach (var row in rows.Skip(1)) - { - var qDetails = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)"); - var title = qDetails?.GetAttribute("alt")?.Trim(); - - var infoUrl = _settings.BaseUrl + qDetails?.GetAttribute("href")?.TrimStart('/'); - var downloadUrl = _settings.BaseUrl + row.QuerySelector("td > a:has(img[alt=\"Download Torrent\"])")?.GetAttribute("href")?.TrimStart('/'); - - var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)")?.TextContent); - var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)")?.TextContent); - - var cat = row.QuerySelector("td:nth-of-type(1) > a > img")?.GetAttribute("src")?.Split('/').Last().Split('.').First() ?? "packs"; - - var release = new TorrentInfo - { - Guid = infoUrl, - InfoUrl = infoUrl, - DownloadUrl = downloadUrl, - Title = title, - Categories = _categories.MapTrackerCatToNewznab(cat), - Seeders = seeders, - Peers = seeders + leechers, - Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-of-type(7)")?.TextContent.Trim()), - Files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(4)")?.TextContent), - Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)")?.TextContent), - DownloadVolumeFactor = 0, // ratioless - UploadVolumeFactor = 1, - MinimumRatio = 1, - MinimumSeedTime = 259200, // 72 hours - }; - - var added = row.QuerySelector("td:nth-of-type(6) > nobr")?.TextContent.Trim(); - if (added.StartsWith("Today ")) - { - release.PublishDate = DateTime.Now.Date + DateTime.ParseExact(added.Split(" ", 2).Last(), "hh:mm tt", CultureInfo.InvariantCulture).TimeOfDay; - } - else if (added.StartsWith("Yesterday ")) - { - release.PublishDate = DateTime.Now.AddDays(-1).Date + DateTime.ParseExact(added.Split(" ", 2).Last(), "hh:mm tt", CultureInfo.InvariantCulture).TimeOfDay; - } - else - { - release.PublishDate = DateTime.ParseExact(added, "MMM d yyyy hh:mm tt", CultureInfo.InvariantCulture); - } - - releaseInfos.Add(release); - } - - return releaseInfos.ToArray(); - } - - public Action, DateTime?> CookiesUpdater { get; set; } -} diff --git a/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs b/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs deleted file mode 100644 index 4b75923a0..000000000 --- a/src/NzbDrone.Core/Indexers/Definitions/TVVault.cs +++ /dev/null @@ -1,287 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Web; -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 -{ - [Obsolete("Remove per Site Request Prowlarr Issue 573")] - public class TVVault : TorrentIndexerBase - { - public override string Name => "TVVault"; - public override string[] IndexerUrls => new[] { "https://tv-vault.me/" }; - private string LoginUrl => Settings.BaseUrl + "login.php"; - public override string Description => "TV-Vault is a very unique tracker dedicated for old TV shows, TV movies and documentaries."; - public override string Language => "en-US"; - public override Encoding Encoding => Encoding.UTF8; - public override IndexerPrivacy Privacy => IndexerPrivacy.Private; - public override IndexerCapabilities Capabilities => SetCapabilities(); - public override TimeSpan RateLimit => TimeSpan.FromSeconds(5); - - public TVVault(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) - : base(httpClient, eventAggregator, indexerStatusService, configService, logger) - { - } - - public override IIndexerRequestGenerator GetRequestGenerator() - { - return new TVVaultRequestGenerator { Settings = Settings, Capabilities = Capabilities }; - } - - public override IParseIndexerResponse GetParser() - { - return new TVVaultParser(Settings, Capabilities.Categories); - } - - protected override async Task DoLogin() - { - var requestBuilder = new HttpRequestBuilder(LoginUrl) - { - LogResponseContent = true, - AllowAutoRedirect = true, - Method = HttpMethod.Post - }; - - var cookies = Cookies; - Cookies = null; - - var authLoginRequest = requestBuilder - .AddFormParameter("username", Settings.Username) - .AddFormParameter("password", Settings.Password) - .AddFormParameter("keeplogged", "1") - .AddFormParameter("login", "Log+In!") - .SetHeader("Content-Type", "application/x-www-form-urlencoded") - .SetHeader("Referer", LoginUrl) - .Build(); - - var response = await ExecuteAuth(authLoginRequest); - - if (CheckIfLoginNeeded(response)) - { - var parser = new HtmlParser(); - using var dom = parser.ParseDocument(response.Content); - var errorMessage = dom.QuerySelector("form#loginform")?.TextContent.Trim(); - - throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report."); - } - - cookies = response.GetCookies(); - UpdateCookies(cookies, DateTime.Now.AddDays(30)); - - _logger.Debug("TVVault authentication succeeded."); - } - - protected override bool CheckIfLoginNeeded(HttpResponse httpResponse) - { - return !httpResponse.Content.Contains("logout.php"); - } - - 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 - }, - Flags = new List - { - IndexerFlag.FreeLeech - } - }; - - caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TV); - caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.Movies); - caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVHD); - caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVSD); - - return caps; - } - } - - public class TVVaultRequestGenerator : IIndexerRequestGenerator - { - public UserPassTorrentBaseSettings Settings { get; set; } - public IndexerCapabilities Capabilities { get; set; } - - private IEnumerable GetPagedRequests(string term, int[] categories, string imdbId = null) - { - var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/')); - - var qc = new NameValueCollection - { - { "order_by", "s3" }, - { "order_way", "DESC" }, - { "disablegrouping", "1" } - }; - - if (imdbId.IsNotNullOrWhiteSpace()) - { - qc.Add("action", "advanced"); - qc.Add("imdbid", imdbId); - } - else if (!string.IsNullOrWhiteSpace(term)) - { - qc.Add("searchstr", StripSearchString(term)); - } - - var catList = Capabilities.Categories.MapTorznabCapsToTrackers(categories); - - foreach (var cat in catList) - { - qc.Add($"filter_cat[{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) - { - return new IndexerPageableRequestChain(); - } - - 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) - { - return new IndexerPageableRequestChain(); - } - - public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) - { - var pageableRequests = new IndexerPageableRequestChain(); - - pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); - - return pageableRequests; - } - - private string StripSearchString(string term) - { - // Search does not support searching with episode numbers so strip it if we have one - // AND filter the result later to achieve the proper result - term = Regex.Replace(term, @"[S|E]\d\d", string.Empty); - return term.Trim(); - } - - public Func> GetCookies { get; set; } - public Action, DateTime?> CookiesUpdater { get; set; } - } - - public class TVVaultParser : IParseIndexerResponse - { - private readonly UserPassTorrentBaseSettings _settings; - private readonly IndexerCapabilitiesCategories _categories; - - public TVVaultParser(UserPassTorrentBaseSettings settings, IndexerCapabilitiesCategories categories) - { - _settings = settings; - _categories = categories; - } - - public IList ParseResponse(IndexerResponse indexerResponse) - { - var torrentInfos = new List(); - - var parser = new HtmlParser(); - using var doc = parser.ParseDocument(indexerResponse.Content); - - // get params to build download link (user could be banned without those params) - var rssFeedUri = new Uri(_settings.BaseUrl + doc.QuerySelector("link[href^=\"/feeds.php?feed=\"]") - .GetAttribute("href")); - var rssFeedQuery = HttpUtility.ParseQueryString(rssFeedUri.Query); - var downloadLinkExtraParams = "&authkey=" + rssFeedQuery["authkey"] + "&torrent_pass=" + rssFeedQuery["passkey"]; - - var rows = doc.QuerySelectorAll("table.torrent_table > tbody > tr.torrent"); - - foreach (var row in rows) - { - var qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]"); - var title = qDetailsLink.TextContent; - - var description = qDetailsLink.NextSibling.TextContent.Trim(); - title += " " + description; - var details = _settings.BaseUrl + qDetailsLink.GetAttribute("href"); - var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last(); - var link = _settings.BaseUrl + "torrents.php?action=download&id=" + torrentId + downloadLinkExtraParams; - - var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent); - var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent); - var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent); - var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent); - var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent); - var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent); - - var dlVolumeFactor = row.QuerySelector("strong.freeleech_normal") != null ? 0 : 1; - - var category = new List { TvCategoryFromQualityParser.ParseTvShowQuality(description) }; - - var release = new TorrentInfo - { - MinimumRatio = 1, - MinimumSeedTime = 0, - Description = description, - Title = title, - PublishDate = publishDate, - Categories = category, - DownloadUrl = link, - InfoUrl = details, - Guid = link, - Seeders = seeders, - Peers = leechers + seeders, - Size = size, - Grabs = grabs, - Files = files, - DownloadVolumeFactor = dlVolumeFactor, - UploadVolumeFactor = 1 - }; - - torrentInfos.Add(release); - } - - return torrentInfos.ToArray(); - } - - public Action, DateTime?> CookiesUpdater { get; set; } - } -}