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); }