/// <summary> /// These tests ensure that the code run for any action (Cq_Start and Cq_End) are applied for either action we test. /// </summary> /// /// <param name="doc"> /// The document. /// </param> protected void Common(CQ doc) { Assert.IsTrue(doc["#cq-footer-1"].HasClass("cq-start"), "the 1st footer should get classes applied by controller-specific startup code"); Assert.IsTrue(doc["#cq-footer-1"].HasClass("cq-end"), "the 1st footer should get classes applied by controller-specific end code"); Assert.IsFalse(doc["#cq-footer-2"][0].HasClasses, "The footer added should have no classes from the controller-specific code"); }
/// <summary> /// [CsQuery] A simple function to turn a sequence of Key/Value pairs into a pick list /// </summary> /// <param name="select"></param> /// <param name="list"></param> private void MakePickList(CQ select, IEnumerable<KeyValuePair<string,string>> list) { foreach (var item in list) { var opt = select["<option />"] .Attr("value",item.Key) .Text(item.Value); select.Append(opt); } }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); NameValueCollection qParams = new NameValueCollection(); if (!string.IsNullOrWhiteSpace(query.ImdbID)) { qParams.Add("search", query.ImdbID); qParams.Add("d", "on"); } else if (!string.IsNullOrEmpty(query.GetQueryString())) { qParams.Add("search", query.GetQueryString()); } List <string> catList = MapTorznabCapsToTrackers(query); foreach (string cat in catList) { qParams.Add("c" + cat, "1"); } string urlSearch = SearchUrl; if (qParams.Count > 0) { urlSearch += $"?{qParams.GetQueryString()}"; } var response = await RequestStringWithCookiesAndRetry(urlSearch); if (!response.Content.Contains("/logout.php")) { //Cookie appears to expire after a period of time or logging in to the site via browser await DoLogin(); response = await RequestStringWithCookiesAndRetry(urlSearch); } try { CQ dom = response.Content; var rows = dom["div[id='torrentTable'] > div[class^='box torrentBox'] > div[class='boxContent'] > table > tbody > tr"]; foreach (IDomObject row in rows) { CQ torrentData = row.OuterHTML; CQ cells = row.Cq().Find("td"); string title = torrentData.Find("td[class='lft'] > div > a").First().Text().Trim(); Uri link = new Uri(SiteLink + torrentData.Find("img[title='Download']").First().Parent().Attr("href").Trim()); Uri guid = link; long size = ReleaseInfo.GetBytes(cells.Elements.ElementAt(4).Cq().Text()); int grabs = ParseUtil.CoerceInt(cells.Elements.ElementAt(5).Cq().Text()); int seeders = ParseUtil.CoerceInt(cells.Elements.ElementAt(6).Cq().Text()); int leechers = ParseUtil.CoerceInt(cells.Elements.ElementAt(7).Cq().Text()); string pubDateStr = torrentData.Find("span[class^='elapsedDate']").First().Attr("title").Trim().Replace(" at", ""); DateTime publishDate = DateTime.ParseExact(pubDateStr, "dddd, MMMM d, yyyy h:mmtt", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); long category = 0; string cat = torrentData.Find("img[class^='Tcat']").First().Parent().Attr("href").Trim().Remove(0, 5); long.TryParse(cat, out category); var release = new ReleaseInfo(); release.Title = title; release.Guid = guid; release.Link = link; release.PublishDate = publishDate; release.Size = size; release.Grabs = grabs; release.Seeders = seeders; release.Peers = seeders + leechers; release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.Category = MapTrackerCatToNewznab(category.ToString()); release.Comments = guid; if (torrentData.Find("span:contains(\"[Freeleech]\")").Any()) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var queryUrl = SearchUrl; var queryCollection = new NameValueCollection(); var cats = MapTorznabCapsToTrackers(query); var tags = string.Empty; var catGroups = new List <string>(); foreach (var cat in cats) { //"cat[]=7&tags=x264" var cSplit = cat.Split('&'); if (cSplit.Length > 0) { var gsplit = cSplit[0].Split('='); if (gsplit.Length > 1) { catGroups.Add(gsplit[1]); } } if (cSplit.Length > 1) { var gsplit = cSplit[1].Split('='); if (gsplit.Length > 1) { if (tags != string.Empty) { tags += ","; } tags += gsplit[1]; } } } if (catGroups.Distinct().Count() == 1) { queryCollection.Add("cat[]", catGroups.First()); } if (!string.IsNullOrWhiteSpace(query.GetQueryString())) { queryCollection.Add("st", "1"); queryCollection.Add("search", query.GetQueryString()); } // Do not include too many tags as it'll mess with their servers. if (tags.Split(',').Length < 7) { queryCollection.Add("tags", tags); if (!string.IsNullOrWhiteSpace(tags)) { // if tags are specified match any queryCollection.Add("tf", "any"); } else { // if no tags are specified match all, with any we get random results queryCollection.Add("tf", "all"); } } if (queryCollection.Count > 0) { queryUrl += "?" + queryCollection.GetQueryString(); } var response = await RequestStringWithCookiesAndRetry(queryUrl); if (response.IsRedirect) { await ApplyConfiguration(null); response = await RequestStringWithCookiesAndRetry(queryUrl); } try { CQ dom = response.Content; var rows = dom["table > tbody > tr.browse"]; foreach (var row in rows) { CQ qRow = row.Cq(); var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; var qLink = row.ChildElements.ElementAt(1).Cq().Find("a").First(); release.Title = qLink.Text().Trim(); if (qLink.Find("span").Count() == 1 && release.Title.StartsWith("NEW! |")) { release.Title = release.Title.Substring(6).Trim(); } release.Comments = new Uri(SiteLink + qLink.Attr("href")); release.Guid = release.Comments; var qDownload = row.ChildElements.ElementAt(2).Cq().Find("a").First(); release.Link = new Uri(SiteLink + qDownload.Attr("href")); var dateStr = row.ChildElements.ElementAt(5).InnerHTML.Replace("<br>", " "); 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).InnerText); release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(10).InnerText) + release.Seeders; var cat = row.ChildElements.ElementAt(0).ChildElements.ElementAt(0).GetAttribute("href").Replace("browse.php?", string.Empty); release.Category = MapTrackerCatToNewznab(cat); var files = qRow.Find("td:nth-child(4)").Text(); release.Files = ParseUtil.CoerceInt(files); var grabs = qRow.Find("td:nth-child(9)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); release.DownloadVolumeFactor = 0; // ratioless release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(response.Content, ex); } 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 = IndexUrl; var queryCollection = new NameValueCollection(); queryCollection.Add("strWebValue", "torrent"); queryCollection.Add("strWebAction", "search"); queryCollection.Add("sort", "torrent_added"); queryCollection.Add("by", "d"); queryCollection.Add("type", "0"); queryCollection.Add("do_search", "suchen"); queryCollection.Add("time", "0"); queryCollection.Add("details", "title"); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("searchstring", searchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("dirs" + cat, "1"); } searchUrl += "?" + queryCollection.GetQueryString(); logger.Error(searchUrl); logger.Error(CookieHeader); var response = await RequestBytesWithCookies(searchUrl); var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content); try { CQ dom = results; var rows = dom["table.torrenttable > tbody > tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); release.MinimumRatio = 0.8; release.MinimumSeedTime = 0; var qRow = row.Cq(); var qDetailsLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=details]").First(); release.Title = qDetailsLink.Text(); var qCatLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=search&dir=]").First(); var qDLLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=download&id=]").First(); var qSeeders = qRow.Find("td.column1:eq(3)"); var qLeechers = qRow.Find("td.column2:eq(3)"); var qDateStr = qRow.Find("font:has(a)").First(); var qSize = qRow.Find("td.column2[align=center]").First(); var catStr = qCatLink.Attr("href").Split('=')[3].Split('#')[0]; release.Category = MapTrackerCatToNewznab(catStr); release.Link = new Uri(SiteLink + qDLLink.Attr("href")); release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href")); release.Guid = release.Link; var sizeStr = qSize.Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qSeeders.Text()); release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders; var dateStr = qDateStr.Text().Trim(); var dateStrParts = dateStr.Split(); DateTime dateGerman; if (dateStrParts[0] == "Heute") { dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStrParts[1]); } else if (dateStrParts[0] == "Gestern") { dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStrParts[1]) - TimeSpan.FromDays(1); } else { dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStrParts[0] + dateStrParts[1], "dd.MM.yyyyHH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); } DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz); release.PublishDate = pubDateUtc.ToLocalTime(); var grabs = qRow.Find("td:nth-child(7)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("img[src=\"themes/images/freeleech.png\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("img[src=\"themes/images/DL50.png\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
/// <summary> /// Return a CQ object, treating the HTML as content /// </summary> /// <returns></returns> public CQ GetContent() { return(CQ.Create(Html)); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = SearchUrl; if (!string.IsNullOrWhiteSpace(searchString)) { searchUrl += "&search=" + WebUtilityHelpers.UrlEncode(searchString, Encoding); } string.Format(SearchUrl, WebUtilityHelpers.UrlEncode(searchString, Encoding)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count > 0) { foreach (var cat in cats) { searchUrl += "&c" + cat + "=1"; } } var response = await RequestStringWithCookies(searchUrl); try { CQ dom = response.Content; CQ qRows = dom[".browse > div > div"]; foreach (var row in qRows) { var release = new ReleaseInfo(); var qRow = row.Cq(); var debug = qRow.Html(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; var titleParts = qRow.Find(".bTitle").Text().Split('/'); if (titleParts.Length >= 2) { release.Title = titleParts[1].Trim(); } else { release.Title = titleParts[0].Trim(); } var qDetailsLink = qRow.Find("a[title][href^=\"details.php\"]"); release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href")); release.Link = new Uri(SiteLink + qRow.Find("a[href^=\"download.php\"]").Attr("href")); release.Guid = release.Link; var dateString = qRow.Find("div:last-child").Text().Trim(); var pattern = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}"; var match = Regex.Match(dateString, pattern); if (match.Success) { release.PublishDate = DateTime.ParseExact(match.Value, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); } var sizeStr = qRow.Find(".bSize").Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Find(".bUping").Text().Trim()); release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find(".bDowning").Text().Trim()); var files = qRow.Find("div.bFiles").Get(0).LastChild.ToString(); release.Files = ParseUtil.CoerceInt(files); var grabs = qRow.Find("div.bFinish").Get(0).LastChild.ToString(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("img[src=\"/pic/free.jpg\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } if (qRow.Find("img[src=\"/pic/triple.jpg\"]").Length >= 1) { release.UploadVolumeFactor = 3; } else if (qRow.Find("img[src=\"/pic/double.jpg\"]").Length >= 1) { release.UploadVolumeFactor = 2; } else { release.UploadVolumeFactor = 1; } releases.Add(release); } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }
protected async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query, String seasonep) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var pairs = new List <KeyValuePair <string, string> >(); if (seasonep != null) { searchString = query.SanitizedSearchTerm; } pairs.Add(new KeyValuePair <string, string>("nyit_sorozat_resz", "true")); pairs.Add(new KeyValuePair <string, string>("miben", "name")); pairs.Add(new KeyValuePair <string, string>("tipus", "kivalasztottak_kozott")); pairs.Add(new KeyValuePair <string, string>("submit.x", "1")); pairs.Add(new KeyValuePair <string, string>("submit.y", "1")); pairs.Add(new KeyValuePair <string, string>("submit", "Ok")); pairs.Add(new KeyValuePair <string, string>("mire", searchString)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count == 0) { cats = GetAllTrackerCategories(); } foreach (var lcat in LanguageCats) { if (!configData.Hungarian.Value) { cats.Remove(lcat + "_hun"); } if (!configData.English.Value) { cats.Remove(lcat); } } foreach (var cat in cats) { pairs.Add(new KeyValuePair <string, string>("kivalasztott_tipus[]", cat)); } var results = await PostDataWithCookiesAndRetry(SearchUrl, pairs); try { CQ dom = results.Content; ReleaseInfo release; var rows = dom[".box_torrent_all"].Find(".box_torrent"); foreach (var row in rows) { CQ qRow = row.Cq(); var key = dom["link[rel=alternate]"].First().Attr("href").Split('=').Last(); release = new ReleaseInfo(); var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0); //if (torrentTxt == null) continue; release.Title = torrentTxt.GetAttribute("title"); release.Description = qRow.Find("div.siterank").Text(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; string downloadLink = SiteLink + torrentTxt.GetAttribute("href"); string downloadId = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4); release.Link = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key); release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId); release.Guid = new Uri(release.Comments.ToString() + "#comments");; release.Seeders = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders; var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdblink); var banner = qRow.Find("img.infobar_ico").Attr("onmouseover"); if (banner != null) { Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled); var BannerMatch = BannerRegEx.Match(banner); var bannerurl = BannerMatch.Groups[1].Value; release.BannerUrl = new Uri(bannerurl); } release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture); string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' '); release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0])); string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href"); string cat = ParseUtil.GetArgumentFromQueryString(catlink, "tipus"); release.Category = MapTrackerCatToNewznab(cat); if (seasonep == null) { releases.Add(release); } else { if (query.MatchQueryStringAND(release.Title, null, seasonep)) { /* For sonnar if the search querry was english the title must be english also so we need to change the Description and Title */ var temp = release.Title; // releasedata everithing after Name.S0Xe0X String releasedata = release.Title.Split(new[] { seasonep }, StringSplitOptions.None)[1].Trim(); /* if the release name not contains the language we add it because it is know from category */ if (cat.Contains("hun") && !releasedata.Contains("hun")) { releasedata += ".hun"; } // release description contains [imdb: ****] but we only need the data before it for title String[] description = { release.Description, "" }; if (release.Description.Contains("[imdb:")) { description = release.Description.Split('['); description[1] = "[" + description[1]; } else { release.Title = (description[0].Trim() + "." + seasonep.Trim() + "." + releasedata.Trim('.')).Replace(' ', '.'); } // if search is done for S0X than we dont want to put . between S0X and E0X Match match = Regex.Match(releasedata, @"^E\d\d?"); if (seasonep.Length == 3 && match.Success) { release.Title = (description[0].Trim() + "." + seasonep.Trim() + releasedata.Trim('.')).Replace(' ', '.'); } // add back imdb points to the description [imdb: 8.7] release.Description = temp + " " + description[1]; release.Description = release.Description.Trim(); releases.Add(release); } } } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { List <ReleaseInfo> releases = new List <ReleaseInfo>(); List <string> searchStrings = new List <string>(new string[] { query.GetQueryString() }); if (string.IsNullOrEmpty(query.Episode) && (query.Season > 0)) { // Tracker naming rules: If query is for a whole season, "Season #" instead of "S##". searchStrings.Add((query.SanitizedSearchTerm + " " + string.Format("\"Season {0}\"", query.Season)).Trim()); } List <string> categories = MapTorznabCapsToTrackers(query); List <string> request_urls = new List <string>(); foreach (var searchString in searchStrings) { var queryCollection = new NameValueCollection(); queryCollection.Add("action", "basic"); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("searchstr", searchString); } foreach (var cat in categories) { queryCollection.Add("filter_cat[" + cat + "]", "1"); } request_urls.Add(SearchUrl + queryCollection.GetQueryString()); } IEnumerable <Task <WebClientStringResult> > downloadTasksQuery = from url in request_urls select RequestStringWithCookiesAndRetry(url); WebClientStringResult[] responses = await Task.WhenAll(downloadTasksQuery.ToArray()); for (int i = 0; i < searchStrings.Count(); i++) { var results = responses[i]; // Occasionally the cookies become invalid, login again if that happens if (results.IsRedirect) { await ApplyConfiguration(null); results = await RequestStringWithCookiesAndRetry(request_urls[i]); } try { CQ dom = results.Content; var rows = dom["#torrent_table > tbody > tr.torrent"]; foreach (var row in rows) { CQ qRow = row.Cq(); var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; var catStr = row.ChildElements.ElementAt(0).FirstElementChild.GetAttribute("href").Split(new char[] { '[', ']' })[1]; release.Category = MapTrackerCatToNewznab(catStr); var qLink = row.ChildElements.ElementAt(1).Cq().Children("a")[0].Cq(); var linkStr = qLink.Attr("href"); release.Comments = new Uri(BaseUrl + "/" + linkStr); release.Guid = release.Comments; var qDownload = row.ChildElements.ElementAt(1).Cq().Find("a[title='Download']")[0].Cq(); release.Link = new Uri(BaseUrl + "/" + qDownload.Attr("href")); var dateStr = row.ChildElements.ElementAt(3).Cq().Text().Trim().Replace(" and", ""); release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr); var sizeStr = row.ChildElements.ElementAt(4).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Files = ParseUtil.CoerceInt(row.ChildElements.ElementAt(2).Cq().Text().Trim()); release.Grabs = ParseUtil.CoerceInt(row.ChildElements.ElementAt(6).Cq().Text().Trim()); release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text().Trim()); release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text().Trim()) + release.Seeders; var grabs = qRow.Find("td:nth-child(6)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("strong:contains(\"Freeleech!\")").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; var title = qRow.Find("td:nth-child(2)"); title.Find("span, strong, div, br").Remove(); release.Title = ParseUtil.NormalizeMultiSpaces(title.Text().Replace(" - ]", "]")); if (catStr == "10") //change "Season #" to "S##" for TV shows { release.Title = Regex.Replace(release.Title, @"Season (\d+)", m => string.Format("S{0:00}", Int32.Parse(m.Groups[1].Value))); } releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } } return(releases); }
private async Task <IEnumerable <ReleaseInfo> > performRegularQuery(TorznabQuery query, string hebName = null) { var releases = new List <ReleaseInfo>(); var searchurls = new List <string>(); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection(); var searchString = query.GetQueryString(); if (hebName != null) { searchString = hebName + " - עונה " + query.Season + " פרק " + query.Episode; } int categoryCounter = 1; foreach (var cat in MapTorznabCapsToTrackers(query)) { searchUrl += "c" + categoryCounter.ToString() + "=" + cat + "&"; categoryCounter++; } if (string.IsNullOrWhiteSpace(searchString)) { searchUrl = SiteLink + "index.php?name=torrents"; } else { var strEncoded = HttpUtility.UrlEncode(searchString, Encoding.GetEncoding("Windows-1255")); searchUrl += "text=" + strEncoded + "&category=0&search=1"; } var data = await RequestStringWithCookiesAndRetry(searchUrl); try { CQ dom = data.Content; ReleaseInfo release; int rowCount = 0; var rows = dom["#collapseobj_module_17 > tr"]; foreach (var row in rows) { CQ qRow = row.Cq(); if (rowCount < 1 || qRow.Children().Count() != 9) //skip 1 row because there's an empty row { rowCount++; continue; } release = new ReleaseInfo(); release.Description = qRow.Find("td:nth-child(2) > a").Text();; if (hebName != null) { release.Title = query.SearchTerm + " " + release.Description.Substring(release.Description.IndexOf(string.Format("S{0:D2}E{1:D2}", query.Season, int.Parse(query.Episode)))); } else { const string DELIMITER = " | "; release.Title = release.Description.Substring(release.Description.IndexOf(DELIMITER) + DELIMITER.Length); } release.MinimumRatio = 1; release.MinimumSeedTime = 172800; int seeders, peers; if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(7) > div").Text(), out seeders)) { release.Seeders = seeders; if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(8) > div").Text(), out peers)) { release.Peers = peers + release.Seeders; } } string fullSize = qRow.Find("td:nth-child(5) > div").Text(); release.Size = ReleaseInfo.GetBytes(fullSize); release.Guid = new Uri(qRow.Find("td:nth-child(2) > a").Attr("href")); release.Link = new Uri(SiteLink + qRow.Find("td:nth-child(3) > a").Attr("href")); release.Comments = release.Guid; string[] dateSplit = qRow.Find("td:nth-child(2) > span.torrentstime").Text().Split(' '); string dateString = dateSplit[1] + " " + dateSplit[3]; release.PublishDate = DateTime.ParseExact(dateString, "dd-MM-yy HH:mm", CultureInfo.InvariantCulture); string category = qRow.Find("script:nth-child(1)").Text(); int index = category.IndexOf("category="); if (index == -1) { /// Other type category = "17"; } else { category = category.Substring(index + "category=".Length, 2); if (category[1] == '\\') { category = category[0].ToString(); } } release.Category = MapTrackerCatToNewznab(category); var grabs = qRow.Find("td:nth-child(6)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("img[src=\"/images/FL.png\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(data.Content, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { // This tracker only deals with full seasons so chop off the episode/season number if we have it D: if (!string.IsNullOrWhiteSpace(query.SearchTerm)) { var splitindex = query.SearchTerm.LastIndexOf(' '); if (splitindex > -1) { query.SearchTerm = query.SearchTerm.Substring(0, splitindex); } } var releases = new List <ReleaseInfo>(); var searchString = query.SanitizedSearchTerm; var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl); try { CQ dom = response.Content; var rows = dom[".torrents tr.torrent"]; foreach (var row in rows) { var qRow = row.Cq(); var qTitleLink = qRow.Find("a.title").First(); var title = qTitleLink.Text().Trim(); // Insert before the release info var taidx = title.IndexOf('('); var tbidx = title.IndexOf('['); if (taidx == -1) { taidx = title.Length; } if (tbidx == -1) { tbidx = title.Length; } var titleSplit = Math.Min(taidx, tbidx); var titleSeries = title.Substring(0, titleSplit); var releaseInfo = title.Substring(titleSplit); // For each over each pipe deliminated name foreach (var name in titleSeries.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { var release = new ReleaseInfo(); release.Title = (name + releaseInfo).Trim(); // Ensure the season is defined as this tracker only deals with full seasons if (release.Title.IndexOf("Season") == -1) { // Insert before the release info var aidx = release.Title.IndexOf('('); var bidx = release.Title.IndexOf('['); if (aidx == -1) { aidx = release.Title.Length; } if (bidx == -1) { bidx = release.Title.Length; } var insertPoint = Math.Min(aidx, bidx); release.Title = release.Title.Substring(0, insertPoint) + "Season 1 " + release.Title.Substring(insertPoint); } release.Description = release.Title; release.Guid = new Uri(SiteLink + qTitleLink.Attr("href")); release.Comments = release.Guid; release.Link = new Uri(SiteLink + qRow.Find(".peers a").First().Attr("href")); release.Seeders = int.Parse(qRow.Find(".peers a").Get(0).InnerText); release.Peers = release.Seeders + int.Parse(qRow.Find(".peers a").Get(1).InnerText); release.MinimumRatio = 1; var size = qRow.Find(".size").First().Text(); release.Size = ReleaseInfo.GetBytes(size); //22 Jul 15 var dateStr = qRow.Find(".added").First().Text().Replace("'", string.Empty); if (dateStr.Split(' ')[0].Length == 1) { dateStr = "0" + dateStr; } if (string.Equals(dateStr, "yesterday", StringComparison.InvariantCultureIgnoreCase)) { release.PublishDate = DateTime.Now.AddDays(-1); } else if (string.Equals(dateStr, "today", StringComparison.InvariantCultureIgnoreCase)) { release.PublishDate = DateTime.Now; } else { release.PublishDate = DateTime.ParseExact(dateStr, "dd MMM yy", CultureInfo.InvariantCulture); } releases.Add(release); } } } catch (Exception ex) { OnParseError(response.Content, ex); } 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 romaniaTz = TimeZoneInfo.CreateCustomTimeZone("Romania Time", new TimeSpan(2, 0, 0), "(GMT+02:00) Romania Time", "Romania Time", "Romania Daylight Time", adjustments); var releases = new List <ReleaseInfo>(); string episodeSearchUrl; if (string.IsNullOrEmpty(query.GetQueryString())) { episodeSearchUrl = SearchUrl; } else { episodeSearchUrl = $"{SearchUrl}?search={HttpUtility.UrlEncode(query.GetQueryString())}&cat=0"; } var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); try { CQ dom = results.Content; var rows = dom["#highlight > tbody > tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qLink = qRow.Find("a.torrent_name_link").First(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.Title = qLink.Attr("title"); if (!query.MatchQueryStringAND(release.Title)) { continue; } release.Description = release.Title; release.Guid = new Uri(SiteLink + qLink.Attr("href").TrimStart('/')); release.Comments = release.Guid; release.Link = new Uri(SiteLink + qRow.Find("td.table_links > a").First().Attr("href").TrimStart('/')); release.Category = TvCategoryParser.ParseTvShowQuality(release.Title); release.Seeders = ParseUtil.CoerceInt(qRow.Find("td.table_seeders").Text().Trim()); release.Peers = ParseUtil.CoerceInt(qRow.Find("td.table_leechers").Text().Trim()) + release.Seeders; var sizeStr = qRow.Find("td.table_size")[0].Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); DateTime pubDateRomania; var dateString = qRow.Find("td.table_added").Text().Trim(); if (dateString.StartsWith("Today ")) { pubDateRomania = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateString.Split(' ')[1]); } else if (dateString.StartsWith("Yesterday ")) { pubDateRomania = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateString.Split(' ')[1]) - TimeSpan.FromDays(1); } else { pubDateRomania = DateTime.SpecifyKind(DateTime.ParseExact(dateString, "d-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); } DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(pubDateRomania, romaniaTz); release.PublishDate = pubDateUtc.ToLocalTime(); var grabs = row.Cq().Find("td.table_snatch").Get(0).FirstChild.ToString(); release.Grabs = ParseUtil.CoerceInt(grabs); if (row.Cq().Find("img[alt=\"100% Free\"]").Any()) { release.DownloadVolumeFactor = 0; } else if (row.Cq().Find("img[alt=\"50% Free\"]").Any()) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public static Page Get(String url) { try { if (url == null || url.Length > 100 || url.Length < 8) { return(null); } Page page = new Page(); page.url = url; CQ doc = CQ.CreateFromUrl(url); //Console.WriteLine(doc.Html()); doc ["script"].Remove(); doc ["style"].Remove(); doc ["Script"].Remove(); doc ["Style"].Remove(); page.title = doc ["title"].Text(); if (page.title == null) { page.title = doc ["Title"].Text(); } if (page.title == null) { page.title = url; } page.title = page.title.Trim(); if (page.title.Length < 2) { page.title = url; } if (page.title.Length > 80) { page.title = page.title.Substring(0, 80); } page.title = page.title.Replace("<", " ") .Replace(">", " ").Replace("$", " "); page.description = doc ["meta[name='description']"].Attr("content"); if (page.description == null) { page.description = doc ["meta[name='Description']"].Attr("content"); } if (page.description == null) { page.description = ""; } if (page.description.Length > 200) { page.description = page.description.Substring(0, 200); } page.description = page.description.Replace("<", " ") .Replace(">", " ").Replace("$", " "); doc = CQ.Create(doc.Text().Replace("<", "<") .Replace(">", ">")); doc ["script"].Remove(); doc ["style"].Remove(); doc ["Script"].Remove(); doc ["Style"].Remove(); String content = doc.Text().Trim(); content = content.Replace("\r", " ") .Replace("\n", " ") .Replace(" ", " ") .Replace(" ", " ") .Replace(" ", " ") .Replace(" ", " ") .Replace(" ", " ").Trim(); if (content.Length < 50) { return(null); } if (content.Length > 5000) { content = content.Substring(0, 5000); } page.content = ((content + " " + page.url + " " + page.description) .Replace("<", " ") .Replace(">", " ").Replace("$", " ") .Replace(" ", " ")); return(page); } catch (Exception ex) { Console.WriteLine(ex.ToString()); return(null); } }
public void CharEncoding() { var res = CQ.Create("<div><span>x…</span></div>"); Assert.AreEqual(res["div span"].Text(), "x" + (char)8230); }
private static async Task GetContent(Uri inputUrl, string outputFolder, bool isRecursive, int depth, bool isVerbose, bool allowDifferentDomain, string recursivePageName = "") { if (isRecursive && depth == -1) { return; } using (var client = new HttpClient()) { HttpResponseMessage response = await client.GetAsync(inputUrl.OriginalString); if (isVerbose) { Console.Write(_rm.GetString("DownloadingMessage") + inputUrl.OriginalString + "..." + "\n"); } if (response.StatusCode == HttpStatusCode.OK) { string filepath; if (inputUrl.Equals(_userInputUrl)) { var fileName = "index.html"; filepath = Path.Combine(outputFolder, fileName); } else { Debugger.Launch(); filepath = Path.Combine(outputFolder, recursivePageName); } CQ dom = response.Content.ReadAsStringAsync().Result; dom = await GetResources(inputUrl, dom, "img", "src", isVerbose, allowDifferentDomain, outputFolder); dom = await GetResources(inputUrl, dom, "link[href]", "href", isVerbose, allowDifferentDomain, outputFolder); dom = await GetResources(inputUrl, dom, "script[src]", "src", isVerbose, allowDifferentDomain, outputFolder); if (isRecursive) { foreach (var link in dom["a[href]"]) { Debugger.Launch(); Uri url = new Uri(link.Attributes.GetAttribute("href"), UriKind.RelativeOrAbsolute); if (!url.IsAbsoluteUri) { url = new Uri(inputUrl, url.OriginalString); } if (IsSameDomain(inputUrl, url) || allowDifferentDomain) { var pageName = Path.GetFileName(url.AbsolutePath); if (pageName.IsNullOrEmpty()) { continue; } Debugger.Launch(); var outputFolderNew = outputFolder + inputUrl.AbsolutePath.Replace("/" + pageName, ""); pageName = pageName.Replace(".html", "").Replace(".htm", ""); pageName = pageName + ".html"; await GetContent(url, outputFolderNew, true, depth - 1, isVerbose, allowDifferentDomain, pageName); link.Attributes.SetAttribute("href", url.OriginalString.Replace(url.Host, "/")); } } } var fileContent = dom.Render(); File.WriteAllText(filepath, string.Empty); var isDir = (File.GetAttributes(filepath) & FileAttributes.Directory) == FileAttributes.Directory; if (isDir) { filepath = Path.Combine(filepath, "index.html"); } File.WriteAllText(filepath, fileContent); } } }
/// <summary> /// Manage scripts. /// </summary> /// /// <param name="cqDoc"> /// The cq document. /// </param> /// <param name="viewPage"> /// The view page. /// </param> /// <param name="viewContext"> /// The active view context /// </param> private void ManageScripts(CQ cqDoc, WebViewPage viewPage, ViewContext viewContext) { ScriptManager mgr = new ScriptManager(new ScriptEnvironment { LibraryPath = LibraryPath, RelativePathRoot =viewContext.RequestContext.HttpContext.Request.AppRelativeCurrentExecutionFilePath, MapPath = viewContext.RequestContext.HttpContext.Server.MapPath }); mgr.Options = Options; mgr.ResolveScriptDependencies(cqDoc); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = BrowseUrl; var queryCollection = new NameValueCollection(); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("q", searchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add(cat, string.Empty); } if (queryCollection.Count > 0) { searchUrl += "?" + queryCollection.GetQueryString(); } var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); var results = response.Content; try { CQ dom = results; var rows = dom["table.torrents > tbody > tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qTitleLink = qRow.Find("a.t_title").First(); release.Title = qTitleLink.Text().Trim(); // If we search an get no results, we still get a table just with no info. if (string.IsNullOrWhiteSpace(release.Title)) { break; } release.Description = release.Title; release.Guid = new Uri(UseLink + qTitleLink.Attr("href").Substring(1)); release.Comments = release.Guid; var descString = qRow.Find(".t_ctime").Text(); var dateString = descString.Split('|').Last().Trim(); dateString = dateString.Split(new string[] { " by " }, StringSplitOptions.None)[0]; release.PublishDate = DateTimeUtil.FromTimeAgo(dateString); var qLink = row.ChildElements.ElementAt(3).Cq().Children("a"); release.Link = new Uri(UseLink + HttpUtility.UrlEncode(qLink.Attr("href").TrimStart('/'))); var sizeStr = row.ChildElements.ElementAt(5).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Find(".t_seeders").Text().Trim()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".t_leechers").Text().Trim()) + release.Seeders; var cat = row.Cq().Find("td:eq(0) a").First().Attr("href").Substring(1); release.Category = MapTrackerCatToNewznab(cat); releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
private void ManageScripts(CQ cqDoc, WebViewPage viewPage) { ScriptManager mgr = new ScriptManager(); mgr.Options = Options; mgr.LibraryPath = LibraryPath; mgr.ResolveScriptDependencies(cqDoc); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); // replace any space, special char, etc. with % (wildcard) Regex ReplaceRegex = new Regex("[^a-zA-Z0-9]+"); searchString = ReplaceRegex.Replace(searchString, "%"); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection(); queryCollection.Add("total", "146"); // Not sure what this is about but its required! var cat = "0"; var queryCats = MapTorznabCapsToTrackers(query); if (queryCats.Count == 1) { cat = queryCats.First().ToString(); } queryCollection.Add("cat", cat); queryCollection.Add("searchin", "filename"); queryCollection.Add("search", searchString); queryCollection.Add("page", "1"); searchUrl += "?" + queryCollection.GetQueryString(); var extraHeaders = new Dictionary <string, string>() { { "X-Requested-With", "XMLHttpRequest" } }; var response = await RequestStringWithCookiesAndRetry(searchUrl, null, SearchUrlReferer, extraHeaders); var results = response.Content; try { CQ dom = results; var rows = dom["tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qTitleLink = qRow.Find("td:eq(1) a:eq(0)").First(); release.Title = qTitleLink.Text().Trim(); // If we search an get no results, we still get a table just with no info. if (string.IsNullOrWhiteSpace(release.Title)) { break; } release.Guid = new Uri(qTitleLink.Attr("href")); release.Comments = release.Guid; var dateString = qRow.Find("td:eq(4)").Text(); release.PublishDate = DateTime.ParseExact(dateString, "dd MMM yy", CultureInfo.InvariantCulture); var qLink = qRow.Find("td:eq(2) a"); if (qLink.Length != 0) // newbie users don't see DL links { release.Link = new Uri(qLink.Attr("href")); } else { // use comments link as placeholder // null causes errors during export to torznab // skipping the release prevents newbie users from adding the tracker (empty result) release.Link = release.Comments; } var sizeStr = qRow.Find("td:eq(5)").Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); var connections = qRow.Find("td:eq(7)").Text().Trim().Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); release.Seeders = ParseUtil.CoerceInt(connections[0].Trim()); release.Peers = ParseUtil.CoerceInt(connections[1].Trim()) + release.Seeders; release.Grabs = ParseUtil.CoerceLong(connections[2].Trim()); var rCat = row.Cq().Find("td:eq(0) a").First().Attr("href"); var rCatIdx = rCat.IndexOf("cat="); if (rCatIdx > -1) { rCat = rCat.Substring(rCatIdx + 4); } release.Category = MapTrackerCatToNewznab(rCat); if (qRow.Find("img[alt=\"Gold Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("img[alt=\"Silver Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } var ULFactorImg = qRow.Find("img[alt*=\"x Multiplier Torrent\"]"); if (ULFactorImg.Length >= 1) { release.UploadVolumeFactor = ParseUtil.CoerceDouble(ULFactorImg.Attr("alt").Split('x')[0]); } else { release.UploadVolumeFactor = 1; } qTitleLink.Remove(); release.Description = qRow.Find("td:eq(1)").Text(); 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> public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var torrentRowList = new List <CQ>(); var searchTerm = query.GetQueryString(); var searchUrl = SearchUrl; int nbResults = 0; int pageLinkCount = 0; // Check cache first so we don't query the server (if search term used or not in dev mode) if (!DevMode && !string.IsNullOrEmpty(searchTerm)) { lock (cache) { // Remove old cache items CleanCache(); // Search in cache var cachedResult = cache.Where(i => i.Query == searchTerm).FirstOrDefault(); if (cachedResult != null) { return(cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray()); } } } // Build our query var request = buildQuery(searchTerm, query, searchUrl); // Getting results & Store content WebClientStringResult results = await queryExec(request); fDom = results.Content; try { // Find torrent rows var firstPageRows = findTorrentRows(); // Add them to torrents list torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq())); // Check if there are pagination links at bottom Boolean pagination = (fDom[".pager_align > a"].Length != 0); // If pagination available if (pagination) { // Calculate numbers of pages available for this search query (Based on number results and number of torrents on first page) pageLinkCount = ParseUtil.CoerceInt(Regex.Match(fDom[".pager_align > a:not(:last-child)"].Last().Attr("href").ToString(), @"\d+").Value) + 1; // Calculate average number of results (based on torrents rows lenght on first page) nbResults = firstPageRows.Count() * pageLinkCount; } else { // Check if we have a minimum of one result if (firstPageRows.Length >= 1) { // Retrieve total count on our alone page nbResults = firstPageRows.Count(); pageLinkCount = 1; } else { output("\nNo result found for your query, please try another search term ...\n", "info"); // No result found for this query return(releases); } } output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !"); output("\nThere are " + firstPageRows.Length + " results on the first page !"); // If we have a term used for search and pagination result superior to one if (!string.IsNullOrWhiteSpace(query.GetQueryString()) && pageLinkCount > 1) { // Starting with page #2 for (int i = 2; i <= Math.Min(Int32.Parse(ConfigData.Pages.Value), pageLinkCount); i++) { output("\nProcessing page #" + i); // Request our page latencyNow(); // Build our query var pageRequest = buildQuery(searchTerm, query, searchUrl, (i - 1)); // Getting results & Store content WebClientStringResult pageResults = await queryExec(pageRequest); // Assign response fDom = pageResults.Content; // Process page results var additionalPageRows = findTorrentRows(); // Add them to torrents list torrentRowList.AddRange(additionalPageRows.Select(fRow => fRow.Cq())); } } else { // No search term, maybe testing... so registring passkey for future uses string infosData = firstPageRows.First().Find("td:eq(2) > a").Attr("href"); IList <string> infosList = infosData.Split('&').Select(s => s.Trim()).Where(s => s != String.Empty).ToList(); IList <string> infosTracker = infosList.Select(s => s.Split(new[] { '=' }, 2)[1].Trim()).ToList(); output("\nStoring Passkey for future uses... \"" + infosTracker[2] + "\""); ConfigData.PassKey.Value = infosTracker[2]; } // Loop on results foreach (CQ tRow in torrentRowList) { output("\n=>> Torrent #" + (releases.Count + 1)); // ID string row = tRow.Html().ToString(); int id = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(1) > a").Attr("href").ToString(), @"\d+").Value); output("ID: " + id); // Release Name string name = tRow.Find("td:eq(1) > a").Attr("title").ToString(); output("Release: " + name); // Category string infosDataCategory = firstPageRows.First().Find("td:eq(0) > a").Attr("href"); IList <string> infosListCategory = infosDataCategory.Split('&').Select(s => s.Trim()).Where(s => s != String.Empty).ToList(); IList <string> infosCategory = infosListCategory.Select(s => s.Split(new[] { '=' }, 2)[0].Trim()).ToList(); string categoryID = infosCategory.Last().TrimStart('c'); output("Category: " + MapTrackerCatToNewznab(categoryID) + " (" + categoryID + ")"); // Seeders int seeders = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(6)").Text(), @"\d+").Value); output("Seeders: " + seeders); // Leechers int leechers = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(7)").Text(), @"\d+").Value); output("Leechers: " + leechers); // Completed int completed = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(5)").Text(), @"\d+").Value); output("Completed: " + completed); // Size string sizeStr = tRow.Find("td:eq(4)").Text().Trim().Replace("Go", "gb").Replace("Mo", "mb").Replace("Ko", "kb"); long size = ReleaseInfo.GetBytes(sizeStr); output("Size: " + sizeStr + " (" + size + " bytes)"); // Health int percent = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(8) > img").Attr("alt").ToString(), @"\d+").Value); output("Health: " + percent + "%"); // Publish DateToString //var date = agoToDate(null); int timestamp = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(1)").Attr("data-added").ToString(), @"\d+").Value); DateTime date = unixTimeStampToDateTime(timestamp); output("Released on: " + date.ToLocalTime() + " (TS >> " + timestamp + ")"); // Torrent Details URL Uri detailsLink = new Uri(TorrentDescriptionUrl + id); output("Details: " + detailsLink.AbsoluteUri); // Torrent Comments URL Uri commentsLink = new Uri(TorrentCommentUrl + id); output("Comments Link: " + commentsLink.AbsoluteUri); // Torrent Download URL Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", ConfigData.PassKey.Value)); output("Download Link: " + downloadLink.AbsoluteUri); // Building release infos var release = new ReleaseInfo(); release.Category = MapTrackerCatToNewznab(categoryID.ToString()); release.Title = name; release.Seeders = seeders; release.Peers = seeders + leechers; release.MinimumRatio = 1; release.MinimumSeedTime = 345600; release.PublishDate = date; release.Size = size; release.Guid = detailsLink; release.Comments = commentsLink; release.Link = downloadLink; releases.Add(release); } } catch (Exception ex) { OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex); } // Return found releases return(releases); }
public async Task <IHttpActionResult> CreateOneFromSteamAppId(int appId, bool fillExisted = false) { if (appId <= 0) { return(this.BadRequest(nameof(appId), Errors.Invalid)); } var gamePoint = await _dbContext.NormalPoints.Where(p => p.SteamAppId == appId).SingleOrDefaultAsync(); if (fillExisted) { if (!User.IsInRole(KeylolRoles.Operator)) { return(Unauthorized()); } if (gamePoint == null) { return(NotFound()); } } else if (gamePoint != null) { return(this.BadRequest(nameof(appId), Errors.Duplicate)); } else { gamePoint = _dbContext.NormalPoints.Create(); _dbContext.NormalPoints.Add(gamePoint); } var cookieContainer = new CookieContainer(); cookieContainer.Add(new Uri("http://store.steampowered.com/"), new Cookie("birthtime", "-473410799")); using (var httpClientHandler = new HttpClientHandler { CookieContainer = cookieContainer }) using (var httpClient = new HttpClient(httpClientHandler) { Timeout = TimeSpan.FromSeconds(30) }) { Task <HttpResponseMessage> picsAwaiter = null; if (!fillExisted) { picsAwaiter = httpClient.GetAsync($"https://steampics-mckay.rhcloud.com/info?apps={appId}"); } var response = await httpClient.GetAsync($"http://store.steampowered.com/app/{appId}/?l=english&cc=us"); response.EnsureSuccessStatusCode(); Config.OutputFormatter = OutputFormatters.HtmlEncodingNone; var dom = CQ.Create(await response.Content.ReadAsStringAsync()); var navTexts = dom[".game_title_area .blockbg a"]; if (!navTexts.Any() || (navTexts[0].InnerText != "All Games" && navTexts[0].InnerText != "All Hardware")) { return(this.BadRequest(nameof(appId), Errors.SteamAppNotSupported)); } if (dom[".game_area_dlc_bubble"].Any()) { return(this.BadRequest(nameof(appId), Errors.SteamDlcNotSupported)); } if (!fillExisted) { gamePoint.SteamAppId = appId; gamePoint.PreferredName = PreferredNameType.English; gamePoint.Type = navTexts[0].InnerText != "All Games" ? NormalPointType.Game : NormalPointType.Hardware; } if (string.IsNullOrWhiteSpace(gamePoint.BackgroundImage)) { var backgroundResponse = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, $"http://steamcdn-a.akamaihd.net/steam/apps/{appId}/page_bg_generated.jpg")); if (backgroundResponse.IsSuccessStatusCode) { gamePoint.BackgroundImage = $"keylol://steam/app-backgrounds/{appId}"; } else { var screenshots = dom[".highlight_strip_screenshot img"]; if (screenshots.Any()) { var match = Regex.Match(screenshots[0].Attributes["src"], @"ss_([^\/]*)\.\d+x\d+\.jpg"); if (match.Success) { gamePoint.BackgroundImage = $"keylol://steam/app-backgrounds/{appId}-{match.Groups[1].Value}"; } } } } if (string.IsNullOrWhiteSpace(gamePoint.CoverImage)) { gamePoint.CoverImage = $"keylol://steam/app-capsules/{appId}"; } gamePoint.Description = dom[".game_description_snippet"].Text().Trim(); var genreNames = new List <string>(); var tags = dom[".popular_tags a.app_tag"].Select(child => child.InnerText.Trim()).Take(5).ToList(); var developerNames = new List <string>(); var publisherNames = new List <string>(); foreach (var child in dom[".game_details .details_block"].First().Find("b")) { var key = child.InnerText.Trim(); var values = new List <string>(); if (string.IsNullOrWhiteSpace(child.NextSibling.NodeValue)) { var current = child; do { current = current.NextElementSibling; values.Add(current.InnerText.Trim()); } while (current.NextSibling.NodeType == NodeType.TEXT_NODE && current.NextSibling.NodeValue.Trim() == ","); } else { values.Add(child.NextSibling.NodeValue.Trim()); } if (!values.Any()) { continue; } switch (key) { case "Title:": if (!fillExisted) { gamePoint.EnglishName = values[0]; } break; case "Genre:": genreNames.AddRange(values); break; case "Developer:": case "Manufacturer:": developerNames.AddRange(values); break; case "Publisher:": publisherNames.AddRange(values); break; case "Release Date:": DateTime releaseDate; gamePoint.ReleaseDate = DateTime.TryParse(values[0], out releaseDate) ? releaseDate : Helpers.DateTimeFromTimeStamp(0); break; } } if (!fillExisted) { gamePoint.IdCode = await GenerateIdCode(gamePoint.EnglishName); } var genrePointsMap = new Dictionary <string, Models.NormalPoint>(); var manufacturerPointsMap = new Dictionary <string, Models.NormalPoint>(); foreach (var pair in genreNames.Concat(tags).Distinct() .Select(n => new KeyValuePair <NormalPointType, string>(NormalPointType.Genre, n)) .Concat(developerNames.Concat(publisherNames).Distinct() .Select(n => new KeyValuePair <NormalPointType, string>(NormalPointType.Manufacturer, n)))) { var relatedPoint = await _dbContext.NormalPoints .Where(p => p.Type == pair.Key && p.SteamStoreNames.Select(n => n.Name).Contains(pair.Value)) .SingleOrDefaultAsync(); if (relatedPoint == null) { relatedPoint = _dbContext.NormalPoints.Create(); relatedPoint.EnglishName = pair.Value; var name = await _dbContext.SteamStoreNames .Where(n => n.Name == pair.Value).SingleOrDefaultAsync(); if (name == null) { name = _dbContext.SteamStoreNames.Create(); name.Name = pair.Value; _dbContext.SteamStoreNames.Add(name); await _dbContext.SaveChangesAsync(); } relatedPoint.SteamStoreNames = new[] { name }; relatedPoint.Type = pair.Key; relatedPoint.PreferredName = PreferredNameType.English; relatedPoint.IdCode = await GenerateIdCode(pair.Value); _dbContext.NormalPoints.Add(relatedPoint); await _dbContext.SaveChangesAsync(); } switch (pair.Key) { case NormalPointType.Genre: genrePointsMap[pair.Value] = relatedPoint; break; case NormalPointType.Manufacturer: manufacturerPointsMap[pair.Value] = relatedPoint; break; } } if (fillExisted) { gamePoint.MajorPlatformPoints.Clear(); gamePoint.GenrePoints.Clear(); gamePoint.TagPoints.Clear(); gamePoint.DeveloperPoints.Clear(); gamePoint.PublisherPoints.Clear(); } else { gamePoint.MajorPlatformPoints = new List <Models.NormalPoint>(); gamePoint.GenrePoints = new List <Models.NormalPoint>(); gamePoint.TagPoints = new List <Models.NormalPoint>(); gamePoint.DeveloperPoints = new List <Models.NormalPoint>(); gamePoint.PublisherPoints = new List <Models.NormalPoint>(); } gamePoint.MajorPlatformPoints.Add( await _dbContext.NormalPoints.SingleAsync(p => p.IdCode == "STEAM")); gamePoint.GenrePoints.AddRange(genreNames.Select(n => genrePointsMap[n]).ToList()); gamePoint.TagPoints.AddRange(tags.Select(n => genrePointsMap[n]).ToList()); gamePoint.DeveloperPoints.AddRange(developerNames.Select(n => manufacturerPointsMap[n]).ToList()); gamePoint.PublisherPoints.AddRange(publisherNames.Select(n => manufacturerPointsMap[n]).ToList()); if (!fillExisted) { response = await picsAwaiter; response.EnsureSuccessStatusCode(); var root = JObject.Parse(await response.Content.ReadAsStringAsync()); if ((bool)root["success"]) { gamePoint.AvatarImage = $"keylol://steam/app-icons/{appId}-{(string) root["apps"][appId.ToString()]["common"]["icon"]}"; } } await _dbContext.SaveChangesAsync(); return(Created($"normal-point/{gamePoint.Id}", new NormalPointDto(gamePoint, false, true) { Description = gamePoint.Description, SteamAppId = gamePoint.SteamAppId, DisplayAliases = gamePoint.DisplayAliases, CoverImage = gamePoint.CoverImage, ReleaseDate = gamePoint.ReleaseDate, DeveloperPoints = gamePoint.DeveloperPoints.Select(p => new NormalPointDto(p, true)).ToList(), PublisherPoints = gamePoint.PublisherPoints.Select(p => new NormalPointDto(p, true)).ToList(), SeriesPoints = gamePoint.SeriesPoints.Select(p => new NormalPointDto(p, true)).ToList(), GenrePoints = gamePoint.GenrePoints.Select(p => new NormalPointDto(p, true)).ToList(), TagPoints = gamePoint.TagPoints.Select(p => new NormalPointDto(p, true)).ToList(), MajorPlatformPoints = gamePoint.MajorPlatformPoints.Select(p => new NormalPointDto(p, true)).ToList(), MinorPlatformPoints = gamePoint.MinorPlatformPoints.Select(p => new NormalPointDto(p, true)).ToList() })); } }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); // search in normal + gems view foreach (var view in new string[] { "0", "1" }) { var queryCollection = new NameValueCollection(); queryCollection.Add("view", view); queryCollection.Add("searchtype", "1"); queryCollection.Add("incldead", "1"); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add(string.Format("c{0}", cat), "1"); } var searchUrl = SearchUrl + "?" + queryCollection.GetQueryString(); var results = await RequestStringWithCookiesAndRetry(searchUrl); if (results.IsRedirect) { // re-login await ApplyConfiguration(null); results = await RequestStringWithCookiesAndRetry(searchUrl); } try { CQ dom = results.Content; var rows = dom["#torrentBrowse > table > tbody > tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); CQ qRow = row.Cq(); release.MinimumRatio = 0; release.MinimumSeedTime = 2 * 24 * 60 * 60; var qLink = qRow.Find("a[title][href^=\"details.php?id=\"]"); release.Title = qLink.Attr("title"); release.Guid = new Uri(SiteLink + qLink.Attr("href").TrimStart('/')); release.Comments = release.Guid; qLink = qRow.Children().ElementAt(3).Cq().Children("a").First(); release.Link = new Uri(string.Format("{0}{1}", SiteLink, qLink.Attr("href"))); var catUrl = qRow.Children().ElementAt(0).FirstElementChild.Cq().Attr("href"); var catNum = catUrl.Split(new char[] { '=', '&' })[2].Replace("c", ""); release.Category = MapTrackerCatToNewznab(catNum); var dateString = qRow.Children().ElementAt(6).Cq().Text().Trim(); if (dateString.Contains("ago")) { release.PublishDate = DateTimeUtil.FromTimeAgo(dateString); } else { release.PublishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); } var sizeStr = qRow.Children().ElementAt(7).Cq().Text().Split(new char[] { '/' })[0]; release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Split(new char[] { '/' })[0].Trim()); release.Peers = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Split(new char[] { '/' })[1].Trim()) + release.Seeders; release.Files = ParseUtil.CoerceLong(qRow.Find("td:nth-child(5)").Text()); release.Grabs = ParseUtil.CoerceLong(qRow.Find("a[href^=\"snatches.php?id=\"]").Text().Split(' ')[0]); release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; var desc = qRow.Find("td:nth-child(2)"); desc.Find("a").Remove(); desc.Find("small").Remove(); // Remove release name (if enabled in the user cp) release.Description = desc.Text().Trim(new char[] { '-', ' ' }); 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 pairs = new List <KeyValuePair <string, string> >(); pairs.Add(new KeyValuePair <string, string>("nyit_sorozat_resz", "true")); pairs.Add(new KeyValuePair <string, string>("miben", "name")); pairs.Add(new KeyValuePair <string, string>("tipus", "kivalasztottak_kozott")); pairs.Add(new KeyValuePair <string, string>("submit.x", "1")); pairs.Add(new KeyValuePair <string, string>("submit.y", "1")); pairs.Add(new KeyValuePair <string, string>("submit", "Ok")); pairs.Add(new KeyValuePair <string, string>("mire", searchString)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count == 0) { cats = GetAllTrackerCategories(); } foreach (var lcat in LanguageCats) { if (!configData.Hungarian.Value) { cats.Remove(lcat + "_hun"); } if (!configData.English.Value) { cats.Remove(lcat); } } foreach (var cat in cats) { pairs.Add(new KeyValuePair <string, string>("kivalasztott_tipus[]", cat)); } var results = await PostDataWithCookiesAndRetry(SearchUrl, pairs); try { CQ dom = results.Content; ReleaseInfo release; var rows = dom[".box_torrent_all"].Find(".box_torrent"); foreach (var row in rows) { CQ qRow = row.Cq(); var key = dom ["link[rel=alternate]"].First().Attr("href").Split('=').Last(); release = new ReleaseInfo(); var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0); //if (torrentTxt == null) continue; release.Title = torrentTxt.GetAttribute("title"); release.Description = qRow.Find("div.siterank").Text(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; string downloadLink = SiteLink + torrentTxt.GetAttribute("href"); string downloadId = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4); release.Link = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key); release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId); release.Guid = new Uri(release.Comments.ToString() + "#comments");; release.Seeders = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders; var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href"); release.Imdb = ParseUtil.GetLongFromString(imdblink); var banner = qRow.Find("img.infobar_ico").Attr("onmouseover"); if (banner != null) { Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled); var BannerMatch = BannerRegEx.Match(banner); var bannerurl = BannerMatch.Groups[1].Value; release.BannerUrl = new Uri(bannerurl); } release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture); string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' '); release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0])); string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href"); string cat = ParseUtil.GetArgumentFromQueryString(catlink, "tipus"); release.Category = MapTrackerCatToNewznab(cat); releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { List <ReleaseInfo> releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection(); queryCollection.Add("incldead", "1"); queryCollection.Add("rel_type", "0"); // Alle if (query.ImdbID != null) { queryCollection.Add("searchin", "imdb"); queryCollection.Add("search", query.ImdbID); } else { queryCollection.Add("searchin", "title"); if (!string.IsNullOrWhiteSpace(searchString)) { // use AND+wildcard operator to avoid getting to many useless results var searchStringArray = Regex.Split(searchString.Trim(), "[ _.-]+", RegexOptions.Compiled).ToList(); searchStringArray = searchStringArray.Where(x => x.Length >= 3).ToList(); // remove words with less than 3 characters searchStringArray = searchStringArray.Where(x => !new string[] { "der", "die", "das", "the" }.Contains(x.ToLower())).ToList(); // remove words with less than 3 characters searchStringArray = searchStringArray.Select(x => "+" + x + "*").ToList(); // add AND operators+wildcards var searchStringFinal = String.Join(" ", searchStringArray); queryCollection.Add("search", searchStringFinal); } } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("c" + cat, "1"); } searchUrl += "?" + queryCollection.GetQueryString(); var results = await RequestStringWithCookiesAndRetry(searchUrl); if (results.IsRedirect) { await ApplyConfiguration(null); results = await RequestStringWithCookiesAndRetry(searchUrl); } try { CQ dom = results.Content; var rows = dom["table.torrent_table > tbody > tr"]; var globalFreeleech = dom.Find("legend:contains(\"Freeleech\")+ul > li > b:contains(\"Freeleech\")").Any(); foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 96 * 60 * 60; var qRow = row.Cq(); var catStr = row.ChildElements.ElementAt(0).FirstElementChild.GetAttribute("href").Split('=')[1]; release.Category = MapTrackerCatToNewznab(catStr); var qLink = row.ChildElements.ElementAt(2).FirstElementChild.Cq(); release.Link = new Uri(SiteLink + qLink.Attr("href")); var torrentId = qLink.Attr("href").Split('=').Last(); var descCol = row.ChildElements.ElementAt(1); var qCommentLink = descCol.FirstElementChild.Cq(); var torrentTag = descCol.Cq().Find("span.torrent-tag"); var torrentTags = torrentTag.Elements.Select(x => x.InnerHTML).ToList(); release.Title = qCommentLink.Attr("title"); release.Description = String.Join(", ", torrentTags); release.Comments = new Uri(SiteLink + qCommentLink.Attr("href").Replace("&hit=1", "")); release.Guid = release.Comments; var torrent_details = descCol.Cq().Find(".torrent_details").Get(0); var rawDateStr = torrent_details.ChildNodes.ElementAt(torrent_details.ChildNodes.Length - 3).Cq().Text(); var dateStr = rawDateStr.Trim().Replace("von", "").Trim(); DateTime dateGerman; if (dateStr.StartsWith("Heute ")) { dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStr.Split(' ')[1]); } else if (dateStr.StartsWith("Gestern ")) { dateGerman = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified) + TimeSpan.Parse(dateStr.Split(' ')[1]) - TimeSpan.FromDays(1); } else { dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); } DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz); release.PublishDate = pubDateUtc.ToLocalTime(); var imdbLink = descCol.Cq().Find("a[href*=\"&searchin=imdb\"]"); if (imdbLink.Any()) { release.Imdb = ParseUtil.GetLongFromString(imdbLink.Attr("href")); } var sizeStr = row.ChildElements.ElementAt(5).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text()); release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text()) + release.Seeders; var grabs = qRow.Find("td:nth-child(7)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (globalFreeleech) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("span.torrent-tag-free").Length >= 1) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public void AttributeEqualsSelector() { CQ res = Dom.Find("span[name=badge_span_bronze]"); Assert.AreEqual("13", res[0].InnerHTML, "InnerHtml of element id=badge_span_bronze did not match"); }
/// <summary> /// Return a CQ object, treating the HTML as a complete document /// </summary> public CQ GetDocument() { return(CQ.CreateDocument(Html)); }
/// <summary> /// Resolve all script dependencies in the bound CQ document. Scripts that cotain a "data- /// location='head'" attribute will be moved to the head. /// </summary> /// /// <param name="doc"> /// The document to resolve. /// </param> public void ResolveScriptDependencies(CQ doc) { string scriptSelector = "script[src][type='text/javascript'], script[src]:not([type]), link[type='text/css']"; CQ scripts = doc[scriptSelector]; if (scripts.Length == 0) { return; } // move scripts first // TODO: Optimize using a query caching mechanism so foreach (var item in scripts.Filter("[data-moveto]")) { var target = doc.Select(item["data-moveto"]); if (target.Length>0) { target.First().Append(item); item.RemoveAttribute("data-moveto"); } } // resolve dependencies ScriptCollection coll = new ScriptCollection(ScriptEnvironment); coll.Options= Options; // identify the insertion point for the script bundle. AddFromCq returns the first script with dependencies, // so scripts should be added right before that one. Otherwise they should be added // at the end of head. var firstScriptEl = coll.AddFromCq(scripts); CQ firstScript=null; if (firstScriptEl != null) { firstScript = firstScriptEl.Cq(); } string bundleUrl; List<ScriptRef> dependencies = coll.GetDependencies() .Where(item=>!coll.Contains(item)) .ToList(); // Now add scripts directly for dependencies marked as NoCombine. var inlineScripts = Options.HasFlag(ViewEngineOptions.NoBundle) ? dependencies : dependencies.Where(item => item.NoCombine); foreach (var item in inlineScripts) { var script = GetScriptHtml(item.Path, item.ScriptHash); if (firstScript != null) { firstScript.Before(script); } else { firstScript = script; doc["body"].Append(script); } } // Before creating the bundle, remove any duplicates of the same script on the page if (!Options.HasFlag(ViewEngineOptions.NoBundle)) { bool hasBundle = Bundles.TryGetValue(coll, out bundleUrl); if (hasBundle) { // when nocache is set, we will regenerate the bundle, but not change the script ID. The v= // flag will be changed by BundleTable. if (Options.HasFlag(ViewEngineOptions.NoCache)) { string removeUrl = "~" + bundleUrl.Before("?"); BundleTable.Bundles.Remove(BundleTable.Bundles.GetBundleFor(removeUrl)); hasBundle = false; ScriptID++; // this code attempts to un-cache the bundle, it doesn't work. // leaving it here until some permanent solution is found as a reminder // // http://stackoverflow.com/questions/12317391/how-to-force-bundlecollection-to-flush-cached-script-bundles-in-mvc4 // //var bundleList = BundleTable.Bundles.ToList(); //BundleTable.Bundles.Clear(); //BundleTable.Bundles.ResetAll(); //BundleTable.EnableOptimizations = false; //foreach (var oldBundle in bundleList) //{ // BundleTable.Bundles.Add(oldBundle); //} } } else { ScriptID++; } if (!hasBundle) { var activeDependencies = dependencies.Where(item => !item.NoCombine).ToList(); if (activeDependencies.Count > 0) { string bundleAlias = "~/cqbundle" + ScriptID; var bundle = GetScriptBundle(bundleAlias); foreach (var item in activeDependencies) { bundle.Include(item.Path); } BundleTable.Bundles.Add(bundle); if (HttpContext.Current != null) { bundleUrl = BundleTable.Bundles.ResolveBundleUrl(bundleAlias, true); } else { bundleUrl = bundleAlias + "_no_http_context"; } Bundles[coll] = bundleUrl; } } var scriptPlaceholder = scripts.First(); // add bundle after all noncombined scripts if (!String.IsNullOrEmpty(bundleUrl)) { firstScript.Before(GetScriptHtml(bundleUrl)); } } }
public 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); } var searchUrl = SearchUrl + queryCollection.GetQueryString(); var trackerCats = MapTorznabCapsToTrackers(query, mapChildrenCatsToParent: true); var results = await RequestStringWithCookiesAndRetry(searchUrl); try { CQ dom = results.Content; dom["#needseed"].Remove(); var rows = dom["table[width='750'] > tbody"].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"); release.Description = release.Title; release.Guid = new Uri(SiteLink + qLink.Attr("href").TrimStart('/')); 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-dd HH: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; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public void Cq_About__LogOnPartial(CQ doc) { doc["a"].Text("Link Text About Only"); }
public void AttributeStartsWithSelector() { CQ res = Dom.Find("span[name^=badge_span]"); Assert.AreEqual(2, res.Length, "Expected two elements starting with badge_span"); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection(); queryCollection.Add("total", "146"); // Not sure what this is about but its required! var cat = "0"; var queryCats = MapTorznabCapsToTrackers(query); if (queryCats.Count == 1) { cat = queryCats.First().ToString(); } queryCollection.Add("cat", cat); queryCollection.Add("searchin", "filename"); queryCollection.Add("search", searchString); queryCollection.Add("page", "1"); searchUrl += "?" + queryCollection.GetQueryString(); var extraHeaders = new Dictionary <string, string>() { { "X-Requested-With", "XMLHttpRequest" } }; var response = await RequestStringWithCookiesAndRetry(searchUrl, null, SearchUrlReferer, extraHeaders); var results = response.Content; try { CQ dom = results; var rows = dom["tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qTitleLink = qRow.Find("td:eq(1) a:eq(0)").First(); release.Title = qTitleLink.Find("strong").Text().Trim(); // If we search an get no results, we still get a table just with no info. if (string.IsNullOrWhiteSpace(release.Title)) { break; } release.Description = release.Title; release.Guid = new Uri(qTitleLink.Attr("href")); release.Comments = release.Guid; var dateString = qRow.Find("td:eq(4)").Text(); release.PublishDate = DateTime.ParseExact(dateString, "dd MMM yy", CultureInfo.InvariantCulture); var qLink = qRow.Find("td:eq(2) a"); release.Link = new Uri(qLink.Attr("href")); var sizeStr = qRow.Find("td:eq(5)").Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); var connections = qRow.Find("td:eq(7)").Text().Trim().Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); release.Seeders = ParseUtil.CoerceInt(connections[0].Trim()); release.Peers = ParseUtil.CoerceInt(connections[1].Trim()) + release.Seeders; var rCat = row.Cq().Find("td:eq(0) a").First().Attr("href"); var rCatIdx = rCat.IndexOf("cat="); if (rCatIdx > -1) { rCat = rCat.Substring(rCatIdx + 4); } release.Category = MapTrackerCatToNewznab(rCat); releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
/// <summary> Cq log on partial. </summary> /// /// <remarks> James Treworgy, 7/1/2012. </remarks> public void Cq__LogOnPartial(CQ doc) { doc["a"].Text("Link Text Always"); }
public void CssSelector() { CQ res = Dom.Find(".badgecount"); Assert.AreEqual(2, res.Length, "Expected 2 .badgecount items"); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var searchUrl = BrowseUrl; var queryCollection = new NameValueCollection(); if (!string.IsNullOrWhiteSpace(query.ImdbID)) { queryCollection.Add("q", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("q", searchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add(cat, string.Empty); } if (queryCollection.Count > 0) { searchUrl += "?" + queryCollection.GetQueryString(); } var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); var results = response.Content; try { CQ dom = results; var rows = dom["table[id='torrents'] > tbody > tr"]; foreach (var row in rows.Skip(1)) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qTitleLink = qRow.Find("a[href^=\"/details.php?id=\"]").First(); release.Title = qTitleLink.Text().Trim(); // If we search an get no results, we still get a table just with no info. if (string.IsNullOrWhiteSpace(release.Title)) { break; } release.Guid = new Uri(SiteLink + qTitleLink.Attr("href").Substring(1)); release.Comments = release.Guid; var descString = qRow.Find(".t_ctime").Text(); var dateString = descString.Split('|').Last().Trim(); dateString = dateString.Split(new string[] { " by " }, StringSplitOptions.None)[0]; release.PublishDate = DateTimeUtil.FromTimeAgo(dateString); var qLink = row.ChildElements.ElementAt(3).Cq().Children("a"); release.Link = new Uri(SiteLink + WebUtility.UrlEncode(qLink.Attr("href").TrimStart('/'))); var sizeStr = row.ChildElements.ElementAt(5).Cq().Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qRow.Find(".t_seeders").Text().Trim()); release.Peers = ParseUtil.CoerceInt(qRow.Find(".t_leechers").Text().Trim()) + release.Seeders; var catIcon = row.Cq().Find("td:eq(0) a"); if (catIcon.Length >= 1) // Torrents - Category column == Icons { release.Category = MapTrackerCatToNewznab(catIcon.First().Attr("href").Substring(1)); } else // Torrents - Category column == Text (Code is not supported) { release.Category = MapTrackerCatDescToNewznab(row.Cq().Find("td:eq(0)").Text()); } var filesElement = row.Cq().Find("a[href*=\"/files\"]"); // optional if (filesElement.Length == 1) { release.Files = ParseUtil.CoerceLong(filesElement.Text()); } var grabs = row.Cq().Find("td:nth-last-child(3)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (row.Cq().Find("span.t_tag_free_leech").Any()) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
protected override void ProcessJob(Guid targetInstanceId) { // check whether there are running instances of timerjob // if there is - the work stops if ((from SPRunningJob job in WebApplication.RunningJobs where String.CompareOrdinal(job.JobDefinition.Name, Name) == 0 select job).Count() > 1) { Logger.WriteMessage(string.Format("You can not run multiple instances of the same timer job. Name: {0}, Time start attempt: {1}", Title, DateTime.Now.ToString("dd:MM:yyyy:hh:mm:ss"))); return; } using (var site = new SPSite(SiteUrl, SPUserToken.SystemAccount)) { using (SPWeb web = site.OpenWeb(WebUrl)) { try { SPList officesList = web.GetList(Constants.Lists.OfficesListUrl); SPList officesList2 = web.GetList(Constants.Lists.Offices2ListUrl); // prepare spquery ti get not copied items by 'iscopied' flag string query = CQ.Where(CQ.Neq.FieldRef(new Guid(Constants.Fields.FieldIsCopiedFieldInternalNameId)).Value(true)); var spQuery = new SPQuery { Query = query, ViewFields = CQ.ViewFields(CQ.FieldRef(new Guid(Constants.Fields.NameFieldInternalNameId)), CQ.FieldRef(new Guid(Constants.Fields.DirectorFieldInternalNameId)), CQ.FieldRef(new Guid(Constants.Fields.FieldIsCopiedFieldInternalNameId)), CQ.FieldRef(SPBuiltInFieldId.Title)) }; // get not copied items SPListItemCollection itemsToCopy = officesList.GetItems(spQuery); SPListItem destItem = officesList2.Items.Add(); foreach (SPListItem srcItem in itemsToCopy) { // set additional title field destItem[SPBuiltInFieldId.Title] = srcItem[SPBuiltInFieldId.Title]; // Copy fields srcItem.CopyItemTo(destItem, new List <string> { Constants.Fields.NameFieldInternalName, Constants.Fields.DirectorFieldInternalName, }); // set flag copied to 'true' at source item srcItem[new Guid(Constants.Fields.FieldIsCopiedFieldInternalNameId)] = true; srcItem.Update(); } } catch (Exception ex) { Logger.WriteError( string.Format("Failed to copy the elements of the list {0} to the list {1}.", Constants.Lists.OfficesListUrl, Constants.Lists.Offices2ListUrl), ex, TraceSeverity.Unexpected); } } } }
/// <summary> /// Resolve all script dependencies in the bound CQ document. Scripts that cotain a "data- /// location='head'" attribute will be moved to the head. /// </summary> /// /// <param name="doc"> /// The document to resolve. /// </param> public void ResolveScriptDependencies(CQ doc) { string scriptSelector = "script[src][type='text/javascript'], link[type='text/css']"; //+ (Options.HasFlag(ViewEngineOptions.ProcessAllScripts) ? // "" : ".csquery-script") // + "[src]"; // Filter out non-relative paths (remote URLs) CQ scripts = doc[scriptSelector].Filter(item => { return !PathList.IsRemoteUrl(item.UrlSource()); }); if (scripts.Length == 0) { return; } // move scripts to head as needed first var toMove = scripts.Filter("[data-location='head']"); var head = doc["head"]; if (toMove.Length > 0) { foreach (var item in toMove) { if (item.ParentNode != head) { head.Append(item); } } } // resolve dependencies ScriptCollection coll = new ScriptCollection(LibraryPath,MapPath); coll.NoCache = Options.HasFlag(ViewEngineOptions.NoCache); coll.IgnoreErrors = Options.HasFlag(ViewEngineOptions.IgnoreMissingScripts); // identify the insertion point for the script bundle var firstScriptEl = coll.AddFromCq(scripts); var firstScript = firstScriptEl == null ? head.Children().First() : firstScriptEl.Cq(); string bundleUrl; List<ScriptRef> dependencies = coll.GetDependencies() .Where(item=>!coll.Contains(item)) .ToList(); // find the first script with dependencies // Now add scripts directly for depenencies marked as NoCombine. foreach (var item in dependencies.Where(item => item.NoCombine)) { firstScript.Before(GetScriptHtml(item.Path, item.ScriptHash)); } // Before creating the bundle, remove any duplicates of the same script on the page bool hasBundle = Bundles.TryGetValue(coll, out bundleUrl); if (hasBundle) { // when nocache is set, we will regenerate the bundle, but not change the script ID. The v= // flag will be changed by BundleTable. if (Options.HasFlag(ViewEngineOptions.NoCache)) { string removeUrl = "~" + bundleUrl.Before("?"); BundleTable.Bundles.Remove(BundleTable.Bundles.GetBundleFor(removeUrl)); hasBundle = false; ScriptID++; // //var bundleList = BundleTable.Bundles.ToList(); //BundleTable.Bundles.Clear(); //BundleTable.Bundles.ResetAll(); //BundleTable.EnableOptimizations = false; //foreach (var oldBundle in bundleList) //{ // BundleTable.Bundles.Add(oldBundle); //} } } else { ScriptID++; } if (!hasBundle) { string bundleAlias = "~/cqbundle" + ScriptID; var bundle = GetScriptBundle(bundleAlias); var activeDependencies = dependencies.Where(item => !item.NoCombine); foreach (var item in activeDependencies) { bundle.Include(item.Path); } BundleTable.Bundles.Add(bundle); if (HttpContext.Current != null) { bundleUrl = BundleTable.Bundles.ResolveBundleUrl(bundleAlias, true); } else { bundleUrl = bundleAlias + "_no_http_context"; } Bundles[coll] = bundleUrl; } var scriptPlaceholder = scripts.First(); // add bundle after all noncombined scripts firstScript.Before(GetScriptHtml(bundleUrl)); }
/// <summary> /// Configure our Provider /// </summary> /// <param name="configJson">Our params in Json</param> /// <returns>Configuration state</returns> public async Task <IndexerConfigurationStatus> ApplyConfiguration(JToken configJson) { // Retrieve config values set by Jackett's user ConfigData.LoadValuesFromJson(configJson); // Check & Validate Config validateConfig(); // Setting our data for a better emulated browser (maximum security) // TODO: Encoded Content not supported by Jackett at this time // emulatedBrowserHeaders.Add("Accept-Encoding", "gzip, deflate"); // If we want to simulate a browser if (ConfigData.Browser.Value) { // Clean headers emulatedBrowserHeaders.Clear(); // Inject headers emulatedBrowserHeaders.Add("Accept", ConfigData.HeaderAccept.Value); emulatedBrowserHeaders.Add("Accept-Language", ConfigData.HeaderAcceptLang.Value); emulatedBrowserHeaders.Add("DNT", Convert.ToInt32(ConfigData.HeaderDNT.Value).ToString()); emulatedBrowserHeaders.Add("Upgrade-Insecure-Requests", Convert.ToInt32(ConfigData.HeaderUpgradeInsecure.Value).ToString()); emulatedBrowserHeaders.Add("User-Agent", ConfigData.HeaderUserAgent.Value); } // Getting login form to retrieve CSRF token /*var myRequest = new Utils.Clients.WebRequest() * { * Url = LoginUrl * };*/ // Add our headers to request //myRequest.Headers = emulatedBrowserHeaders; // Building login form data var pairs = new Dictionary <string, string> { { "username", ConfigData.Username.Value }, { "password", ConfigData.Password.Value } }; // Do the login var request = new Utils.Clients.WebRequest() { PostData = pairs, Referer = LoginUrl, Type = RequestType.POST, Url = LoginUrl, Headers = emulatedBrowserHeaders }; // Perform loggin latencyNow(); output("\nPerform loggin.. with " + LoginUrl); var response = await webclient.GetString(request); // Test if we are logged in await ConfigureIfOK(response.Cookies, !response.Cookies.Contains("deleted"), () => { // Parse error page CQ dom = response.Content; string message = dom[".error"].Text().Trim().Replace("X\n\t\t", "").Replace("\n\t\tX", ""); // Oops, unable to login output("-> Login failed: \"" + message + "\".", "error"); throw new ExceptionWithConfigData("Login failed: << " + message + " >>", configData); }); output("-> Login Success"); return(IndexerConfigurationStatus.RequiresTesting); }
public SelectorCache(CQ cqSource) { CqSource = cqSource; }
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("incldead", "1"); queryCollection.Add("_by", "0"); queryCollection.Add("sort", "4"); queryCollection.Add("type", "desc"); if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("search", searchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("c" + cat, "1"); } searchUrl += "?" + queryCollection.GetQueryString(); var response = await RequestStringWithCookies(searchUrl); var results = response.Content; try { CQ dom = results; var rows = dom["table[border=1] > tbody > tr:has(td.torrenttable)"]; foreach (var row in rows) { var release = new ReleaseInfo(); release.MinimumRatio = 0.8; release.MinimumSeedTime = 48 * 60 * 60; var qRow = row.Cq(); var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First(); var qTitle = qDetailsLink.Find("b").First(); release.Title = qTitle.Text(); var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First(); var qDLLink = qRow.Find("a.download").First(); var qSeeders = qRow.Find("td.torrenttable:eq(7)"); var qLeechers = qRow.Find("td.torrenttable:eq(8)"); var qDateStr = qRow.Find("td.torrenttable:eq(4)").First(); var qSize = qRow.Find("td.torrenttable:eq(5)").First(); var catStr = qCatLink.Attr("href").Split('=')[1].Split('\'')[0]; release.Category = MapTrackerCatToNewznab(catStr); release.Link = new Uri(SiteLink + qDLLink.Attr("href")); release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href")); release.Guid = release.Link; var sizeStr = qSize.Text(); release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(qSeeders.Text()); release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders; var dateStr = qDateStr.Text().Trim(); DateTime dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "MMM d yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified); DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz); release.PublishDate = pubDateUtc.ToLocalTime(); var files = qRow.Find("td:nth-child(4)").Text(); release.Files = ParseUtil.CoerceInt(files); var grabs = qRow.Find("td:nth-child(8)").Get(0).FirstChild.ToString(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("img[src=\"pic/torrent_ou.gif\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("font[color=\"gray\"]:contains(50% Down)").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }