Cleanup obsolete definitions for ANT/ABB/BB/MTV/PTN/TVV

pull/2040/head
Bogdan 9 months ago
parent 7287abc77c
commit ede9879c99

@ -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<UserPassTorrentBaseSettings>
{
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>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
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<IndexerRequest> 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<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, 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<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
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<IndexerCategory>
{
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<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
}

@ -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<NoAuthTorrentBaseSettings>
{
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<byte[]> Download(Uri link)
{
var request = new HttpRequestBuilder(link.ToString())
.SetCookies(GetCookies() ?? new Dictionary<string, string>())
.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>
{
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<IndexerRequest> 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<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, 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<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var releaseInfos = new List<ReleaseInfo>();
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<string>();
var release = new TorrentInfo
{
Guid = infoUrl,
InfoUrl = infoUrl,
DownloadUrl = infoUrl,
Title = CleanTitle(title),
Categories = new List<IndexerCategory> { 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<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

@ -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<UserPassTorrentBaseSettings>
{
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<string>();
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>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
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<IndexerRequest> 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<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, 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<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
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<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
}

@ -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<CookieTorrentBaseSettings>
{
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>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV");
return caps;
}
protected override IDictionary<string, string> 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<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
private IndexerPageableRequestChain PerformRequest(SearchCriteriaBase query)
{
var chain = new IndexerPageableRequestChain();
var requests = new List<IndexerRequest> { 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<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var releases = new List<ReleaseInfo>();
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);
}
}
/// <summary>
/// Gather Release info from torrent table. Target using css
/// </summary>
/// <param name="row"></param>
/// <param name="downloadAnchor"></param>
/// <param name="title"></param>
/// <param name="category"></param>
/// <returns></returns>
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<IndexerCategory> { 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
};
}
/// <summary>
/// Clean Up any title stuff
/// </summary>
/// <param name="title"></param>
/// <returns></returns>
private string CleanUpTitle(string title)
{
return title
.Replace(".", " ")
.Replace("4K", "2160p"); // sonarr cleanup
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

@ -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<UserPassTorrentBaseSettings>
{
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>
{
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<IndexerRequest> 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<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, 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<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var releaseInfos = new List<ReleaseInfo>();
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<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

@ -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<UserPassTorrentBaseSettings>
{
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>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId
},
Flags = new List<IndexerFlag>
{
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<IndexerRequest> 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<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, 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<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
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<IndexerCategory> { 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<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
}
Loading…
Cancel
Save