Example #1
0
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases        = new List <ReleaseInfo>();
            var searchurls      = new List <string>();
            var searchUrl       = SearchUrl;// string.Format(SearchUrl, HttpUtility.UrlEncode()));
            var queryCollection = new NameValueCollection();
            var searchString    = query.GetQueryString();

            foreach (var cat in MapTorznabCapsToTrackers(query))
            {
                searchUrl += "category%5B%5D=" + cat + "&";
            }

            if (query.ImdbID != null)
            {
                queryCollection.Add("search", query.ImdbID);
            }
            else if (!string.IsNullOrWhiteSpace(searchString))
            {
                queryCollection.Add("search", searchString);
            }

            queryCollection.Add("active", "0");
            queryCollection.Add("options", "0");

            searchUrl += queryCollection.GetQueryString().Replace("(", "%28").Replace(")", "%29"); // maually url encode brackets to prevent "hacking" detection

            var results = await RequestStringWithCookiesAndRetry(searchUrl);

            try
            {
                CQ          dom = results.Content;
                ReleaseInfo release;

                CQ     userInfo = dom[".mainmenu > table > tbody > tr:has(td[title=\"Active-Torrents\"])"][0].Cq();
                string rank     = userInfo.Find("td:nth-child(2)").Text().Substring(6);

                HashSet <string> freeleechRanks = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                freeleechRanks.Add("VIP");
                freeleechRanks.Add("Uploader");
                freeleechRanks.Add("HD Internal");
                freeleechRanks.Add("Moderator");
                freeleechRanks.Add("Administrator");
                freeleechRanks.Add("Owner");
                bool hasFreeleech = freeleechRanks.Contains(rank);

                var rows = dom[".mainblockcontenttt > tbody > tr:has(a[href^=\"details.php?id=\"])"];
                foreach (var row in rows)
                {
                    CQ qRow = row.Cq();

                    release = new ReleaseInfo();

                    release.Title       = qRow.Find("td.mainblockcontent b a").Text();
                    release.Description = qRow.Find("td:nth-child(3) > span").Text();

                    release.MinimumRatio    = 1;
                    release.MinimumSeedTime = 172800; // 48 hours

                    int tdIndex = 0;
                    if (qRow.Find("td:nth-last-child(1)").Text() == "Edit")
                    {
                        tdIndex = 1;
                    }
                    // moderators get additional delete, recomend and like links
                    if (qRow.Find("td:nth-last-child(4)").Text() == "Edit")
                    {
                        tdIndex = 4;
                    }

                    // Sometimes the uploader column is missing
                    if (ParseUtil.TryCoerceInt(qRow.Find($"td:nth-last-child({tdIndex + 3})").Text(), out int seeders))
                    {
                        release.Seeders = seeders;
                        if (ParseUtil.TryCoerceInt(qRow.Find($"td:nth-last-child({tdIndex + 2})").Text(), out int peers))
                        {
                            release.Peers = peers + release.Seeders;
                        }
                    }

                    // Sometimes the grabs column is missing
                    if (ParseUtil.TryCoerceLong(qRow.Find($"td:nth-last-child({tdIndex + 1})").Text(), out long grabs))
                    {
                        release.Grabs = grabs;
                    }

                    string fullSize = qRow.Find("td.mainblockcontent").Get(6).InnerText;
                    release.Size = ReleaseInfo.GetBytes(fullSize);

                    release.Guid     = new Uri(SiteLink + qRow.Find("td.mainblockcontent b a").Attr("href"));
                    release.Link     = new Uri(SiteLink + qRow.Find("td.mainblockcontent").Get(3).FirstChild.GetAttribute("href"));
                    release.Comments = new Uri(SiteLink + qRow.Find("td.mainblockcontent b a").Attr("href"));

                    string[] dateSplit  = qRow.Find("td.mainblockcontent").Get(5).InnerHTML.Split(',');
                    string   dateString = dateSplit[1].Substring(0, dateSplit[1].IndexOf('>')).Trim();
                    release.PublishDate = DateTime.ParseExact(dateString, "dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture).ToLocalTime();

                    string category = qRow.Find("td:eq(0) a").Attr("href").Replace("torrents.php?category=", "");
                    release.Category = MapTrackerCatToNewznab(category);

                    release.UploadVolumeFactor = 1;

                    if (qRow.Find("img[alt=\"Free Torrent\"]").Length >= 1)
                    {
                        release.DownloadVolumeFactor = 0;
                        release.UploadVolumeFactor   = 0;
                    }
                    else if (hasFreeleech)
                    {
                        release.DownloadVolumeFactor = 0;
                    }
                    else if (qRow.Find("img[alt=\"Silver Torrent\"]").Length >= 1)
                    {
                        release.DownloadVolumeFactor = 0.5;
                    }
                    else if (qRow.Find("img[alt=\"Bronze Torrent\"]").Length >= 1)
                    {
                        release.DownloadVolumeFactor = 0.75;
                    }
                    else if (qRow.Find("img[alt=\"Blue Torrent\"]").Length >= 1)
                    {
                        release.DownloadVolumeFactor = 0.25;
                    }
                    else
                    {
                        release.DownloadVolumeFactor = 1;
                    }

                    var imdblink = qRow.Find("a[href*=\"www.imdb.com/title/\"]").Attr("href");
                    release.Imdb = ParseUtil.GetLongFromString(imdblink);

                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.Content, ex);
            }

            return(releases);
        }
Example #2
0
        private async Task <IEnumerable <ReleaseInfo> > performRegularQuery(TorznabQuery query, string hebName = null)
        {
            var releases        = new List <ReleaseInfo>();
            var searchurls      = new List <string>();
            var searchUrl       = SearchUrl;
            var queryCollection = new NameValueCollection();
            var searchString    = query.GetQueryString();

            if (query.IsImdbQuery)
            {
                searchString = query.ImdbID;
            }

            if (hebName != null)
            {
                searchString = hebName + " - עונה " + query.Season + " פרק " + query.Episode;
            }
            searchUrl += "?";
            if (!string.IsNullOrWhiteSpace(searchString))
            {
                var strEncoded = WebUtilityHelpers.UrlEncode(searchString, Encoding);
                searchUrl += "&query=" + strEncoded + "&matchquery=any";
            }

            foreach (var cat in MapTorznabCapsToTrackers(query))
            {
                searchUrl += "&c[]=" + cat;
            }

            var data = await RequestStringWithCookiesAndRetry(searchUrl);

            try
            {
                CQ  dom  = data.Content;
                var rows = dom["tr.box_torrent"];
                foreach (var row in rows)
                {
                    CQ qRow = row.Cq();

                    var release         = new ReleaseInfo();
                    var main_title_link = qRow.Find("div.main_title > a");
                    release.Title = main_title_link.Attr("longtitle");
                    if (release.Title.IsNullOrEmptyOrWhitespace())
                    {
                        release.Title = main_title_link.Text();
                    }

                    release.MinimumRatio    = 1;
                    release.MinimumSeedTime = 172800;

                    int seeders, peers;
                    if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(7) > div").Text(), out seeders))
                    {
                        release.Seeders = seeders;
                        if (ParseUtil.TryCoerceInt(qRow.Find("td:nth-child(8) > div").Text(), out peers))
                        {
                            release.Peers = peers + release.Seeders;
                        }
                    }
                    release.Grabs   = ParseUtil.CoerceLong(qRow.Find("td:nth-child(5)").Text().Replace(",", ""));
                    release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:nth-child(6)").Text().Replace(",", ""));
                    release.Peers   = ParseUtil.CoerceInt(qRow.Find("td:nth-child(7)").Text().Replace(",", "")) + release.Seeders;
                    string fullSize = qRow.Find("td:nth-child(4)").Text();
                    release.Size = ReleaseInfo.GetBytes(fullSize);

                    release.Comments = new Uri(SiteLink + qRow.Find("a.threadlink[href]").Attr("href"));
                    release.Link     = new Uri(SiteLink + qRow.Find("a:has(div.dlimg)").Attr("href"));
                    release.Guid     = release.Comments;
                    try
                    {
                        release.BannerUrl = new Uri(qRow.Find("a[imgsrc]").Attr("imgsrc"));
                    }
                    catch (Exception)
                    {
                        // do nothing, some releases have invalid banner URLs, ignore the banners in this case
                    }

                    var    dateStringAll = qRow.Find("div.up_info2")[0].ChildNodes.Last().ToString();
                    var    dateParts     = dateStringAll.Split(' ');
                    string dateString    = dateParts[dateParts.Length - 2] + " " + dateParts[dateParts.Length - 1];
                    release.PublishDate = DateTime.ParseExact(dateString, "dd/MM/yy HH:mm", CultureInfo.InvariantCulture);

                    string categoryLink = qRow.Find("a[href^=\"/browse.php?cat=\"]").Attr("href");
                    var    catid        = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat");
                    release.Category = MapTrackerCatToNewznab(catid);

                    if (qRow.Find("a[href^=\"?freeleech=1\"]").Length >= 1)
                    {
                        release.DownloadVolumeFactor = 0;
                    }
                    else
                    {
                        release.DownloadVolumeFactor = 1;
                    }

                    release.UploadVolumeFactor = 1;

                    var sub_title = qRow.Find("div.sub_title");
                    var imdb_link = sub_title.Find("span.imdb-inline > a");
                    release.Imdb = ParseUtil.GetLongFromString(imdb_link.Attr("href"));
                    sub_title.Find("span.imdb-inline").Remove();
                    release.Description = sub_title.Text();

                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(data.Content, ex);
            }

            return(releases);
        }
Example #3
0
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases  = new List <ReleaseInfo>();
            var searchUrl = SearchUrl + string.Join(
                string.Empty, MapTorznabCapsToTrackers(query).Select(cat => $"category[]={cat}&"));
            var queryCollection = new NameValueCollection
            {
                { "search", query.ImdbID ?? query.GetQueryString() },
                { "active", "0" },
                { "options", "0" }
            };

            // manually url encode parenthesis to prevent "hacking" detection
            searchUrl += queryCollection.GetQueryString().Replace("(", "%28").Replace(")", "%29");

            var results = await RequestWithCookiesAndRetryAsync(searchUrl);

            try
            {
                var parser = new HtmlParser();
                var dom    = parser.ParseDocument(results.ContentString);

                var userInfo     = dom.QuerySelector("table.navus tr");
                var userRank     = userInfo.Children[1].TextContent.Replace("Rank:", string.Empty).Trim();
                var hasFreeleech = _freeleechRanks.Contains(userRank);

                var rows = dom.QuerySelectorAll("table.mainblockcontenttt tr:has(td.mainblockcontent)");
                foreach (var row in rows.Skip(1))
                {
                    var mainLink = row.Children[2].QuerySelector("a");
                    var title    = mainLink.TextContent;
                    var comments = new Uri(SiteLink + mainLink.GetAttribute("href"));

                    var bannerMatch = _bannerRegex.Match(mainLink.GetAttribute("onmouseover"));
                    var banner      = bannerMatch.Success ? new Uri(SiteLink + bannerMatch.Groups[1].Value.Replace("\\", "/")) : null;

                    var link        = new Uri(SiteLink + row.Children[4].FirstElementChild.GetAttribute("href"));
                    var description = row.Children[2].QuerySelector("span").TextContent;
                    var size        = ReleaseInfo.GetBytes(row.Children[7].TextContent);

                    var dateTag     = row.Children[6].FirstElementChild;
                    var dateString  = string.Join(" ", dateTag.Attributes.Select(attr => attr.Name));
                    var publishDate = DateTime.ParseExact(dateString, "dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture).ToLocalTime();

                    var catStr = row.FirstElementChild.FirstElementChild.GetAttribute("href").Split('=')[1];
                    var cat    = MapTrackerCatToNewznab(catStr);

                    // Sometimes the uploader column is missing, so seeders, leechers, and grabs may be at a different index.
                    // There's room for improvement, but this works for now.
                    var endIndex = row.Children.Length;

                    //Maybe use row.Children.Index(Node) after searching for an element instead?
                    if (row.Children[endIndex - 1].TextContent == "Edit")
                    {
                        endIndex -= 1;
                    }
                    // moderators get additional delete, recommend and like links
                    else if (row.Children[endIndex - 4].TextContent == "Edit")
                    {
                        endIndex -= 4;
                    }

                    int?seeders = null;
                    int?peers   = null;
                    if (ParseUtil.TryCoerceInt(row.Children[endIndex - 3].TextContent, out var rSeeders))
                    {
                        seeders = rSeeders;
                        if (ParseUtil.TryCoerceInt(row.Children[endIndex - 2].TextContent, out var rLeechers))
                        {
                            peers = rLeechers + rSeeders;
                        }
                    }

                    var grabs = ParseUtil.TryCoerceLong(row.Children[endIndex - 1].TextContent, out var rGrabs)
                        ? (long?)rGrabs
                        : null;

                    var dlVolumeFactor = 1.0;
                    var upVolumeFactor = 1.0;
                    if (row.QuerySelector("img[src$=\"no_ratio.png\"]") != null)
                    {
                        dlVolumeFactor = 0;
                        upVolumeFactor = 0;
                    }
                    else if (hasFreeleech || row.QuerySelector("img[src$=\"free.png\"]") != null)
                    {
                        dlVolumeFactor = 0;
                    }
                    else if (row.QuerySelector("img[src$=\"50.png\"]") != null)
                    {
                        dlVolumeFactor = 0.5;
                    }
                    else if (row.QuerySelector("img[src$=\"25.png\"]") != null)
                    {
                        dlVolumeFactor = 0.75;
                    }
                    else if (row.QuerySelector("img[src$=\"75.png\"]") != null)
                    {
                        dlVolumeFactor = 0.25;
                    }

                    var imdbLink = row.QuerySelector("a[href*=\"www.imdb.com/title/\"]")?.GetAttribute("href");
                    var imdb     = !string.IsNullOrWhiteSpace(imdbLink) ? ParseUtil.GetLongFromString(imdbLink) : null;

                    var release = new ReleaseInfo
                    {
                        Title                = title,
                        Comments             = comments,
                        Guid                 = comments,
                        Link                 = link,
                        PublishDate          = publishDate,
                        Category             = cat,
                        Description          = description,
                        BannerUrl            = banner,
                        Imdb                 = imdb,
                        Size                 = size,
                        Grabs                = grabs,
                        Seeders              = seeders,
                        Peers                = peers,
                        DownloadVolumeFactor = dlVolumeFactor,
                        UploadVolumeFactor   = upVolumeFactor,
                        MinimumRatio         = 1,
                        MinimumSeedTime      = 172800 // 48 hours
                    };

                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.ContentString, ex);
            }

            return(releases);
        }
Example #4
0
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases = new List <ReleaseInfo>();

            var qc = new NameValueCollection
            {
                { "incldead", "1" }
            };

            if (query.IsImdbQuery)
            {
                qc.Add("titleonly", "0");
                qc.Add("search", query.ImdbID);
            }
            else
            {
                qc.Add("titleonly", "1");
                qc.Add("search", query.GetQueryString());
            }

            var cats = MapTorznabCapsToTrackers(query);

            if (cats.Count > 0)
            {
                foreach (var cat in cats)
                {
                    qc.Add($"c{cat}", "1");
                }
            }

            var searchUrl = SearchUrl + "?" + qc.GetQueryString();
            var results   = await RequestWithCookiesAndRetryAsync(searchUrl);

            if (results.IsRedirect) // re-login
            {
                await ApplyConfiguration(null);

                results = await RequestWithCookiesAndRetryAsync(searchUrl);
            }

            try
            {
                var parser = new HtmlParser();
                var dom    = parser.ParseDocument(results.ContentString);
                var rows   = dom.QuerySelectorAll("#torrents-table > tbody > tr");

                foreach (var row in rows.Skip(1))
                {
                    var qDetails = row.QuerySelector(".br_right > a");
                    var details  = new Uri(SiteLink + qDetails.GetAttribute("href"));
                    var title    = qDetails.QuerySelector("b").TextContent;

                    var qLink = row.QuerySelector("td:nth-child(4) > a");
                    if (qLink == null)
                    {
                        continue; // support/donation banner
                    }
                    var link = new Uri(SiteLink + qLink.GetAttribute("href"));

                    // dateString format "yyyy-MMM-dd hh:mm:ss" => eg "2015-04-25 23:38:12"
                    var dateString  = row.QuerySelector("td:nth-child(6) nobr").TextContent.Trim();
                    var publishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);

                    var size     = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(7)").InnerHtml.Split('<').First().Trim());
                    var files    = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(7) > a").TextContent);
                    var grabs    = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(8)").TextContent);
                    var seeders  = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)").TextContent);
                    var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(10)").TextContent);

                    var category = row.QuerySelector(".br_type > a").GetAttribute("href").Replace("browse.php?cat=", string.Empty);

                    var qImdb = row.QuerySelector("a[href*=\"www.imdb.com/\"]");
                    var imdb  = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null;

                    var release = new ReleaseInfo
                    {
                        Details              = details,
                        Guid                 = details,
                        Title                = title,
                        Link                 = link,
                        PublishDate          = publishDate,
                        Size                 = size,
                        Seeders              = seeders,
                        Peers                = seeders + leechers,
                        Grabs                = grabs,
                        Files                = files,
                        Category             = MapTrackerCatToNewznab(category),
                        Imdb                 = imdb,
                        MinimumRatio         = 1,
                        MinimumSeedTime      = 172800, // 48 hours
                        UploadVolumeFactor   = 1,
                        DownloadVolumeFactor = 1
                    };
                    releases.Add(release);
                }
            }
            catch (Exception e)
            {
                OnParseError(results.ContentString, e);
            }

            return(releases);
        }
Example #5
0
        List <ReleaseInfo> parseTorrents(WebClientStringResult results, String seasonep, TorznabQuery query, int already_founded, int limit)
        {
            var releases = new List <ReleaseInfo>();

            try
            {
                CQ dom = results.Content;

                ReleaseInfo release;
                var         rows = dom[".box_torrent_all"].Find(".box_torrent");

                // Check torrents only till we reach the query Limit
                for (int i = 0; (i < rows.Length && ((already_founded + releases.Count) < limit)); i++)
                {
                    try
                    {
                        CQ  qRow = rows[i].Cq();
                        var key  = dom["link[rel=alternate]"].First().Attr("href").Split('=').Last();

                        release = new ReleaseInfo();
                        var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0);
                        //if (torrentTxt == null) continue;
                        release.Title       = torrentTxt.GetAttribute("title");
                        release.Description = qRow.Find("span").Get(0).GetAttribute("title") + " " + qRow.Find("a.infolink").Text();

                        release.MinimumRatio         = 1;
                        release.MinimumSeedTime      = 172800;
                        release.DownloadVolumeFactor = 0;
                        release.UploadVolumeFactor   = 1;

                        string downloadLink = SiteLink + torrentTxt.GetAttribute("href");
                        string downloadId   = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4);

                        release.Link     = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key);
                        release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId);
                        release.Guid     = new Uri(release.Comments.ToString() + "#comments");;
                        release.Seeders  = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text());
                        release.Peers    = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders;
                        var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href");
                        release.Imdb = ParseUtil.GetLongFromString(imdblink);
                        var banner = qRow.Find("img.infobar_ico").Attr("onmouseover");
                        if (banner != null)
                        {
                            Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled);
                            var   BannerMatch = BannerRegEx.Match(banner);
                            var   bannerurl   = BannerMatch.Groups[1].Value;
                            release.BannerUrl = new Uri(bannerurl);
                        }
                        release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture);
                        string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' ');
                        release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0]));
                        string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href");
                        string cat     = ParseUtil.GetArgumentFromQueryString(catlink, "tipus");
                        release.Category = MapTrackerCatToNewznab(cat);

                        /* if the release name not contains the language we add it because it is know from category */
                        if (cat.Contains("hun") && !release.Title.Contains("hun"))
                        {
                            release.Title += ".hun";
                        }

                        if (seasonep == null)
                        {
                            releases.Add(release);
                        }

                        else
                        {
                            if (query.MatchQueryStringAND(release.Title, null, seasonep))
                            {
                                /* For sonnar if the search querry was english the title must be english also so we need to change the Description and Title */
                                var temp = release.Title;

                                // releasedata everithing after Name.S0Xe0X
                                String releasedata = release.Title.Split(new[] { seasonep }, StringSplitOptions.None)[1].Trim();

                                /* if the release name not contains the language we add it because it is know from category */
                                if (cat.Contains("hun") && !releasedata.Contains("hun"))
                                {
                                    releasedata += ".hun";
                                }

                                // release description contains [imdb: ****] but we only need the data before it for title
                                String[] description = { release.Description, "" };
                                if (release.Description.Contains("[imdb:"))
                                {
                                    description    = release.Description.Split('[');
                                    description[1] = "[" + description[1];
                                }

                                release.Title = (description[0].Trim() + "." + seasonep.Trim() + "." + releasedata.Trim('.')).Replace(' ', '.');

                                // if search is done for S0X than we dont want to put . between S0X and E0X
                                Match match = Regex.Match(releasedata, @"^E\d\d?");
                                if (seasonep.Length == 3 && match.Success)
                                {
                                    release.Title = (description[0].Trim() + "." + seasonep.Trim() + releasedata.Trim('.')).Replace(' ', '.');
                                }

                                // add back imdb points to the description [imdb: 8.7]
                                release.Description = temp + " " + description[1];
                                release.Description = release.Description.Trim();
                                releases.Add(release);
                            }
                        }
                    }
                    catch (FormatException ex)
                    {
                        logger.Error("Problem of parsing Torrent:" + rows[i].InnerHTML);
                        logger.Error("Exception was the following:" + ex);
                    }
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.Content, ex);
            }

            return(releases);
        }
Example #6
0
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases        = new List <ReleaseInfo>();
            var searchUrl       = BrowseUrl;
            var searchTerm      = FixSearchTerm(query);
            var queryCollection = new NameValueCollection
            {
                { "searchstr", StripSearchString(searchTerm) },
                { "order_by", "time" },
                { "order_way", "desc" },
                { "group_results", "1" },
                { "action", "basic" },
                { "searchsubmit", "1" }
            };

            searchUrl += "?" + queryCollection.GetQueryString();
            var results = await RequestWithCookiesAsync(searchUrl);

            try
            {
                const string rowsSelector         = "table.torrent_table > tbody > tr:not(tr.colhead)";
                var          searchResultParser   = new HtmlParser();
                var          searchResultDocument = searchResultParser.ParseDocument(results.ContentString);
                var          rows         = searchResultDocument.QuerySelectorAll(rowsSelector);
                string       groupTitle   = null;
                string       groupYearStr = null;
                Uri          groupPoster  = null;
                string       imdbLink     = null;
                string       tmdbLink     = null;
                foreach (var row in rows)
                {
                    try
                    {
                        // ignore sub groups info row, it's just an row with an info about the next section, something like "Dual Áudio" or "Legendado"
                        if (row.QuerySelector(".edition_info") != null)
                        {
                            continue;
                        }

                        // some torrents has more than one link, and the one with .tooltip is the wrong one in that case,
                        // so let's try to pick up first without the .tooltip class,
                        // if nothing is found, then we try again without that filter
                        var qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]:not(.tooltip)");
                        if (qDetailsLink == null)
                        {
                            qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]");
                            // if still can't find the right link, skip it
                            if (qDetailsLink == null)
                            {
                                logger.Error($"{Id}: Error while parsing row '{row.OuterHtml}': Can't find the right details link");
                                continue;
                            }
                        }
                        var title = StripSearchString(qDetailsLink.TextContent);

                        var    seasonEl = row.QuerySelector("a[href^=\"torrents.php?torrentid=\"]");
                        string seasonEp = null;
                        if (seasonEl != null)
                        {
                            var seasonMatch = _EpisodeRegex.Match(seasonEl.TextContent);
                            seasonEp = seasonMatch.Success ? seasonMatch.Value : null;
                        }
                        seasonEp ??= _EpisodeRegex.Match(qDetailsLink.TextContent).Value;

                        ICollection <int> category = new List <int> {
                            TorznabCatType.Other.ID
                        };
                        string yearStr = null;
                        if (row.ClassList.Contains("group") || row.ClassList.Contains("torrent")) // group/ungrouped headers
                        {
                            var qCatLink = row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]");

                            var torrentInfoEl = row.QuerySelector("div.torrent_info");
                            if (torrentInfoEl != null)
                            {
                                // valid for torrent grouped but that has only 1 episode yet
                                yearStr = torrentInfoEl.GetAttribute("data-year");
                            }
                            yearStr ??= qDetailsLink.NextSibling.TextContent.Trim().TrimStart('[').TrimEnd(']');

                            if (Uri.TryCreate(row.QuerySelector("img[alt=\"Cover\"]")?.GetAttribute("src"),
                                              UriKind.Absolute, out var posterUri))
                            {
                                groupPoster = posterUri;
                            }
                            if (row.ClassList.Contains("group")) // group headers
                            {
                                groupTitle   = title;
                                groupYearStr = yearStr;
                                imdbLink     = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href");
                                tmdbLink     = row.QuerySelector("a[href*=\"themoviedb.org/\"]")?.GetAttribute("href");
                                continue;
                            }
                        }

                        var release = new ReleaseInfo
                        {
                            MinimumRatio    = 1,
                            MinimumSeedTime = 172800
                        };
                        var qDlLink    = row.QuerySelector("a[href^=\"torrents.php?action=download\"]");
                        var qSize      = row.QuerySelector("td:nth-last-child(4)");
                        var qGrabs     = row.QuerySelector("td:nth-last-child(3)");
                        var qSeeders   = row.QuerySelector("td:nth-last-child(2)");
                        var qLeechers  = row.QuerySelector("td:nth-last-child(1)");
                        var qFreeLeech = row.QuerySelector("strong[title=\"Free\"]");
                        if (row.ClassList.Contains("group_torrent")) // torrents belonging to a group
                        {
                            release.Description = qDetailsLink.TextContent;
                            release.Title       = ParseTitle(groupTitle, seasonEp, groupYearStr);
                        }
                        else if (row.ClassList.Contains("torrent")) // standalone/un grouped torrents
                        {
                            release.Description = row.QuerySelector("div.torrent_info").TextContent;
                            release.Title       = ParseTitle(title, seasonEp, yearStr);
                            imdbLink            = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href");
                            tmdbLink            = row.QuerySelector("a[href*=\"themoviedb.org/\"]")?.GetAttribute("href");
                        }
                        release.Poster      = groupPoster;
                        release.Imdb        = ParseUtil.GetLongFromString(imdbLink);
                        release.TMDb        = ParseUtil.GetLongFromString(tmdbLink);
                        release.Category    = category;
                        release.Description = release.Description.Replace(" / Free", "");         // Remove Free Tag
                        release.Description = release.Description.Replace("/ WEB ", "/ WEB-DL "); // Fix web/web-dl
                        release.Description = release.Description.Replace("Full HD", "1080p");
                        // Handles HDR conflict
                        release.Description = release.Description.Replace("/ HD /", "/ 720p /");
                        release.Description = release.Description.Replace("/ HD]", "/ 720p]");
                        release.Description = release.Description.Replace("4K", "2160p");
                        release.Description = release.Description.Replace("SD", "480p");
                        release.Description = release.Description.Replace("Dual Áudio", "Dual");
                        release.Description = release.Description.Replace("Dual Audio", "Dual");

                        // Adjust the description in order to can be read by Radarr and Sonarr
                        var      cleanDescription = release.Description.Trim().TrimStart('[').TrimEnd(']');
                        string[] titleElements;

                        //Formats the title so it can be parsed later
                        var stringSeparators = new[]
                        {
                            " / "
                        };
                        titleElements = cleanDescription.Split(stringSeparators, StringSplitOptions.None);
                        // release.Title += string.Join(" ", titleElements);
                        release.Title = release.Title.Trim();
                        if (titleElements.Length < 6)
                        {
                            // Usually non movies / series could have less than 6 elements, eg: Books.
                            release.Title += " " + string.Join(" ", titleElements);
                        }
                        else
                        {
                            release.Title += " " + titleElements[5] + " " + titleElements[3] + " " + titleElements[1] + " " +
                                             titleElements[2] + " " + titleElements[4] + " " + string.Join(
                                " ", titleElements.Skip(6));
                        }

                        if (Regex.IsMatch(release.Description, "(Dual|[Nn]acional|[Dd]ublado)"))
                        {
                            release.Title += " Brazilian";
                        }

                        // This tracker does not provide an publish date to search terms (only on last 24h page)
                        release.PublishDate = DateTime.Today;

                        // check for previously stripped search terms
                        if (!query.IsImdbQuery && !query.MatchQueryStringAND(release.Title, null, searchTerm))
                        {
                            continue;
                        }
                        var size = qSize.TextContent;
                        release.Size    = ReleaseInfo.GetBytes(size);
                        release.Link    = new Uri(SiteLink + qDlLink.GetAttribute("href"));
                        release.Details = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
                        release.Guid    = release.Link;
                        release.Grabs   = ParseUtil.CoerceLong(qGrabs.TextContent);
                        release.Seeders = ParseUtil.CoerceInt(qSeeders.TextContent);
                        release.Peers   = ParseUtil.CoerceInt(qLeechers.TextContent) + release.Seeders;
                        release.DownloadVolumeFactor = qFreeLeech != null ? 0 : 1;
                        release.UploadVolumeFactor   = 1;
                        releases.Add(release);
                    }
                    catch (Exception ex)
                    {
                        logger.Error($"{Id}: Error while parsing row '{row.OuterHtml}': {ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.ContentString, ex);
            }

            return(releases);
        }
Example #7
0
        private List <ReleaseInfo> ParseTorrents(WebResult results, string episodeString, TorznabQuery query,
                                                 int alreadyFound, int limit, int previouslyParsedOnPage)
        {
            var releases = new List <ReleaseInfo>();

            try
            {
                var parser = new HtmlParser();
                var dom    = parser.ParseDocument(results.ContentString);
                var rows   = dom.QuerySelectorAll(".box_torrent").Skip(previouslyParsedOnPage).Take(limit - alreadyFound);

                var key = ParseUtil.GetArgumentFromQueryString(
                    dom.QuerySelector("link[rel=alternate]").GetAttribute("href"), "key");
                // Check torrents only till we reach the query Limit
                foreach (var row in rows)
                {
                    try
                    {
                        var torrentTxt = row.QuerySelector(".torrent_txt, .torrent_txt2").QuerySelector("a");
                        //if (torrentTxt == null) continue;
                        var infoLink = row.QuerySelector("a.infolink");
                        var imdbId   = ParseUtil.GetLongFromString(infoLink?.GetAttribute("href"));
                        var desc     = row.QuerySelector("span")?.GetAttribute("title") + " " +
                                       infoLink?.TextContent;
                        var torrentLink = SiteLink + torrentTxt.GetAttribute("href");
                        var downloadId  = ParseUtil.GetArgumentFromQueryString(torrentLink, "id");

                        //Build site links
                        var baseLink     = SiteLink + "torrents.php?action=details&id=" + downloadId;
                        var downloadLink = SiteLink + "torrents.php?action=download&id=" + downloadId;
                        var commentsUri  = new Uri(baseLink + "#comments");
                        var guidUri      = new Uri(baseLink);
                        var linkUri      = new Uri(QueryHelpers.AddQueryString(downloadLink, "key", key));

                        var seeders     = ParseUtil.CoerceInt(row.QuerySelector(".box_s2 a").TextContent);
                        var leechers    = ParseUtil.CoerceInt(row.QuerySelector(".box_l2 a").TextContent);
                        var publishDate = DateTime.Parse(
                            row.QuerySelector(".box_feltoltve2").InnerHtml.Replace("<br>", " "),
                            CultureInfo.InvariantCulture);
                        var sizeSplit = row.QuerySelector(".box_meret2").TextContent.Split(' ');
                        var size      = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0]));
                        var catLink   = row.QuerySelector("a:has(img[class='categ_link'])").GetAttribute("href");
                        var cat       = ParseUtil.GetArgumentFromQueryString(catLink, "tipus");
                        var title     = torrentTxt.GetAttribute("title");
                        // if the release name does not contain the language we add from the category
                        if (cat.Contains("hun") && !title.ToLower().Contains("hun"))
                        {
                            title += ".hun";
                        }

                        // Minimum seed time is 48 hours + 24 minutes (.4 hours) per GB of torrent size if downloaded in full.
                        // Or a 1.0 ratio on the torrent
                        var seedTime = TimeSpan.FromHours(48) +
                                       TimeSpan.FromMinutes(24 * ReleaseInfo.GigabytesFromBytes(size).Value);

                        var release = new ReleaseInfo
                        {
                            Title                = title,
                            Description          = desc.Trim(),
                            MinimumRatio         = 1,
                            MinimumSeedTime      = (long)seedTime.TotalSeconds,
                            DownloadVolumeFactor = 0,
                            UploadVolumeFactor   = 1,
                            Link        = linkUri,
                            Comments    = commentsUri,
                            Guid        = guidUri,
                            Seeders     = seeders,
                            Peers       = leechers + seeders,
                            Imdb        = imdbId,
                            PublishDate = publishDate,
                            Size        = size,
                            Category    = MapTrackerCatToNewznab(cat)
                        };
                        var banner = row.QuerySelector("img.infobar_ico")?.GetAttribute("onmouseover");
                        if (banner != null)
                        {
                            // static call to Regex.Match caches the pattern, so we aren't recompiling every loop.
                            var bannerMatch = Regex.Match(banner, @"mutat\('(.*?)', '", RegexOptions.Compiled);
                            release.BannerUrl = new Uri(bannerMatch.Groups[1].Value);
                        }

                        //TODO there is room for improvement here.
                        if (episodeString != null &&
                            query.MatchQueryStringAND(release.Title, queryStringOverride: episodeString) &&
                            !query.IsImdbQuery)
                        {
                            // For Sonarr if the search query was english the title must be english also
                            // The description holds the alternate language name
                            // so we need to swap title and description names
                            var tempTitle = release.Title;

                            // releaseData everything after Name.S0Xe0X
                            var releaseIndex = tempTitle.IndexOf(episodeString, StringComparison.OrdinalIgnoreCase) +
                                               episodeString.Length;
                            var releaseData = tempTitle.Substring(releaseIndex).Trim();

                            // release description contains [imdb: ****] but we only need the data before it for title
                            var description = new[]
                            {
                                release.Description,
                                ""
                            };
                            if (release.Description.Contains("[imdb:"))
                            {
                                description    = release.Description.Split('[');
                                description[1] = "[" + description[1];
                            }

                            var match = Regex.Match(releaseData, @"^E\d\d?");
                            // if search is done for S0X than we don't want to put . between S0X and E0X
                            var episodeSeparator = episodeString.Length == 3 && match.Success ? null : ".";
                            release.Title =
                                (description[0].Trim() + "." + episodeString.Trim() + episodeSeparator +
                                 releaseData.Trim('.')).Replace(' ', '.');

                            // add back imdb points to the description [imdb: 8.7]
                            release.Description = tempTitle + " " + description[1];
                            release.Description = release.Description.Trim();
                        }

                        releases.Add(release);
                    }
                    catch (FormatException ex)
                    {
                        logger.Error("Problem of parsing Torrent:" + row.InnerHtml);
                        logger.Error("Exception was the following:" + ex);
                    }
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.ContentString, ex);
            }

            return(releases);
        }
Example #8
0
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases     = new List <ReleaseInfo>();
            var searchString = query.GetQueryString();
            var searchUrl    = SearchUrl;

            // If query is empty, use the RSS Feed
            if (string.IsNullOrWhiteSpace(searchString))
            {
                var rssPage = await RequestStringWithCookiesAndRetry(RSSUrl + configData.RSSKey.Value);

                var rssDoc = XDocument.Parse(rssPage.Content);

                foreach (var item in rssDoc.Descendants("item"))
                {
                    var title = item.Descendants("title").First().Value;
                    if (title.StartsWith("Support YOUR site!"))
                    {
                        continue;
                    }
                    var description = item.Descendants("description").First().Value;
                    var link        = item.Descendants("link").First().Value;
                    var date        = item.Descendants("pubDate").First().Value;

                    var torrentIdMatch = Regex.Match(link, "(?<=download\\.php/)([a-zA-z0-9]*)");
                    var torrentId      = torrentIdMatch.Success ? torrentIdMatch.Value : string.Empty;
                    if (string.IsNullOrWhiteSpace(torrentId))
                    {
                        throw new Exception("Missing torrent id");
                    }

                    var infoMatch = Regex.Match(description, @"Category:\W(?<cat>.*)\W\n\WSize:\W(?<size>.*)\n\WStatus:\W(?<seeders>.*)\Wseeder(.*)\Wand\W(?<leechers>.*)\Wleecher(.*)\n\WAdded:\W(?<added>.*)\n\WDescription:");
                    if (!infoMatch.Success)
                    {
                        throw new Exception("Unable to find info");
                    }

                    var  imdbMatch = Regex.Match(description, "(?<=www.imdb.com/title/tt)([0-9]*)");
                    long?imdbID    = null;
                    if (imdbMatch.Success)
                    {
                        long l;
                        if (long.TryParse(imdbMatch.Value, out l))
                        {
                            imdbID = l;
                        }
                    }
                    var Now              = DateTime.Now;
                    var PublishDate      = DateTime.ParseExact(date, "ddd, dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture);
                    var PublishDateLocal = PublishDate.ToLocalTime();
                    var diff             = Now - PublishDateLocal;

                    var release = new ReleaseInfo()
                    {
                        Title       = title,
                        Description = title,
                        Guid        = new Uri(string.Format(DetailsURL, torrentId)),
                        Comments    = new Uri(string.Format(DetailsURL, torrentId)),
                        //PublishDate = DateTime.ParseExact(infoMatch.Groups["added"].Value, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31 TODO: correct timezone (always -4)
                        PublishDate = PublishDateLocal,
                        Link        = new Uri(link),
                        Seeders     = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value == "no" ? "0" : infoMatch.Groups["seeders"].Value),
                        Peers       = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value == "no" ? "0" : infoMatch.Groups["leechers"].Value),
                        Size        = ReleaseInfo.GetBytes(infoMatch.Groups["size"].Value),
                        Category    = MapTrackerCatToNewznab(infoMatch.Groups["cat"].Value),
                        Imdb        = imdbID
                    };

                    //  if unknown category, set to "other"
                    if (release.Category.Count() == 0)
                    {
                        release.Category.Add(7000);
                    }

                    release.Peers += release.Seeders;
                    releases.Add(release);
                }
            }
            else
            {
                searchUrl += "?titleonly=1&search=" + WebUtility.UrlEncode(searchString);
                string.Format(SearchUrl, WebUtility.UrlEncode(searchString));

                var cats = MapTorznabCapsToTrackers(query);
                if (cats.Count > 0)
                {
                    foreach (var cat in cats)
                    {
                        searchUrl += "&c" + cat + "=1";
                    }
                }

                var results = await RequestStringWithCookiesAndRetry(searchUrl);

                if (results.IsRedirect)
                {
                    // re-login
                    await ApplyConfiguration(null);

                    results = await RequestStringWithCookiesAndRetry(searchUrl);
                }

                try
                {
                    CQ dom = results.Content;

                    //  table header is the first <tr> in table body, get all rows except this
                    CQ qRows = dom["#torrents-table > tbody > tr:not(:first-child)"];

                    foreach (var row in qRows)
                    {
                        var release = new ReleaseInfo();

                        var qRow = row.Cq();

                        var debug = qRow.Html();

                        release.MinimumRatio    = 1;
                        release.MinimumSeedTime = 172800; // 48 hours

                        CQ qLink = qRow.Find(".br_right > a").First();
                        release.Guid        = new Uri(SiteLink + qLink.Attr("href"));
                        release.Comments    = new Uri(SiteLink + qLink.Attr("href"));
                        release.Title       = qLink.Find("b").Text();
                        release.Description = release.Title;

                        var releaseLinkURI = qRow.Find("td:nth-child(4) > a").Attr("href");
                        release.Link = new Uri(SiteLink + releaseLinkURI);
                        if (releaseLinkURI != null)
                        {
                            var dateString = qRow.Find("td:nth-child(6) nobr")[0].TextContent.Trim();
                            //"2015-04-25 23:38:12"
                            //"yyyy-MMM-dd hh:mm:ss"
                            release.PublishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);

                            var sizeStr = qRow.Children().ElementAt(6).InnerHTML.Trim();
                            sizeStr      = sizeStr.Substring(0, sizeStr.IndexOf('<'));
                            release.Size = ReleaseInfo.GetBytes(sizeStr);

                            release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:nth-child(9)").Text());
                            release.Peers   = release.Seeders + ParseUtil.CoerceInt(qRow.Find("td:nth-child(10)").Text());

                            var grabsStr = qRow.Find("td:nth-child(8)").Text();
                            release.Grabs = ParseUtil.GetLongFromString(grabsStr);

                            var filesStr = qRow.Find("td:nth-child(7) > a").Text();
                            release.Files = ParseUtil.GetLongFromString(filesStr);

                            var category = qRow.Find(".br_type > a").Attr("href").Replace("browse.php?cat=", string.Empty);
                            release.Category = MapTrackerCatToNewznab(category);
                        }
                        releases.Add(release);
                    }
                }
                catch (Exception ex)
                {
                    OnParseError(results.Content, ex);
                }
            }

            return(releases);
        }
Example #9
0
        /// <summary>
        /// Execute our search query
        /// </summary>
        /// <param name="query">Query</param>
        /// <returns>Releases</returns>
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases        = new List <ReleaseInfo>();
            var torrentRowList  = new List <CQ>();
            var exactSearchTerm = query.GetQueryString();
            var searchUrl       = SearchUrl;

            // Check login before performing a query
            await CheckLogin();

            // Check cache first so we don't query the server (if search term used or not in dev mode)
            if (!DevMode && !string.IsNullOrEmpty(exactSearchTerm))
            {
                lock (cache)
                {
                    // Remove old cache items
                    CleanCache();

                    // Search in cache
                    var cachedResult = cache.FirstOrDefault(i => i.Query == exactSearchTerm);
                    if (cachedResult != null)
                    {
                        return(cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray());
                    }
                }
            }

            var SearchTerms = new List <string> {
                exactSearchTerm
            };

            // duplicate search without diacritics
            var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm);

            if (baseSearchTerm != exactSearchTerm)
            {
                SearchTerms.Add(baseSearchTerm);
            }

            foreach (var searchTerm in SearchTerms)
            {
                // Build our query
                var request = BuildQuery(searchTerm, query, searchUrl);

                // Getting results & Store content
                var response = await RequestStringWithCookiesAndRetry(request, ConfigData.CookieHeader.Value);

                _fDom = response.Content;

                try
                {
                    var firstPageRows = FindTorrentRows();

                    // Add them to torrents list
                    torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq()));

                    // If pagination available
                    int nbResults;
                    int pageLinkCount;
                    nbResults     = 1;
                    pageLinkCount = 1;

                    // Check if we have a minimum of one result
                    if (firstPageRows.Length > 1)
                    {
                        // Retrieve total count on our alone page
                        nbResults = firstPageRows.Count();
                    }
                    else
                    {
                        // Check if no result
                        if (torrentRowList.Count == 0)
                        {
                            // No results found
                            Output("\nNo result found for your query, please try another search term or change the theme you're currently using on the site as this is an unsupported solution...\n", "info");

                            // No result found for this query
                            break;
                        }
                    }

                    Output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
                    Output("\nThere are " + (firstPageRows.Length - 2) + " results on the first page !");

                    // Loop on results

                    foreach (var tRow in torrentRowList.Skip(1).Take(torrentRowList.Count - 2))
                    {
                        Output("Torrent #" + (releases.Count + 1));

                        // ID
                        var idOrig = tRow.Find("td:eq(1) > a:eq(0)").Attr("href").Split('=')[1];
                        var id     = idOrig.Substring(0, idOrig.Length - 4);
                        Output("ID: " + id);

                        // Release Name
                        var name = tRow.Find("td:eq(1) > a:eq(0)").Text();

                        // Category
                        string categoryID = tRow.Find("td:eq(0) > a:eq(0)").Attr("href").Split('?').Last();
                        var    newznab    = MapTrackerCatToNewznab(categoryID);
                        Output("Category: " + (newznab.Count > 0 ? newznab.First().ToString() : "unknown category") + " (" + categoryID + ")");

                        // Seeders
                        int seeders = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(9)").Text(), @"\d+").Value);
                        Output("Seeders: " + seeders);

                        // Leechers
                        int leechers = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(10)").Text(), @"\d+").Value);
                        Output("Leechers: " + leechers);

                        // Files
                        int files = 1;
                        files = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(4)").Text(), @"\d+").Value);
                        Output("Files: " + files);

                        // Completed
                        int completed = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(8)").Text(), @"\d+").Value);
                        Output("Completed: " + completed);

                        // Size
                        var humanSize = tRow.Find("td:eq(7)").Text().ToLowerInvariant();
                        var size      = ReleaseInfo.GetBytes(humanSize);
                        Output("Size: " + humanSize + " (" + size + " bytes)");

                        // Publish DateToString
                        var dateTimeOrig = tRow.Find("td:eq(6)").Text();
                        var datestr      = Regex.Replace(dateTimeOrig, @"<[^>]+>|&nbsp;", "").Trim();
                        datestr = Regex.Replace(datestr, "Today", DateTime.Now.ToString("MMM dd yyyy"), RegexOptions.IgnoreCase);
                        datestr = Regex.Replace(datestr, "Yesterday", DateTime.Now.Date.AddDays(-1).ToString("MMM dd yyyy"), RegexOptions.IgnoreCase);
                        DateTime date = DateTimeUtil.FromUnknown(datestr, "DK");
                        Output("Released on: " + date);

                        // Torrent Details URL
                        var detailsLink = new Uri(TorrentDescriptionUrl.Replace("{id}", id.ToString()));
                        Output("Details: " + detailsLink.AbsoluteUri);

                        // Torrent Comments URL
                        var commentsLink = new Uri(TorrentCommentUrl.Replace("{id}", id.ToString()));
                        Output("Comments Link: " + commentsLink.AbsoluteUri);

                        // Torrent Download URL
                        var passkey      = tRow.Find("td:eq(2) > a:eq(0)").Attr("href");
                        var key          = Regex.Match(passkey, "(?<=torrent_pass\\=)([a-zA-z0-9]*)");
                        Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString()));
                        Output("Download Link: " + downloadLink.AbsoluteUri);

                        // Building release infos
                        var release = new ReleaseInfo
                        {
                            Category        = newznab,
                            Title           = name,
                            Seeders         = seeders,
                            Peers           = seeders + leechers,
                            MinimumRatio    = 1,
                            MinimumSeedTime = 172800,
                            PublishDate     = date,
                            Size            = size,
                            Files           = files,
                            Grabs           = completed,
                            Guid            = detailsLink,
                            Comments        = commentsLink,
                            Link            = downloadLink
                        };

                        // IMDB
                        var imdbLink = tRow.Find("a[href*=\"http://imdb.com/title/\"]").First().Attr("href");
                        release.Imdb = ParseUtil.GetLongFromString(imdbLink);

                        if (tRow.Find("img[title=\"Free Torrent\"]").Length >= 1)
                        {
                            release.DownloadVolumeFactor = 0;
                        }
                        else if (tRow.Find("img[title=\"Halfleech\"]").Length >= 1)
                        {
                            release.DownloadVolumeFactor = 0.5;
                        }
                        else if (tRow.Find("img[title=\"90% Freeleech\"]").Length >= 1)
                        {
                            release.DownloadVolumeFactor = 0.1;
                        }
                        else
                        {
                            release.DownloadVolumeFactor = 1;
                        }

                        release.UploadVolumeFactor = 1;

                        releases.Add(release);
                    }
                }
                catch (Exception ex)
                {
                    OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex);
                }
            }
            // Return found releases
            return(releases);
        }
Example #10
0
        private async Task <IEnumerable <ReleaseInfo> > PerformRegularQueryAsync(TorznabQuery query, string hebName = null)
        {
            var releases     = new List <ReleaseInfo>();
            var searchUrl    = SearchUrl;
            var searchString = query.GetQueryString();

            if (query.IsImdbQuery)
            {
                searchString = query.ImdbID;
            }
            if (hebName != null)
            {
                searchString = hebName + " - עונה " + query.Season + " פרק " + query.Episode;
            }
            searchUrl += "?";
            if (!string.IsNullOrWhiteSpace(searchString))
            {
                var strEncoded = WebUtilityHelpers.UrlEncode(searchString, Encoding);
                searchUrl += "&query=" + strEncoded + "&matchquery=any";
            }

            searchUrl = MapTorznabCapsToTrackers(query).Aggregate(searchUrl, (current, cat) => $"{current}&c[]={cat}");
            var data = await RequestWithCookiesAndRetryAsync(searchUrl);

            try
            {
                var parser = new HtmlParser();
                var dom    = parser.ParseDocument(data.ContentString);
                var rows   = dom.QuerySelectorAll("tr.box_torrent");
                foreach (var row in rows)
                {
                    var release       = new ReleaseInfo();
                    var mainTitleLink = row.QuerySelector("div.main_title > a");
                    release.Title = mainTitleLink.GetAttribute("longtitle");
                    if (string.IsNullOrWhiteSpace(release.Title))
                    {
                        release.Title = mainTitleLink.TextContent;
                    }
                    release.MinimumRatio    = 1;
                    release.MinimumSeedTime = 172800; // 48 hours
                    release.Grabs           = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(5)").TextContent.Replace(",", ""));
                    release.Seeders         = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent.Replace(",", ""));
                    release.Peers           = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent.Replace(",", "")) +
                                              release.Seeders;
                    var fullSize = row.QuerySelector("td:nth-child(4)").TextContent;
                    release.Size     = ReleaseInfo.GetBytes(fullSize);
                    release.Comments = new Uri(SiteLink + row.QuerySelector("a.threadlink[href]").GetAttribute("href"));
                    release.Link     = new Uri(SiteLink + row.QuerySelector("a:has(div.dlimg)").GetAttribute("href"));
                    release.Guid     = release.Comments;
                    //some releases have invalid banner URLs, ignore the banners in this case
                    if (Uri.TryCreate(row.QuerySelector("a[imgsrc]").GetAttribute("imgsrc"),
                                      UriKind.Absolute, out var banner))
                    {
                        release.BannerUrl = banner;
                    }
                    var dateStringAll = row.QuerySelector("div.up_info2").ChildNodes.Last().TextContent;
                    var dateParts     = dateStringAll.Split(' ');
                    var dateString    = dateParts[dateParts.Length - 2] + " " + dateParts[dateParts.Length - 1];
                    release.PublishDate = DateTime.ParseExact(dateString, "dd/MM/yy HH:mm", CultureInfo.InvariantCulture);
                    var categoryLink = row.QuerySelector("a[href^=\"/browse.php?cat=\"]").GetAttribute("href");
                    var catid        = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat");
                    release.Category = MapTrackerCatToNewznab(catid);
                    if (row.QuerySelector("a[href^=\"?freeleech=1\"]") != null)
                    {
                        release.DownloadVolumeFactor = 0;
                    }
                    else
                    {
                        release.DownloadVolumeFactor = 1;
                    }
                    release.UploadVolumeFactor = 1;
                    var subTitle = row.QuerySelector("div.sub_title");
                    var imdbLink = subTitle.QuerySelector("span.imdb-inline > a");
                    if (imdbLink != null)
                    {
                        release.Imdb = ParseUtil.GetLongFromString(imdbLink.GetAttribute("href"));
                    }
                    release.Description = subTitle.FirstChild.TextContent;
                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(data.ContentString, ex);
            }

            return(releases);
        }
Example #11
0
        public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases     = new List <ReleaseInfo>();
            var searchString = query.GetQueryString();
            var pairs        = new List <KeyValuePair <string, string> >();

            pairs.Add(new KeyValuePair <string, string>("nyit_sorozat_resz", "true"));
            pairs.Add(new KeyValuePair <string, string>("miben", "name"));
            pairs.Add(new KeyValuePair <string, string>("tipus", "kivalasztottak_kozott"));
            pairs.Add(new KeyValuePair <string, string>("submit.x", "1"));
            pairs.Add(new KeyValuePair <string, string>("submit.y", "1"));
            pairs.Add(new KeyValuePair <string, string>("submit", "Ok"));
            pairs.Add(new KeyValuePair <string, string>("mire", searchString));

            var cats = MapTorznabCapsToTrackers(query);

            if (cats.Count == 0)
            {
                cats = GetAllTrackerCategories();
            }

            foreach (var lcat in LanguageCats)
            {
                if (!configData.Hungarian.Value)
                {
                    cats.Remove(lcat + "_hun");
                }
                if (!configData.English.Value)
                {
                    cats.Remove(lcat);
                }
            }

            foreach (var cat in cats)
            {
                pairs.Add(new KeyValuePair <string, string>("kivalasztott_tipus[]", cat));
            }

            var results = await PostDataWithCookiesAndRetry(SearchUrl, pairs);

            try
            {
                CQ dom = results.Content;

                ReleaseInfo release;
                var         rows = dom[".box_torrent_all"].Find(".box_torrent");

                foreach (var row in rows)
                {
                    CQ qRow = row.Cq();

                    var key = dom ["link[rel=alternate]"].First().Attr("href").Split('=').Last();

                    release = new ReleaseInfo();
                    var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0);
                    //if (torrentTxt == null) continue;
                    release.Title                = torrentTxt.GetAttribute("title");
                    release.Description          = qRow.Find("div.siterank").Text();
                    release.MinimumRatio         = 1;
                    release.MinimumSeedTime      = 172800;
                    release.DownloadVolumeFactor = 0;
                    release.UploadVolumeFactor   = 1;

                    string downloadLink = SiteLink + torrentTxt.GetAttribute("href");
                    string downloadId   = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4);

                    release.Link     = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key);
                    release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId);
                    release.Guid     = new Uri(release.Comments.ToString() + "#comments");;
                    release.Seeders  = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text());
                    release.Peers    = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders;
                    var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href");
                    release.Imdb = ParseUtil.GetLongFromString(imdblink);
                    var banner = qRow.Find("img.infobar_ico").Attr("onmouseover");
                    if (banner != null)
                    {
                        Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled);
                        var   BannerMatch = BannerRegEx.Match(banner);
                        var   bannerurl   = BannerMatch.Groups[1].Value;
                        release.BannerUrl = new Uri(bannerurl);
                    }
                    release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture);
                    string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' ');
                    release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0]));
                    string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href");
                    string cat     = ParseUtil.GetArgumentFromQueryString(catlink, "tipus");
                    release.Category = MapTrackerCatToNewznab(cat);

                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.Content, ex);
            }

            return(releases);
        }
Example #12
0
        protected async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query, String seasonep)
        {
            var releases     = new List <ReleaseInfo>();
            var searchString = query.GetQueryString();
            var pairs        = new List <KeyValuePair <string, string> >();

            if (seasonep != null)
            {
                searchString = query.SanitizedSearchTerm;
            }

            pairs.Add(new KeyValuePair <string, string>("nyit_sorozat_resz", "true"));
            pairs.Add(new KeyValuePair <string, string>("miben", "name"));
            pairs.Add(new KeyValuePair <string, string>("tipus", "kivalasztottak_kozott"));
            pairs.Add(new KeyValuePair <string, string>("submit.x", "1"));
            pairs.Add(new KeyValuePair <string, string>("submit.y", "1"));
            pairs.Add(new KeyValuePair <string, string>("submit", "Ok"));
            pairs.Add(new KeyValuePair <string, string>("mire", searchString));

            var cats = MapTorznabCapsToTrackers(query);

            if (cats.Count == 0)
            {
                cats = GetAllTrackerCategories();
            }

            foreach (var lcat in LanguageCats)
            {
                if (!configData.Hungarian.Value)
                {
                    cats.Remove(lcat + "_hun");
                }
                if (!configData.English.Value)
                {
                    cats.Remove(lcat);
                }
            }

            foreach (var cat in cats)
            {
                pairs.Add(new KeyValuePair <string, string>("kivalasztott_tipus[]", cat));
            }

            var results = await PostDataWithCookiesAndRetry(SearchUrl, pairs);

            try
            {
                CQ dom = results.Content;

                ReleaseInfo release;
                var         rows = dom[".box_torrent_all"].Find(".box_torrent");

                foreach (var row in rows)
                {
                    CQ qRow = row.Cq();

                    var key = dom["link[rel=alternate]"].First().Attr("href").Split('=').Last();

                    release = new ReleaseInfo();
                    var torrentTxt = qRow.Find(".torrent_txt, .torrent_txt2").Find("a").Get(0);
                    //if (torrentTxt == null) continue;
                    release.Title                = torrentTxt.GetAttribute("title");
                    release.Description          = qRow.Find("div.siterank").Text();
                    release.MinimumRatio         = 1;
                    release.MinimumSeedTime      = 172800;
                    release.DownloadVolumeFactor = 0;
                    release.UploadVolumeFactor   = 1;

                    string downloadLink = SiteLink + torrentTxt.GetAttribute("href");
                    string downloadId   = downloadLink.Substring(downloadLink.IndexOf("&id=") + 4);

                    release.Link     = new Uri(SiteLink.ToString() + "torrents.php?action=download&id=" + downloadId + "&key=" + key);
                    release.Comments = new Uri(SiteLink.ToString() + "torrents.php?action=details&id=" + downloadId);
                    release.Guid     = new Uri(release.Comments.ToString() + "#comments");;
                    release.Seeders  = ParseUtil.CoerceInt(qRow.Find(".box_s2").Find("a").First().Text());
                    release.Peers    = ParseUtil.CoerceInt(qRow.Find(".box_l2").Find("a").First().Text()) + release.Seeders;
                    var imdblink = qRow.Find("a[href*=\".imdb.com/title\"]").Attr("href");
                    release.Imdb = ParseUtil.GetLongFromString(imdblink);
                    var banner = qRow.Find("img.infobar_ico").Attr("onmouseover");
                    if (banner != null)
                    {
                        Regex BannerRegEx = new Regex(@"mutat\('(.*?)', '", RegexOptions.Compiled);
                        var   BannerMatch = BannerRegEx.Match(banner);
                        var   bannerurl   = BannerMatch.Groups[1].Value;
                        release.BannerUrl = new Uri(bannerurl);
                    }
                    release.PublishDate = DateTime.Parse(qRow.Find(".box_feltoltve2").Get(0).InnerHTML.Replace("<br />", " "), CultureInfo.InvariantCulture);
                    string[] sizeSplit = qRow.Find(".box_meret2").Get(0).InnerText.Split(' ');
                    release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0]));
                    string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href");
                    string cat     = ParseUtil.GetArgumentFromQueryString(catlink, "tipus");
                    release.Category = MapTrackerCatToNewznab(cat);
                    if (seasonep == null)
                    {
                        releases.Add(release);
                    }

                    else
                    {
                        if (query.MatchQueryStringAND(release.Title, null, seasonep))
                        {
                            /* For sonnar if the search querry was english the title must be english also so we need to change the Description and Title */
                            var temp = release.Title;

                            // releasedata everithing after Name.S0Xe0X
                            String releasedata = release.Title.Split(new[] { seasonep }, StringSplitOptions.None)[1].Trim();

                            /* if the release name not contains the language we add it because it is know from category */
                            if (cat.Contains("hun") && !releasedata.Contains("hun"))
                            {
                                releasedata += ".hun";
                            }

                            // release description contains [imdb: ****] but we only need the data before it for title
                            String[] description = { release.Description, "" };
                            if (release.Description.Contains("[imdb:"))
                            {
                                description    = release.Description.Split('[');
                                description[1] = "[" + description[1];
                            }

                            release.Title = (description[0].Trim() + "." + seasonep.Trim() + "." + releasedata.Trim('.')).Replace(' ', '.');

                            // if search is done for S0X than we dont want to put . between S0X and E0X
                            Match match = Regex.Match(releasedata, @"^E\d\d?");
                            if (seasonep.Length == 3 && match.Success)
                            {
                                release.Title = (description[0].Trim() + "." + seasonep.Trim() + releasedata.Trim('.')).Replace(' ', '.');
                            }

                            // add back imdb points to the description [imdb: 8.7]
                            release.Description = temp + " " + description[1];
                            release.Description = release.Description.Trim();
                            releases.Add(release);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.Content, ex);
            }

            return(releases);
        }
Example #13
0
        public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
        {
            var torrentInfos = new List <TorrentInfo>();

            var parser = new HtmlParser();
            var dom    = parser.ParseDocument(indexerResponse.Content);
            var rows   = dom.QuerySelectorAll("#torrents-table > tbody > tr");

            foreach (var row in rows.Skip(1))
            {
                var qDetails = row.QuerySelector(".br_right > a");
                var details  = _settings.BaseUrl + qDetails.GetAttribute("href");
                var title    = qDetails.QuerySelector("b").TextContent;

                // Remove auto-generated [REQ] tag from fulfilled requests
                if (title.StartsWith("[REQ] "))
                {
                    title = title.Substring(6);
                }

                var qLink = row.QuerySelector("td:nth-child(4) > a");
                if (qLink == null)
                {
                    continue; // support/donation banner
                }

                var link = _settings.BaseUrl + qLink.GetAttribute("href");

                // dateString format "yyyy-MMM-dd hh:mm:ss" => eg "2015-04-25 23:38:12"
                var dateString  = row.QuerySelector("td:nth-child(6) nobr").TextContent.Trim();
                var publishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);

                var size     = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(7)").InnerHtml.Split('<').First().Trim());
                var files    = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(7) > a").TextContent);
                var grabs    = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(8)").TextContent);
                var seeders  = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)").TextContent);
                var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(10)").TextContent);

                var category = row.QuerySelector(".br_type > a").GetAttribute("href").Replace("browse.php?cat=", string.Empty);

                var qImdb = row.QuerySelector("a[href*=\"www.imdb.com/\"]");
                var imdb  = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null;

                var release = new TorrentInfo
                {
                    InfoUrl              = details,
                    Guid                 = details,
                    Title                = title,
                    DownloadUrl          = link,
                    PublishDate          = publishDate,
                    Size                 = size,
                    Seeders              = seeders,
                    Peers                = seeders + leechers,
                    Grabs                = (int)grabs,
                    Files                = (int)files,
                    Categories           = _categories.MapTrackerCatToNewznab(category),
                    ImdbId               = imdb ?? 0,
                    MinimumRatio         = 1,
                    MinimumSeedTime      = 172800, // 48 hours
                    UploadVolumeFactor   = 1,
                    DownloadVolumeFactor = 1
                };

                torrentInfos.Add(release);
            }

            return(torrentInfos.ToArray());
        }
Example #14
0
        private string ParseFields(string value, string fieldName, TorrentInfo release, List <string> fieldModifiers, Uri searchUrlUri)
        {
            switch (fieldName)
            {
            case "download":
                if (string.IsNullOrEmpty(value))
                {
                    value = null;
                    release.DownloadUrl = null;
                    break;
                }

                if (value.StartsWith("magnet:"))
                {
                    release.MagnetUrl = value;
                    value             = release.MagnetUrl;
                }
                else
                {
                    release.DownloadUrl = ResolvePath(value, searchUrlUri).AbsoluteUri;
                    value = release.DownloadUrl;
                }

                release.Guid = value;

                break;

            case "magnet":
                var magnetUri = value;
                release.MagnetUrl = magnetUri;
                value             = magnetUri.ToString();
                break;

            case "infohash":
                release.InfoHash = value;
                break;

            case "details":
                var url = ResolvePath(value, searchUrlUri)?.AbsoluteUri;
                release.InfoUrl = url;
                value           = url.ToString();
                break;

            case "comments":
                var commentsUrl = ResolvePath(value, searchUrlUri);
                if (release.CommentUrl == null)
                {
                    release.CommentUrl = commentsUrl.AbsoluteUri;
                }

                value = commentsUrl.ToString();
                break;

            case "title":
                if (fieldModifiers.Contains("append"))
                {
                    release.Title += value;
                }
                else
                {
                    release.Title = value;
                }

                value = release.Title;
                break;

            case "description":
                if (fieldModifiers.Contains("append"))
                {
                    release.Description += value;
                }
                else
                {
                    release.Description = value;
                }

                value = release.Description;
                break;

            case "category":
                var cats = MapTrackerCatToNewznab(value);
                if (cats.Any())
                {
                    if (release.Categories == null || fieldModifiers.Contains("noappend"))
                    {
                        release.Categories = cats;
                    }
                    else
                    {
                        release.Categories = release.Categories.Union(cats).ToList();
                    }
                }

                value = release.Categories.ToString();
                break;

            case "categorydesc":
                var catsDesc = MapTrackerCatDescToNewznab(value);
                if (catsDesc.Any())
                {
                    if (release.Categories == null || fieldModifiers.Contains("noappend"))
                    {
                        release.Categories = catsDesc;
                    }
                    else
                    {
                        release.Categories = release.Categories.Union(catsDesc).ToList();
                    }
                }

                value = release.Categories.ToString();
                break;

            case "size":
                release.Size = ParseUtil.GetBytes(value);
                value        = release.Size.ToString();
                break;

            case "leechers":
                var leechers = ParseUtil.CoerceLong(value);
                leechers = leechers < 5000000L ? leechers : 0;     // to fix #6558
                if (release.Peers == null)
                {
                    release.Peers = (int)leechers;
                }
                else
                {
                    release.Peers += (int)leechers;
                }

                value = leechers.ToString();
                break;

            case "seeders":
                release.Seeders = ParseUtil.CoerceInt(value);
                release.Seeders = release.Seeders < 5000000L ? release.Seeders : 0;     // to fix #6558
                if (release.Peers == null)
                {
                    release.Peers = release.Seeders;
                }
                else
                {
                    release.Peers += release.Seeders;
                }

                value = release.Seeders.ToString();
                break;

            case "date":
                release.PublishDate = DateTimeUtil.FromUnknown(value);
                value = release.PublishDate.ToString(DateTimeUtil.Rfc1123ZPattern);
                break;

            case "files":
                release.Files = ParseUtil.CoerceInt(value);
                value         = release.Files.ToString();
                break;

            case "grabs":
                release.Grabs = ParseUtil.CoerceInt(value);
                value         = release.Grabs.ToString();
                break;

            case "downloadvolumefactor":
                release.DownloadVolumeFactor = ParseUtil.CoerceDouble(value);
                value = release.DownloadVolumeFactor.ToString();
                break;

            case "uploadvolumefactor":
                release.UploadVolumeFactor = ParseUtil.CoerceDouble(value);
                value = release.UploadVolumeFactor.ToString();
                break;

            case "minimumratio":
                release.MinimumRatio = ParseUtil.CoerceDouble(value);
                value = release.MinimumRatio.ToString();
                break;

            case "minimumseedtime":
                release.MinimumSeedTime = ParseUtil.CoerceLong(value);
                value = release.MinimumSeedTime.ToString();
                break;

            case "imdb":
            case "imdbid":
                release.ImdbId = (int)ParseUtil.GetLongFromString(value);
                value          = release.ImdbId.ToString();
                break;

            case "tmdbid":
                var tmdbIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
                var tmdbIDMatch = tmdbIDRegEx.Match(value);
                var tmdbID      = tmdbIDMatch.Groups[1].Value;
                release.TmdbId = (int)ParseUtil.CoerceLong(tmdbID);
                value          = release.TmdbId.ToString();
                break;

            case "rageid":
                var rageIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
                var rageIDMatch = rageIDRegEx.Match(value);
                var rageID      = rageIDMatch.Groups[1].Value;
                release.TvRageId = (int)ParseUtil.CoerceLong(rageID);
                value            = release.TvRageId.ToString();
                break;

            case "traktid":
                var traktIDRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
                var traktIDMatch = traktIDRegEx.Match(value);
                var traktID      = traktIDMatch.Groups[1].Value;
                release.TraktId = (int)ParseUtil.CoerceLong(traktID);
                value           = release.TraktId.ToString();
                break;

            case "tvdbid":
                var tvdbIdRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
                var tvdbIdMatch = tvdbIdRegEx.Match(value);
                var tvdbId      = tvdbIdMatch.Groups[1].Value;
                release.TvdbId = (int)ParseUtil.CoerceLong(tvdbId);
                value          = release.TvdbId.ToString();
                break;

            case "poster":
                if (!string.IsNullOrWhiteSpace(value))
                {
                    var poster = ResolvePath(value, searchUrlUri);
                    release.PosterUrl = poster.AbsoluteUri;
                }

                value = release.PosterUrl;
                break;

            case "genre":
                release.Genres = release.Genres.Union(value.Split(',')).ToList();
                value          = release.Genres.ToString();
                break;

            case "year":
                release.Year = ParseUtil.CoerceInt(value);
                value        = release.Year.ToString();
                break;

            case "author":
                release.Author = value;
                break;

            case "booktitle":
                release.BookTitle = value;
                break;

            case "artist":
                release.Artist = value;
                break;

            case "album":
                release.Album = value;
                break;

            default:
                break;
            }

            return(value);
        }
Example #15
0
        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);
        }
Example #16
0
        /// <summary>
        /// Execute our search query
        /// </summary>
        /// <param name="query">Query</param>
        /// <returns>Releases</returns>
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases        = new List <ReleaseInfo>();
            var exactSearchTerm = query.GetQueryString();
            var searchUrl       = SearchUrl;

            // Check login before performing a query
            await CheckLoginAsync();

            var SearchTerms = new List <string> {
                exactSearchTerm
            };

            // duplicate search without diacritics
            var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm);

            if (baseSearchTerm != exactSearchTerm)
            {
                SearchTerms.Add(baseSearchTerm);
            }

            foreach (var searchTerm in SearchTerms)
            {
                // Build our query
                var request = BuildQuery(searchTerm, query, searchUrl);

                // Getting results & Store content
                var response = await RequestWithCookiesAndRetryAsync(request, ConfigData.CookieHeader.Value);

                var parser = new HtmlParser();
                var dom    = parser.ParseDocument(response.ContentString);

                try
                {
                    var firstPageRows = FindTorrentRows(dom);

                    // If pagination available
                    int nbResults;
                    var pageLinkCount = 1;

                    // Check if we have a minimum of one result
                    if (firstPageRows?.Length >= 1)
                    {
                        // Retrieve total count on our alone page
                        nbResults = firstPageRows.Count();
                    }
                    else
                    {
                        // No result found for this query
                        logger.Info("\nNorBits - No result found for your query, please try another search term ...\n", "info");
                        break;
                    }

                    logger.Info("\nNorBits - Found " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
                    logger.Info("\nNorBits - There are " + firstPageRows.Length + " results on the first page !");

                    // Loop on results

                    foreach (var row in firstPageRows)
                    {
                        var id           = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("href").Split('=').Last();        // ID
                        var name         = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("title");                         // Release Name
                        var categoryName = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("title");                   // Category
                        var mainCat      = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("href").Split('?').Last();
                        var qSubCat2     = row.QuerySelector("td:nth-of-type(1) > div > a[href^=\"/browse.php?sub2_cat[]=\"]");
                        var cat          = mainCat;
                        if (qSubCat2 != null)
                        {
                            cat += '&' + qSubCat2.GetAttribute("href").Split('?').Last();
                        }
                        var seeders    = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent);                                   // Seeders
                        var leechers   = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent);                                  // Leechers
                        var regexObj   = new Regex(@"[^\d]");                                                                                       // Completed
                        var completed2 = row.QuerySelector("td:nth-of-type(8)").TextContent;
                        var completed  = ParseUtil.CoerceLong(regexObj.Replace(completed2, ""));
                        var qFiles     = row.QuerySelector("td:nth-of-type(3) > a");                                                                // Files
                        var files      = qFiles != null?ParseUtil.CoerceInt(Regex.Match(qFiles.TextContent, @"\d+").Value) : 1;

                        var humanSize    = row.QuerySelector("td:nth-of-type(7)").TextContent.ToLowerInvariant();                                   // Size
                        var size         = ReleaseInfo.GetBytes(humanSize);                                                                         // Date
                        var dateTimeOrig = row.QuerySelector("td:nth-of-type(5)").TextContent;
                        var dateTime     = Regex.Replace(dateTimeOrig, @"<[^>]+>|&nbsp;", "").Trim();
                        var date         = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
                        var details      = new Uri(TorrentDetailsUrl.Replace("{id}", id.ToString()));                                               // Description Link
                        var passkey      = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(2)").GetAttribute("href");                          // Download Link
                        var key          = Regex.Match(passkey, "(?<=passkey\\=)([a-zA-z0-9]*)");
                        var downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString()));

                        // Building release infos
                        var release = new ReleaseInfo
                        {
                            Category        = MapTrackerCatToNewznab(cat),
                            Title           = name,
                            Seeders         = seeders,
                            Peers           = seeders + leechers,
                            PublishDate     = date,
                            Size            = size,
                            Files           = files,
                            Grabs           = completed,
                            Guid            = details,
                            Details         = details,
                            Link            = downloadLink,
                            MinimumRatio    = 1,
                            MinimumSeedTime = 172800 // 48 hours
                        };

                        var genres = row.QuerySelector("span.genres")?.TextContent;
                        if (!string.IsNullOrEmpty(genres))
                        {
                            release.Description = genres;
                        }

                        // IMDB
                        var imdbLink = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href");
                        release.Imdb = ParseUtil.GetLongFromString(imdbLink);

                        if (row.QuerySelector("img[title=\"100% freeleech\"]") != null)
                        {
                            release.DownloadVolumeFactor = 0;
                        }
                        else if (row.QuerySelector("img[title=\"Halfleech\"]") != null)
                        {
                            release.DownloadVolumeFactor = 0.5;
                        }
                        else if (row.QuerySelector("img[title=\"90% Freeleech\"]") != null)
                        {
                            release.DownloadVolumeFactor = 0.1;
                        }
                        else
                        {
                            release.DownloadVolumeFactor = 1;
                        }

                        release.UploadVolumeFactor = 1;

                        releases.Add(release);
                    }
                }
                catch (Exception ex)
                {
                    OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex);
                }
            }
            // Return found releases
            return(releases);
        }
Example #17
0
        /// <summary>
        /// Execute our search query
        /// </summary>
        /// <param name="query">Query</param>
        /// <returns>Releases</returns>
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases        = new List <ReleaseInfo>();
            var torrentRowList  = new List <CQ>();
            var exactSearchTerm = query.GetQueryString();
            var searchUrl       = SearchUrl;

            // Check login before performing a query
            await CheckLogin();

            // Check cache first so we don't query the server (if search term used or not in dev mode)
            if (!DevMode && !string.IsNullOrEmpty(exactSearchTerm))
            {
                lock (cache)
                {
                    // Remove old cache items
                    CleanCache();

                    // Search in cache
                    var cachedResult = cache.FirstOrDefault(i => i.Query == exactSearchTerm);
                    if (cachedResult != null)
                    {
                        return(cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray());
                    }
                }
            }

            var SearchTerms = new List <string> {
                exactSearchTerm
            };

            // duplicate search without diacritics
            var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm);

            if (baseSearchTerm != exactSearchTerm)
            {
                SearchTerms.Add(baseSearchTerm);
            }

            foreach (var searchTerm in SearchTerms)
            {
                // Build our query
                var request = BuildQuery(searchTerm, query, searchUrl);

                // Getting results & Store content
                var response = await RequestStringWithCookiesAndRetry(request, ConfigData.CookieHeader.Value);

                _fDom = response.Content;

                try
                {
                    var firstPageRows = FindTorrentRows();

                    // Add them to torrents list
                    torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq()));

                    // If pagination available
                    int nbResults;
                    int pageLinkCount;
                    nbResults     = 1;
                    pageLinkCount = 1;

                    // Check if we have a minimum of one result
                    if (firstPageRows.Length > 1)
                    {
                        // Retrieve total count on our alone page
                        nbResults = firstPageRows.Count();
                    }
                    else
                    {
                        // Check if no result
                        if (torrentRowList.Count == 0)
                        {
                            // No results found
                            Output("\nNo result found for your query, please try another search term ...\n", "info");

                            // No result found for this query
                            break;
                        }
                    }

                    Output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
                    Output("\nThere are " + firstPageRows.Length + " results on the first page !");

                    // Loop on results

                    foreach (var tRow in torrentRowList)
                    {
                        Output("Torrent #" + (releases.Count + 1));

                        // ID
                        var id = tRow.Find("td:eq(1) > a:eq(0)").Attr("href").Split('=').Last();
                        Output("ID: " + id);

                        // Release Name
                        var name = tRow.Find("td:eq(1) > a:eq(0)").Attr("title");

                        // Category
                        var categoryId   = tRow.Find("td:eq(0) > div > a:eq(0)").Attr("href").Split('?').Last();
                        var categoryName = tRow.Find("td:eq(0) > div > a:eq(0)").Attr("title");

                        var MainCat = tRow.Find("td:eq(0) > div > a:eq(0)").Attr("href").Split('?').Last();
                        var SubCat1 = "none";
                        var SubCat2 = "none";

                        var testcat = MainCat;

                        if (tRow.Find("td:eq(0) > div > a:eq(1)").Length == 1)
                        {
                            SubCat1 = tRow.Find("td:eq(0) > div > a:eq(1)").Attr("href").Split('?').Last();
                        }
                        if (tRow.Find("td:eq(0) > div > a[href^=\"/browse.php?sub2_cat[]=\"]").Length == 1)
                        {
                            SubCat2 = tRow.Find("td:eq(0) > div > a[href^=\"/browse.php?sub2_cat[]=\"]").Attr("href").Split('?').Last();
                            testcat = MainCat + '&' + SubCat2;
                        }

                        Output("Category: " + testcat + " - " + categoryName);

                        // Seeders
                        var seeders = ParseUtil.CoerceInt(tRow.Find("td:eq(9)").Text());
                        Output("Seeders: " + seeders);

                        // Leechers
                        var leechers = ParseUtil.CoerceInt(tRow.Find("td:eq(10)").Text());
                        Output("Leechers: " + leechers);

                        // Completed
                        Regex regexObj   = new Regex(@"[^\d]");
                        var   completed2 = tRow.Find("td:eq(7)").Text();
                        var   completed  = ParseUtil.CoerceLong(regexObj.Replace(completed2, ""));
                        Output("Completed: " + completed);

                        // Files
                        var files = 1;
                        if (tRow.Find("td:eq(2) > a").Length == 1)
                        {
                            files = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(2) > a").Text(), @"\d+").Value);
                        }
                        Output("Files: " + files);

                        // Health
                        var percent = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(8)").Text(), @"\d+").Value.Trim());
                        Output("Health: " + percent + "%");

                        // Size
                        var humanSize = tRow.Find("td:eq(6)").Text().ToLowerInvariant();
                        var size      = ReleaseInfo.GetBytes(humanSize);
                        Output("Size: " + humanSize + " (" + size + " bytes)");

                        // --> Date
                        var dateTimeOrig = tRow.Find("td:eq(4)").Text();
                        var dateTime     = Regex.Replace(dateTimeOrig, @"<[^>]+>|&nbsp;", "").Trim();
                        var date         = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
                        Output("Released on: " + date);

                        // Torrent Details URL
                        var detailsLink = new Uri(TorrentDescriptionUrl.Replace("{id}", id.ToString()));
                        Output("Details: " + detailsLink.AbsoluteUri);

                        // Torrent Comments URL
                        var commentsLink = new Uri(TorrentCommentUrl.Replace("{id}", id.ToString()));
                        Output("Comments Link: " + commentsLink.AbsoluteUri);

                        // Torrent Download URL
                        var passkey      = tRow.Find("td:eq(1) > a:eq(1)").Attr("href");
                        var key          = Regex.Match(passkey, "(?<=passkey\\=)([a-zA-z0-9]*)");
                        Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString()));
                        Output("Download Link: " + downloadLink.AbsoluteUri);

                        // Building release infos
                        var release = new ReleaseInfo
                        {
                            Category        = MapTrackerCatToNewznab(testcat.ToString()),
                            Title           = name,
                            Seeders         = seeders,
                            Peers           = seeders + leechers,
                            MinimumRatio    = 1,
                            MinimumSeedTime = 172800,
                            PublishDate     = date,
                            Size            = size,
                            Files           = files,
                            Grabs           = completed,
                            Guid            = detailsLink,
                            Comments        = commentsLink,
                            Link            = downloadLink
                        };

                        var genres = tRow.Find("span.genres").Text();
                        if (!string.IsNullOrEmpty(genres))
                        {
                            release.Description = genres;
                        }

                        // IMDB
                        var imdbLink = tRow.Find("a[href*=\"http://imdb.com/title/\"]").First().Attr("href");
                        release.Imdb = ParseUtil.GetLongFromString(imdbLink);

                        if (tRow.Find("img[title=\"100% freeleech\"]").Length >= 1)
                        {
                            release.DownloadVolumeFactor = 0;
                        }
                        else if (tRow.Find("img[title=\"Halfleech\"]").Length >= 1)
                        {
                            release.DownloadVolumeFactor = 0.5;
                        }
                        else if (tRow.Find("img[title=\"90% Freeleech\"]").Length >= 1)
                        {
                            release.DownloadVolumeFactor = 0.1;
                        }
                        else
                        {
                            release.DownloadVolumeFactor = 1;
                        }

                        release.UploadVolumeFactor = 1;

                        releases.Add(release);
                    }
                }
                catch (Exception ex)
                {
                    OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex);
                }
            }
            // Return found releases
            return(releases);
        }
Example #18
0
        protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query)
        {
            var releases = new List <ReleaseInfo>();

            var qc = new NameValueCollection
            {
                { "order_by", "time" },
                { "order_way", "desc" },
                { "action", "basic" },
                { "searchsubmit", "1" }
            };

            if (query.IsImdbQuery)
            {
                qc.Add("imdb", query.ImdbID);
            }
            else if (query.IsTmdbQuery)
            {
                qc.Add("tmdb", query.TmdbID.ToString());
            }
            else
            {
                qc.Add("searchstr", query.GetQueryString());
            }

            var catList = MapTorznabCapsToTrackers(query);

            foreach (var cat in catList)
            {
                qc.Add($"filter_cat[{cat}]", "1");
            }

            // remove . as not used in titles
            var searchUrl = BrowseUrl + "?" + qc.GetQueryString().Replace(".", " ");
            var results   = await RequestWithCookiesAsync(searchUrl);

            try
            {
                var parser = new HtmlParser();
                var doc    = parser.ParseDocument(results.ContentString);
                var rows   = doc.QuerySelectorAll("table.torrent_table > tbody > tr.torrent");
                foreach (var row in rows)
                {
                    var qDetailsLink = row.QuerySelector("a.torrent_name");
                    var year         = qDetailsLink.NextSibling.TextContent.Replace("[", "").Replace("]", "").Trim();
                    var tags         = row.QuerySelector("div.torrent_info").FirstChild.TextContent.Replace(" / ", " ").Trim();
                    var title        = $"{qDetailsLink.TextContent} {year} {tags}";
                    var description  = row.QuerySelector("div.tags").TextContent.Trim();
                    var details      = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
                    var torrentId    = qDetailsLink.GetAttribute("href").Split('=').Last();
                    var link         = new Uri(SiteLink + "torrents.php?action=download&id=" + torrentId);
                    var posterStr    = qDetailsLink.GetAttribute("data-cover");
                    var poster       = !string.IsNullOrWhiteSpace(posterStr) ? new Uri(qDetailsLink.GetAttribute("data-cover")) : null;

                    var files       = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(3)").TextContent);
                    var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent);
                    var size        = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent);
                    var grabs       = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(6)").TextContent);
                    var seeders     = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent);
                    var leechers    = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);

                    var dlVolumeFactor = row.QuerySelector("strong.tl_free") != null ? 0 : 1;

                    var cat      = row.QuerySelector("td.cats_col > div").GetAttribute("class").Replace("tooltip cats_", "");
                    var category = new List <int> {
                        cat switch
                        {
                            "featurefilm" => TorznabCatType.Movies.ID,
                            "shortfilm" => TorznabCatType.Movies.ID,
                            "miniseries" => TorznabCatType.TV.ID,
                            "other" => TorznabCatType.Other.ID,
                            _ => throw new Exception($"Unknown category: {cat}")
                        }
                    };

                    var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]");
                    var imdb  = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null;

                    qImdb = row.QuerySelector("a[href^=\"https://www.themoviedb.org\"]");
                    var tmdb = qImdb != null?ParseUtil.GetLongFromString(qImdb.GetAttribute("href").Split('/').Last()) : null;

                    var release = new ReleaseInfo
                    {
                        MinimumRatio         = 1,
                        MinimumSeedTime      = 259200,
                        Description          = description,
                        Title                = title,
                        PublishDate          = publishDate,
                        Category             = category,
                        Link                 = link,
                        Details              = details,
                        Guid                 = link,
                        Imdb                 = imdb,
                        TMDb                 = tmdb,
                        Poster               = poster,
                        Seeders              = seeders,
                        Peers                = leechers + seeders,
                        Size                 = size,
                        Grabs                = grabs,
                        Files                = files,
                        DownloadVolumeFactor = dlVolumeFactor,
                        UploadVolumeFactor   = 1
                    };
                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(results.ContentString, ex);
            }

            return(releases);
        }