/// <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); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var queryCollection = new NameValueCollection(); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } queryCollection.Add("incldead", "1"); var searchUrl = SearchUrl + queryCollection.GetQueryString(); var trackerCats = MapTorznabCapsToTrackers(query, mapChildrenCatsToParent: true); var results = await RequestStringWithCookiesAndRetry(searchUrl); try { CQ dom = results.Content; dom["#needseed"].Remove(); foreach (var table in dom["table[align=center] + br + table > tbody"]) { var rows = table.Cq().Children(); foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qLink = qRow.Children().ElementAt(2).Cq().Children("a").First(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.Title = qLink.Attr("title"); if (!query.MatchQueryStringAND(release.Title)) { continue; } release.Files = ParseUtil.CoerceLong(qRow.Find("td:nth-child(4)").Text()); release.Grabs = ParseUtil.CoerceLong(qRow.Find("td:nth-child(8)").Text()); release.Guid = new Uri(qLink.Attr("href")); release.Comments = release.Guid; release.Link = new Uri(string.Format(DownloadUrl, qLink.Attr("href").Split('=')[1])); var catUrl = qRow.Children().ElementAt(1).FirstElementChild.Cq().Attr("href"); var catNum = catUrl.Split(new char[] { '=', '&' })[1]; release.Category = MapTrackerCatToNewznab(catNum); // This tracker cannot search multiple cats at a time, so search all cats then filter out results from different cats if (trackerCats.Count > 0 && !trackerCats.Contains(catNum)) { continue; } var dateString = qRow.Children().ElementAt(5).Cq().Text().Trim(); var pubDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); release.PublishDate = DateTime.SpecifyKind(pubDate, DateTimeKind.Local); var sizeStr = qRow.Children().ElementAt(6).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Trim()); release.Peers = ParseUtil.CoerceInt(qRow.Children().ElementAt(9).Cq().Text().Trim()) + release.Seeders; var bgcolor = qRow.Attr("bgcolor"); if (bgcolor == "#DDDDDD") { release.DownloadVolumeFactor = 1; release.UploadVolumeFactor = 2; } else if (bgcolor == "#FFFF99") { release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; } else if (bgcolor == "#CCFF99") { release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 2; } 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>(); NameValueCollection qParams = new NameValueCollection(); qParams.Add("tor[text]", query.GetQueryString()); qParams.Add("tor[srchIn][title]", "true"); qParams.Add("tor[srchIn][author]", "true"); qParams.Add("tor[searchType]", "all"); qParams.Add("tor[searchIn]", "torrents"); qParams.Add("tor[hash]", ""); qParams.Add("tor[sortType]", "default"); qParams.Add("tor[startNumber]", "0"); List <string> catList = MapTorznabCapsToTrackers(query); if (catList.Any()) { foreach (string cat in catList) { qParams.Add("tor[cat][]", cat); } } else { qParams.Add("tor[cat][]", "0"); } string urlSearch = SearchUrl; if (qParams.Count > 0) { urlSearch += $"?{qParams.GetQueryString()}"; } var response = await RequestStringWithCookiesAndRetry(urlSearch); try { CQ dom = response.Content; var rows = dom["table[class='newTorTable'] > tbody > tr[id^=\"tdr\"]"]; foreach (IDomObject row in rows) { CQ torrentData = row.OuterHTML; CQ cells = row.Cq().Find("td"); string tid = torrentData.Attr("id").Substring(4); var qTitle = torrentData.Find("a[class='title']").First(); string title = qTitle.Text().Trim(); var details = new Uri(SiteLink + qTitle.Attr("href")); string author = torrentData.Find("a[class='author']").First().Text().Trim(); Uri link = new Uri(SiteLink + "tor/download.php?tid=" + tid); // DL Link is no long available directly, build it ourself long files = ParseUtil.CoerceLong(cells.Elements.ElementAt(4).Cq().Find("a").Text()); long size = ReleaseInfo.GetBytes(cells.Elements.ElementAt(4).Cq().Text().Split('[')[1].TrimEnd(']')); int seeders = ParseUtil.CoerceInt(cells.Elements.ElementAt(6).Cq().Find("p").ElementAt(0).Cq().Text()); int leechers = ParseUtil.CoerceInt(cells.Elements.ElementAt(6).Cq().Find("p").ElementAt(1).Cq().Text()); long grabs = ParseUtil.CoerceLong(cells.Elements.ElementAt(6).Cq().Find("p").ElementAt(2).Cq().Text()); bool freeleech = torrentData.Find("img[alt=\"freeleech\"]").Any(); string pubDateStr = cells.Elements.ElementAt(5).Cq().Text().Split('[')[0]; DateTime publishDate = DateTime.Parse(pubDateStr).ToLocalTime(); long category = 0; string cat = torrentData.Find("a[class='newCatLink']").First().Attr("href").Remove(0, "/tor/browse.php?tor[cat][]]=".Length); long.TryParse(cat, out category); var release = new ReleaseInfo(); release.Title = String.IsNullOrEmpty(author) ? title : String.Format("{0} by {1}", title, author); release.Guid = link; release.Link = link; release.PublishDate = publishDate; release.Files = files; release.Size = size; release.Description = release.Title; release.Seeders = seeders; release.Peers = seeders + leechers; release.Grabs = grabs; release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.Category = MapTrackerCatToNewznab(category.ToString()); release.Comments = details; if (freeleech) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); WebClientStringResult results = null; var queryCollection = new NameValueCollection(); queryCollection.Add("st", "0"); queryCollection.Add("sd", "d"); queryCollection.Add("sk", "t"); queryCollection.Add("tracker_search", "torrent"); queryCollection.Add("t", "0"); queryCollection.Add("submit", "Search"); queryCollection.Add("sr", "topics"); //queryCollection.Add("sr", "posts"); //queryCollection.Add("ch", "99999"); // if the search string is empty use the getnew view if (string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search_id", "active_topics"); queryCollection.Add("ot", "1"); } else // use the normal search { searchString = searchString.Replace("-", " "); queryCollection.Add("keywords", searchString); queryCollection.Add("sf", "titleonly"); queryCollection.Add("sr", "topics"); queryCollection.Add("pt", "t"); queryCollection.Add("ot", "1"); } var searchUrl = SearchUrl + "?" + queryCollection.GetQueryString(); results = await RequestStringWithCookies(searchUrl); if (!results.Content.Contains("ucp.php?mode=logout")) { await ApplyConfiguration(null); results = await RequestStringWithCookies(searchUrl); } try { string RowsSelector = "ul.topics > li.row"; var ResultParser = new HtmlParser(); var SearchResultDocument = ResultParser.Parse(results.Content); var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector); foreach (var Row in Rows) { try { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 0; var qDetailsLink = Row.QuerySelector("a.topictitle"); release.Title = qDetailsLink.TextContent; release.Comments = new Uri(SiteLink + qDetailsLink.GetAttribute("href")); release.Guid = release.Comments; var detailsResult = await RequestStringWithCookies(SiteLink + qDetailsLink.GetAttribute("href")); var DetailsResultDocument = ResultParser.Parse(detailsResult.Content); var qDownloadLink = DetailsResultDocument.QuerySelector("table.table2 > tbody > tr > td > a[href^=\"/download/torrent.php?id\"]"); release.Link = new Uri(SiteLink + qDownloadLink.GetAttribute("href").TrimStart('/')); release.Seeders = ParseUtil.CoerceInt(Row.QuerySelector("span.seed").TextContent); release.Peers = ParseUtil.CoerceInt(Row.QuerySelector("span.leech").TextContent) + release.Seeders; release.Grabs = ParseUtil.CoerceLong(Row.QuerySelector("span.complet").TextContent); var author = Row.QuerySelector("dd.lastpost > span"); var timestr = author.TextContent.Split('\n')[4].Trim(); release.PublishDate = DateTimeUtil.FromUnknown(timestr, "UK"); var forum = Row.QuerySelector("a[href^=\"./viewforum.php?f=\"]"); var forumid = forum.GetAttribute("href").Split('=')[1]; release.Category = MapTrackerCatToNewznab(forumid); var size = Row.QuerySelector("dl.row-item > dt > div.list-inner > div[style^=\"float:right\"]").TextContent; size = size.Replace("GiB", "GB"); size = size.Replace("MiB", "MB"); size = size.Replace("KiB", "KB"); size = size.Replace("ГБ", "GB"); size = size.Replace("МБ", "MB"); size = size.Replace("КБ", "KB"); release.Size = ReleaseInfo.GetBytes(size); release.DownloadVolumeFactor = 1; release.UploadVolumeFactor = 1; releases.Add(release); } catch (Exception ex) { logger.Error(string.Format("{0}: Error while parsing row '{1}':\n\n{2}", ID, Row.OuterHtml, 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 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 PostDataWithCookies(BrowseUrl, pairs); if (results.IsRedirect) { // re-login await ApplyConfiguration(null); results = await PostDataWithCookies(BrowseUrl, pairs); } try { var lastDate = DateTime.Now; var parser = new HtmlParser(); var doc = parser.ParseDocument(results.Content); 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"); } 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.Content, 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 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); }
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); }
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); }