public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) { var pageableRequests = new IndexerPageableRequestChain(); var query = new TorrentQuery(); var tvdbId = searchCriteria.TvdbId.GetValueOrDefault(0); var imdbId = ParseUtil.GetImdbID(searchCriteria.ImdbId).GetValueOrDefault(0); if (searchCriteria.Categories?.Length > 0) { query.Category = Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Select(int.Parse).ToArray(); } if (tvdbId == 0 && imdbId == 0 && searchCriteria.SearchTerm.IsNotNullOrWhiteSpace()) { query.Search = searchCriteria.SanitizedTvSearchString; } if (tvdbId != 0) { query.TvdbInfo = query.TvdbInfo ?? new TvdbInfo(); query.TvdbInfo.Id = tvdbId; query.TvdbInfo.Season = searchCriteria.Season; query.TvdbInfo.Episode = searchCriteria.Episode; } if (imdbId != 0) { query.ImdbInfo = query.ImdbInfo ?? new ImdbInfo(); query.ImdbInfo.Id = imdbId; } pageableRequests.Add(GetRequest(query)); return(pageableRequests); }
private ReleaseInfo CreateReleaseInfo(QueryResponseItem item) { var magnetUri = new Uri(_MagnetUri.Replace(KeyInfoHash, item.InfoHash)); return(new ReleaseInfo { Title = item.Name, Category = MapTrackerCatToNewznab(item.Category.ToString()), Comments = item.Id == 0 ? null : new Uri($"{SiteLink}description.php?id={item.Id}"), MagnetUri = magnetUri, InfoHash = item.InfoHash, PublishDate = DateTimeUtil.UnixTimestampToDateTime(item.Added), Guid = magnetUri, Seeders = item.Seeders, Peers = item.Seeders + item.Leechers, Size = item.Size, Files = item.NumFiles, DownloadVolumeFactor = 0, UploadVolumeFactor = 1, Imdb = string.IsNullOrEmpty(item.Imdb) ? null : ParseUtil.GetImdbID(item.Imdb) }); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <TorrentInfo>(); var indexerHttpResponse = indexerResponse.HttpResponse; if (indexerHttpResponse.StatusCode != HttpStatusCode.OK) { throw new IndexerException(indexerResponse, $"Unexpected response status {indexerHttpResponse.StatusCode} code from API request"); } if (!indexerHttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) { throw new IndexerException(indexerResponse, $"Unexpected response header {indexerHttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}"); } if (indexerResponse.Content.ContainsIgnoreCase("Invalid API Key")) { throw new IndexerAuthException("API Key invalid or not authorized"); } var jsonResponse = new HttpResponse <BeyondHDResponse>(indexerHttpResponse); foreach (var row in jsonResponse.Resource.Results) { var details = row.InfoUrl; var link = row.DownloadLink; // BHD can return crazy values for tmdb var tmdbId = row.TmdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.TryCoerceInt(row.TmdbId.Split("/")[1], out var tmdbResult) ? tmdbResult : 0; var imdbId = ParseUtil.GetImdbID(row.ImdbId).GetValueOrDefault(); var release = new TorrentInfo { Title = row.Name, DownloadUrl = link, InfoHash = row.InfoHash, InfoUrl = details, Guid = details, Categories = _categories.MapTrackerCatDescToNewznab(row.Category), PublishDate = DateTime.Parse(row.CreatedAt, CultureInfo.InvariantCulture), Size = row.Size, Grabs = row.Grabs, Seeders = row.Seeders, ImdbId = imdbId, TmdbId = tmdbId, Peers = row.Leechers + row.Seeders, DownloadVolumeFactor = row.Freeleech || row.Limited ? 0 : row.Promo75 ? 0.25 : row.Promo50 ? 0.5 : row.Promo25 ? 0.75 : 1, UploadVolumeFactor = 1, MinimumRatio = 1, MinimumSeedTime = 172800, // 120 hours }; torrentInfos.Add(release); } // order by date return(torrentInfos.OrderByDescending(o => o.PublishDate).ToArray()); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var response = await CallProviderAsync(query); if (response.StartsWith("{\"error\"")) { throw new ExceptionWithConfigData(response, configData); } try { var json = JArray.Parse(response); foreach (var row in json) { var detailsUri = new Uri(DetailsUrl + "?id=" + (string)row["id"]); var seeders = (int)row["seeders"]; var peers = seeders + (int)row["leechers"]; var publishDate = DateTimeUtil.FromFuzzyTime((string)row["upload_date"] + " +0200"); var downloadVolumeFactor = (int)row["freeleech"] == 1 ? 0 : 1; var uploadVolumeFactor = (int)row["doubleup"] == 1 ? 2 : 1; var imdbId = ((JObject)row).ContainsKey("imdb") ? ParseUtil.GetImdbID((string)row["imdb"]) : null; var link = new Uri((string)row["download_link"]); var release = new ReleaseInfo { Title = (string)row["name"], Details = detailsUri, Link = link, Category = MapTrackerCatDescToNewznab((string)row["category"]), Size = (long)row["size"], Files = (long)row["files"], Grabs = (long)row["times_completed"], Seeders = seeders, Peers = peers, MinimumRatio = 1, MinimumSeedTime = 172800, //48 hours PublishDate = publishDate, DownloadVolumeFactor = downloadVolumeFactor, UploadVolumeFactor = uploadVolumeFactor, Guid = detailsUri, Imdb = imdbId }; releases.Add(release); } return(releases); } catch (Exception ex) { OnParseError(response, ex); } return(releases); }
private List <ReleaseInfo> ParseMultiResult(WebResult response) { var releases = new List <ReleaseInfo>(); var results = response.ContentString; try { var parser = new HtmlParser(); var dom = parser.ParseDocument(results); var rows = dom.QuerySelectorAll("table.table-bordered > tbody > tr[class*=\"torrent_row_\"]"); foreach (var row in rows) { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 72 * 60 * 60; var qCatLink = row.QuerySelector("a[href^=\"/browse_elastic.php?cat=\"]"); var catStr = qCatLink.GetAttribute("href").Split('=')[1]; release.Category = MapTrackerCatToNewznab(catStr); var qDetailsLink = row.QuerySelector("a[href^=\"/details.php?id=\"]"); var qDetailsTitle = row.QuerySelector("td:has(a[href^=\"/details.php?id=\"]) b"); release.Title = qDetailsTitle.TextContent.Trim(); var qDlLink = row.QuerySelector("a[href^=\"/download.php?torrent=\"]"); release.Link = new Uri(SiteLink + qDlLink.GetAttribute("href").TrimStart('/')); release.Details = new Uri(SiteLink + qDetailsLink.GetAttribute("href").TrimStart('/')); release.Guid = release.Details; var qColumns = row.QuerySelectorAll("td"); release.Files = ParseUtil.CoerceInt(qColumns[3].TextContent); release.PublishDate = DateTimeUtil.FromUnknown(qColumns[5].TextContent); release.Size = ReleaseInfo.GetBytes(qColumns[6].TextContent); release.Grabs = ParseUtil.CoerceInt(qColumns[7].TextContent.Replace("Times", "")); release.Seeders = ParseUtil.CoerceInt(qColumns[8].TextContent); release.Peers = ParseUtil.CoerceInt(qColumns[9].TextContent) + release.Seeders; var qImdb = row.QuerySelector("a[href*=\"www.imdb.com\"]"); if (qImdb != null) { var deRefUrl = qImdb.GetAttribute("href"); release.Imdb = ParseUtil.GetImdbID(WebUtility.UrlDecode(deRefUrl).Split('/').Last()); } release.DownloadVolumeFactor = row.QuerySelector("span.freeleech") != null ? 0 : 1; release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
public virtual IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <TorrentInfo>(); if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) { throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}"); } var jsonResponse = new HttpResponse <Unit3dResponse>(indexerResponse.HttpResponse); foreach (var row in jsonResponse.Resource.Data) { var details = row.Attributes.DetailsLink; var link = row.Attributes.DownloadLink; var release = new TorrentInfo { Title = row.Attributes.Name, DownloadUrl = link, InfoHash = row.Id, InfoUrl = details, Guid = details, Categories = _categories.MapTrackerCatDescToNewznab(row.Attributes.Category), PublishDate = DateTime.Parse(row.Attributes.CreatedAt, CultureInfo.InvariantCulture), Size = row.Attributes.Size, Files = row.Attributes.Files, Grabs = row.Attributes.Grabs, Seeders = row.Attributes.Seeders, ImdbId = ParseUtil.GetImdbID(row.Attributes.ImdbId).GetValueOrDefault(), TmdbId = row.Attributes.TmdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.CoerceInt(row.Attributes.TmdbId), TvdbId = row.Attributes.TvdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.CoerceInt(row.Attributes.TvdbId), Peers = row.Attributes.Leechers + row.Attributes.Seeders, DownloadVolumeFactor = row.Attributes.Freeleech ? 0 : 1, UploadVolumeFactor = row.Attributes.DoubleUpload ? 2 : 1, MinimumRatio = 1, MinimumSeedTime = 172800, // 48 hours }; torrentInfos.Add(release); } // order by date return(torrentInfos.OrderByDescending(o => o.PublishDate).ToArray()); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <ReleaseInfo>(); var detailsUrl = _settings.BaseUrl + "details.php?"; var downloadUrl = _settings.BaseUrl + "download.php?"; if (indexerResponse.Content?.Contains("User not found or passkey not set") == true) { throw new IndexerAuthException("The passkey is invalid. Check the indexer configuration."); } var jsonContent = JArray.Parse(indexerResponse.Content); foreach (var item in jsonContent) { var title = item.Value <string>("name"); var id = item.Value <long>("id"); var details = new Uri(detailsUrl + "id=" + id).AbsoluteUri; var link = new Uri(downloadUrl + "id=" + id + "&passkey=" + _settings.Passkey).AbsoluteUri; var publishDate = DateTime.ParseExact(item.Value <string>("added"), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); var dlVolumeFactor = item.Value <int>("is_freeleech") == 1 ? 0 : 1; var release = new TorrentInfo { Title = title, DownloadUrl = link, InfoUrl = details, Guid = details, Categories = _categories.MapTrackerCatToNewznab(item.Value <string>("category")), PublishDate = publishDate, Size = item.Value <long>("size"), Grabs = item.Value <int>("times_completed"), Files = item.Value <int>("numfiles"), Seeders = item.Value <int>("seeders"), Peers = item.Value <int>("leechers") + item.Value <int>("seeders"), ImdbId = ParseUtil.GetImdbID(item.Value <string>("imdbid")) ?? 0, MinimumRatio = 1, MinimumSeedTime = 0, DownloadVolumeFactor = dlVolumeFactor, UploadVolumeFactor = 1 }; torrentInfos.Add(release); } return(torrentInfos.ToArray()); }
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("table.table-bordered > tbody > tr[class*=\"torrent_row_\"]"); foreach (var row in rows) { var release = new TorrentInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 72 * 60 * 60; var qCatLink = row.QuerySelector("a[href^=\"/browse_elastic.php?cat=\"]"); var catStr = qCatLink.GetAttribute("href").Split('=')[1]; release.Categories = _categories.MapTrackerCatToNewznab(catStr); var qDetailsLink = row.QuerySelector("a[href^=\"/details.php?id=\"]"); var qDetailsTitle = row.QuerySelector("td:has(a[href^=\"/details.php?id=\"]) b"); release.Title = qDetailsTitle.TextContent.Trim(); var qDlLink = row.QuerySelector("a[href^=\"/download.php?torrent=\"]"); release.DownloadUrl = _settings.BaseUrl + qDlLink.GetAttribute("href").TrimStart('/'); release.InfoUrl = _settings.BaseUrl + qDetailsLink.GetAttribute("href").TrimStart('/'); release.Guid = release.InfoUrl; var qColumns = row.QuerySelectorAll("td"); release.Files = ParseUtil.CoerceInt(qColumns[3].TextContent); release.PublishDate = DateTimeUtil.FromUnknown(qColumns[5].TextContent); release.Size = ParseUtil.GetBytes(qColumns[6].TextContent); release.Grabs = ParseUtil.CoerceInt(qColumns[7].TextContent.Replace("Times", "")); release.Seeders = ParseUtil.CoerceInt(qColumns[8].TextContent); release.Peers = ParseUtil.CoerceInt(qColumns[9].TextContent) + release.Seeders; var qImdb = row.QuerySelector("a[href*=\"www.imdb.com\"]"); if (qImdb != null) { var deRefUrl = qImdb.GetAttribute("href"); release.ImdbId = ParseUtil.GetImdbID(WebUtility.UrlDecode(deRefUrl).Split('/').Last()) ?? 0; } release.DownloadVolumeFactor = row.QuerySelector("span.freeleech") != null ? 0 : 1; release.UploadVolumeFactor = 1; torrentInfos.Add(release); } return(torrentInfos.ToArray()); }
private ReleaseInfo getMovieBasicInformation(JToken jsonData) { var release = new ReleaseInfo(); release.Title = jsonData.Value <string>("title"); release.Description = jsonData.Value <string>("description_full"); release.Comments = new Uri(jsonData.Value <string>("url")); string imdbId = jsonData.Value <string>("imdb_code"); if (!String.IsNullOrEmpty(imdbId)) { release.Imdb = ParseUtil.GetImdbID(imdbId); } ; return(release); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <TorrentInfo>(); var rows = JsonConvert.DeserializeObject <dynamic>(indexerResponse.Content); foreach (var row in rows) { var title = (string)row.name; var torrentId = (long)row.t; var details = new Uri(_settings.BaseUrl + "details.php?id=" + torrentId); var seeders = (int)row.seeders; var imdbId = (string)row["imdb-id"]; var downloadMultiplier = (double?)row["download-multiplier"] ?? 1; var link = new Uri(_settings.BaseUrl + "download.php/" + torrentId + "/" + torrentId + ".torrent"); var publishDate = DateTimeUtil.UnixTimestampToDateTime((long)row.ctime).ToLocalTime(); var imdb = ParseUtil.GetImdbID(imdbId) ?? 0; var release = new TorrentInfo { Title = title, Guid = details.AbsoluteUri, DownloadUrl = link.AbsoluteUri, InfoUrl = details.AbsoluteUri, PublishDate = publishDate, Categories = _categories.MapTrackerCatToNewznab(row.c.ToString()), Size = (long)row.size, Files = (int)row.files, Grabs = (int)row.completed, Seeders = seeders, Peers = seeders + (int)row.leechers, ImdbId = imdb, DownloadVolumeFactor = downloadMultiplier, UploadVolumeFactor = 1, MinimumRatio = 1, MinimumSeedTime = 172800 // 48 hours }; torrentInfos.Add(release); } return(torrentInfos.ToArray()); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <ReleaseInfo>(); var queryResponseItems = JsonConvert.DeserializeObject <List <ThePirateBayTorrent> >(indexerResponse.Content); // The API returns a single item to represent a state of no results. Avoid returning this result. if (queryResponseItems.Count == 1 && queryResponseItems.First().Id == 0) { return(torrentInfos); } foreach (var item in queryResponseItems) { var details = item.Id == 0 ? null : $"{_settings.BaseUrl}description.php?id={item.Id}"; var imdbId = string.IsNullOrEmpty(item.Imdb) ? null : ParseUtil.GetImdbID(item.Imdb); var torrentItem = new TorrentInfo { Title = item.Name, Categories = _categories.MapTrackerCatToNewznab(item.Category.ToString()), Guid = details, InfoUrl = details, InfoHash = item.InfoHash, // magnet link is auto generated from infohash PublishDate = DateTimeUtil.UnixTimestampToDateTime(item.Added), Seeders = item.Seeders, Peers = item.Seeders + item.Leechers, Size = item.Size, Files = item.NumFiles, DownloadVolumeFactor = 0, UploadVolumeFactor = 1, ImdbId = imdbId.GetValueOrDefault() }; if (item.InfoHash != null) { torrentItem.MagnetUrl = MagnetLinkBuilder.BuildPublicMagnetLink(item.InfoHash, item.Name); } torrentInfos.Add(torrentItem); } return(torrentInfos.ToArray()); }
private ReleaseInfo CreateReleaseInfo(QueryResponseItem item) { var details = item.Id == 0 ? null : new Uri($"{SiteLink}description.php?id={item.Id}"); var imdbId = string.IsNullOrEmpty(item.Imdb) ? null : ParseUtil.GetImdbID(item.Imdb); return(new ReleaseInfo { Title = item.Name, Category = MapTrackerCatToNewznab(item.Category.ToString()), Guid = details, Details = details, InfoHash = item.InfoHash, // magnet link is auto generated from infohash PublishDate = DateTimeUtil.UnixTimestampToDateTime(item.Added), Seeders = item.Seeders, Peers = item.Seeders + item.Leechers, Size = item.Size, Files = item.NumFiles, DownloadVolumeFactor = 0, UploadVolumeFactor = 1, Imdb = imdbId }); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) { throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request"); } if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value)) { throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}"); } var jsonResponse = new HttpResponse <List <SpeedAppTorrent> >(indexerResponse.HttpResponse); return(jsonResponse.Resource.Select(torrent => new TorrentInfo { Guid = torrent.Id.ToString(), Title = torrent.Name, Description = torrent.ShortDescription, Size = torrent.Size, ImdbId = ParseUtil.GetImdbID(torrent.ImdbId).GetValueOrDefault(), DownloadUrl = $"{_settings.BaseUrl}/api/torrent/{torrent.Id}/download", PosterUrl = torrent.Poster, InfoUrl = torrent.Url, Grabs = torrent.TimesCompleted, PublishDate = torrent.CreatedAt, Categories = _categories.MapTrackerCatToNewznab(torrent.Category.Id.ToString()), InfoHash = null, Seeders = torrent.Seeders, Peers = torrent.Leechers + torrent.Seeders, MinimumRatio = 1, MinimumSeedTime = 172800, DownloadVolumeFactor = torrent.DownloadVolumeFactor, UploadVolumeFactor = torrent.UploadVolumeFactor, }).ToArray()); }
private async Task <IEnumerable <ReleaseInfo> > PerformQueryWithRetry(TorznabQuery query, bool retry) { var releases = new List <ReleaseInfo>(); // check the token and renewal if necessary await RenewalTokenAsync(); var response = await RequestWithCookiesAndRetryAsync(BuildSearchUrl(query)); var jsonContent = JObject.Parse(response.ContentString); var errorCode = jsonContent.Value <int>("error_code"); switch (errorCode) { case 0: // valid response with results break; case 2: case 4: // invalid token await RenewalTokenAsync(true); // force renewal token response = await RequestWithCookiesAndRetryAsync(BuildSearchUrl(query)); jsonContent = JObject.Parse(response.ContentString); break; case 10: // imdb not found, see issue #1486 case 20: // no results found // the api returns "no results" in some valid queries. we do one retry on this case but we can't do more // because we can't distinguish between search without results and api malfunction return(retry ? await PerformQueryWithRetry(query, false) : releases); default: throw new Exception("Unknown error code: " + errorCode + " response: " + response.ContentString); } try { foreach (var item in jsonContent.Value <JArray>("torrent_results")) { var title = WebUtility.HtmlDecode(item.Value <string>("title")); var magnetStr = item.Value <string>("download"); var magnetUri = new Uri(magnetStr); var infoHash = magnetStr.Split(':')[3].Split('&')[0]; // append app_id to prevent api server returning 403 forbidden var details = new Uri(item.Value <string>("info_page") + "&app_id=" + _appId); // ex: 2015-08-16 21:25:08 +0000 var dateStr = item.Value <string>("pubdate").Replace(" +0000", ""); var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); var publishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime(); var size = item.Value <long>("size"); var seeders = item.Value <int>("seeders"); var leechers = item.Value <int>("leechers"); var release = new ReleaseInfo { Title = title, Category = MapTrackerCatDescToNewznab(item.Value <string>("category")), MagnetUri = magnetUri, InfoHash = infoHash, Details = details, PublishDate = publishDate, Guid = magnetUri, Seeders = seeders, Peers = leechers + seeders, Size = size, DownloadVolumeFactor = 0, UploadVolumeFactor = 1 }; var episodeInfo = item.Value <JToken>("episode_info"); if (episodeInfo.HasValues) { release.Imdb = ParseUtil.GetImdbID(episodeInfo.Value <string>("imdb")); release.TVDBId = episodeInfo.Value <long?>("tvdb"); release.RageID = episodeInfo.Value <long?>("tvrage"); release.TMDb = episodeInfo.Value <long?>("themoviedb"); } releases.Add(release); } } catch (Exception ex) { OnParseError(response.ContentString, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query, int attempts) { await CheckToken(); var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var queryCollection = new NameValueCollection(); queryCollection.Add("token", token); queryCollection.Add("format", "json_extended"); queryCollection.Add("app_id", app_id); queryCollection.Add("limit", "100"); queryCollection.Add("ranked", "0"); if (query.ImdbID != null) { queryCollection.Add("mode", "search"); queryCollection.Add("search_imdb", query.ImdbID); } else if (query.RageID != null) { queryCollection.Add("mode", "search"); queryCollection.Add("search_tvrage", query.RageID.ToString()); } /*else if (query.TvdbID != null) * { * queryCollection.Add("mode", "search"); * queryCollection.Add("search_tvdb", query.TvdbID); * }*/ else if (!string.IsNullOrWhiteSpace(searchString)) { searchString = searchString.Replace("'", ""); // ignore ' (e.g. search for america's Next Top Model) queryCollection.Add("mode", "search"); queryCollection.Add("search_string", searchString); } else { queryCollection.Add("mode", "list"); } var cats = string.Join(";", MapTorznabCapsToTrackers(query)); if (!string.IsNullOrEmpty(cats)) { queryCollection.Add("category", cats); } var searchUrl = ApiEndpoint + "?" + queryCollection.GetQueryString(); var response = await RequestStringWithCookiesAndRetry(searchUrl, string.Empty); try { var jsonContent = JObject.Parse(response.Content); int errorCode = jsonContent.Value <int>("error_code"); if (errorCode == 20) // no results found { return(releases.ToArray()); } // return empty results in case of invalid imdb ID, see issue #1486 if (errorCode == 10) // Cant find imdb in database. Are you sure this imdb exists? { return(releases); } if (errorCode == 2 || // Invalid token set! errorCode == 4) // Invalid token. Use get_token for a new one! { token = null; if (attempts < 3) { return(await PerformQuery(query, ++attempts)); } else { throw new Exception("error " + errorCode.ToString() + " after " + attempts.ToString() + " attempts: " + jsonContent.Value <string>("error")); } } if (errorCode > 0) // too many requests per seconds ??? { // we use the IwebClient rate limiter now, this shouldn't happen throw new Exception("error " + errorCode.ToString() + ": " + jsonContent.Value <string>("error")); } foreach (var item in jsonContent.Value <JArray>("torrent_results")) { var release = new ReleaseInfo(); release.Title = item.Value <string>("title"); release.Category = MapTrackerCatDescToNewznab(item.Value <string>("category")); release.MagnetUri = new Uri(item.Value <string>("download")); release.InfoHash = release.MagnetUri.ToString().Split(':')[3].Split('&')[0]; release.Comments = new Uri(item.Value <string>("info_page")); release.Link = release.Comments; // in case of a torrent download we grab the link from the details page in Download() release.Guid = release.MagnetUri; var episode_info = item.Value <JToken>("episode_info"); if (episode_info.HasValues) { var imdb = episode_info.Value <string>("imdb"); release.Imdb = ParseUtil.GetImdbID(imdb); release.TVDBId = episode_info.Value <long?>("tvdb"); release.RageID = episode_info.Value <long?>("tvrage"); release.TMDb = episode_info.Value <long?>("themoviedb"); } // ex: 2015-08-16 21:25:08 +0000 var dateStr = item.Value <string>("pubdate").Replace(" +0000", ""); var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime(); release.Seeders = item.Value <int>("seeders"); release.Peers = item.Value <int>("leechers") + release.Seeders; release.Size = item.Value <long>("size"); release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var prevCook = CookieHeader + ""; var searchStringIsImdbQuery = (ParseUtil.GetImdbID(searchString) != null); // If we have no query use the RSS Page as their server is slow enough at times! // ~15.01.2019 they removed the description tag making the RSS feed almost useless, we don't use it for now. See #4458 // if (false && query.IsTest || string.IsNullOrWhiteSpace(searchString)) /* * if (false) * { * var rssPage = await RequestStringWithCookiesAndRetry(string.Format(RSSUrl, configData.RSSKey.Value)); * try * { * if (rssPage.Content.EndsWith("\0")) * { * rssPage.Content = rssPage.Content.Substring(0, rssPage.Content.Length - 1); * } * rssPage.Content = RemoveInvalidXmlChars(rssPage.Content); * var rssDoc = XDocument.Parse(rssPage.Content); * * foreach (var item in rssDoc.Descendants("item")) * { * var title = item.Descendants("title").First().Value; * var description = item.Descendants("description").First().Value; * var link = item.Descendants("link").First().Value; * var category = item.Descendants("category").First().Value; * var date = item.Descendants("pubDate").First().Value; * * var torrentIdMatch = Regex.Match(link, "(?<=id=)(\\d)*"); * var torrentId = torrentIdMatch.Success ? torrentIdMatch.Value : string.Empty; * if (string.IsNullOrWhiteSpace(torrentId)) * throw new Exception("Missing torrent id"); * * var infoMatch = Regex.Match(description, @"Category:\W(?<cat>.*)\W\/\WSeeders:\W(?<seeders>[\d\,]*)\W\/\WLeechers:\W(?<leechers>[\d\,]*)\W\/\WSize:\W(?<size>[\d\.]*\W\S*)"); * if (!infoMatch.Success) * throw new Exception("Unable to find info"); * * var release = new ReleaseInfo * { * Title = title, * Description = title, * Guid = new Uri(string.Format(DownloadUrl, torrentId)), * Comments = new Uri(string.Format(CommentUrl, torrentId)), * PublishDate = DateTime.ParseExact(date, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31 * Link = new Uri(string.Format(DownloadUrl, torrentId)), * Seeders = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value), * Peers = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value), * Size = ReleaseInfo.GetBytes(infoMatch.Groups["size"].Value), * Category = MapTrackerCatToNewznab(category) * }; * * release.Peers += release.Seeders; * releases.Add(release); * } * } * catch (Exception ex) * { * logger.Error("XSpeeds: Error while parsing the RSS feed:"); * logger.Error(rssPage.Content); * throw ex; * } * } */ //if (query.IsTest || !string.IsNullOrWhiteSpace(searchString)) /* * if (searchString.Length < 3 && !query.IsTest) * { * OnParseError("", new Exception("Minimum search length is 3")); * return releases; * } */ var searchParams = new Dictionary <string, string> { { "do", "search" }, { "category", "0" }, { "include_dead_torrents", "no" } }; if (query.IsImdbQuery) { searchParams.Add("keywords", query.ImdbID); searchParams.Add("search_type", "t_both"); } else { searchParams.Add("keywords", searchString); if (searchStringIsImdbQuery) { searchParams.Add("search_type", "t_both"); } else { searchParams.Add("search_type", "t_name"); } } var searchPage = await PostDataWithCookiesAndRetry(SearchUrl, searchParams, CookieHeader); // Occasionally the cookies become invalid, login again if that happens if (searchPage.IsRedirect) { await ApplyConfiguration(null); searchPage = await PostDataWithCookiesAndRetry(SearchUrl, searchParams, CookieHeader); } try { CQ dom = searchPage.Content; var rows = dom["table#sortabletable > tbody > tr:has(div > a[href*=\"details.php?id=\"])"]; foreach (var row in rows) { var release = new ReleaseInfo(); var qRow = row.Cq(); var qDetails = qRow.Find("div > a[href*=\"details.php?id=\"]"); // details link, release name get's shortened if it's to long var qTitle = qRow.Find("td:eq(1) .tooltip-content div:eq(0)"); // use Title from tooltip if (!qTitle.Any()) // fallback to Details link if there's no tooltip { qTitle = qDetails; } release.Title = qTitle.Text(); release.Guid = new Uri(qRow.Find("td:eq(2) a").Attr("href")); release.Link = release.Guid; release.Comments = new Uri(qDetails.Attr("href")); release.PublishDate = DateTime.ParseExact(qRow.Find("td:eq(1) div").Last().Text().Trim(), "dd-MM-yyyy H:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); //08-08-2015 12:51 release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:eq(6)").Text()); release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find("td:eq(7)").Text().Trim()); release.Size = ReleaseInfo.GetBytes(qRow.Find("td:eq(4)").Text().Trim()); var qBanner = qRow.Find("td:eq(1) .tooltip-content img").First(); if (qBanner.Length > 0) { release.BannerUrl = new Uri(qBanner.Attr("src")); } var cat = row.Cq().Find("td:eq(0) a").First().Attr("href"); var catSplit = cat.LastIndexOf('='); if (catSplit > -1) { cat = cat.Substring(catSplit + 1); } release.Category = MapTrackerCatToNewznab(cat); var grabs = qRow.Find("td:nth-child(6)").Text(); release.Grabs = ParseUtil.CoerceInt(grabs); if (qRow.Find("img[alt^=\"Free Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0; } else if (qRow.Find("img[alt^=\"Silver Torrent\"]").Length >= 1) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } if (qRow.Find("img[alt^=\"x2 Torrent\"]").Length >= 1) { release.UploadVolumeFactor = 2; } else { release.UploadVolumeFactor = 1; } releases.Add(release); } } catch (Exception ex) { OnParseError(searchPage.Content, ex); } if (!CookieHeader.Trim().Equals(prevCook.Trim())) { SaveConfig(); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var qc = new NameValueCollection { { "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); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <ReleaseInfo>(); var parser = new HtmlParser(); var dom = parser.ParseDocument(indexerResponse.Content); var firstPageRows = dom.QuerySelectorAll("#torrentTable > tbody > tr").Skip(1).ToCollection(); // If pagination available int nbResults; // 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 return(torrentInfos); } var torrentDetailsUrl = _settings.BaseUrl + "details.php?id={id}"; var torrentDownloadUrl = _settings.BaseUrl + "download.php?id={id}&passkey={passkey}"; // 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.CoerceInt(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 = ParseUtil.GetBytes(humanSize); // Date var dateTimeOrig = row.QuerySelector("td:nth-of-type(5)").TextContent; var dateTime = Regex.Replace(dateTimeOrig, @"<[^>]+>| ", "").Trim(); var date = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(); 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 TorrentInfo { Categories = _categories.MapTrackerCatToNewznab(cat), Title = name, Seeders = seeders, Peers = seeders + leechers, PublishDate = date, Size = size, Files = files, Grabs = completed, Guid = details.AbsoluteUri, InfoUrl = details.AbsoluteUri, DownloadUrl = downloadLink.AbsoluteUri, 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.ImdbId = ParseUtil.GetImdbID(imdbLink) ?? 0; 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; torrentInfos.Add(release); } return(torrentInfos.ToArray()); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); // remove operator characters var cleanSearchString = Regex.Replace(query.GetQueryString().Trim(), "[ _.+-]+", " ", RegexOptions.Compiled); var searchUrl = SearchUrl; var queryCollection = new NameValueCollection { { "search_in", "name" }, { "search_mode", "all" }, { "order_by", "added" }, { "order_way", "desc" } }; if (!string.IsNullOrWhiteSpace(cleanSearchString)) { queryCollection.Add("query", cleanSearchString); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add($"cat[{cat}]", "1"); } searchUrl += "?" + queryCollection.GetQueryString(); var response = await RequestWithCookiesAndRetryAsync(searchUrl); var results = response.ContentString; if (!results.Contains("/logout.php?")) { await ApplyConfiguration(null); response = await RequestWithCookiesAndRetryAsync(searchUrl); results = response.ContentString; } try { var parser = new HtmlParser(); var dom = parser.ParseDocument(results); var rows = dom.QuerySelectorAll("table.table-bordered > tbody > tr[class*=\"torrent_row_\"]"); foreach (var row in rows) { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 72 * 60 * 60; var qCatLink = row.QuerySelector("a[href^=\"/browse_elastic.php?cat=\"]"); var catStr = qCatLink.GetAttribute("href").Split('=')[1]; release.Category = MapTrackerCatToNewznab(catStr); var qDetailsLink = row.QuerySelector("a[href^=\"/details.php?id=\"]"); var qDetailsTitle = row.QuerySelector("td:has(a[href^=\"/details.php?id=\"]) b"); release.Title = qDetailsTitle.TextContent.Trim(); var qDlLink = row.QuerySelector("a[href^=\"/download.php?torrent=\"]"); release.Link = new Uri(SiteLink + qDlLink.GetAttribute("href").TrimStart('/')); release.Comments = new Uri(SiteLink + qDetailsLink.GetAttribute("href").TrimStart('/')); release.Guid = release.Comments; var qColumns = row.QuerySelectorAll("td"); release.Files = ParseUtil.CoerceInt(qColumns[3].TextContent); release.PublishDate = DateTimeUtil.FromUnknown(qColumns[5].TextContent); release.Size = ReleaseInfo.GetBytes(qColumns[6].TextContent); release.Grabs = ParseUtil.CoerceInt(qColumns[7].TextContent.Replace("Times", "")); release.Seeders = ParseUtil.CoerceInt(qColumns[8].TextContent); release.Peers = ParseUtil.CoerceInt(qColumns[9].TextContent) + release.Seeders; var qImdb = row.QuerySelector("a[href*=\"www.imdb.com\"]"); if (qImdb != null) { var deRefUrl = qImdb.GetAttribute("href"); release.Imdb = ParseUtil.GetImdbID(WebUtility.UrlDecode(deRefUrl).Split('/').Last()); } release.DownloadVolumeFactor = row.QuerySelector("span.freeleech") != null ? 0 : 1; release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); // remove dashes at the beginning of keywords as they exclude search strings (see issue #3096) var searchString = query.GetQueryString(); searchString = Regex.Replace(searchString, @"(^|\s)-", " "); var searchUrl = SearchUrl; if (((BoolConfigurationItem)configData.GetDynamic("freeleech")).Value) { searchUrl += "facets/tags%3AFREELEECH/"; } if (query.IsImdbQuery) { searchUrl += "imdbID/" + query.ImdbID + "/"; } else if (!string.IsNullOrWhiteSpace(searchString)) { searchUrl += "exact/1/query/" + WebUtility.UrlEncode(searchString) + "/"; } var cats = MapTorznabCapsToTrackers(query); if (cats.Count > 0) { searchUrl += "categories/" + string.Join(",", cats); } else { searchUrl += "newfilter/2"; // include 0day and music } var results = await RequestWithCookiesAndRetryAsync(searchUrl); if (results.ContentString.Contains("/user/account/login")) // re-login { await DoLogin(); results = await RequestWithCookiesAndRetryAsync(searchUrl); } try { var rows = (JArray)((JObject)JsonConvert.DeserializeObject(results.ContentString))["torrentList"]; foreach (var row in rows) { var title = row["name"].ToString(); if (!query.MatchQueryStringAND(title)) { continue; } var torrentId = row["fid"].ToString(); var details = new Uri(SiteLink + "torrent/" + torrentId); var link = new Uri(SiteLink + "download/" + torrentId + "/" + row["filename"]); var publishDate = DateTime.ParseExact(row["addedTimestamp"].ToString(), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); var seeders = (int)row["seeders"]; // freeleech #6579 #6624 #7367 var dlMultiplier = row["download_multiplier"].ToString(); var dlVolumeFactor = string.IsNullOrEmpty(dlMultiplier) ? 1 : ParseUtil.CoerceInt(dlMultiplier); var release = new ReleaseInfo { Title = title, Details = details, Guid = details, Link = link, PublishDate = publishDate, Category = MapTrackerCatToNewznab(row["categoryID"].ToString()), Size = (long)row["size"], Grabs = (int)row["completed"], Seeders = seeders, Peers = seeders + (int)row["leechers"], Imdb = ParseUtil.GetImdbID(row["imdbID"].ToString()), UploadVolumeFactor = 1, DownloadVolumeFactor = dlVolumeFactor, MinimumRatio = 1, MinimumSeedTime = 864000 // 10 days for registered users, less for upgraded users }; releases.Add(release); } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var cats = MapTorznabCapsToTrackers(query); if (cats.Count == 0) { cats = GetAllTrackerCategories(); } var catStr = string.Join(";", cats); var searchUrl = SearchUrl + "?" + catStr; if (query.IsImdbQuery) { searchUrl += ";q=" + query.ImdbID; } else { searchUrl += ";q=" + WebUtilityHelpers.UrlEncode(query.GetQueryString(), Encoding); } if (((BoolConfigurationItem)configData.GetDynamic("freeleech")).Value) { searchUrl += ";free=on"; } var results = await RequestWithCookiesAndRetryAsync(searchUrl); // Check for being logged out if (results.IsRedirect) { if (results.RedirectingTo.Contains("login.php")) { throw new Exception("The user is not logged in. It is possible that the cookie has expired or you made a mistake when copying it. Please check the settings."); } else { throw new Exception($"Got a redirect to {results.RedirectingTo}, please adjust your the alternative link"); } } try { var rows = JsonConvert.DeserializeObject <dynamic>(results.ContentString); foreach (var row in rows) { var title = (string)row.name; if ((!query.IsImdbQuery || !TorznabCaps.MovieSearchImdbAvailable) && !query.MatchQueryStringAND(title)) { continue; } var torrentId = (long)row.t; var details = new Uri(SiteLink + "details.php?id=" + torrentId); var seeders = (int)row.seeders; var imdbId = (string)row["imdb-id"]; var downloadMultiplier = (double?)row["download-multiplier"] ?? 1; var link = new Uri(SiteLink + "download.php/" + torrentId + "/" + torrentId + ".torrent"); var publishDate = DateTimeUtil.UnixTimestampToDateTime((long)row.ctime).ToLocalTime(); var imdb = ParseUtil.GetImdbID(imdbId); var release = new ReleaseInfo { Title = title, Details = details, Guid = details, Link = link, PublishDate = publishDate, Category = MapTrackerCatToNewznab(row.c.ToString()), Size = (long)row.size, Files = (long)row.files, Grabs = (long)row.completed, Seeders = seeders, Peers = seeders + (int)row.leechers, Imdb = imdb, DownloadVolumeFactor = downloadMultiplier, UploadVolumeFactor = 1, MinimumRatio = 1, MinimumSeedTime = 172800 // 48 hours }; releases.Add(release); } } catch (Exception ex) { OnParseError(results.ContentString, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = GetSearchTerm(query); var searchUrl = APIUrl; var queryCollection = new NameValueCollection { { "action", "browse" }, //{"group_results", "0"}, # results won't include all information { "order_by", "time" }, { "order_way", "desc" } }; if (!string.IsNullOrWhiteSpace(query.ImdbID)) { if (imdbInTags) { queryCollection.Add("taglist", query.ImdbID); } else { queryCollection.Add("cataloguenumber", query.ImdbID); } } else if (!string.IsNullOrWhiteSpace(searchString)) { queryCollection.Add("searchstr", searchString); } if (query.Artist != null) { queryCollection.Add("artistname", query.Artist); } if (query.Label != null) { queryCollection.Add("recordlabel", query.Label); } if (query.Year != null) { queryCollection.Add("year", query.Year.ToString()); } if (query.Album != null) { queryCollection.Add("groupname", query.Album); } foreach (var cat in MapTorznabCapsToTrackers(query)) { queryCollection.Add("filter_cat[" + cat + "]", "1"); } searchUrl += "?" + queryCollection.GetQueryString(); var apiKey = configData.ApiKey; var headers = apiKey != null ? new Dictionary <string, string> { ["Authorization"] = String.Format(AuthorizationFormat, apiKey.Value) } : null; var response = await RequestWithCookiesAndRetryAsync(searchUrl, headers : headers); // we get a redirect in html pages and an error message in json response (api) if (response.IsRedirect || (response.ContentString != null && response.ContentString.Contains("\"bad credentials\""))) { // re-login await ApplyConfiguration(null); response = await RequestWithCookiesAndRetryAsync(searchUrl); } try { var json = JObject.Parse(response.ContentString); foreach (JObject r in json["response"]["results"]) { var groupTime = DateTimeUtil.UnixTimestampToDateTime(long.Parse((string)r["groupTime"])); var groupName = WebUtility.HtmlDecode((string)r["groupName"]); var artist = WebUtility.HtmlDecode((string)r["artist"]); var cover = (string)r["cover"]; var tags = r["tags"].ToList(); var groupYear = (string)r["groupYear"]; var releaseType = (string)r["releaseType"]; var title = new StringBuilder(); if (!string.IsNullOrEmpty(artist)) { title.Append(artist + " - "); } title.Append(groupName); if (!string.IsNullOrEmpty(groupYear) && groupYear != "0") { title.Append(" [" + groupYear + "]"); } if (!string.IsNullOrEmpty(releaseType) && releaseType != "Unknown") { title.Append(" [" + releaseType + "]"); } var description = tags?.Any() == true && !string.IsNullOrEmpty(tags[0].ToString()) ? "Tags: " + string.Join(", ", tags) + "\n" : null; Uri poster = null; if (!string.IsNullOrEmpty(cover)) { poster = new Uri(cover); } var release = new ReleaseInfo { PublishDate = groupTime, Title = title.ToString(), Description = description, Poster = poster }; if (imdbInTags) { release.Imdb = tags .Select(tag => ParseUtil.GetImdbID((string)tag)) .Where(tag => tag != null).FirstIfSingleOrDefault(); } if (r["torrents"] is JArray) { foreach (JObject torrent in r["torrents"]) { var release2 = (ReleaseInfo)release.Clone(); FillReleaseInfoFromJson(release2, torrent); if (ReleaseInfoPostParse(release2, torrent, r)) { releases.Add(release2); } } } else { FillReleaseInfoFromJson(release, r); if (ReleaseInfoPostParse(release, r, r)) { releases.Add(release); } } } } catch (Exception ex) { OnParseError(response.ContentString, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var passkey = configData.Passkey.Value; var qc = new NameValueCollection { { "passkey", passkey } }; if (query.IsImdbQuery) { qc.Add("action", "imdbsearch"); qc.Add("imdb", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(query.GetQueryString())) { // not use query.GetQueryString(), because it includes the season var searchTerm = query.SearchTerm; // search for tv series if (query.Season > 0) { searchTerm += $": Season {query.Season:D2}"; } // remove the year, it's not supported in the api searchTerm = _removeYearRegex.Replace(searchTerm, ""); qc.Add("action", "titlesearch"); qc.Add("title", searchTerm); } else { qc.Add("action", "latestmovies"); // the endpoint 'latestmovies' only returns movies, this hack overwrites categories to get movies even if // you are searching for tv series. this allows to configure the tracker in Sonarr query.Categories = new int[] { }; } var searchUrl = SearchUrl + "?" + qc.GetQueryString(); var results = await RequestStringWithCookies(searchUrl); if (string.IsNullOrWhiteSpace(results.Content)) { throw new Exception("Empty response. Please, check the Passkey."); } try { var doc = XDocument.Parse(results.Content); var errorMsg = doc.Descendants("error").FirstOrDefault()?.Value; if (errorMsg?.Contains("No Results") == true) { return(releases); // no results } if (errorMsg != null) { throw new Exception(errorMsg); } var authkey = doc.Descendants("authkey").First().Value; var torrents = doc.Descendants("torrent"); foreach (var torrent in torrents) { var torrentName = torrent.FirstValue("name").Trim(); // the field <type> is always Movie, so we have to guess if it's a tv series var isSerie = torrentName.Contains(": Season "); if (isSerie) { torrentName = torrentName.Replace(": Season ", " S"); } // we have to guess if it's an audio track too var isAudio = string.IsNullOrWhiteSpace((string)torrent.Element("resolution")); var cat = new List <int> { isSerie?TorznabCatType.TVHD.ID: isAudio ? TorznabCatType.Audio.ID : TorznabCatType.MoviesHD.ID }; // if the category is not in the search categories, skip if (query.Categories.Any() && !query.Categories.Intersect(cat).Any()) { continue; } var title = new StringBuilder(torrentName); if (!isSerie && torrent.Element("year") != null) // only for movies { title.Append($" {torrent.FirstValue("year")}"); } if (torrent.Element("internal")?.Value == "1") { title.Append(" iNTERNAL"); } if (torrent.Element("resolution") != null) { title.Append($" {torrent.FirstValue("resolution")}"); } if (torrent.Element("media") != null) { title.Append($" {torrent.FirstValue("media")}"); } if (torrent.Element("encoding") != null) { title.Append($" {torrent.FirstValue("encoding")}"); } if (torrent.Element("audioformat") != null) { title.Append($" {torrent.FirstValue("audioformat")}"); } if (torrent.Element("releasegroup") != null) { title.Append($"-{torrent.FirstValue("releasegroup")}"); } var torrentId = torrent.FirstValue("id"); var groupId = torrent.FirstValue("groupid"); var comments = new Uri($"{TorrentUrl}?id={groupId}&torrentid={torrentId}"); var link = new Uri($"{TorrentUrl}?action=download&id={torrentId}&authkey={authkey}&torrent_pass={passkey}"); var publishDate = DateTime.Parse(torrent.FirstValue("time")); var size = long.Parse(torrent.FirstValue("size")); var grabs = int.Parse(torrent.FirstValue("snatched")); var seeders = int.Parse(torrent.FirstValue("seeders")); var peers = seeders + int.Parse(torrent.FirstValue("leechers")); var freeleech = double.Parse(torrent.FirstValue("freeleech")); Uri banner = null; string description = null; if (!isAudio) // audio tracks don't have banner either description { // small cover only for movies if (!isSerie && !string.IsNullOrWhiteSpace(torrent.Element("smallcover")?.Value)) { banner = new Uri(torrent.FirstValue("smallcover")); } else if (!string.IsNullOrWhiteSpace(torrent.Element("cover")?.Value)) { banner = new Uri(torrent.FirstValue("cover")); } description = torrent.Element("encodestatus") != null ? $"Encode status: {torrent.FirstValue("encodestatus")}" : null; } var imdb = ParseUtil.GetImdbID(torrent.Element("imdb")?.Value); var release = new ReleaseInfo { Title = title.ToString(), Comments = comments, Link = link, Guid = link, PublishDate = publishDate, Category = cat, BannerUrl = banner, Description = description, Imdb = imdb, Size = size, Grabs = grabs, Seeders = seeders, Peers = peers, MinimumRatio = 1, MinimumSeedTime = 259200, // 72 hours DownloadVolumeFactor = freeleech, UploadVolumeFactor = 1 }; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public IList <ReleaseInfo> ParseResponse(IndexerResponse indexerResponse) { var torrentInfos = new List <ReleaseInfo>(); var parser = new HtmlParser(); var doc = parser.ParseDocument(indexerResponse.Content); 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 = _settings.BaseUrl + qDetailsLink.GetAttribute("href"); var torrentId = qDetailsLink.GetAttribute("href").Split('=').Last(); var link = _settings.BaseUrl + "torrents.php?action=download&id=" + torrentId; var posterStr = qDetailsLink.GetAttribute("data-cover"); var poster = !string.IsNullOrWhiteSpace(posterStr) ? posterStr : null; var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent); var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent); var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent); var grabs = ParseUtil.CoerceInt(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 <IndexerCategory> { cat switch { "featurefilm" => NewznabStandardCategory.Movies, "shortfilm" => NewznabStandardCategory.Movies, "miniseries" => NewznabStandardCategory.TV, "other" => NewznabStandardCategory.Other, _ => throw new Exception($"Unknown category: {cat}") } }; // TODO: TMDb is also available var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]"); var imdb = qImdb != null?ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null; var release = new TorrentInfo { MinimumRatio = 1, MinimumSeedTime = 259200, Description = description, Title = title, PublishDate = publishDate, Categories = category, DownloadUrl = link, InfoUrl = details, PosterUrl = poster, Guid = link, ImdbId = imdb.GetValueOrDefault(), Seeders = seeders, Peers = leechers + seeders, Size = size, Grabs = grabs, Files = files, DownloadVolumeFactor = dlVolumeFactor, UploadVolumeFactor = 1 }; torrentInfos.Add(release); } return(torrentInfos.ToArray()); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var qc = new List <KeyValuePair <string, string> > // NameValueCollection don't support cat[]=19&cat[]=6 { { "tpage", "1" } }; if (query.IsImdbQuery) { qc.Add("search", query.ImdbID); qc.Add("nfo", "true"); } else if (!string.IsNullOrWhiteSpace(query.GetQueryString())) { qc.Add("search", query.GetQueryString()); } foreach (var cat in MapTorznabCapsToTrackers(query)) { qc.Add("cat[]", cat); } var searchUrl = SearchUrl + "?" + qc.GetQueryString(); var result = await RequestWithCookiesAndRetryAsync(searchUrl, referer : SearchUrl); if (result.IsRedirect) { throw new Exception($"Your cookie did not work. Please, configure the tracker again. Message: {result.ContentString}"); } if (!result.ContentString.StartsWith("{")) // not JSON => error { throw new ExceptionWithConfigData(result.ContentString, configData); } var json = JsonConvert.DeserializeObject <dynamic>(result.ContentString); try { var torrents = json["torrents"]; // latest torrents if (json["hits"] != null) // is search result { torrents = json.SelectTokens("$.hits[?(@._type == 'torrent')]._source"); } foreach (var torrent in torrents) { var torrentId = (long)torrent.id; var comments = new Uri(SiteLink + "torrents/" + torrentId); var link = new Uri(SiteLink + "download/" + torrentId); var publishDate = DateTime.Parse(torrent.added.ToString()); var imdbId = ParseUtil.GetImdbID(torrent.imdb_id.ToString()); Uri banner = null; if ((bool)torrent.poster) { if (torrent["imdb_id"] != null) { banner = new Uri(CdnUrl + "images/torrents/poster/imd/l/" + torrent["imdb_id"] + ".jpg"); } else if (torrent["cdu_id"] != null) { banner = new Uri(CdnUrl + "images/torrents/poster/cdu/b/" + torrent["cdu_id"] + "_front.jpg"); } else if (torrent["steam_id"] != null) { banner = new Uri(CdnUrl + "images/torrents/poster/ste/l/" + torrent["steam_id"] + ".jpg"); } } var descriptions = new List <string>(); var language = (string)torrent.language; if (!string.IsNullOrEmpty(language)) { descriptions.Add("Language: " + language); } else if ((bool?)torrent.polish == true) { descriptions.Add("Language: pl"); } var description = descriptions.Any() ? string.Join("<br />\n", descriptions) : null; var release = new ReleaseInfo { Title = torrent.name.ToString(), Comments = comments, Guid = comments, Link = link, PublishDate = publishDate, Category = MapTrackerCatToNewznab(torrent.category.ToString()), Size = (long)torrent.size, Grabs = (long)torrent.completed, Seeders = (int)torrent.seeders, Peers = (int)torrent.seeders + (int)torrent.leechers, Imdb = imdbId, BannerUrl = banner, Description = description, MinimumRatio = 1, MinimumSeedTime = 259200, // 72 hours (I can't verify this, but this is a safe value in most trackers) UploadVolumeFactor = 1, DownloadVolumeFactor = 1 }; releases.Add(release); } } catch (Exception ex) { OnParseError(result.ToString(), ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query, int attempts) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var queryCollection = new NameValueCollection(); // without this the API sometimes returns nothing queryCollection.Add("sort", "date_added"); queryCollection.Add("limit", "50"); if (query.ImdbID != null) { queryCollection.Add("query_term", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(searchString)) { searchString = searchString.Replace("'", ""); // ignore ' (e.g. search for america's Next Top Model) queryCollection.Add("query_term", searchString); } var searchUrl = ApiEndpoint + "?" + queryCollection.GetQueryString(); var response = await RequestStringWithCookiesAndRetry(searchUrl, string.Empty); try { // returned content might start with an html error message, remove it first var jsonStart = response.Content.IndexOf('{'); var jsonContentStr = response.Content.Remove(0, jsonStart); var jsonContent = JObject.Parse(jsonContentStr); string result = jsonContent.Value <string>("status"); if (result != "ok") // query was not successful { return(releases.ToArray()); } var data_items = jsonContent.Value <JToken>("data"); int movie_count = data_items.Value <int>("movie_count"); if (movie_count < 1) // no results found in query { return(releases.ToArray()); } var movies = data_items.Value <JToken>("movies"); if (movies == null) { throw new Exception("API error, movies missing"); } foreach (var movie_item in movies) { var torrents = movie_item.Value <JArray>("torrents"); if (torrents == null) { continue; } foreach (var torrent_info in torrents) { var release = new ReleaseInfo(); // append type: BRRip or WEBRip, resolves #3558 via #4577 var type = torrent_info.Value <string>("type"); switch (type) { case "web": type = " WEBRip"; break; default: type = " BRRip"; break; } var quality = torrent_info.Value <string>("quality"); release.Title = "[YTS] " + movie_item.Value <string>("title_long") + " " + quality + type; var imdb = movie_item.Value <string>("imdb_code"); release.Imdb = ParseUtil.GetImdbID(imdb); // API does not provide magnet link, so, construct it string magnet_uri = "magnet:?xt=urn:btih:" + torrent_info.Value <string>("hash") + "&dn=" + movie_item.Value <string>("slug") + "&tr=udp://open.demonii.com:1337/announce" + "&tr=udp://tracker.openbittorrent.com:80" + "&tr=udp://tracker.coppersurfer.tk:6969" + "&tr=udp://glotorrents.pw:6969/announce" + "&tr=udp://tracker.opentrackr.org:1337/announce" + "&tr=udp://torrent.gresille.org:80/announce" + "&tr=udp://p4p.arenabg.com:1337" + "&tr=udp://tracker.leechers-paradise.org:6969"; release.MagnetUri = new Uri(magnet_uri); release.InfoHash = torrent_info.Value <string>("hash"); // ex: 2015-08-16 21:25:08 +0000 var dateStr = torrent_info.Value <string>("date_uploaded"); var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime(); release.Link = new Uri(torrent_info.Value <string>("url")); release.Seeders = torrent_info.Value <int>("seeds"); release.Peers = torrent_info.Value <int>("peers") + release.Seeders; release.Size = torrent_info.Value <long>("size_bytes"); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; // 48 hours release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; release.Comments = new Uri(movie_item.Value <string>("url")); release.BannerUrl = new Uri(movie_item.Value <string>("large_cover_image")); release.Guid = release.Link; // map the quality to a newznab category for torznab compatibility (for Radarr, etc) switch (quality) { case "720p": release.Category = MapTrackerCatToNewznab("45"); break; case "1080p": release.Category = MapTrackerCatToNewznab("44"); break; case "2160p": release.Category = MapTrackerCatToNewznab("46"); break; case "3D": release.Category = MapTrackerCatToNewznab("47"); break; default: release.Category = MapTrackerCatToNewznab("45"); break; } releases.Add(release); } } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var queryCollection = new NameValueCollection { { "active", "0" }, { "category", string.Join(";", MapTorznabCapsToTrackers(query)) } }; if (query.IsImdbQuery) { queryCollection.Add("options", "2"); queryCollection.Add("search", query.ImdbIDShort); } else { queryCollection.Add("options", "0"); queryCollection.Add("search", query.GetQueryString()); } var response = await RequestWithCookiesAndRetryAsync(SearchUrl + queryCollection.GetQueryString()); try { var resultParser = new HtmlParser(); var searchResultDocument = resultParser.ParseDocument(response.ContentString); var rows = searchResultDocument.QuerySelectorAll("table.lista > tbody > tr"); foreach (var row in rows) { // this tracker has horrible markup, find the result rows by looking for the style tag before each one var prev = row.PreviousElementSibling; if (prev == null || !string.Equals(prev.NodeName, "style", StringComparison.OrdinalIgnoreCase)) { continue; } var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 86400; // 24 hours var qLink = row.Children[1].FirstElementChild; release.Title = qLink.TextContent.Trim(); release.Details = new Uri(SiteLink + qLink.GetAttribute("href")); release.Guid = release.Details; var imdbLink = row.Children[1].QuerySelector("a[href*=imdb]"); if (imdbLink != null) { release.Imdb = ParseUtil.GetImdbID(imdbLink.GetAttribute("href").Split('/').Last()); } var qDownload = row.Children[3].FirstElementChild; release.Link = new Uri(SiteLink + qDownload.GetAttribute("href")); var dateStr = row.Children[4].TextContent.Trim(); //"July 11, 2015, 13:34:09", "Today|Yesterday at 20:04:23" release.PublishDate = DateTimeUtil.FromUnknown(dateStr); var sizeStr = row.Children[5].TextContent; release.Size = ReleaseInfo.GetBytes(sizeStr); release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent); release.Peers = ParseUtil.CoerceInt(row.Children[8].TextContent) + release.Seeders; var grabs = row.QuerySelector("td:nth-child(10)").TextContent; grabs = grabs.Replace("---", "0"); release.Grabs = ParseUtil.CoerceInt(grabs); if (row.QuerySelector("img[title=\"FreeLeech\"]") != null) { release.DownloadVolumeFactor = 0; } else if (row.QuerySelector("img[src=\"images/sf.png\"]") != null) // side freeleech { release.DownloadVolumeFactor = 0; } else if (row.QuerySelector("img[title=\"Half FreeLeech\"]") != null) { release.DownloadVolumeFactor = 0.5; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; var qCat = row.QuerySelector("a[href^=\"index.php?page=torrents&category=\"]"); var cat = qCat.GetAttribute("href").Split('=')[2]; release.Category = MapTrackerCatToNewznab(cat); releases.Add(release); } } catch (Exception ex) { OnParseError(response.ContentString, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var queryUrl = SearchUrl; var cats = MapTorznabCapsToTrackers(query); if (cats.Count == 0) { cats = GetAllTrackerCategories(); } var catStr = string.Join(";", cats); queryUrl += "?" + catStr; if (!string.IsNullOrWhiteSpace(query.ImdbID)) { queryUrl += ";q=" + query.ImdbID; } else { queryUrl += ";q=" + WebUtilityHelpers.UrlEncode(searchString, Encoding); } var results = await RequestStringWithCookiesAndRetry(queryUrl); // Check for being logged out if (results.IsRedirect) { if (results.RedirectingTo.Contains("login.php")) { throw new ExceptionWithConfigData("Login failed, please reconfigure the tracker to update the cookies", configData); } else { throw new ExceptionWithConfigData(string.Format("Got a redirect to {0}, please adjust your the alternative link", results.RedirectingTo), configData); } } try { dynamic json = JsonConvert.DeserializeObject <dynamic>(results.Content); foreach (var torrent in json) { var release = new ReleaseInfo(); release.Title = torrent.name; if ((query.ImdbID == null || !TorznabCaps.SupportsImdbMovieSearch) && !query.MatchQueryStringAND(release.Title)) { continue; } release.MinimumRatio = 1; release.MinimumSeedTime = 172800; // 48 hours release.Category = MapTrackerCatToNewznab(torrent.c.ToString()); var torrentID = (long)torrent.t; release.Comments = new Uri(SiteLink + "details.php?id=" + torrentID); release.Guid = release.Comments; release.Link = new Uri(SiteLink + "download.php/" + torrentID + "/" + torrentID + ".torrent"); release.PublishDate = DateTimeUtil.UnixTimestampToDateTime((long)torrent.ctime).ToLocalTime(); release.Size = (long)torrent.size; release.Seeders = (int)torrent.seeders; release.Peers = release.Seeders + (int)torrent.leechers; release.Files = (long)torrent.files; release.Grabs = (long)torrent.completed; var imdbId = (string)torrent["imdb-id"]; release.Imdb = ParseUtil.GetImdbID(imdbId); var downloadMultiplier = (double?)torrent["download-multiplier"]; release.DownloadVolumeFactor = downloadMultiplier ?? 1; release.UploadVolumeFactor = 1; releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }
public async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query, int attempts) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); var queryCollection = new NameValueCollection(); if (query.ImdbID != null) { queryCollection.Add("query_term", query.ImdbID); } else if (!string.IsNullOrWhiteSpace(searchString)) { searchString = searchString.Replace("'", ""); // ignore ' (e.g. search for america's Next Top Model) queryCollection.Add("query_term", searchString); } var searchUrl = ApiEndpoint + "?" + queryCollection.GetQueryString(); var response = await RequestStringWithCookiesAndRetry(searchUrl, string.Empty); try { var jsonContent = JObject.Parse(response.Content); string result = jsonContent.Value <string>("status"); if (result != "ok") // query was not successful { return(releases.ToArray()); } var data_items = jsonContent.Value <JToken>("data"); int movie_count = data_items.Value <int>("movie_count"); if (movie_count < 1) // no results found in query { return(releases.ToArray()); } foreach (var movie_item in data_items.Value <JToken>("movies")) { var torrents = movie_item.Value <JArray>("torrents"); if (torrents == null) { continue; } foreach (var torrent_info in torrents) { var release = new ReleaseInfo(); // Append the quality to the title because thats how radarr seems to be determining the quality? // All releases are BRRips, see issue #2200 release.Title = movie_item.Value <string>("title_long") + " " + torrent_info.Value <string>("quality") + " BRRip"; var imdb = movie_item.Value <string>("imdb_code"); release.Imdb = ParseUtil.GetImdbID(imdb); // API does not provide magnet link, so, construct it string magnet_uri = "magnet:?xt=urn:btih:" + torrent_info.Value <string>("hash") + "&dn=" + movie_item.Value <string>("slug") + "&tr=udp://open.demonii.com:1337/announce" + "&tr=udp://tracker.openbittorrent.com:80" + "&tr=udp://tracker.coppersurfer.tk:6969" + "&tr=udp://glotorrents.pw:6969/announce" + "&tr=udp://tracker.opentrackr.org:1337/announce" + "&tr=udp://torrent.gresille.org:80/announce" + "&tr=udp://p4p.arenabg.com:1337" + "&tr=udp://tracker.leechers-paradise.org:6969"; release.MagnetUri = new Uri(magnet_uri); release.InfoHash = torrent_info.Value <string>("hash"); var dateStr = torrent_info.Value <long>("date_uploaded_unix"); var dateTime = DateTimeUtil.UnixTimestampToDateTime(dateStr); release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime(); release.Link = new Uri(torrent_info.Value <string>("url")); release.Seeders = torrent_info.Value <int>("seeds"); release.Peers = torrent_info.Value <int>("peers") + release.Seeders; release.Size = torrent_info.Value <long>("size_bytes"); release.DownloadVolumeFactor = 0; release.UploadVolumeFactor = 1; release.Comments = new Uri(movie_item.Value <string>("url")); release.BannerUrl = new Uri(movie_item.Value <string>("medium_cover_image")); release.Guid = release.Link; // API doesn't provide Categories, resolve via quality release.Category = MapTrackerCatToNewznab("1"); // default if (torrent_info.Value <string>("quality") == "720p") { release.Category = MapTrackerCatToNewznab("1"); } if (torrent_info.Value <string>("quality") == "1080p") { release.Category = MapTrackerCatToNewznab("2"); } if (torrent_info.Value <string>("quality") == "2160p") { release.Category = MapTrackerCatToNewznab("3"); } if (torrent_info.Value <string>("quality") == "3D") { release.Category = MapTrackerCatToNewznab("4"); } releases.Add(release); } } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = GetSearchString(query); var searchUrl = APIUrl; var queryCollection = new NameValueCollection(); if (!string.IsNullOrEmpty(query.ImdbID)) { queryCollection.Add("imdbid", query.ImdbID); } if (searchString != null) { queryCollection.Add("search", searchString); } queryCollection.Add("passkey", configData.Passkey.Value); queryCollection.Add("user", configData.Username.Value); searchUrl += "?" + queryCollection.GetQueryString(); var response = await RequestStringWithCookiesAndRetry(searchUrl); JObject json = null; try { json = JObject.Parse(response.Content); } catch (Exception ex) { throw new Exception("Error while parsing json: " + response.Content, ex); } var error = (string)json["error"]; if (error != null) { throw new Exception(error); } if ((int)json["total_results"] == 0) { return(releases); } try { foreach (JObject r in json["results"]) { var release = new ReleaseInfo(); release.Title = (string)r["release_name"]; release.Comments = new Uri((string)r["details_url"]); release.Link = new Uri((string)r["download_url"]); release.Guid = release.Link; release.Imdb = ParseUtil.GetImdbID((string)r["imdb_id"]); var freeleech = (bool)r["freeleech"]; if (freeleech) { release.DownloadVolumeFactor = 0; } else { release.DownloadVolumeFactor = 1; } release.UploadVolumeFactor = 1; var type = (string)r["type"]; release.Category = MapTrackerCatToNewznab(type); release.Size = (long?)r["size"] * 1024 * 1024; release.Seeders = (int?)r["seeders"]; release.Peers = release.Seeders + (int?)r["leechers"]; release.PublishDate = DateTimeUtil.FromUnknown((string)r["publish_date"]); releases.Add(release); } } catch (Exception ex) { OnParseError(response.Content, ex); } return(releases); }