using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using AngleSharp.Html.Parser; using Jackett.Common.Models; using Jackett.Common.Models.IndexerConfig; using Jackett.Common.Services.Interfaces; using Jackett.Common.Utils; using Jackett.Common.Utils.Clients; using Newtonsoft.Json.Linq; using NLog; namespace Jackett.Common.Indexers { [ExcludeFromCodeCoverage] public class PixelHD : BaseWebIndexer { private string LoginUrl => SiteLink + "login.php"; private string BrowseUrl => SiteLink + "torrents.php"; private new ConfigurationDataCaptchaLogin configData { get => (ConfigurationDataCaptchaLogin)base.configData; set => base.configData = value; } private string input_captcha = null; private string input_username = null; private string input_password = null; public PixelHD(IIndexerConfigurationService configService, WebClient webClient, Logger logger, IProtectionService ps, ICacheService cs) : base(id: "pixelhd", name: "PiXELHD", description: "PixelHD (PxHD) is a Private Torrent Tracker for HD .MP4 MOVIES / TV", link: "https://pixelhd.me/", caps: new TorznabCapabilities { MovieSearchParams = new List { MovieSearchParam.Q, MovieSearchParam.ImdbId } }, configService: configService, logger: logger, p: ps, cacheService: cs, client: webClient, configData: new ConfigurationDataCaptchaLogin() ) { Encoding = Encoding.UTF8; Language = "en-us"; Type = "private"; AddCategoryMapping(1, TorznabCatType.MoviesHD); } public override async Task GetConfigurationForSetup() { var loginPage = await RequestWithCookiesAsync(LoginUrl, string.Empty); var LoginParser = new HtmlParser(); var LoginDocument = LoginParser.ParseDocument(loginPage.ContentString); configData.CaptchaCookie.Value = loginPage.Cookies; var catchaImg = LoginDocument.QuerySelector("img[alt=\"FuckOff Image\"]"); if (catchaImg != null) { var catchaInput = LoginDocument.QuerySelector("input[maxlength=\"6\"]"); input_captcha = catchaInput.GetAttribute("name"); var captchaImage = await RequestWithCookiesAsync(SiteLink + catchaImg.GetAttribute("src"), loginPage.Cookies, RequestType.GET, LoginUrl); configData.CaptchaImage.Value = captchaImage.ContentBytes; } else { input_captcha = null; configData.CaptchaImage.Value = null; } var usernameInput = LoginDocument.QuerySelector("input[maxlength=\"20\"]"); input_username = usernameInput.GetAttribute("name"); var passwordInput = LoginDocument.QuerySelector("input[maxlength=\"40\"]"); input_password = passwordInput.GetAttribute("name"); return configData; } public override async Task ApplyConfiguration(JToken configJson) { LoadValuesFromJson(configJson); var pairs = new Dictionary { { input_username, configData.Username.Value }, { input_password, configData.Password.Value }, { "keeplogged", "1" } }; if (input_captcha != null) pairs.Add(input_captcha, configData.CaptchaText.Value); var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CaptchaCookie.Value, true); await ConfigureIfOK(result.Cookies, result.ContentString.Contains("logout.php"), () => { var LoginParser = new HtmlParser(); var LoginDocument = LoginParser.ParseDocument(result.ContentString); var errorMessage = LoginDocument.QuerySelector("span.warning[id!=\"no-cookies\"]:has(br)").TextContent; throw new ExceptionWithConfigData(errorMessage, configData); }); return IndexerConfigurationStatus.RequiresTesting; } protected override async Task> PerformQuery(TorznabQuery query) { var releases = new List(); var searchString = query.GetQueryString(); var queryCollection = new NameValueCollection { { "order_by", "time" }, { "order_way", "desc" } }; if (!string.IsNullOrWhiteSpace(query.ImdbID)) { queryCollection.Add("imdbid", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("groupname", searchString); } //Add anyway after checking above? queryCollection.Add("groupname", searchString); var searchUrl = BrowseUrl + "?" + queryCollection.GetQueryString(); var results = await RequestWithCookiesAsync(searchUrl); if (results.IsRedirect) { // re login await GetConfigurationForSetup(); await ApplyConfiguration(null); results = await RequestWithCookiesAsync(searchUrl); } var IMDBRegEx = new Regex(@"tt(\d+)", RegexOptions.Compiled); var hParser = new HtmlParser(); var ResultDocument = hParser.ParseDocument(results.ContentString); try { var Groups = ResultDocument.QuerySelectorAll("div.browsePoster"); foreach (var Group in Groups) { var groupPoster = Group.QuerySelector("img.classBrowsePoster"); var poster = new Uri(SiteLink + groupPoster.GetAttribute("src")); long? IMDBId = null; var imdbLink = Group.QuerySelector("a[href*=\"www.imdb.com/title/tt\"]"); if (imdbLink != null) { var IMDBMatch = IMDBRegEx.Match(imdbLink.GetAttribute("href")); IMDBId = ParseUtil.CoerceLong(IMDBMatch.Groups[1].Value); } var group = Group.QuerySelector("strong:has(a[title=\"View Torrent\"])").TextContent.Replace(" ]", "]"); var Rows = Group.QuerySelectorAll("tr.group_torrent:has(a[href^=\"torrents.php?id=\"])"); foreach (var Row in Rows) { var title = Row.QuerySelector("a[href^=\"torrents.php?id=\"]"); var added = Row.QuerySelector("td:nth-child(3)"); var Size = Row.QuerySelector("td:nth-child(4)"); var Grabs = Row.QuerySelector("td:nth-child(6)"); var Seeders = Row.QuerySelector("td:nth-child(7)"); var Leechers = Row.QuerySelector("td:nth-child(8)"); var link = new Uri(SiteLink + Row.QuerySelector("a[href^=\"torrents.php?action=download\"]").GetAttribute("href")); var seeders = ParseUtil.CoerceInt(Seeders.TextContent); var details = new Uri(SiteLink + title.GetAttribute("href")); var size = ReleaseInfo.GetBytes(Size.TextContent); var leechers = ParseUtil.CoerceInt(Leechers.TextContent); var grabs = ParseUtil.CoerceLong(Grabs.TextContent); var publishDate = DateTimeUtil.FromTimeAgo(added.TextContent); var release = new ReleaseInfo { MinimumRatio = 1, MinimumSeedTime = 72 * 60 * 60, Title = group + " " + title.TextContent, Category = new List { TorznabCatType.MoviesHD.ID }, Link = link, Details = details, Guid = link, Size = size, Seeders = seeders, Peers = leechers + seeders, Grabs = grabs, PublishDate = publishDate, Poster = poster, Imdb = IMDBId }; releases.Add(release); } } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return releases; } } }