protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var pairs = new Dictionary <string, string> { { "do", "search" }, { "search_type", query.IsImdbQuery ? "t_genre" : "t_name" }, { "keywords", query.IsImdbQuery ? query.ImdbID : query.GetQueryString() }, { "category", "0" } // multi cat search not supported }; var results = await RequestWithCookiesAsync(BrowseUrl, method : RequestType.POST, data : pairs); if (results.IsRedirect) { // re-login await ApplyConfiguration(null); results = await RequestWithCookiesAsync(BrowseUrl, method : RequestType.POST, data : pairs); } try { var lastDate = DateTime.Now; var parser = new HtmlParser(); var doc = parser.ParseDocument(results.ContentString); var rows = doc.QuerySelectorAll("table[id='sortabletable'] > tbody > tr"); foreach (var row in rows.Skip(1)) { if (row.Children.Length != 9) { continue; // not a torrent line } var cat = row.Children[0].QuerySelector("a").GetAttribute("href").Split('=')[1]; var title = row.Children[1].QuerySelector("a").TextContent; var qLinks = row.Children[2].QuerySelectorAll("a"); var link = new Uri(configData.TorrentHTTPSMode.Value ? qLinks[1].GetAttribute("href") : qLinks[0].GetAttribute("href")); var comments = new Uri(row.Children[1].QuerySelector("a").GetAttribute("href")); var size = row.Children[4].TextContent; var grabs = row.Children[5].QuerySelector("a").TextContent; var seeders = ParseUtil.CoerceInt(row.Children[6].QuerySelector("a").TextContent); var leechers = ParseUtil.CoerceInt(row.Children[7].QuerySelector("a").TextContent); var qTags = row.Children[1].QuerySelector("div:has(span[style=\"float: right;\"])"); var dlVolumeFactor = 1.0; if (qTags.QuerySelector("img[alt^=\"TORRENT GRATUIT\"]") != null) { dlVolumeFactor = 0.0; } else if (qTags.QuerySelector("img[alt^=\"TORRENT SILVER\"]") != null) { dlVolumeFactor = 0.5; } var upVolumeFactor = qTags.QuerySelector("img[alt^=\"TORRENT X2\"]") != null ? 2.0 : 1.0; var release = new ReleaseInfo { MinimumRatio = 1, MinimumSeedTime = 0, Category = MapTrackerCatToNewznab(cat), Title = title, Link = link, Comments = comments, Size = ReleaseInfo.GetBytes(size), Seeders = seeders, Grabs = ParseUtil.CoerceLong(grabs), DownloadVolumeFactor = dlVolumeFactor, UploadVolumeFactor = upVolumeFactor, Peers = leechers + seeders, Guid = link }; var qTooltip = row.Children[1].QuerySelector("div.tooltip-content"); if (qTooltip != null) { var banner = qTooltip.QuerySelector("img"); if (banner != null) { release.BannerUrl = new Uri(banner.GetAttribute("src")); banner.Remove(); } qTooltip.QuerySelector("div:contains(\"Total Hits\")").Remove(); var qLongTitle = qTooltip.QuerySelector("div"); release.Title = qLongTitle.TextContent; qLongTitle.Remove(); var description = qTooltip.TextContent.Trim(); if (!string.IsNullOrWhiteSpace(description)) { release.Description = description; } } // issue #5064 replace multi keyword if (!string.IsNullOrEmpty(configData.ReplaceMulti.Value)) { var regex = new Regex("(?i)([\\.\\- ])MULTI([\\.\\- ])"); release.Title = regex.Replace(release.Title, "$1" + configData.ReplaceMulti.Value + "$2"); } // issue #6855 Replace VOSTFR with ENGLISH if (configData.Vostfr.Value) { release.Title = release.Title.Replace("VOSTFR", "ENGLISH").Replace("SUBFRENCH", "ENGLISH"); } var qPretime = qTags.QuerySelector("font.mkprettytime"); if (qPretime != null) { if (release.Description == null) { release.Description = qPretime.TextContent; } else { release.Description += "<br>\n" + qPretime.TextContent; } release.PublishDate = lastDate; } else { release.PublishDate = DateTime.ParseExact(qTags.TextContent.Trim(), "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture); lastDate = release.PublishDate; } releases.Add(release); } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }
private async Task <List <ReleaseInfo> > ParseUserSearchAsync(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchUrl = BrowseUrl; var isSearchAnime = query.Categories.Any(s => s == TorznabCatType.TVAnime.ID); var searchTerm = FixSearchTerm(query); var queryCollection = new NameValueCollection { { "searchstr", StripSearchString(searchTerm, isSearchAnime) }, { "order_by", "time" }, { "order_way", "desc" }, { "group_results", "1" }, { "action", "basic" }, { "searchsubmit", "1" } }; foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("filter_cat[" + cat + "]", "1"); } searchUrl += "?" + queryCollection.GetQueryString(); var results = await RequestWithCookiesAsync(searchUrl); if (IsSessionIsClosed(results)) { // re-login await ApplyConfiguration(null); 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); ICollection <int> groupCategory = null; string groupTitle = null; string groupYearStr = null; var categoryStr = ""; 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, false); 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 = null; 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\"]"); categoryStr = qCatLink.GetAttribute("href").Split('=')[1].Split('&')[0]; category = MapTrackerCatToNewznab(categoryStr); 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 (row.ClassList.Contains("group")) // group headers { groupCategory = category; groupTitle = title; groupYearStr = yearStr; continue; } } var release = new ReleaseInfo { MinimumRatio = 1, MinimumSeedTime = 0 }; 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 = Regex.Match(qDetailsLink.TextContent, @"\[.*?\]").Value; release.Title = ParseTitle(groupTitle, seasonEp, groupYearStr, categoryStr); release.Category = groupCategory; } else if (row.ClassList.Contains("torrent")) // standalone/un grouped torrents { release.Description = row.QuerySelector("div.torrent_info").TextContent; release.Title = ParseTitle(title, seasonEp, yearStr, categoryStr); 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"); // 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)); } // 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); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var trackerCats = MapTorznabCapsToTrackers(query); var cat = (trackerCats.Count == 1 ? trackerCats.ElementAt(0) : "0"); var episodeSearchUrl = string.Format(SearchUrl, cat, HttpUtility.UrlEncode(query.GetQueryString())); var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); if (results.Content.Contains("No torrents found")) { return(releases); } try { CQ dom = results.Content; var rows = dom[".ctable_content_no_pad > table > tbody > tr"].ToArray(); DateTime lastDateTime = default(DateTime); for (var i = 0; i < rows.Length; i++) { var rowA = rows[i]; var rAlign = rowA.Attributes["align"]; if (rAlign == "right" || rAlign == "center") { continue; } if (rAlign == "left") { // ex: "Monday, Jun 01, 2015", "Monday, Aug 03, 2015" var dateStr = rowA.Cq().Text().Trim().Replace("Added on ", ""); if (dateStr.ToLowerInvariant().Contains("today")) { lastDateTime = DateTime.Now; } else { lastDateTime = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dddd, MMM dd, yyyy", CultureInfo.InvariantCulture), DateTimeKind.Utc).ToLocalTime(); } continue; } if (rowA.ChildElements.Count() < 2) { continue; } var rowB = rows[++i]; var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.PublishDate = lastDateTime; var qLink = rowA.ChildElements.ElementAt(1).FirstElementChild.Cq(); release.Title = qLink.Text().Trim(); release.Description = release.Title; release.Comments = new Uri(SiteLink + qLink.Attr("href")); release.Guid = release.Comments; var qDownload = rowB.ChildElements.ElementAt(2).ChildElements.ElementAt(1).Cq(); release.Link = new Uri(SiteLink + qDownload.Attr("href")); var sizeStr = rowB.ChildElements.ElementAt(3).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text()); release.Peers = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text()) + release.Seeders; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, 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; 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); }
private List <NewpctRelease> ParseSearchJsonContent(string content, string year) { var releases = new List <NewpctRelease>(); if (string.IsNullOrWhiteSpace(content)) { return(releases); } try { var jo = JObject.Parse(content); var numItems = int.Parse(jo["data"]["items"].ToString()); for (var i = 0; i < numItems; i++) { var item = jo["data"]["torrents"]["0"][i.ToString()]; var title = item["torrentName"].ToString(); var detailsUrl = SiteLink + item["guid"]; var quality = item["calidad"].ToString(); var sizeString = item["torrentSize"].ToString(); var size = !sizeString.Contains("NAN") ? ReleaseInfo.GetBytes(sizeString) : 0; DateTime.TryParseExact(item["torrentDateAdded"].ToString(), "dd/MM/yyyy", null, DateTimeStyles.None, out var publishDate); var poster = SiteLink + item["imagen"].ToString().TrimStart('/'); // we have another search for series var titleLower = title.ToLower(); var isSeries = quality != null && quality.ToLower().Contains("hdtv"); var isGame = titleLower.Contains("pcdvd"); if (isSeries || isGame) { continue; } // at this point we assume that this is a movie release, we need to parse the title. examples: // Quien Es Harry Crumb (1989) [BluRay 720p X264 MKV][AC3 5.1 Castellano][www.descargas2020.ORG] // Harry Potter y la orden del Fenix [4K UHDrip][2160p][HDR][AC3 5.1 Castellano DTS 5.1-Ingles+Subs][ES-EN] // Harry Potter Y El Misterio Del Principe [DVDFULL][Spanish][2009] // Harry Potter 2 Y La Camara Secreta [DVD9 FULL][Spanish_English][Inc Subs.] // The Avengers [DVDRIP][VOSE English_Subs. Español][2012] // Harry Potter y las Reliquias de la Muerte Parte I.DVD5 [ DVDR] [AC3 5.1] [Multilenguaje] [2010] // Joker (2019) 720p [Web Screener 720p ][Castellano][www.descargas2020.ORG][www.pctnew.ORG] // remove quality and language from the title var titleParts = title.Split('['); title = titleParts[0].Replace("720p", "").Trim(); // quality in the field quality/calidad is wrong in many cases if (!string.IsNullOrWhiteSpace(quality)) { if (titleLower.Contains("720") && !quality.Contains("720")) { quality += " 720p"; } if (titleLower.Contains("265") || titleLower.Contains("hevc")) { quality += " x265"; } if (titleLower.Contains("dvdfull") || titleLower.Contains("dvd5") || titleLower.Contains("dvd9")) { quality = "DVDR"; } if (titleLower.Contains("[web screener]") || titleLower.Contains("[hd-tc]")) { quality = "TS Screener"; } } else if (titleParts.Length > 2) { quality = titleParts[1].Replace("]", "").Replace("MKV", "").Trim(); } // we have to guess the language (words DUAL or MULTI are not supported in Radarr) var language = "spanish"; if (titleLower.Contains("latino")) { language += " latino"; } if ((titleLower.Contains("castellano") && titleLower.Contains("ingles")) || (titleLower.Contains("spanish") && titleLower.Contains("english")) || titleLower.Contains("[es-en]") || titleLower.Contains("multilenguaje")) { language += " english"; } else if (titleLower.Contains("vose")) { language = "english vose"; } // remove the movie year if the user chooses (the year in the title is wrong in many cases) if (_removeMovieYear) { title = _titleYearRegex.Replace(title, ""); } // we add the year from search if it's not in the title if (!string.IsNullOrWhiteSpace(year) && !_titleYearRegex.Match(title).Success) { title += " " + year; } var release = GetReleaseFromData(ReleaseType.Movie, title, detailsUrl, quality, language, size, publishDate, poster); releases.Add(release); } } catch (Exception ex) { OnParseError(content, ex); } return(releases); }
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(); if (!string.IsNullOrWhiteSpace(searchString)) { Regex ReplaceRegex = new Regex("[^a-zA-Z0-9]+"); searchString = "%" + ReplaceRegex.Replace(searchString, "%") + "%"; searchString = Regex.Replace(searchString, @"(%\d{3,4})[ip](%)", "$1$2"); // remove i/p from resolution tags (see #835) queryCollection.Add("search", searchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("c" + cat, "1"); } searchUrl += queryCollection.GetQueryString(); var results = await RequestStringWithCookiesAndRetry(searchUrl); await FollowIfRedirect(results); try { CQ dom = results.Content; var rows = dom["table.torrenttable > tbody > tr.browse_color"]; foreach (var row in rows) { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; 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).FirstChild.Cq(); release.Link = new Uri(SiteLink + "/" + qLink.Attr("href")); var torrentId = qLink.Attr("href").Split('=').Last(); var descCol = row.ChildElements.ElementAt(3); var qCommentLink = descCol.FirstChild.Cq(); release.Title = qCommentLink.Text(); release.Comments = new Uri(SiteLink + "/" + qCommentLink.Attr("href")); release.Guid = release.Comments; release.Link = new Uri($"{SiteLink}download.php?torrent={torrentId}"); var dateStr = descCol.ChildElements.Last().Cq().Text().Split('|').Last().ToLowerInvariant().Replace("ago.", "").Trim(); release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr); var sizeStr = row.ChildElements.ElementAt(7).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text()); release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(10).Cq().Text()) + release.Seeders; var files = qRow.Find("td:nth-child(6)").Text(); release.Files = ParseUtil.CoerceInt(files); var grabs = qRow.Find("td:nth-child(9) > a").Get(0).FirstChild.ToString(); release.Grabs = ParseUtil.CoerceInt(grabs); release.DownloadVolumeFactor = 1; release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public 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 (!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; 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(); if (0 != qRow.Find("td.mainblockcontent u").Length) { var imdbStr = qRow.Find("td.mainblockcontent u").Parent().First().Attr("href").Replace("http://www.imdb.com/title/tt", "").Replace("/", ""); long imdb; if (ParseUtil.TryCoerceLong(imdbStr, out imdb)) { release.Imdb = imdb; } } release.MinimumRatio = 1; release.MinimumSeedTime = 172800; int seeders, peers; if (ParseUtil.TryCoerceInt(qRow.Find("td").Get(9).FirstChild.FirstChild.InnerText, out seeders)) { release.Seeders = seeders; if (ParseUtil.TryCoerceInt(qRow.Find("td").Get(10).FirstChild.FirstChild.InnerText, out peers)) { release.Peers = peers + release.Seeders; } } release.Grabs = ParseUtil.CoerceLong(qRow.Find("td:nth-child(12)").Text()); 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('>')); release.PublishDate = DateTime.Parse(dateString, CultureInfo.InvariantCulture); string category = qRow.Find("td:eq(0) a").Attr("href").Replace("torrents.php?category=", ""); release.Category = MapTrackerCatToNewznab(category); 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; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var queryUrl = SearchUrl; var queryCollection = new NameValueCollection(); if (!string.IsNullOrWhiteSpace(query.ImdbID) && query.ImdbID.ToLower().StartsWith("tt")) { queryCollection.Add("search", query.ImdbID); } else { if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("c" + cat, "1"); } if (queryCollection.Count > 0) { queryUrl += "?" + queryCollection.GetQueryString(); } var results = await RequestStringWithCookiesAndRetry(queryUrl); // Check for being logged out if (results.IsRedirect) { if (results.RedirectingTo.Contains("login.php")) { throw new ExceptionWithConfigData("Login failed, please reconfigure the tracker to update the cookies", configData); } else { throw new ExceptionWithConfigData(string.Format("Got a redirect to {0}, please adjust your the alternative link", results.RedirectingTo), configData); } } try { CQ dom = results.Content; var rows = dom["#torrentTable > tbody > tr.browse"]; foreach (var row in rows) { CQ qRow = row.Cq(); var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.Title = qRow.Find(".torrentName").Text(); if ((query.ImdbID == null || !TorznabCaps.SupportsImdbSearch) && !query.MatchQueryStringAND(release.Title)) { continue; } release.Guid = new Uri(SiteLink + qRow.Find(".torrentName").Attr("href")); release.Comments = release.Guid; release.Link = new Uri(SiteLink + qRow.Find(".dlLinksInfo > a").Attr("href")); var sizeStr = qRow.Find(".sizeInfo").Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); var dateStr = qRow.Find(".ulInfo").Text().Split('|').Last().Trim(); var agoIdx = dateStr.IndexOf("ago"); if (agoIdx > -1) { dateStr = dateStr.Substring(0, agoIdx); } release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr); release.Seeders = ParseUtil.CoerceInt(qRow.Find(".seedersInfo").Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".leechersInfo").Text()) + release.Seeders; var cat = qRow.Find("td:eq(0) a").First().Attr("href").Split('#')[0].Substring(15);//browse.php?cat=24 release.Category = MapTrackerCatToNewznab(cat); if (qRow.Find("span.flTags").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } 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 searchString = query.GetQueryString(); var cats = MapTorznabCapsToTrackers(query); string cat = "0"; if (cats.Count == 1) { cat = cats[0]; } var queryCollection = new NameValueCollection(); if (query.ImdbID != null) { queryCollection.Add("search", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } queryCollection.Add("cat", cat); queryCollection.Add("searchin", "1"); queryCollection.Add("sort", "2"); searchUrl += "?" + queryCollection.GetQueryString(); var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); // Occasionally the cookies become invalid, login again if that happens if (response.IsRedirect) { await ApplyConfiguration(null); response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); } var results = response.Content; try { CQ dom = results; var globalFreeLeech = dom.Find("div.globalFreeLeech").Any(); var rows = dom[".torrentrow"]; foreach (var row in rows) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qTitleLink = qRow.Find(".torrenttable:eq(1) a").First(); release.Title = qRow.Find(".torrenttable:eq(1) b").Text(); var longtitle = qRow.Find(".torrenttable:eq(1) a[title]").Attr("title"); if (!string.IsNullOrEmpty(longtitle) && !longtitle.Contains("<")) // releases with cover image have no full title { release.Title = longtitle; } if (query.ImdbID == null && !query.MatchQueryStringAND(release.Title)) { continue; } release.Description = qRow.Find(".torrenttable:eq(1) > span > font.small").First().Text(); var tooltip = qTitleLink.Attr("title"); if (!string.IsNullOrEmpty(tooltip)) { var ImgRegexp = new Regex("src='(.*?)'"); var ImgRegexpMatch = ImgRegexp.Match(tooltip); if (ImgRegexpMatch.Success) { release.BannerUrl = new Uri(ImgRegexpMatch.Groups[1].Value); } } release.Guid = new Uri(SiteLink + qTitleLink.Attr("href")); release.Comments = release.Guid; //22:05:3716/02/2013 var dateStr = qRow.Find(".torrenttable:eq(5)").Text().Trim() + " +0200"; release.PublishDate = DateTime.ParseExact(dateStr, "H:mm:ssdd/MM/yyyy zzz", CultureInfo.InvariantCulture); var qLink = qRow.Find("a[href^=\"download.php?id=\"]").First(); release.Link = new Uri(SiteLink + qLink.Attr("href").Replace("&usetoken=1", "")); var sizeStr = qRow.Find(".torrenttable:eq(6)").Text().Trim(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Find(".torrenttable:eq(8)").Text().Trim()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".torrenttable:eq(9)").Text().Trim()) + release.Seeders; var catId = qRow.Find(".torrenttable:eq(0) a").First().Attr("href").Substring(15); release.Category = MapTrackerCatToNewznab(catId); var grabs = qRow.Find(".torrenttable:eq(7)").First().Get(0).FirstChild; release.Grabs = ParseUtil.CoerceLong(catId); if (globalFreeLeech || row.Cq().Find("img[alt=\"FreeLeech\"]").Any()) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; // Skip Romanian releases if (release.Category.Contains(TorznabCatType.MoviesForeign.ID) && !configData.IncludeRomanianReleases.Value) { continue; } releases.Add(release); } } catch (Exception ex) { OnParseError(results, 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 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 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 Output("\nNo result found for your query, please try another search term ...\n", "info"); 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 row in firstPageRows) { Output("Torrent #" + (releases.Count + 1)); // ID var id = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("href").Split('=').Last(); Output("ID: " + id); // Release Name var name = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("title"); // Category var categoryName = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("title"); 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(); } Output("Category: " + cat + " - " + categoryName); // Seeders var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent); Output("Seeders: " + seeders); // Leechers var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent); Output("Leechers: " + leechers); // Completed var regexObj = new Regex(@"[^\d]"); var completed2 = row.QuerySelector("td:nth-of-type(8)").TextContent; var completed = ParseUtil.CoerceLong(regexObj.Replace(completed2, "")); Output("Completed: " + completed); // Files var qFiles = row.QuerySelector("td:nth-of-type(3) > a"); var files = qFiles != null?ParseUtil.CoerceInt(Regex.Match(qFiles.TextContent, @"\d+").Value) : 1; Output("Files: " + files); // Size var humanSize = row.QuerySelector("td:nth-of-type(7)").TextContent.ToLowerInvariant(); var size = ReleaseInfo.GetBytes(humanSize); Output("Size: " + humanSize + " (" + size + " bytes)"); // --> 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(); Output("Released on: " + date); // Torrent Details URL var details = new Uri(TorrentDetailsUrl.Replace("{id}", id.ToString())); Output("Details: " + details.AbsoluteUri); // Torrent Download URL var passkey = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(2)").GetAttribute("href"); var key = Regex.Match(passkey, "(?<=passkey\\=)([a-zA-z0-9]*)"); var 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(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); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday); TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday); TimeSpan delta = new TimeSpan(1, 0, 0); TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition); TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment }; TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments); var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = BrowseUrl; var queryCollection = new NameValueCollection(); queryCollection.Add("showsearch", "1"); queryCollection.Add("incldead", "1"); queryCollection.Add("orderby", "added"); queryCollection.Add("sort", "desc"); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } var cats = MapTorznabCapsToTrackers(query); string cat = "0"; if (cats.Count == 1) { cat = cats[0]; } queryCollection.Add("cat", cat); searchUrl += "?" + queryCollection.GetQueryString(); var response = await RequestBytesWithCookies(searchUrl); var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content); try { CQ dom = results; var rows = dom["table.testtable> tbody > tr:has(td.tableb)"]; foreach (var row in rows) { var release = new ReleaseInfo(); release.MinimumRatio = 0.75; release.MinimumSeedTime = 0; var qRow = row.Cq(); var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First(); release.Title = qDetailsLink.Text(); if (!query.MatchQueryStringAND(release.Title)) { continue; } var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First(); var qSeeders = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(3)"); var qLeechers = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(4)"); var qDateStr = qRow.Find("td > table.testtable > tbody > tr > td:eq(6)"); var qSize = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(1)"); var qDownloadLink = qRow.Find("a[href*=download]").First(); var catStr = qCatLink.Attr("href").Split('=')[1]; release.Category = MapTrackerCatToNewznab(catStr); var dlLink = qDownloadLink.Attr("href"); if (dlLink.Contains("javascript")) // depending on the user agent the DL link is a javascript call { var dlLinkParts = dlLink.Split(new char[] { '\'', ',' }); dlLink = SiteLink + "download/" + dlLinkParts[3] + "/" + dlLinkParts[5]; } release.Link = new Uri(dlLink); release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href")); release.Guid = release.Link; var sizeStr = qSize.Text(); logger.Error(sizeStr); release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(".", "").Replace(",", ".")); release.Seeders = ParseUtil.CoerceInt(qSeeders.Text()); release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders; var dateStr = qDateStr.Text().Replace('\xA0', ' '); var dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz); release.PublishDate = pubDateUtc; var files = qRow.Find("td:contains(Datei) > strong ~ strong").Text(); release.Files = ParseUtil.CoerceInt(files); if (qRow.Find("img[title=\"OnlyUpload\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var prevCook = CookieHeader + ""; var searchStringIsImdbQuery = (ParseUtil.GetImdbID(searchString) != null); // If we have no query use the RSS Page as their server is slow enough at times! // ~15.01.2019 they removed the description tag making the RSS feed almost useless, we don't use it for now. See #4458 // if (false && query.IsTest || string.IsNullOrWhiteSpace(searchString)) /* * if (false) * { * var rssPage = await RequestStringWithCookiesAndRetry(string.Format(RSSUrl, configData.RSSKey.Value)); * try * { * if (rssPage.Content.EndsWith("\0")) * { * rssPage.Content = rssPage.Content.Substring(0, rssPage.Content.Length - 1); * } * rssPage.Content = RemoveInvalidXmlChars(rssPage.Content); * var rssDoc = XDocument.Parse(rssPage.Content); * * foreach (var item in rssDoc.Descendants("item")) * { * var title = item.Descendants("title").First().Value; * var description = item.Descendants("description").First().Value; * var link = item.Descendants("link").First().Value; * var category = item.Descendants("category").First().Value; * var date = item.Descendants("pubDate").First().Value; * * var torrentIdMatch = Regex.Match(link, "(?<=id=)(\\d)*"); * 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\/\WSeeders:\W(?<seeders>[\d\,]*)\W\/\WLeechers:\W(?<leechers>[\d\,]*)\W\/\WSize:\W(?<size>[\d\.]*\W\S*)"); * if (!infoMatch.Success) * throw new Exception("Unable to find info"); * * var release = new ReleaseInfo * { * Title = title, * Description = title, * Guid = new Uri(string.Format(DownloadUrl, torrentId)), * Comments = new Uri(string.Format(CommentUrl, torrentId)), * PublishDate = DateTime.ParseExact(date, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31 * Link = new Uri(string.Format(DownloadUrl, torrentId)), * Seeders = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value), * Peers = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value), * Size = ReleaseInfo.GetBytes(infoMatch.Groups["size"].Value), * Category = MapTrackerCatToNewznab(category) * }; * * release.Peers += release.Seeders; * releases.Add(release); * } * } * catch (Exception ex) * { * logger.Error("XSpeeds: Error while parsing the RSS feed:"); * logger.Error(rssPage.Content); * throw ex; * } * } */ //if (query.IsTest || !string.IsNullOrWhiteSpace(searchString)) /* * if (searchString.Length < 3 && !query.IsTest) * { * OnParseError("", new Exception("Minimum search length is 3")); * return releases; * } */ var searchParams = new Dictionary <string, string> { { "do", "search" }, { "category", "0" }, { "include_dead_torrents", "no" } }; if (query.IsImdbQuery) { searchParams.Add("keywords", query.ImdbID); searchParams.Add("search_type", "t_both"); } else { searchParams.Add("keywords", searchString); if (searchStringIsImdbQuery) { searchParams.Add("search_type", "t_both"); } else { searchParams.Add("search_type", "t_name"); } } var searchPage = await PostDataWithCookiesAndRetry(SearchUrl, searchParams, CookieHeader); // Occasionally the cookies become invalid, login again if that happens if (searchPage.IsRedirect) { await ApplyConfiguration(null); searchPage = await PostDataWithCookiesAndRetry(SearchUrl, searchParams, CookieHeader); } try { CQ dom = searchPage.Content; var rows = dom["table#sortabletable > tbody > tr:has(div > a[href*=\"details.php?id=\"])"]; foreach (var row in rows) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qDetails = qRow.Find("div > a[href*=\"details.php?id=\"]"); // details link, release name get's shortened if it's to long var qTitle = qRow.Find("td:eq(1) .tooltip-content div:eq(0)"); // use Title from tooltip if (!qTitle.Any()) // fallback to Details link if there's no tooltip { qTitle = qDetails; } release.Title = qTitle.Text(); release.Guid = new Uri(qRow.Find("td:eq(2) a").Attr("href")); release.Link = release.Guid; release.Comments = new Uri(qDetails.Attr("href")); release.PublishDate = DateTime.ParseExact(qRow.Find("td:eq(1) div").Last().Text().Trim(), "dd-MM-yyyy H:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); //08-08-2015 12:51 release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:eq(6)").Text()); release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find("td:eq(7)").Text().Trim()); release.Size = ReleaseInfo.GetBytes(qRow.Find("td:eq(4)").Text().Trim()); var qBanner = qRow.Find("td:eq(1) .tooltip-content img").First(); if (qBanner.Length > 0) { release.BannerUrl = new Uri(qBanner.Attr("src")); } var cat = row.Cq().Find("td:eq(0) a").First().Attr("href"); var catSplit = cat.LastIndexOf('='); if (catSplit > -1) { cat = cat.Substring(catSplit + 1); } release.Category = MapTrackerCatToNewznab(cat); var grabs = qRow.Find("td:nth-child(6)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("img[alt^=\"Free Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("img[alt^=\"Silver Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } if (qRow.Find("img[alt^=\"x2 Torrent\"]").Length >= 1) { release.UploadVolumeFactor = 2; } else { release.UploadVolumeFactor = 1; } releases.Add(release); } } catch (Exception ex) { OnParseError(searchPage.Content, ex); } if (!CookieHeader.Trim().Equals(prevCook.Trim())) { SaveConfig(); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var qc = new NameValueCollection(); if (query.IsImdbQuery) { qc.Add("q", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(query.GetQueryString())) { qc.Add("q", query.GetQueryString()); } foreach (var cat in MapTorznabCapsToTrackers(query)) { qc.Add(cat, string.Empty); } var searchUrl = SearchUrl + "?" + qc.GetQueryString(); var response = await RequestWithCookiesAndRetryAsync(searchUrl, referer : SearchUrl); var results = response.ContentString; if (results == null || !results.Contains("/lout.php")) { throw new Exception("The user is not logged in. It is possible that the cookie has expired or you made a mistake when copying it. Please check the settings."); } if (string.IsNullOrWhiteSpace(query.ImdbID) && string.IsNullOrWhiteSpace(query.SearchTerm) && results.Contains("No Torrents Found!")) { throw new Exception("Got No Torrents Found! Make sure your IPTorrents profile config contain proper default category settings."); } try { var parser = new HtmlParser(); var doc = parser.ParseDocument(results); var rows = doc.QuerySelectorAll("table[id='torrents'] > tbody > tr"); foreach (var row in rows.Skip(1)) { var qTitleLink = row.QuerySelector("a[href^=\"/details.php?id=\"]"); if (qTitleLink == null) // no results { continue; } // drop invalid char that seems to have cropped up in some titles. #6582 var title = qTitleLink.TextContent.Trim().Replace("\u000f", ""); var comments = new Uri(SiteLink + qTitleLink.GetAttribute("href").TrimStart('/')); var qLink = row.QuerySelector("a[href^=\"/download.php/\"]"); var link = new Uri(SiteLink + qLink.GetAttribute("href").TrimStart('/')); var descrSplit = row.QuerySelector(".t_ctime").TextContent.Split('|'); var dateSplit = descrSplit.Last().Trim().Split(new[] { " by " }, StringSplitOptions.None); var publishDate = DateTimeUtil.FromTimeAgo(dateSplit.First()); var description = descrSplit.Length > 1 ? "Tags: " + descrSplit.First().Trim() : ""; if (dateSplit.Length > 1) { description += " Uploader: " + dateSplit.Last(); } description = description.Trim(); var catIcon = row.QuerySelector("td:nth-of-type(1) a"); if (catIcon == null) { // Torrents - Category column == Text or Code // release.Category = MapTrackerCatDescToNewznab(row.Cq().Find("td:eq(0)").Text()); // Works for "Text" but only contains the parent category throw new Exception("Please, change the 'Torrents - Category column' option to 'Icons' in the website Settings. Wait a minute (cache) and then try again."); } // Torrents - Category column == Icons var cat = MapTrackerCatToNewznab(catIcon.GetAttribute("href").Substring(1)); var size = ReleaseInfo.GetBytes(row.Children[5].TextContent); var grabs = ParseUtil.CoerceInt(row.Children[6].TextContent); var seeders = ParseUtil.CoerceInt(row.QuerySelector(".t_seeders").TextContent.Trim()); var leechers = ParseUtil.CoerceInt(row.QuerySelector(".t_leechers").TextContent.Trim()); var dlVolumeFactor = row.QuerySelector("span.t_tag_free_leech") != null ? 0 : 1; var release = new ReleaseInfo { Title = title, Comments = comments, Guid = comments, Link = link, PublishDate = publishDate, Category = cat, Description = description, Size = size, Grabs = grabs, Seeders = seeders, Peers = seeders + leechers, DownloadVolumeFactor = dlVolumeFactor, UploadVolumeFactor = 1, MinimumRatio = 1, MinimumSeedTime = 1209600 // 336 hours }; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > GetResults(string searchTerm) { var cleanSearchTerm = HttpUtility.UrlEncode(searchTerm); // This tracker only deals with full seasons so chop off the episode/season number if we have it D: if (!string.IsNullOrWhiteSpace(searchTerm)) { var splitindex = searchTerm.LastIndexOf(' '); if (splitindex > -1) { searchTerm = searchTerm.Substring(0, splitindex); } } // The result list var releases = new List <ReleaseInfo>(); // Check cache first so we don't query the server for each episode when searching for each episode in a series. lock (cache) { // Remove old cache items CleanCache(); var cachedResult = cache.Where(i => i.Query == searchTerm).FirstOrDefault(); if (cachedResult != null) { return(cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray()); } } var queryUrl = SearchUrl; // Only include the query bit if its required as hopefully the site caches the non query page if (!string.IsNullOrWhiteSpace(searchTerm)) { queryUrl += string.Format("searchstr={0}&action=advanced&search_type=title&year=&year2=&tags=&tags_type=0&sort=time_added&way=desc&hentai=2&releasegroup=&epcount=&epcount2=&artbooktitle=", cleanSearchTerm); } // Get the content from the tracker var response = await RequestStringWithCookiesAndRetry(queryUrl); CQ dom = response.Content; // Parse try { var releaseInfo = "S01"; var root = dom.Find(".group_cont"); // We may have got redirected to the series page if we have none of these if (root.Count() == 0) { root = dom.Find(".torrent_table"); } foreach (var series in root) { var seriesCq = series.Cq(); var synonyms = new List <string>(); var mainTitle = seriesCq.Find(".group_title strong a").First().Text().Trim(); var yearStr = seriesCq.Find(".group_title strong").First().Text().Trim().Replace("]", "").Trim(); int yearIndex = yearStr.LastIndexOf("["); if (yearIndex > -1) { yearStr = yearStr.Substring(yearIndex + 1); } int year = 0; if (!int.TryParse(yearStr, out year)) { year = DateTime.Now.Year; } synonyms.Add(mainTitle); // If the title contains a comma then we can't use the synonyms as they are comma seperated if (!mainTitle.Contains(",")) { var symnomnNames = string.Empty; foreach (var e in seriesCq.Find(".group_statbox li")) { if (e.FirstChild.InnerText == "Synonyms:") { symnomnNames = e.InnerText; } } if (!string.IsNullOrWhiteSpace(symnomnNames)) { foreach (var name in symnomnNames.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { var theName = name.Trim(); if (!theName.Contains("&#") && !string.IsNullOrWhiteSpace(theName)) { synonyms.Add(theName); } } } } foreach (var title in synonyms) { var releaseRows = seriesCq.Find(".torrent_group tr"); // Skip the first two info rows for (int r = 1; r < releaseRows.Count(); r++) { var row = releaseRows.Get(r); var rowCq = row.Cq(); if (rowCq.HasClass("edition_info")) { releaseInfo = rowCq.Find("td").Text(); if (string.IsNullOrWhiteSpace(releaseInfo)) { // Single episodes alpha - Reported that this info is missing. // It should self correct when availible break; } releaseInfo = releaseInfo.Replace("Episode ", ""); releaseInfo = releaseInfo.Replace("Season ", "S"); releaseInfo = releaseInfo.Trim(); } else if (rowCq.HasClass("torrent")) { var links = rowCq.Find("a"); // Protect against format changes if (links.Count() != 2) { continue; } var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 259200; var downloadLink = links.Get(0); // We dont know this so try to fake based on the release year release.PublishDate = new DateTime(year, 1, 1); release.PublishDate = release.PublishDate.AddDays(Math.Min(DateTime.Now.DayOfYear, 365) - 1); var infoLink = links.Get(1); release.Comments = new Uri(SiteLink + infoLink.Attributes.GetAttribute("href")); release.Guid = new Uri(SiteLink + infoLink.Attributes.GetAttribute("href") + "&nh=" + StringUtil.Hash(title)); // Sonarr should dedupe on this url - allow a url per name. release.Link = new Uri(downloadLink.Attributes.GetAttribute("href"), UriKind.Relative); var category = seriesCq.Find("a[title=\"View Torrent\"]").Text().Trim(); if (category == "TV Series") { release.Category = TorznabCatType.TVAnime.ID; } // Ignore these categories as they'll cause hell with the matcher // TV Special, OVA, ONA, DVD Special, BD Special if (category == "Movie") { release.Category = TorznabCatType.Movies.ID; } if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel") { release.Category = TorznabCatType.BooksComics.ID; } if (category == "Novel" || category == "Artbook") { release.Category = TorznabCatType.BooksComics.ID; } if (category == "Game" || category == "Visual Novel") { var description = rowCq.Find(".torrent_properties a:eq(1)").Text(); if (description.Contains(" PSP ")) { release.Category = TorznabCatType.ConsolePSP.ID; } if (description.Contains("PSX")) { release.Category = TorznabCatType.ConsoleOther.ID; } if (description.Contains(" NES ")) { release.Category = TorznabCatType.ConsoleOther.ID; } if (description.Contains(" PC ")) { release.Category = TorznabCatType.PCGames.ID; } } // We dont actually have a release name >.> so try to create one var releaseTags = infoLink.InnerText.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList(); for (int i = releaseTags.Count - 1; i >= 0; i--) { releaseTags[i] = releaseTags[i].Trim(); if (string.IsNullOrWhiteSpace(releaseTags[i])) { releaseTags.RemoveAt(i); } } var group = releaseTags.Last(); if (group.Contains("(") && group.Contains(")")) { // Skip raws if set if (group.ToLowerInvariant().StartsWith("raw") && !AllowRaws) { continue; } var start = group.IndexOf("("); group = "[" + group.Substring(start + 1, (group.IndexOf(")") - 1) - start) + "] "; } else { group = string.Empty; } var infoString = ""; for (int i = 0; i + 1 < releaseTags.Count(); i++) { if (releaseTags[i] == "Raw" && !AllowRaws) { continue; } infoString += "[" + releaseTags[i] + "]"; } if (category == "Movie") { release.Title = string.Format("{0} {1} {2}{3}", title, year, group, infoString); } else { release.Title = string.Format("{0}{1} {2} {3}", group, title, releaseInfo, infoString); } release.Description = title; var size = rowCq.Find(".torrent_size"); if (size.Count() > 0) { release.Size = ReleaseInfo.GetBytes(size.First().Text()); } // Additional 5 hours per GB release.MinimumSeedTime += (release.Size / 1000000000) * 18000; // Peer info release.Seeders = ParseUtil.CoerceInt(rowCq.Find(".torrent_seeders").Text()); release.Peers = release.Seeders + ParseUtil.CoerceInt(rowCq.Find(".torrent_leechers").Text()); if (release.Category != 0) { releases.Add(release); } } } } } } catch (Exception ex) { OnParseError(response.Content, ex); } // Add to the cache lock (cache) { cache.Add(new CachedQueryResult(searchTerm, releases)); } return(releases.Select(s => (ReleaseInfo)s.Clone())); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 3, 5, DayOfWeek.Sunday); TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 10, 5, DayOfWeek.Sunday); TimeSpan delta = new TimeSpan(1, 0, 0); TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition); TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment }; TimeZoneInfo denmarkTz = TimeZoneInfo.CreateCustomTimeZone("Denmark Time", new TimeSpan(1, 0, 0), "(GMT+01:00) Denmark Time", "Denmark Time", "Denmark DST", adjustments); var releasesPerPage = 100; var releases = new List <ReleaseInfo>(); var page = (query.Offset / releasesPerPage) + 1; string episodeSearchUrl; if (string.IsNullOrEmpty(query.GetQueryString())) { episodeSearchUrl = SearchUrl + "?page=" + page; } else { var cats = MapTorznabCapsToTrackers(query); var catsUrlPart = string.Join("&", cats.Select(c => $"filter_{c}=on")); episodeSearchUrl = $"{SearchUrl}?page={page}&group=0&{catsUrlPart}&search={HttpUtility.UrlEncode(query.GetQueryString())}&pre_type=torrents&type="; } var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); if (string.IsNullOrEmpty(results.Content)) { CookieHeader = string.Empty; var pairs = new Dictionary <string, string> { { "username", configData.Username.Value }, { "password", configData.Password.Value }, { "langlang", null }, { "login", "login" } }; var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl); await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("logout.php"), () => { CQ dom = response.Content; var messageEl = dom["#loginform .warning"]; var errorMessage = messageEl.Text().Trim(); throw new ExceptionWithConfigData(errorMessage, configData); }); results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); } try { CQ dom = results.Content; var rows = dom["#torrent_table tr.torrent"]; foreach (var row in rows) { var qRow = row.Cq(); var release = new ReleaseInfo { MinimumRatio = 1, MinimumSeedTime = 172800 }; var catAnchor = row.FirstChild.FirstChild; var catUrl = catAnchor.GetAttribute("href"); var catStr = Regex.Match(catUrl, "filter_(?<catNo>[0-9]+)=on").Groups["catNo"].Value; var catNo = int.Parse(catStr); var moviesCatsDanish = new[] { 2, 3, 10, 28, 29, 31 }; var moviesCatsIntl = new[] { 8, 9, 11, 22, 24 }; var moviesCats = configData.OnlyDanishCategories.Value ? moviesCatsDanish : moviesCatsDanish.Concat(moviesCatsIntl); var seriesCatsDanish = new[] { 1, 4, 30 }; var seriesCatsIntl = new[] { 20, 21 }; var seriesCats = configData.OnlyDanishCategories.Value ? seriesCatsDanish : seriesCatsDanish.Concat(seriesCatsIntl); if (moviesCats.Contains(catNo)) { release.Category = TorznabCatType.Movies.ID; } else if (seriesCats.Contains(catNo)) { release.Category = TorznabCatType.TV.ID; } else if (catNo == 12) { release.Category = TorznabCatType.BooksEbook.ID; } else if (catNo == 6) { release.Category = TorznabCatType.AudioAudiobook.ID; } else { continue; } var titleAnchor = qRow.Find("div.croptorrenttext a").FirstElement(); var title = titleAnchor.GetAttribute("title"); release.Title = title; var dlUrlAnchor = qRow.Find("span.right a").FirstElement(); var dlUrl = dlUrlAnchor.GetAttribute("href"); var authkey = Regex.Match(dlUrl, "authkey=(?<authkey>[0-9a-zA-Z]+)").Groups["authkey"].Value; var torrentPass = Regex.Match(dlUrl, "torrent_pass=(?<torrent_pass>[0-9a-zA-Z]+)").Groups["torrent_pass"].Value; var torrentId = Regex.Match(dlUrl, "id=(?<id>[0-9]+)").Groups["id"].Value; release.Link = new Uri($"{SearchUrl}/{title}.torrent/?action=download&authkey={authkey}&torrent_pass={torrentPass}&id={torrentId}"); var torrentLink = titleAnchor.GetAttribute("href"); release.Guid = new Uri(SiteLink + torrentLink); release.Comments = new Uri(SearchUrl + torrentLink); var addedElement = qRow.Find("span.time").FirstElement(); var addedStr = addedElement.GetAttribute("title"); release.PublishDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.ParseExact(addedStr, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture), denmarkTz).ToLocalTime(); var columns = qRow.Children(); var seedersElement = columns.Reverse().Skip(1).First(); release.Seeders = int.Parse(seedersElement.InnerText); var leechersElement = columns.Last().FirstElement(); release.Peers = release.Seeders + int.Parse(leechersElement.InnerText); var sizeElement = columns.Skip(2).First(); var sizeStr = sizeElement.InnerText; release.Size = ReleaseInfo.GetBytes(sizeStr); var imdbAnchor = qRow.Find(".torrentnotes a") .FirstOrDefault(a => a.GetAttribute("href").Contains("imdb.com")); if (imdbAnchor != null) { var referrerUrl = imdbAnchor.GetAttribute("href"); release.Imdb = long.Parse(Regex.Match(referrerUrl, "tt(?<imdbId>[0-9]+)").Groups["imdbId"].Value); } var Files = qRow.Find("td:nth-child(3) > div"); release.Files = ParseUtil.CoerceLong(Files.Text().Split(' ')[0]); var Grabs = qRow.Find("td:nth-child(6)"); release.Grabs = ParseUtil.CoerceLong(Grabs.Text()); if (qRow.Find("img[src=\"/static/common/torrents/gratis.png\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } if (qRow.Find("img[src=\"/static/common/torrents/toxupload.png\"]").Length >= 1) { release.UploadVolumeFactor = 2; } else { release.UploadVolumeFactor = 1; } releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }