protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchurls = new List <string>(); var searchUrl = SearchUrl;// string.Format(SearchUrl, HttpUtility.UrlEncode())); var queryCollection = new NameValueCollection(); var searchString = query.GetQueryString(); foreach (var cat in MapTorznabCapsToTrackers(query)) { searchUrl += "category%5B%5D=" + cat + "&"; } if (query.ImdbID != null) { queryCollection.Add("search", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } queryCollection.Add("active", "0"); queryCollection.Add("options", "0"); searchUrl += queryCollection.GetQueryString().Replace("(", "%28").Replace(")", "%29"); // maually url encode brackets to prevent "hacking" detection var results = await RequestStringWithCookiesAndRetry(searchUrl); try { CQ dom = results.Content; ReleaseInfo release; CQ userInfo = dom[".mainmenu > table > tbody > tr:has(td[title=\"Active-Torrents\"])"][0].Cq(); string rank = userInfo.Find("td:nth-child(2)").Text().Substring(6); HashSet <string> freeleechRanks = new HashSet <string>(StringComparer.OrdinalIgnoreCase); freeleechRanks.Add("VIP"); freeleechRanks.Add("Uploader"); freeleechRanks.Add("HD Internal"); freeleechRanks.Add("Moderator"); freeleechRanks.Add("Administrator"); freeleechRanks.Add("Owner"); bool hasFreeleech = freeleechRanks.Contains(rank); var rows = dom[".mainblockcontenttt > tbody > tr:has(a[href^=\"details.php?id=\"])"]; foreach (var row in rows) { CQ qRow = row.Cq(); release = new ReleaseInfo(); release.Title = qRow.Find("td.mainblockcontent b a").Text(); release.Description = qRow.Find("td:nth-child(3) > span").Text(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; // 48 hours int tdIndex = 0; if (qRow.Find("td:nth-last-child(1)").Text() == "Edit") { tdIndex = 1; } // moderators get additional delete, recomend and like links if (qRow.Find("td:nth-last-child(4)").Text() == "Edit") { tdIndex = 4; } // Sometimes the uploader column is missing if (ParseUtil.TryCoerceInt(qRow.Find($"td:nth-last-child({tdIndex + 3})").Text(), out int seeders)) { release.Seeders = seeders; if (ParseUtil.TryCoerceInt(qRow.Find($"td:nth-last-child({tdIndex + 2})").Text(), out int peers)) { release.Peers = peers + release.Seeders; } } // Sometimes the grabs column is missing if (ParseUtil.TryCoerceLong(qRow.Find($"td:nth-last-child({tdIndex + 1})").Text(), out long grabs)) { release.Grabs = grabs; } string fullSize = qRow.Find("td.mainblockcontent").Get(6).InnerText; release.Size = ReleaseInfo.GetBytes(fullSize); release.Guid = new Uri(SiteLink + qRow.Find("td.mainblockcontent b a").Attr("href")); release.Link = new Uri(SiteLink + qRow.Find("td.mainblockcontent").Get(3).FirstChild.GetAttribute("href")); release.Comments = new Uri(SiteLink + qRow.Find("td.mainblockcontent b a").Attr("href")); string[] dateSplit = qRow.Find("td.mainblockcontent").Get(5).InnerHTML.Split(','); string dateString = dateSplit[1].Substring(0, dateSplit[1].IndexOf('>')).Trim(); release.PublishDate = DateTime.ParseExact(dateString, "dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture).ToLocalTime(); string category = qRow.Find("td:eq(0) a").Attr("href").Replace("torrents.php?category=", ""); release.Category = MapTrackerCatToNewznab(category); release.UploadVolumeFactor = 1; if (qRow.Find("img[alt=\"Free Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 0; } else if (hasFreeleech) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("img[alt=\"Silver Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else if (qRow.Find("img[alt=\"Bronze Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0.75; } else if (qRow.Find("img[alt=\"Blue Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0.25; } else { release.DownloadVolumeFactor = 1; } var imdblink = qRow.Find("a[href*=\"www.imdb.com/title/\"]").Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdblink); releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
private async Task <IEnumerable <ReleaseInfo> > performRegularQuery(TorznabQuery query, string hebName = null) { var releases = new List <ReleaseInfo>(); var searchurls = new List <string>(); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection(); var searchString = query.GetQueryString(); if (query.IsImdbQuery) { searchString = query.ImdbID; } if (hebName != null) { searchString = hebName + " - עונה " + query.Season + " פרק " + query.Episode; } searchUrl += "?"; if (!string.IsNullOrWhiteSpace(searchString)) { var strEncoded = WebUtilityHelpers.UrlEncode(searchString, Encoding); searchUrl += "&query=" + strEncoded + "&matchquery=any"; } foreach (var cat in MapTorznabCapsToTrackers(query)) { searchUrl += "&c[]=" + cat; } var data = await RequestStringWithCookiesAndRetry(searchUrl); try { CQ dom = data.Content; var rows = dom["tr.box_torrent"]; foreach (var row in rows) { CQ qRow = row.Cq(); var release = new ReleaseInfo(); var main_title_link = qRow.Find("div.main_title > a"); release.Title = main_title_link.Attr("longtitle"); if (release.Title.IsNullOrEmptyOrWhitespace()) { release.Title = main_title_link.Text(); } release.MinimumRatio = 1; release.MinimumSeedTime = 172800; int seeders, peers; if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(7) > div").Text(), out seeders)) { release.Seeders = seeders; if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(8) > div").Text(), out peers)) { release.Peers = peers + release.Seeders; } } release.Grabs = ParseUtil.CoerceLong(qRow.Find("td:nth-child(5)").Text().Replace(",", "")); release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:nth-child(6)").Text().Replace(",", "")); release.Peers = ParseUtil.CoerceInt(qRow.Find("td:nth-child(7)").Text().Replace(",", "")) + release.Seeders; string fullSize = qRow.Find("td:nth-child(4)").Text(); release.Size = ReleaseInfo.GetBytes(fullSize); release.Comments = new Uri(SiteLink + qRow.Find("a.threadlink[href]").Attr("href")); release.Link = new Uri(SiteLink + qRow.Find("a:has(div.dlimg)").Attr("href")); release.Guid = release.Comments; try { release.BannerUrl = new Uri(qRow.Find("a[imgsrc]").Attr("imgsrc")); } catch (Exception) { // do nothing, some releases have invalid banner URLs, ignore the banners in this case } var dateStringAll = qRow.Find("div.up_info2")[0].ChildNodes.Last().ToString(); var dateParts = dateStringAll.Split(' '); string dateString = dateParts[dateParts.Length - 2] + " " + dateParts[dateParts.Length - 1]; release.PublishDate = DateTime.ParseExact(dateString, "dd/MM/yy HH:mm", CultureInfo.InvariantCulture); string categoryLink = qRow.Find("a[href^=\"/browse.php?cat=\"]").Attr("href"); var catid = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat"); release.Category = MapTrackerCatToNewznab(catid); if (qRow.Find("a[href^=\"?freeleech=1\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; var sub_title = qRow.Find("div.sub_title"); var imdb_link = sub_title.Find("span.imdb-inline > a"); release.Imdb = ParseUtil.GetLongFromString(imdb_link.Attr("href")); sub_title.Find("span.imdb-inline").Remove(); release.Description = sub_title.Text(); releases.Add(release); } } catch (Exception ex) { OnParseError(data.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchUrl = SearchUrl + string.Join( string.Empty, MapTorznabCapsToTrackers(query).Select(cat => $"category[]={cat}&")); var queryCollection = new NameValueCollection { { "search", query.ImdbID ?? query.GetQueryString() }, { "active", "0" }, { "options", "0" } }; // manually url encode parenthesis to prevent "hacking" detection searchUrl += queryCollection.GetQueryString().Replace("(", "%28").Replace(")", "%29"); var results = await RequestWithCookiesAndRetryAsync(searchUrl); try { var parser = new HtmlParser(); var dom = parser.ParseDocument(results.ContentString); var userInfo = dom.QuerySelector("table.navus tr"); var userRank = userInfo.Children[1].TextContent.Replace("Rank:", string.Empty).Trim(); var hasFreeleech = _freeleechRanks.Contains(userRank); var rows = dom.QuerySelectorAll("table.mainblockcontenttt tr:has(td.mainblockcontent)"); foreach (var row in rows.Skip(1)) { var mainLink = row.Children[2].QuerySelector("a"); var title = mainLink.TextContent; var comments = new Uri(SiteLink + mainLink.GetAttribute("href")); var bannerMatch = _bannerRegex.Match(mainLink.GetAttribute("onmouseover")); var banner = bannerMatch.Success ? new Uri(SiteLink + bannerMatch.Groups[1].Value.Replace("\\", "/")) : null; var link = new Uri(SiteLink + row.Children[4].FirstElementChild.GetAttribute("href")); var description = row.Children[2].QuerySelector("span").TextContent; var size = ReleaseInfo.GetBytes(row.Children[7].TextContent); var dateTag = row.Children[6].FirstElementChild; var dateString = string.Join(" ", dateTag.Attributes.Select(attr => attr.Name)); var publishDate = DateTime.ParseExact(dateString, "dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture).ToLocalTime(); var catStr = row.FirstElementChild.FirstElementChild.GetAttribute("href").Split('=')[1]; var cat = MapTrackerCatToNewznab(catStr); // Sometimes the uploader column is missing, so seeders, leechers, and grabs may be at a different index. // There's room for improvement, but this works for now. var endIndex = row.Children.Length; //Maybe use row.Children.Index(Node) after searching for an element instead? if (row.Children[endIndex - 1].TextContent == "Edit") { endIndex -= 1; } // moderators get additional delete, recommend and like links else if (row.Children[endIndex - 4].TextContent == "Edit") { endIndex -= 4; } int?seeders = null; int?peers = null; if (ParseUtil.TryCoerceInt(row.Children[endIndex - 3].TextContent, out var rSeeders)) { seeders = rSeeders; if (ParseUtil.TryCoerceInt(row.Children[endIndex - 2].TextContent, out var rLeechers)) { peers = rLeechers + rSeeders; } } var grabs = ParseUtil.TryCoerceLong(row.Children[endIndex - 1].TextContent, out var rGrabs) ? (long?)rGrabs : null; var dlVolumeFactor = 1.0; var upVolumeFactor = 1.0; if (row.QuerySelector("img[src$=\"no_ratio.png\"]") != null) { dlVolumeFactor = 0; upVolumeFactor = 0; } else if (hasFreeleech || row.QuerySelector("img[src$=\"free.png\"]") != null) { dlVolumeFactor = 0; } else if (row.QuerySelector("img[src$=\"50.png\"]") != null) { dlVolumeFactor = 0.5; } else if (row.QuerySelector("img[src$=\"25.png\"]") != null) { dlVolumeFactor = 0.75; } else if (row.QuerySelector("img[src$=\"75.png\"]") != null) { dlVolumeFactor = 0.25; } var imdbLink = row.QuerySelector("a[href*=\"www.imdb.com/title/\"]")?.GetAttribute("href"); var imdb = !string.IsNullOrWhiteSpace(imdbLink) ? ParseUtil.GetLongFromString(imdbLink) : null; var release = new ReleaseInfo { Title = title, Comments = comments, Guid = comments, Link = link, PublishDate = publishDate, Category = cat, Description = description, BannerUrl = banner, Imdb = imdb, Size = size, Grabs = grabs, Seeders = seeders, Peers = peers, DownloadVolumeFactor = dlVolumeFactor, UploadVolumeFactor = upVolumeFactor, MinimumRatio = 1, MinimumSeedTime = 172800 // 48 hours }; releases.Add(release); } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var qc = new NameValueCollection { { "incldead", "1" } }; if (query.IsImdbQuery) { qc.Add("titleonly", "0"); qc.Add("search", query.ImdbID); } else { qc.Add("titleonly", "1"); qc.Add("search", query.GetQueryString()); } var cats = MapTorznabCapsToTrackers(query); if (cats.Count > 0) { foreach (var cat in cats) { qc.Add($"c{cat}", "1"); } } var searchUrl = SearchUrl + "?" + qc.GetQueryString(); var results = await RequestWithCookiesAndRetryAsync(searchUrl); if (results.IsRedirect) // re-login { await ApplyConfiguration(null); results = await RequestWithCookiesAndRetryAsync(searchUrl); } try { var parser = new HtmlParser(); var dom = parser.ParseDocument(results.ContentString); var rows = dom.QuerySelectorAll("#torrents-table > tbody > tr"); foreach (var row in rows.Skip(1)) { var qDetails = row.QuerySelector(".br_right > a"); var details = new Uri(SiteLink + qDetails.GetAttribute("href")); var title = qDetails.QuerySelector("b").TextContent; var qLink = row.QuerySelector("td:nth-child(4) > a"); if (qLink == null) { continue; // support/donation banner } var link = new Uri(SiteLink + qLink.GetAttribute("href")); // dateString format "yyyy-MMM-dd hh:mm:ss" => eg "2015-04-25 23:38:12" var dateString = row.QuerySelector("td:nth-child(6) nobr").TextContent.Trim(); var publishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); var size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(7)").InnerHtml.Split('<').First().Trim()); var files = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(7) > a").TextContent); var grabs = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(8)").TextContent); var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)").TextContent); var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(10)").TextContent); var category = row.QuerySelector(".br_type > a").GetAttribute("href").Replace("browse.php?cat=", string.Empty); var qImdb = row.QuerySelector("a[href*=\"www.imdb.com/\"]"); var imdb = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null; var release = new ReleaseInfo { Details = details, Guid = details, Title = title, Link = link, PublishDate = publishDate, Size = size, Seeders = seeders, Peers = seeders + leechers, Grabs = grabs, Files = files, Category = MapTrackerCatToNewznab(category), Imdb = imdb, MinimumRatio = 1, MinimumSeedTime = 172800, // 48 hours UploadVolumeFactor = 1, DownloadVolumeFactor = 1 }; releases.Add(release); } } catch (Exception e) { OnParseError(results.ContentString, e); } return(releases); }
List <ReleaseInfo> parseTorrents(WebClientStringResult results, String seasonep, TorznabQuery query, int already_founded, int limit) { var releases = new List <ReleaseInfo>(); try { CQ dom = results.Content; ReleaseInfo release; var rows = dom[".box_torrent_all"].Find(".box_torrent"); // Check torrents only till we reach the query Limit for (int i = 0; (i < rows.Length && ((already_founded + releases.Count) < limit)); i++) { try { CQ qRow = rows[i].Cq(); var key = dom["link[rel=alternate]"].First().Attr("href").Split('=').Last(); release = new ReleaseInfo(); var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0); //if (torrentTxt == null) continue; release.Title = torrentTxt.GetAttribute("title"); release.Description = qRow.Find("span").Get(0).GetAttribute("title") + " " + qRow.Find("a.infolink").Text(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; string downloadLink = SiteLink + torrentTxt.GetAttribute("href"); string downloadId = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4); release.Link = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key); release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId); release.Guid = new Uri(release.Comments.ToString() + "#comments");; release.Seeders = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders; var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdblink); var banner = qRow.Find("img.infobar_ico").Attr("onmouseover"); if (banner != null) { Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled); var BannerMatch = BannerRegEx.Match(banner); var bannerurl = BannerMatch.Groups[1].Value; release.BannerUrl = new Uri(bannerurl); } release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture); string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' '); release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0])); string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href"); string cat = ParseUtil.GetArgumentFromQueryString(catlink, "tipus"); release.Category = MapTrackerCatToNewznab(cat); /* if the release name not contains the language we add it because it is know from category */ if (cat.Contains("hun") && !release.Title.Contains("hun")) { release.Title += ".hun"; } if (seasonep == null) { releases.Add(release); } else { if (query.MatchQueryStringAND(release.Title, null, seasonep)) { /* For sonnar if the search querry was english the title must be english also so we need to change the Description and Title */ var temp = release.Title; // releasedata everithing after Name.S0Xe0X String releasedata = release.Title.Split(new[] { seasonep }, StringSplitOptions.None)[1].Trim(); /* if the release name not contains the language we add it because it is know from category */ if (cat.Contains("hun") && !releasedata.Contains("hun")) { releasedata += ".hun"; } // release description contains [imdb: ****] but we only need the data before it for title String[] description = { release.Description, "" }; if (release.Description.Contains("[imdb:")) { description = release.Description.Split('['); description[1] = "[" + description[1]; } release.Title = (description[0].Trim() + "." + seasonep.Trim() + "." + releasedata.Trim('.')).Replace(' ', '.'); // if search is done for S0X than we dont want to put . between S0X and E0X Match match = Regex.Match(releasedata, @"^E\d\d?"); if (seasonep.Length == 3 && match.Success) { release.Title = (description[0].Trim() + "." + seasonep.Trim() + releasedata.Trim('.')).Replace(' ', '.'); } // add back imdb points to the description [imdb: 8.7] release.Description = temp + " " + description[1]; release.Description = release.Description.Trim(); releases.Add(release); } } } catch (FormatException ex) { logger.Error("Problem of parsing Torrent:" + rows[i].InnerHTML); logger.Error("Exception was the following:" + ex); } } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchUrl = BrowseUrl; var searchTerm = FixSearchTerm(query); var queryCollection = new NameValueCollection { { "searchstr", StripSearchString(searchTerm) }, { "order_by", "time" }, { "order_way", "desc" }, { "group_results", "1" }, { "action", "basic" }, { "searchsubmit", "1" } }; searchUrl += "?" + queryCollection.GetQueryString(); var results = await RequestWithCookiesAsync(searchUrl); try { const string rowsSelector = "table.torrent_table > tbody > tr:not(tr.colhead)"; var searchResultParser = new HtmlParser(); var searchResultDocument = searchResultParser.ParseDocument(results.ContentString); var rows = searchResultDocument.QuerySelectorAll(rowsSelector); string groupTitle = null; string groupYearStr = null; Uri groupPoster = null; string imdbLink = null; string tmdbLink = null; foreach (var row in rows) { try { // ignore sub groups info row, it's just an row with an info about the next section, something like "Dual Áudio" or "Legendado" if (row.QuerySelector(".edition_info") != null) { continue; } // some torrents has more than one link, and the one with .tooltip is the wrong one in that case, // so let's try to pick up first without the .tooltip class, // if nothing is found, then we try again without that filter var qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]:not(.tooltip)"); if (qDetailsLink == null) { qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]"); // if still can't find the right link, skip it if (qDetailsLink == null) { logger.Error($"{Id}: Error while parsing row '{row.OuterHtml}': Can't find the right details link"); continue; } } var title = StripSearchString(qDetailsLink.TextContent); var seasonEl = row.QuerySelector("a[href^=\"torrents.php?torrentid=\"]"); string seasonEp = null; if (seasonEl != null) { var seasonMatch = _EpisodeRegex.Match(seasonEl.TextContent); seasonEp = seasonMatch.Success ? seasonMatch.Value : null; } seasonEp ??= _EpisodeRegex.Match(qDetailsLink.TextContent).Value; ICollection <int> category = new List <int> { TorznabCatType.Other.ID }; string yearStr = null; if (row.ClassList.Contains("group") || row.ClassList.Contains("torrent")) // group/ungrouped headers { var qCatLink = row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]"); var torrentInfoEl = row.QuerySelector("div.torrent_info"); if (torrentInfoEl != null) { // valid for torrent grouped but that has only 1 episode yet yearStr = torrentInfoEl.GetAttribute("data-year"); } yearStr ??= qDetailsLink.NextSibling.TextContent.Trim().TrimStart('[').TrimEnd(']'); if (Uri.TryCreate(row.QuerySelector("img[alt=\"Cover\"]")?.GetAttribute("src"), UriKind.Absolute, out var posterUri)) { groupPoster = posterUri; } if (row.ClassList.Contains("group")) // group headers { groupTitle = title; groupYearStr = yearStr; imdbLink = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href"); tmdbLink = row.QuerySelector("a[href*=\"themoviedb.org/\"]")?.GetAttribute("href"); continue; } } var release = new ReleaseInfo { MinimumRatio = 1, MinimumSeedTime = 172800 }; var qDlLink = row.QuerySelector("a[href^=\"torrents.php?action=download\"]"); var qSize = row.QuerySelector("td:nth-last-child(4)"); var qGrabs = row.QuerySelector("td:nth-last-child(3)"); var qSeeders = row.QuerySelector("td:nth-last-child(2)"); var qLeechers = row.QuerySelector("td:nth-last-child(1)"); var qFreeLeech = row.QuerySelector("strong[title=\"Free\"]"); if (row.ClassList.Contains("group_torrent")) // torrents belonging to a group { release.Description = qDetailsLink.TextContent; release.Title = ParseTitle(groupTitle, seasonEp, groupYearStr); } else if (row.ClassList.Contains("torrent")) // standalone/un grouped torrents { release.Description = row.QuerySelector("div.torrent_info").TextContent; release.Title = ParseTitle(title, seasonEp, yearStr); imdbLink = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href"); tmdbLink = row.QuerySelector("a[href*=\"themoviedb.org/\"]")?.GetAttribute("href"); } release.Poster = groupPoster; release.Imdb = ParseUtil.GetLongFromString(imdbLink); release.TMDb = ParseUtil.GetLongFromString(tmdbLink); release.Category = category; release.Description = release.Description.Replace(" / Free", ""); // Remove Free Tag release.Description = release.Description.Replace("/ WEB ", "/ WEB-DL "); // Fix web/web-dl release.Description = release.Description.Replace("Full HD", "1080p"); // Handles HDR conflict release.Description = release.Description.Replace("/ HD /", "/ 720p /"); release.Description = release.Description.Replace("/ HD]", "/ 720p]"); release.Description = release.Description.Replace("4K", "2160p"); release.Description = release.Description.Replace("SD", "480p"); release.Description = release.Description.Replace("Dual Áudio", "Dual"); release.Description = release.Description.Replace("Dual Audio", "Dual"); // Adjust the description in order to can be read by Radarr and Sonarr var cleanDescription = release.Description.Trim().TrimStart('[').TrimEnd(']'); string[] titleElements; //Formats the title so it can be parsed later var stringSeparators = new[] { " / " }; titleElements = cleanDescription.Split(stringSeparators, StringSplitOptions.None); // release.Title += string.Join(" ", titleElements); release.Title = release.Title.Trim(); if (titleElements.Length < 6) { // Usually non movies / series could have less than 6 elements, eg: Books. release.Title += " " + string.Join(" ", titleElements); } else { release.Title += " " + titleElements[5] + " " + titleElements[3] + " " + titleElements[1] + " " + titleElements[2] + " " + titleElements[4] + " " + string.Join( " ", titleElements.Skip(6)); } if (Regex.IsMatch(release.Description, "(Dual|[Nn]acional|[Dd]ublado)")) { release.Title += " Brazilian"; } // This tracker does not provide an publish date to search terms (only on last 24h page) release.PublishDate = DateTime.Today; // check for previously stripped search terms if (!query.IsImdbQuery && !query.MatchQueryStringAND(release.Title, null, searchTerm)) { continue; } var size = qSize.TextContent; release.Size = ReleaseInfo.GetBytes(size); release.Link = new Uri(SiteLink + qDlLink.GetAttribute("href")); release.Details = new Uri(SiteLink + qDetailsLink.GetAttribute("href")); release.Guid = release.Link; release.Grabs = ParseUtil.CoerceLong(qGrabs.TextContent); release.Seeders = ParseUtil.CoerceInt(qSeeders.TextContent); release.Peers = ParseUtil.CoerceInt(qLeechers.TextContent) + release.Seeders; release.DownloadVolumeFactor = qFreeLeech != null ? 0 : 1; release.UploadVolumeFactor = 1; releases.Add(release); } catch (Exception ex) { logger.Error($"{Id}: Error while parsing row '{row.OuterHtml}': {ex.Message}"); } } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }
private List <ReleaseInfo> ParseTorrents(WebResult results, string episodeString, TorznabQuery query, int alreadyFound, int limit, int previouslyParsedOnPage) { var releases = new List <ReleaseInfo>(); try { var parser = new HtmlParser(); var dom = parser.ParseDocument(results.ContentString); var rows = dom.QuerySelectorAll(".box_torrent").Skip(previouslyParsedOnPage).Take(limit - alreadyFound); var key = ParseUtil.GetArgumentFromQueryString( dom.QuerySelector("link[rel=alternate]").GetAttribute("href"), "key"); // Check torrents only till we reach the query Limit foreach (var row in rows) { try { var torrentTxt = row.QuerySelector(".torrent_txt, .torrent_txt2").QuerySelector("a"); //if (torrentTxt == null) continue; var infoLink = row.QuerySelector("a.infolink"); var imdbId = ParseUtil.GetLongFromString(infoLink?.GetAttribute("href")); var desc = row.QuerySelector("span")?.GetAttribute("title") + " " + infoLink?.TextContent; var torrentLink = SiteLink + torrentTxt.GetAttribute("href"); var downloadId = ParseUtil.GetArgumentFromQueryString(torrentLink, "id"); //Build site links var baseLink = SiteLink + "torrents.php?action=details&id=" + downloadId; var downloadLink = SiteLink + "torrents.php?action=download&id=" + downloadId; var commentsUri = new Uri(baseLink + "#comments"); var guidUri = new Uri(baseLink); var linkUri = new Uri(QueryHelpers.AddQueryString(downloadLink, "key", key)); var seeders = ParseUtil.CoerceInt(row.QuerySelector(".box_s2 a").TextContent); var leechers = ParseUtil.CoerceInt(row.QuerySelector(".box_l2 a").TextContent); var publishDate = DateTime.Parse( row.QuerySelector(".box_feltoltve2").InnerHtml.Replace("<br>", " "), CultureInfo.InvariantCulture); var sizeSplit = row.QuerySelector(".box_meret2").TextContent.Split(' '); var size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0])); var catLink = row.QuerySelector("a:has(img[class='categ_link'])").GetAttribute("href"); var cat = ParseUtil.GetArgumentFromQueryString(catLink, "tipus"); var title = torrentTxt.GetAttribute("title"); // if the release name does not contain the language we add from the category if (cat.Contains("hun") && !title.ToLower().Contains("hun")) { title += ".hun"; } // Minimum seed time is 48 hours + 24 minutes (.4 hours) per GB of torrent size if downloaded in full. // Or a 1.0 ratio on the torrent var seedTime = TimeSpan.FromHours(48) + TimeSpan.FromMinutes(24 * ReleaseInfo.GigabytesFromBytes(size).Value); var release = new ReleaseInfo { Title = title, Description = desc.Trim(), MinimumRatio = 1, MinimumSeedTime = (long)seedTime.TotalSeconds, DownloadVolumeFactor = 0, UploadVolumeFactor = 1, Link = linkUri, Comments = commentsUri, Guid = guidUri, Seeders = seeders, Peers = leechers + seeders, Imdb = imdbId, PublishDate = publishDate, Size = size, Category = MapTrackerCatToNewznab(cat) }; var banner = row.QuerySelector("img.infobar_ico")?.GetAttribute("onmouseover"); if (banner != null) { // static call to Regex.Match caches the pattern, so we aren't recompiling every loop. var bannerMatch = Regex.Match(banner, @"mutat\('(.*?)', '", RegexOptions.Compiled); release.BannerUrl = new Uri(bannerMatch.Groups[1].Value); } //TODO there is room for improvement here. if (episodeString != null && query.MatchQueryStringAND(release.Title, queryStringOverride: episodeString) && !query.IsImdbQuery) { // For Sonarr if the search query was english the title must be english also // The description holds the alternate language name // so we need to swap title and description names var tempTitle = release.Title; // releaseData everything after Name.S0Xe0X var releaseIndex = tempTitle.IndexOf(episodeString, StringComparison.OrdinalIgnoreCase) + episodeString.Length; var releaseData = tempTitle.Substring(releaseIndex).Trim(); // release description contains [imdb: ****] but we only need the data before it for title var description = new[] { release.Description, "" }; if (release.Description.Contains("[imdb:")) { description = release.Description.Split('['); description[1] = "[" + description[1]; } var match = Regex.Match(releaseData, @"^E\d\d?"); // if search is done for S0X than we don't want to put . between S0X and E0X var episodeSeparator = episodeString.Length == 3 && match.Success ? null : "."; release.Title = (description[0].Trim() + "." + episodeString.Trim() + episodeSeparator + releaseData.Trim('.')).Replace(' ', '.'); // add back imdb points to the description [imdb: 8.7] release.Description = tempTitle + " " + description[1]; release.Description = release.Description.Trim(); } releases.Add(release); } catch (FormatException ex) { logger.Error("Problem of parsing Torrent:" + row.InnerHtml); logger.Error("Exception was the following:" + ex); } } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = SearchUrl; // If query is empty, use the RSS Feed if (string.IsNullOrWhiteSpace(searchString)) { var rssPage = await RequestStringWithCookiesAndRetry(RSSUrl + configData.RSSKey.Value); var rssDoc = XDocument.Parse(rssPage.Content); foreach (var item in rssDoc.Descendants("item")) { var title = item.Descendants("title").First().Value; if (title.StartsWith("Support YOUR site!")) { continue; } var description = item.Descendants("description").First().Value; var link = item.Descendants("link").First().Value; var date = item.Descendants("pubDate").First().Value; var torrentIdMatch = Regex.Match(link, "(?<=download\\.php/)([a-zA-z0-9]*)"); var torrentId = torrentIdMatch.Success ? torrentIdMatch.Value : string.Empty; if (string.IsNullOrWhiteSpace(torrentId)) { throw new Exception("Missing torrent id"); } var infoMatch = Regex.Match(description, @"Category:\W(?<cat>.*)\W\n\WSize:\W(?<size>.*)\n\WStatus:\W(?<seeders>.*)\Wseeder(.*)\Wand\W(?<leechers>.*)\Wleecher(.*)\n\WAdded:\W(?<added>.*)\n\WDescription:"); if (!infoMatch.Success) { throw new Exception("Unable to find info"); } var imdbMatch = Regex.Match(description, "(?<=www.imdb.com/title/tt)([0-9]*)"); long?imdbID = null; if (imdbMatch.Success) { long l; if (long.TryParse(imdbMatch.Value, out l)) { imdbID = l; } } var Now = DateTime.Now; var PublishDate = DateTime.ParseExact(date, "ddd, dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture); var PublishDateLocal = PublishDate.ToLocalTime(); var diff = Now - PublishDateLocal; var release = new ReleaseInfo() { Title = title, Description = title, Guid = new Uri(string.Format(DetailsURL, torrentId)), Comments = new Uri(string.Format(DetailsURL, torrentId)), //PublishDate = DateTime.ParseExact(infoMatch.Groups["added"].Value, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31 TODO: correct timezone (always -4) PublishDate = PublishDateLocal, Link = new Uri(link), Seeders = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value == "no" ? "0" : infoMatch.Groups["seeders"].Value), Peers = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value == "no" ? "0" : infoMatch.Groups["leechers"].Value), Size = ReleaseInfo.GetBytes(infoMatch.Groups["size"].Value), Category = MapTrackerCatToNewznab(infoMatch.Groups["cat"].Value), Imdb = imdbID }; // if unknown category, set to "other" if (release.Category.Count() == 0) { release.Category.Add(7000); } release.Peers += release.Seeders; releases.Add(release); } } else { searchUrl += "?titleonly=1&search=" + WebUtility.UrlEncode(searchString); string.Format(SearchUrl, WebUtility.UrlEncode(searchString)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count > 0) { foreach (var cat in cats) { searchUrl += "&c" + cat + "=1"; } } var results = await RequestStringWithCookiesAndRetry(searchUrl); if (results.IsRedirect) { // re-login await ApplyConfiguration(null); results = await RequestStringWithCookiesAndRetry(searchUrl); } try { CQ dom = results.Content; // table header is the first <tr> in table body, get all rows except this CQ qRows = dom["#torrents-table > tbody > tr:not(:first-child)"]; foreach (var row in qRows) { var release = new ReleaseInfo(); var qRow = row.Cq(); var debug = qRow.Html(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; // 48 hours CQ qLink = qRow.Find(".br_right > a").First(); release.Guid = new Uri(SiteLink + qLink.Attr("href")); release.Comments = new Uri(SiteLink + qLink.Attr("href")); release.Title = qLink.Find("b").Text(); release.Description = release.Title; var releaseLinkURI = qRow.Find("td:nth-child(4) > a").Attr("href"); release.Link = new Uri(SiteLink + releaseLinkURI); if (releaseLinkURI != null) { var dateString = qRow.Find("td:nth-child(6) nobr")[0].TextContent.Trim(); //"2015-04-25 23:38:12" //"yyyy-MMM-dd hh:mm:ss" release.PublishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); var sizeStr = qRow.Children().ElementAt(6).InnerHTML.Trim(); sizeStr = sizeStr.Substring(0, sizeStr.IndexOf('<')); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:nth-child(9)").Text()); release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find("td:nth-child(10)").Text()); var grabsStr = qRow.Find("td:nth-child(8)").Text(); release.Grabs = ParseUtil.GetLongFromString(grabsStr); var filesStr = qRow.Find("td:nth-child(7) > a").Text(); release.Files = ParseUtil.GetLongFromString(filesStr); var category = qRow.Find(".br_type > a").Attr("href").Replace("browse.php?cat=", string.Empty); release.Category = MapTrackerCatToNewznab(category); } releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } } return(releases); }
/// <summary> /// Execute our search query /// </summary> /// <param name="query">Query</param> /// <returns>Releases</returns> protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var torrentRowList = new List <CQ>(); var exactSearchTerm = query.GetQueryString(); var searchUrl = SearchUrl; // Check login before performing a query await CheckLogin(); // Check cache first so we don't query the server (if search term used or not in dev mode) if (!DevMode && !string.IsNullOrEmpty(exactSearchTerm)) { lock (cache) { // Remove old cache items CleanCache(); // Search in cache var cachedResult = cache.FirstOrDefault(i => i.Query == exactSearchTerm); if (cachedResult != null) { return(cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray()); } } } var SearchTerms = new List <string> { exactSearchTerm }; // duplicate search without diacritics var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm); if (baseSearchTerm != exactSearchTerm) { SearchTerms.Add(baseSearchTerm); } foreach (var searchTerm in SearchTerms) { // Build our query var request = BuildQuery(searchTerm, query, searchUrl); // Getting results & Store content var response = await RequestStringWithCookiesAndRetry(request, ConfigData.CookieHeader.Value); _fDom = response.Content; try { var firstPageRows = FindTorrentRows(); // Add them to torrents list torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq())); // If pagination available int nbResults; int pageLinkCount; nbResults = 1; pageLinkCount = 1; // Check if we have a minimum of one result if (firstPageRows.Length > 1) { // Retrieve total count on our alone page nbResults = firstPageRows.Count(); } else { // Check if no result if (torrentRowList.Count == 0) { // No results found Output("\nNo result found for your query, please try another search term or change the theme you're currently using on the site as this is an unsupported solution...\n", "info"); // No result found for this query break; } } Output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !"); Output("\nThere are " + (firstPageRows.Length - 2) + " results on the first page !"); // Loop on results foreach (var tRow in torrentRowList.Skip(1).Take(torrentRowList.Count - 2)) { Output("Torrent #" + (releases.Count + 1)); // ID var idOrig = tRow.Find("td:eq(1) > a:eq(0)").Attr("href").Split('=')[1]; var id = idOrig.Substring(0, idOrig.Length - 4); Output("ID: " + id); // Release Name var name = tRow.Find("td:eq(1) > a:eq(0)").Text(); // Category string categoryID = tRow.Find("td:eq(0) > a:eq(0)").Attr("href").Split('?').Last(); var newznab = MapTrackerCatToNewznab(categoryID); Output("Category: " + (newznab.Count > 0 ? newznab.First().ToString() : "unknown category") + " (" + categoryID + ")"); // Seeders int seeders = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(9)").Text(), @"\d+").Value); Output("Seeders: " + seeders); // Leechers int leechers = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(10)").Text(), @"\d+").Value); Output("Leechers: " + leechers); // Files int files = 1; files = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(4)").Text(), @"\d+").Value); Output("Files: " + files); // Completed int completed = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(8)").Text(), @"\d+").Value); Output("Completed: " + completed); // Size var humanSize = tRow.Find("td:eq(7)").Text().ToLowerInvariant(); var size = ReleaseInfo.GetBytes(humanSize); Output("Size: " + humanSize + " (" + size + " bytes)"); // Publish DateToString var dateTimeOrig = tRow.Find("td:eq(6)").Text(); var datestr = Regex.Replace(dateTimeOrig, @"<[^>]+>| ", "").Trim(); datestr = Regex.Replace(datestr, "Today", DateTime.Now.ToString("MMM dd yyyy"), RegexOptions.IgnoreCase); datestr = Regex.Replace(datestr, "Yesterday", DateTime.Now.Date.AddDays(-1).ToString("MMM dd yyyy"), RegexOptions.IgnoreCase); DateTime date = DateTimeUtil.FromUnknown(datestr, "DK"); Output("Released on: " + date); // Torrent Details URL var detailsLink = new Uri(TorrentDescriptionUrl.Replace("{id}", id.ToString())); Output("Details: " + detailsLink.AbsoluteUri); // Torrent Comments URL var commentsLink = new Uri(TorrentCommentUrl.Replace("{id}", id.ToString())); Output("Comments Link: " + commentsLink.AbsoluteUri); // Torrent Download URL var passkey = tRow.Find("td:eq(2) > a:eq(0)").Attr("href"); var key = Regex.Match(passkey, "(?<=torrent_pass\\=)([a-zA-z0-9]*)"); Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString())); Output("Download Link: " + downloadLink.AbsoluteUri); // Building release infos var release = new ReleaseInfo { Category = newznab, Title = name, Seeders = seeders, Peers = seeders + leechers, MinimumRatio = 1, MinimumSeedTime = 172800, PublishDate = date, Size = size, Files = files, Grabs = completed, Guid = detailsLink, Comments = commentsLink, Link = downloadLink }; // IMDB var imdbLink = tRow.Find("a[href*=\"http://imdb.com/title/\"]").First().Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdbLink); if (tRow.Find("img[title=\"Free Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (tRow.Find("img[title=\"Halfleech\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else if (tRow.Find("img[title=\"90% Freeleech\"]").Length >= 1) { release.DownloadVolumeFactor = 0.1; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex); } } // Return found releases return(releases); }
private async Task <IEnumerable <ReleaseInfo> > PerformRegularQueryAsync(TorznabQuery query, string hebName = null) { var releases = new List <ReleaseInfo>(); var searchUrl = SearchUrl; var searchString = query.GetQueryString(); if (query.IsImdbQuery) { searchString = query.ImdbID; } if (hebName != null) { searchString = hebName + " - עונה " + query.Season + " פרק " + query.Episode; } searchUrl += "?"; if (!string.IsNullOrWhiteSpace(searchString)) { var strEncoded = WebUtilityHelpers.UrlEncode(searchString, Encoding); searchUrl += "&query=" + strEncoded + "&matchquery=any"; } searchUrl = MapTorznabCapsToTrackers(query).Aggregate(searchUrl, (current, cat) => $"{current}&c[]={cat}"); var data = await RequestWithCookiesAndRetryAsync(searchUrl); try { var parser = new HtmlParser(); var dom = parser.ParseDocument(data.ContentString); var rows = dom.QuerySelectorAll("tr.box_torrent"); foreach (var row in rows) { var release = new ReleaseInfo(); var mainTitleLink = row.QuerySelector("div.main_title > a"); release.Title = mainTitleLink.GetAttribute("longtitle"); if (string.IsNullOrWhiteSpace(release.Title)) { release.Title = mainTitleLink.TextContent; } release.MinimumRatio = 1; release.MinimumSeedTime = 172800; // 48 hours release.Grabs = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(5)").TextContent.Replace(",", "")); release.Seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent.Replace(",", "")); release.Peers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent.Replace(",", "")) + release.Seeders; var fullSize = row.QuerySelector("td:nth-child(4)").TextContent; release.Size = ReleaseInfo.GetBytes(fullSize); release.Comments = new Uri(SiteLink + row.QuerySelector("a.threadlink[href]").GetAttribute("href")); release.Link = new Uri(SiteLink + row.QuerySelector("a:has(div.dlimg)").GetAttribute("href")); release.Guid = release.Comments; //some releases have invalid banner URLs, ignore the banners in this case if (Uri.TryCreate(row.QuerySelector("a[imgsrc]").GetAttribute("imgsrc"), UriKind.Absolute, out var banner)) { release.BannerUrl = banner; } var dateStringAll = row.QuerySelector("div.up_info2").ChildNodes.Last().TextContent; var dateParts = dateStringAll.Split(' '); var dateString = dateParts[dateParts.Length - 2] + " " + dateParts[dateParts.Length - 1]; release.PublishDate = DateTime.ParseExact(dateString, "dd/MM/yy HH:mm", CultureInfo.InvariantCulture); var categoryLink = row.QuerySelector("a[href^=\"/browse.php?cat=\"]").GetAttribute("href"); var catid = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat"); release.Category = MapTrackerCatToNewznab(catid); if (row.QuerySelector("a[href^=\"?freeleech=1\"]") != null) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; var subTitle = row.QuerySelector("div.sub_title"); var imdbLink = subTitle.QuerySelector("span.imdb-inline > a"); if (imdbLink != null) { release.Imdb = ParseUtil.GetLongFromString(imdbLink.GetAttribute("href")); } release.Description = subTitle.FirstChild.TextContent; releases.Add(release); } } catch (Exception ex) { OnParseError(data.ContentString, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var pairs = new List <KeyValuePair <string, string> >(); pairs.Add(new KeyValuePair <string, string>("nyit_sorozat_resz", "true")); pairs.Add(new KeyValuePair <string, string>("miben", "name")); pairs.Add(new KeyValuePair <string, string>("tipus", "kivalasztottak_kozott")); pairs.Add(new KeyValuePair <string, string>("submit.x", "1")); pairs.Add(new KeyValuePair <string, string>("submit.y", "1")); pairs.Add(new KeyValuePair <string, string>("submit", "Ok")); pairs.Add(new KeyValuePair <string, string>("mire", searchString)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count == 0) { cats = GetAllTrackerCategories(); } foreach (var lcat in LanguageCats) { if (!configData.Hungarian.Value) { cats.Remove(lcat + "_hun"); } if (!configData.English.Value) { cats.Remove(lcat); } } foreach (var cat in cats) { pairs.Add(new KeyValuePair <string, string>("kivalasztott_tipus[]", cat)); } var results = await PostDataWithCookiesAndRetry(SearchUrl, pairs); try { CQ dom = results.Content; ReleaseInfo release; var rows = dom[".box_torrent_all"].Find(".box_torrent"); foreach (var row in rows) { CQ qRow = row.Cq(); var key = dom ["link[rel=alternate]"].First().Attr("href").Split('=').Last(); release = new ReleaseInfo(); var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0); //if (torrentTxt == null) continue; release.Title = torrentTxt.GetAttribute("title"); release.Description = qRow.Find("div.siterank").Text(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; string downloadLink = SiteLink + torrentTxt.GetAttribute("href"); string downloadId = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4); release.Link = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key); release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId); release.Guid = new Uri(release.Comments.ToString() + "#comments");; release.Seeders = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders; var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdblink); var banner = qRow.Find("img.infobar_ico").Attr("onmouseover"); if (banner != null) { Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled); var BannerMatch = BannerRegEx.Match(banner); var bannerurl = BannerMatch.Groups[1].Value; release.BannerUrl = new Uri(bannerurl); } release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture); string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' '); release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0])); string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href"); string cat = ParseUtil.GetArgumentFromQueryString(catlink, "tipus"); release.Category = MapTrackerCatToNewznab(cat); releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
protected async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query, String seasonep) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var pairs = new List <KeyValuePair <string, string> >(); if (seasonep != null) { searchString = query.SanitizedSearchTerm; } pairs.Add(new KeyValuePair <string, string>("nyit_sorozat_resz", "true")); pairs.Add(new KeyValuePair <string, string>("miben", "name")); pairs.Add(new KeyValuePair <string, string>("tipus", "kivalasztottak_kozott")); pairs.Add(new KeyValuePair <string, string>("submit.x", "1")); pairs.Add(new KeyValuePair <string, string>("submit.y", "1")); pairs.Add(new KeyValuePair <string, string>("submit", "Ok")); pairs.Add(new KeyValuePair <string, string>("mire", searchString)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count == 0) { cats = GetAllTrackerCategories(); } foreach (var lcat in LanguageCats) { if (!configData.Hungarian.Value) { cats.Remove(lcat + "_hun"); } if (!configData.English.Value) { cats.Remove(lcat); } } foreach (var cat in cats) { pairs.Add(new KeyValuePair <string, string>("kivalasztott_tipus[]", cat)); } var results = await PostDataWithCookiesAndRetry(SearchUrl, pairs); try { CQ dom = results.Content; ReleaseInfo release; var rows = dom[".box_torrent_all"].Find(".box_torrent"); foreach (var row in rows) { CQ qRow = row.Cq(); var key = dom["link[rel=alternate]"].First().Attr("href").Split('=').Last(); release = new ReleaseInfo(); var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0); //if (torrentTxt == null) continue; release.Title = torrentTxt.GetAttribute("title"); release.Description = qRow.Find("div.siterank").Text(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; string downloadLink = SiteLink + torrentTxt.GetAttribute("href"); string downloadId = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4); release.Link = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key); release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId); release.Guid = new Uri(release.Comments.ToString() + "#comments");; release.Seeders = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders; var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdblink); var banner = qRow.Find("img.infobar_ico").Attr("onmouseover"); if (banner != null) { Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled); var BannerMatch = BannerRegEx.Match(banner); var bannerurl = BannerMatch.Groups[1].Value; release.BannerUrl = new Uri(bannerurl); } release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture); string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' '); release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0])); string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href"); string cat = ParseUtil.GetArgumentFromQueryString(catlink, "tipus"); release.Category = MapTrackerCatToNewznab(cat); if (seasonep == null) { releases.Add(release); } else { if (query.MatchQueryStringAND(release.Title, null, seasonep)) { /* For sonnar if the search querry was english the title must be english also so we need to change the Description and Title */ var temp = release.Title; // releasedata everithing after Name.S0Xe0X String releasedata = release.Title.Split(new[] { seasonep }, StringSplitOptions.None)[1].Trim(); /* if the release name not contains the language we add it because it is know from category */ if (cat.Contains("hun") && !releasedata.Contains("hun")) { releasedata += ".hun"; } // release description contains [imdb: ****] but we only need the data before it for title String[] description = { release.Description, "" }; if (release.Description.Contains("[imdb:")) { description = release.Description.Split('['); description[1] = "[" + description[1]; } release.Title = (description[0].Trim() + "." + seasonep.Trim() + "." + releasedata.Trim('.')).Replace(' ', '.'); // if search is done for S0X than we dont want to put . between S0X and E0X Match match = Regex.Match(releasedata, @"^E\d\d?"); if (seasonep.Length == 3 && match.Success) { release.Title = (description[0].Trim() + "." + seasonep.Trim() + releasedata.Trim('.')).Replace(' ', '.'); } // add back imdb points to the description [imdb: 8.7] release.Description = temp + " " + description[1]; release.Description = release.Description.Trim(); releases.Add(release); } } } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <TorrentInfo>(); var parser = new HtmlParser(); var dom = parser.ParseDocument(indexerResponse.Content); var rows = dom.QuerySelectorAll("#torrents-table > tbody > tr"); foreach (var row in rows.Skip(1)) { var qDetails = row.QuerySelector(".br_right > a"); var details = _settings.BaseUrl + qDetails.GetAttribute("href"); var title = qDetails.QuerySelector("b").TextContent; // Remove auto-generated [REQ] tag from fulfilled requests if (title.StartsWith("[REQ] ")) { title = title.Substring(6); } var qLink = row.QuerySelector("td:nth-child(4) > a"); if (qLink == null) { continue; // support/donation banner } var link = _settings.BaseUrl + qLink.GetAttribute("href"); // dateString format "yyyy-MMM-dd hh:mm:ss" => eg "2015-04-25 23:38:12" var dateString = row.QuerySelector("td:nth-child(6) nobr").TextContent.Trim(); var publishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(7)").InnerHtml.Split('<').First().Trim()); var files = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(7) > a").TextContent); var grabs = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(8)").TextContent); var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)").TextContent); var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(10)").TextContent); var category = row.QuerySelector(".br_type > a").GetAttribute("href").Replace("browse.php?cat=", string.Empty); var qImdb = row.QuerySelector("a[href*=\"www.imdb.com/\"]"); var imdb = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null; var release = new TorrentInfo { InfoUrl = details, Guid = details, Title = title, DownloadUrl = link, PublishDate = publishDate, Size = size, Seeders = seeders, Peers = seeders + leechers, Grabs = (int)grabs, Files = (int)files, Categories = _categories.MapTrackerCatToNewznab(category), ImdbId = imdb ?? 0, MinimumRatio = 1, MinimumSeedTime = 172800, // 48 hours UploadVolumeFactor = 1, DownloadVolumeFactor = 1 }; torrentInfos.Add(release); } return(torrentInfos.ToArray()); }
private string ParseFields(string value, string fieldName, TorrentInfo release, List <string> fieldModifiers, Uri searchUrlUri) { switch (fieldName) { case "download": if (string.IsNullOrEmpty(value)) { value = null; release.DownloadUrl = null; break; } if (value.StartsWith("magnet:")) { release.MagnetUrl = value; value = release.MagnetUrl; } else { release.DownloadUrl = ResolvePath(value, searchUrlUri).AbsoluteUri; value = release.DownloadUrl; } release.Guid = value; break; case "magnet": var magnetUri = value; release.MagnetUrl = magnetUri; value = magnetUri.ToString(); break; case "infohash": release.InfoHash = value; break; case "details": var url = ResolvePath(value, searchUrlUri)?.AbsoluteUri; release.InfoUrl = url; value = url.ToString(); break; case "comments": var commentsUrl = ResolvePath(value, searchUrlUri); if (release.CommentUrl == null) { release.CommentUrl = commentsUrl.AbsoluteUri; } value = commentsUrl.ToString(); break; case "title": if (fieldModifiers.Contains("append")) { release.Title += value; } else { release.Title = value; } value = release.Title; break; case "description": if (fieldModifiers.Contains("append")) { release.Description += value; } else { release.Description = value; } value = release.Description; break; case "category": var cats = MapTrackerCatToNewznab(value); if (cats.Any()) { if (release.Categories == null || fieldModifiers.Contains("noappend")) { release.Categories = cats; } else { release.Categories = release.Categories.Union(cats).ToList(); } } value = release.Categories.ToString(); break; case "categorydesc": var catsDesc = MapTrackerCatDescToNewznab(value); if (catsDesc.Any()) { if (release.Categories == null || fieldModifiers.Contains("noappend")) { release.Categories = catsDesc; } else { release.Categories = release.Categories.Union(catsDesc).ToList(); } } value = release.Categories.ToString(); break; case "size": release.Size = ParseUtil.GetBytes(value); value = release.Size.ToString(); break; case "leechers": var leechers = ParseUtil.CoerceLong(value); leechers = leechers < 5000000L ? leechers : 0; // to fix #6558 if (release.Peers == null) { release.Peers = (int)leechers; } else { release.Peers += (int)leechers; } value = leechers.ToString(); break; case "seeders": release.Seeders = ParseUtil.CoerceInt(value); release.Seeders = release.Seeders < 5000000L ? release.Seeders : 0; // to fix #6558 if (release.Peers == null) { release.Peers = release.Seeders; } else { release.Peers += release.Seeders; } value = release.Seeders.ToString(); break; case "date": release.PublishDate = DateTimeUtil.FromUnknown(value); value = release.PublishDate.ToString(DateTimeUtil.Rfc1123ZPattern); break; case "files": release.Files = ParseUtil.CoerceInt(value); value = release.Files.ToString(); break; case "grabs": release.Grabs = ParseUtil.CoerceInt(value); value = release.Grabs.ToString(); break; case "downloadvolumefactor": release.DownloadVolumeFactor = ParseUtil.CoerceDouble(value); value = release.DownloadVolumeFactor.ToString(); break; case "uploadvolumefactor": release.UploadVolumeFactor = ParseUtil.CoerceDouble(value); value = release.UploadVolumeFactor.ToString(); break; case "minimumratio": release.MinimumRatio = ParseUtil.CoerceDouble(value); value = release.MinimumRatio.ToString(); break; case "minimumseedtime": release.MinimumSeedTime = ParseUtil.CoerceLong(value); value = release.MinimumSeedTime.ToString(); break; case "imdb": case "imdbid": release.ImdbId = (int)ParseUtil.GetLongFromString(value); value = release.ImdbId.ToString(); break; case "tmdbid": var tmdbIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled); var tmdbIDMatch = tmdbIDRegEx.Match(value); var tmdbID = tmdbIDMatch.Groups[1].Value; release.TmdbId = (int)ParseUtil.CoerceLong(tmdbID); value = release.TmdbId.ToString(); break; case "rageid": var rageIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled); var rageIDMatch = rageIDRegEx.Match(value); var rageID = rageIDMatch.Groups[1].Value; release.TvRageId = (int)ParseUtil.CoerceLong(rageID); value = release.TvRageId.ToString(); break; case "traktid": var traktIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled); var traktIDMatch = traktIDRegEx.Match(value); var traktID = traktIDMatch.Groups[1].Value; release.TraktId = (int)ParseUtil.CoerceLong(traktID); value = release.TraktId.ToString(); break; case "tvdbid": var tvdbIdRegEx = new Regex(@"(\d+)", RegexOptions.Compiled); var tvdbIdMatch = tvdbIdRegEx.Match(value); var tvdbId = tvdbIdMatch.Groups[1].Value; release.TvdbId = (int)ParseUtil.CoerceLong(tvdbId); value = release.TvdbId.ToString(); break; case "poster": if (!string.IsNullOrWhiteSpace(value)) { var poster = ResolvePath(value, searchUrlUri); release.PosterUrl = poster.AbsoluteUri; } value = release.PosterUrl; break; case "genre": release.Genres = release.Genres.Union(value.Split(',')).ToList(); value = release.Genres.ToString(); break; case "year": release.Year = ParseUtil.CoerceInt(value); value = release.Year.ToString(); break; case "author": release.Author = value; break; case "booktitle": release.BookTitle = value; break; case "artist": release.Artist = value; break; case "album": release.Album = value; break; default: break; } return(value); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { List <ReleaseInfo> releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection(); queryCollection.Add("incldead", "1"); queryCollection.Add("rel_type", "0"); // Alle if (query.ImdbID != null) { queryCollection.Add("searchin", "imdb"); queryCollection.Add("search", query.ImdbID); } else { queryCollection.Add("searchin", "title"); if (!string.IsNullOrWhiteSpace(searchString)) { // use AND+wildcard operator to avoid getting to many useless results var searchStringArray = Regex.Split(searchString.Trim(), "[ _.-]+", RegexOptions.Compiled).ToList(); searchStringArray = searchStringArray.Where(x => x.Length >= 3).ToList(); // remove words with less than 3 characters searchStringArray = searchStringArray.Where(x => !new string[] { "der", "die", "das", "the" }.Contains(x.ToLower())).ToList(); // remove words with less than 3 characters searchStringArray = searchStringArray.Select(x => "+" + x + "*").ToList(); // add AND operators+wildcards var searchStringFinal = String.Join(" ", searchStringArray); queryCollection.Add("search", searchStringFinal); } } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("c" + cat, "1"); } searchUrl += "?" + queryCollection.GetQueryString(); var results = await RequestStringWithCookiesAndRetry(searchUrl); if (results.IsRedirect) { await ApplyConfiguration(null); results = await RequestStringWithCookiesAndRetry(searchUrl); } try { CQ dom = results.Content; var rows = dom["table.torrent_table > tbody > tr"]; var globalFreeleech = dom.Find("legend:contains(\"Freeleech\")+ul > li > b:contains(\"Freeleech\")").Any(); foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 96 * 60 * 60; var qRow = row.Cq(); var catStr = row.ChildElements.ElementAt(0).FirstElementChild.GetAttribute("href").Split('=')[1]; release.Category = MapTrackerCatToNewznab(catStr); var qLink = row.ChildElements.ElementAt(2).FirstElementChild.Cq(); release.Link = new Uri(SiteLink + qLink.Attr("href")); var torrentId = qLink.Attr("href").Split('=').Last(); var descCol = row.ChildElements.ElementAt(1); var qCommentLink = descCol.FirstElementChild.Cq(); var torrentTag = descCol.Cq().Find("span.torrent-tag"); var torrentTags = torrentTag.Elements.Select(x => x.InnerHTML).ToList(); release.Title = qCommentLink.Attr("title"); release.Description = String.Join(", ", torrentTags); release.Comments = new Uri(SiteLink + qCommentLink.Attr("href").Replace("&hit=1", "")); release.Guid = release.Comments; var torrent_details = descCol.Cq().Find(".torrent_details").Get(0); var rawDateStr = torrent_details.ChildNodes.ElementAt(torrent_details.ChildNodes.Length - 3).Cq().Text(); var dateStr = rawDateStr.Trim().Replace("von", "").Trim(); DateTime dateGerman; if (dateStr.StartsWith("Heute ")) { dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStr.Split(' ')[1]); } else if (dateStr.StartsWith("Gestern ")) { dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStr.Split(' ')[1]) - TimeSpan.FromDays(1); } else { dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); } DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz); release.PublishDate = pubDateUtc.ToLocalTime(); var imdbLink = descCol.Cq().Find("a[href*=\"&searchin=imdb\"]"); if (imdbLink.Any()) { release.Imdb = ParseUtil.GetLongFromString(imdbLink.Attr("href")); } var sizeStr = row.ChildElements.ElementAt(5).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text()); release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text()) + release.Seeders; var grabs = qRow.Find("td:nth-child(7)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (globalFreeleech) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("span.torrent-tag-free").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
/// <summary> /// Execute our search query /// </summary> /// <param name="query">Query</param> /// <returns>Releases</returns> protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var exactSearchTerm = query.GetQueryString(); var searchUrl = SearchUrl; // Check login before performing a query await CheckLoginAsync(); var SearchTerms = new List <string> { exactSearchTerm }; // duplicate search without diacritics var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm); if (baseSearchTerm != exactSearchTerm) { SearchTerms.Add(baseSearchTerm); } foreach (var searchTerm in SearchTerms) { // Build our query var request = BuildQuery(searchTerm, query, searchUrl); // Getting results & Store content var response = await RequestWithCookiesAndRetryAsync(request, ConfigData.CookieHeader.Value); var parser = new HtmlParser(); var dom = parser.ParseDocument(response.ContentString); try { var firstPageRows = FindTorrentRows(dom); // If pagination available int nbResults; var pageLinkCount = 1; // Check if we have a minimum of one result if (firstPageRows?.Length >= 1) { // Retrieve total count on our alone page nbResults = firstPageRows.Count(); } else { // No result found for this query logger.Info("\nNorBits - No result found for your query, please try another search term ...\n", "info"); break; } logger.Info("\nNorBits - Found " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !"); logger.Info("\nNorBits - There are " + firstPageRows.Length + " results on the first page !"); // Loop on results foreach (var row in firstPageRows) { var id = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("href").Split('=').Last(); // ID var name = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("title"); // Release Name var categoryName = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("title"); // Category var mainCat = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("href").Split('?').Last(); var qSubCat2 = row.QuerySelector("td:nth-of-type(1) > div > a[href^=\"/browse.php?sub2_cat[]=\"]"); var cat = mainCat; if (qSubCat2 != null) { cat += '&' + qSubCat2.GetAttribute("href").Split('?').Last(); } var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent); // Seeders var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent); // Leechers var regexObj = new Regex(@"[^\d]"); // Completed var completed2 = row.QuerySelector("td:nth-of-type(8)").TextContent; var completed = ParseUtil.CoerceLong(regexObj.Replace(completed2, "")); var qFiles = row.QuerySelector("td:nth-of-type(3) > a"); // Files var files = qFiles != null?ParseUtil.CoerceInt(Regex.Match(qFiles.TextContent, @"\d+").Value) : 1; var humanSize = row.QuerySelector("td:nth-of-type(7)").TextContent.ToLowerInvariant(); // Size var size = ReleaseInfo.GetBytes(humanSize); // Date var dateTimeOrig = row.QuerySelector("td:nth-of-type(5)").TextContent; var dateTime = Regex.Replace(dateTimeOrig, @"<[^>]+>| ", "").Trim(); var date = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); var details = new Uri(TorrentDetailsUrl.Replace("{id}", id.ToString())); // Description Link var passkey = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(2)").GetAttribute("href"); // Download Link var key = Regex.Match(passkey, "(?<=passkey\\=)([a-zA-z0-9]*)"); var downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString())); // Building release infos var release = new ReleaseInfo { Category = MapTrackerCatToNewznab(cat), Title = name, Seeders = seeders, Peers = seeders + leechers, PublishDate = date, Size = size, Files = files, Grabs = completed, Guid = details, Details = details, Link = downloadLink, MinimumRatio = 1, MinimumSeedTime = 172800 // 48 hours }; var genres = row.QuerySelector("span.genres")?.TextContent; if (!string.IsNullOrEmpty(genres)) { release.Description = genres; } // IMDB var imdbLink = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href"); release.Imdb = ParseUtil.GetLongFromString(imdbLink); if (row.QuerySelector("img[title=\"100% freeleech\"]") != null) { release.DownloadVolumeFactor = 0; } else if (row.QuerySelector("img[title=\"Halfleech\"]") != null) { release.DownloadVolumeFactor = 0.5; } else if (row.QuerySelector("img[title=\"90% Freeleech\"]") != null) { release.DownloadVolumeFactor = 0.1; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex); } } // Return found releases return(releases); }
/// <summary> /// Execute our search query /// </summary> /// <param name="query">Query</param> /// <returns>Releases</returns> protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var torrentRowList = new List <CQ>(); var exactSearchTerm = query.GetQueryString(); var searchUrl = SearchUrl; // Check login before performing a query await CheckLogin(); // Check cache first so we don't query the server (if search term used or not in dev mode) if (!DevMode && !string.IsNullOrEmpty(exactSearchTerm)) { lock (cache) { // Remove old cache items CleanCache(); // Search in cache var cachedResult = cache.FirstOrDefault(i => i.Query == exactSearchTerm); if (cachedResult != null) { return(cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray()); } } } var SearchTerms = new List <string> { exactSearchTerm }; // duplicate search without diacritics var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm); if (baseSearchTerm != exactSearchTerm) { SearchTerms.Add(baseSearchTerm); } foreach (var searchTerm in SearchTerms) { // Build our query var request = BuildQuery(searchTerm, query, searchUrl); // Getting results & Store content var response = await RequestStringWithCookiesAndRetry(request, ConfigData.CookieHeader.Value); _fDom = response.Content; try { var firstPageRows = FindTorrentRows(); // Add them to torrents list torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq())); // If pagination available int nbResults; int pageLinkCount; nbResults = 1; pageLinkCount = 1; // Check if we have a minimum of one result if (firstPageRows.Length > 1) { // Retrieve total count on our alone page nbResults = firstPageRows.Count(); } else { // Check if no result if (torrentRowList.Count == 0) { // No results found Output("\nNo result found for your query, please try another search term ...\n", "info"); // No result found for this query break; } } Output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !"); Output("\nThere are " + firstPageRows.Length + " results on the first page !"); // Loop on results foreach (var tRow in torrentRowList) { Output("Torrent #" + (releases.Count + 1)); // ID var id = tRow.Find("td:eq(1) > a:eq(0)").Attr("href").Split('=').Last(); Output("ID: " + id); // Release Name var name = tRow.Find("td:eq(1) > a:eq(0)").Attr("title"); // Category var categoryId = tRow.Find("td:eq(0) > div > a:eq(0)").Attr("href").Split('?').Last(); var categoryName = tRow.Find("td:eq(0) > div > a:eq(0)").Attr("title"); var MainCat = tRow.Find("td:eq(0) > div > a:eq(0)").Attr("href").Split('?').Last(); var SubCat1 = "none"; var SubCat2 = "none"; var testcat = MainCat; if (tRow.Find("td:eq(0) > div > a:eq(1)").Length == 1) { SubCat1 = tRow.Find("td:eq(0) > div > a:eq(1)").Attr("href").Split('?').Last(); } if (tRow.Find("td:eq(0) > div > a[href^=\"/browse.php?sub2_cat[]=\"]").Length == 1) { SubCat2 = tRow.Find("td:eq(0) > div > a[href^=\"/browse.php?sub2_cat[]=\"]").Attr("href").Split('?').Last(); testcat = MainCat + '&' + SubCat2; } Output("Category: " + testcat + " - " + categoryName); // Seeders var seeders = ParseUtil.CoerceInt(tRow.Find("td:eq(9)").Text()); Output("Seeders: " + seeders); // Leechers var leechers = ParseUtil.CoerceInt(tRow.Find("td:eq(10)").Text()); Output("Leechers: " + leechers); // Completed Regex regexObj = new Regex(@"[^\d]"); var completed2 = tRow.Find("td:eq(7)").Text(); var completed = ParseUtil.CoerceLong(regexObj.Replace(completed2, "")); Output("Completed: " + completed); // Files var files = 1; if (tRow.Find("td:eq(2) > a").Length == 1) { files = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(2) > a").Text(), @"\d+").Value); } Output("Files: " + files); // Health var percent = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(8)").Text(), @"\d+").Value.Trim()); Output("Health: " + percent + "%"); // Size var humanSize = tRow.Find("td:eq(6)").Text().ToLowerInvariant(); var size = ReleaseInfo.GetBytes(humanSize); Output("Size: " + humanSize + " (" + size + " bytes)"); // --> Date var dateTimeOrig = tRow.Find("td:eq(4)").Text(); var dateTime = Regex.Replace(dateTimeOrig, @"<[^>]+>| ", "").Trim(); var date = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); Output("Released on: " + date); // Torrent Details URL var detailsLink = new Uri(TorrentDescriptionUrl.Replace("{id}", id.ToString())); Output("Details: " + detailsLink.AbsoluteUri); // Torrent Comments URL var commentsLink = new Uri(TorrentCommentUrl.Replace("{id}", id.ToString())); Output("Comments Link: " + commentsLink.AbsoluteUri); // Torrent Download URL var passkey = tRow.Find("td:eq(1) > a:eq(1)").Attr("href"); var key = Regex.Match(passkey, "(?<=passkey\\=)([a-zA-z0-9]*)"); Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString())); Output("Download Link: " + downloadLink.AbsoluteUri); // Building release infos var release = new ReleaseInfo { Category = MapTrackerCatToNewznab(testcat.ToString()), Title = name, Seeders = seeders, Peers = seeders + leechers, MinimumRatio = 1, MinimumSeedTime = 172800, PublishDate = date, Size = size, Files = files, Grabs = completed, Guid = detailsLink, Comments = commentsLink, Link = downloadLink }; var genres = tRow.Find("span.genres").Text(); if (!string.IsNullOrEmpty(genres)) { release.Description = genres; } // IMDB var imdbLink = tRow.Find("a[href*=\"http://imdb.com/title/\"]").First().Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdbLink); if (tRow.Find("img[title=\"100% freeleech\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (tRow.Find("img[title=\"Halfleech\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else if (tRow.Find("img[title=\"90% Freeleech\"]").Length >= 1) { release.DownloadVolumeFactor = 0.1; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex); } } // Return found releases return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var qc = new NameValueCollection { { "order_by", "time" }, { "order_way", "desc" }, { "action", "basic" }, { "searchsubmit", "1" } }; if (query.IsImdbQuery) { qc.Add("imdb", query.ImdbID); } else if (query.IsTmdbQuery) { qc.Add("tmdb", query.TmdbID.ToString()); } else { qc.Add("searchstr", query.GetQueryString()); } var catList = MapTorznabCapsToTrackers(query); foreach (var cat in catList) { qc.Add($"filter_cat[{cat}]", "1"); } // remove . as not used in titles var searchUrl = BrowseUrl + "?" + qc.GetQueryString().Replace(".", " "); var results = await RequestWithCookiesAsync(searchUrl); try { var parser = new HtmlParser(); var doc = parser.ParseDocument(results.ContentString); 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 = new Uri(SiteLink + qDetailsLink.GetAttribute("href")); var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last(); var link = new Uri(SiteLink + "torrents.php?action=download&id=" + torrentId); var posterStr = qDetailsLink.GetAttribute("data-cover"); var poster = !string.IsNullOrWhiteSpace(posterStr) ? new Uri(qDetailsLink.GetAttribute("data-cover")) : null; var files = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(3)").TextContent); var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent); var size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent); var grabs = ParseUtil.CoerceLong(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 <int> { cat switch { "featurefilm" => TorznabCatType.Movies.ID, "shortfilm" => TorznabCatType.Movies.ID, "miniseries" => TorznabCatType.TV.ID, "other" => TorznabCatType.Other.ID, _ => throw new Exception($"Unknown category: {cat}") } }; var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]"); var imdb = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null; qImdb = row.QuerySelector("a[href^=\"https://www.themoviedb.org\"]"); var tmdb = qImdb != null?ParseUtil.GetLongFromString(qImdb.GetAttribute("href").Split('/').Last()) : null; var release = new ReleaseInfo { MinimumRatio = 1, MinimumSeedTime = 259200, Description = description, Title = title, PublishDate = publishDate, Category = category, Link = link, Details = details, Guid = link, Imdb = imdb, TMDb = tmdb, Poster = poster, Seeders = seeders, Peers = leechers + seeders, Size = size, Grabs = grabs, Files = files, DownloadVolumeFactor = dlVolumeFactor, UploadVolumeFactor = 1 }; releases.Add(release); } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }