public static TorznabQuery ToTorznabQuery(ApiSearch request) { var stringQuery = new TorznabQuery(); stringQuery.QueryType = "search"; var queryStr = request.Query; if (queryStr != null) { var seasonMatch = Regex.Match(queryStr, @"S(\d{2,4})"); if (seasonMatch.Success) { stringQuery.Season = int.Parse(seasonMatch.Groups[1].Value); queryStr = queryStr.Remove(seasonMatch.Index, seasonMatch.Length); } var episodeMatch = Regex.Match(queryStr, @"E(\d{2,4}[A-Za-z]?)"); if (episodeMatch.Success) { stringQuery.Episode = episodeMatch.Groups[1].Value.TrimStart(new char[] { '0' }); queryStr = queryStr.Remove(episodeMatch.Index, episodeMatch.Length); } queryStr = queryStr.Trim(); } else { queryStr = ""; // empty string search is interpreted as null } stringQuery.SearchTerm = queryStr; stringQuery.Categories = request.Category; if (stringQuery.Categories == null) { stringQuery.Categories = new int[0]; } stringQuery.ExpandCatsToSubCats(); // try to build an IMDB Query (tt plus 6 to 8 digits) if (stringQuery.SanitizedSearchTerm.StartsWith("tt") && stringQuery.SanitizedSearchTerm.Length <= 10) { var imdbID = ParseUtil.GetFullImdbID(stringQuery.SanitizedSearchTerm); TorznabQuery imdbQuery = null; if (imdbID != null) { imdbQuery = new TorznabQuery() { ImdbID = imdbID, Categories = stringQuery.Categories, Season = stringQuery.Season, Episode = stringQuery.Episode, }; imdbQuery.ExpandCatsToSubCats(); return(imdbQuery); } } return(stringQuery); }
public static TorznabQuery ToTorznabQuery(ApiSearch request) { var stringQuery = new TorznabQuery(); var queryStr = request.Query; if (queryStr != null) { var seasonMatch = Regex.Match(queryStr, @"S(\d{2,4})"); if (seasonMatch.Success) { stringQuery.Season = int.Parse(seasonMatch.Groups[1].Value); queryStr = queryStr.Remove(seasonMatch.Index, seasonMatch.Length); } var episodeMatch = Regex.Match(queryStr, @"E(\d{2,4}[A-Za-z]?)"); if (episodeMatch.Success) { stringQuery.Episode = episodeMatch.Groups[1].Value; queryStr = queryStr.Remove(episodeMatch.Index, episodeMatch.Length); } queryStr = queryStr.Trim(); } stringQuery.SearchTerm = queryStr; stringQuery.Categories = request.Category == 0 ? new int[0] : new int[1] { request.Category }; stringQuery.ExpandCatsToSubCats(); // try to build an IMDB Query var imdbID = ParseUtil.GetFullImdbID(stringQuery.SanitizedSearchTerm); TorznabQuery imdbQuery = null; if (imdbID != null) { imdbQuery = new TorznabQuery() { ImdbID = imdbID, Categories = stringQuery.Categories, Season = stringQuery.Season, Episode = stringQuery.Episode, }; imdbQuery.ExpandCatsToSubCats(); return(imdbQuery); } return(stringQuery); }
public async Task <TorrentPotatoResponse> Potato([FromQuery] TorrentPotatoRequest request) { var result = await CurrentIndexer.ResultsForQuery(CurrentQuery); // Cache non query results if (string.IsNullOrEmpty(CurrentQuery.SanitizedSearchTerm)) { cacheService.CacheRssResults(CurrentIndexer, result.Releases); } // Log info if (string.IsNullOrWhiteSpace(CurrentQuery.SanitizedSearchTerm)) { logger.Info($"Found {result.Releases.Count()} torrentpotato releases from {CurrentIndexer.DisplayName}"); } else { logger.Info($"Found {result.Releases.Count()} torrentpotato releases from {CurrentIndexer.DisplayName} for: {CurrentQuery.GetQueryString()}"); } var serverUrl = serverService.GetServerUrl(Request); var potatoReleases = result.Releases.Where(r => r.Link != null || r.MagnetUri != null).Select(r => { var release = AutoMapper.Mapper.Map <ReleaseInfo>(r); release.Link = serverService.ConvertToProxyLink(release.Link, serverUrl, CurrentIndexer.ID, "dl", release.Title); var item = new TorrentPotatoResponseItem() { release_name = release.Title + "[" + CurrentIndexer.DisplayName + "]", // Suffix the indexer so we can see which tracker we are using in CPS as it just says torrentpotato >.> torrent_id = release.Guid.ToString(), details_url = release.Comments.ToString(), download_url = (release.Link != null ? release.Link.ToString() : release.MagnetUri.ToString()), imdb_id = release.Imdb.HasValue ? ParseUtil.GetFullImdbID("tt" + release.Imdb) : null, freeleech = (release.DownloadVolumeFactor == 0 ? true : false), type = "movie", size = (long)release.Size / (1024 * 1024), // This is in MB leechers = (release.Peers ?? -1) - (release.Seeders ?? 0), seeders = release.Seeders ?? -1, publish_date = r.PublishDate == DateTime.MinValue ? null : release.PublishDate.ToUniversalTime().ToString("s") }; return(item); }); var potatoResponse = new TorrentPotatoResponse() { results = potatoReleases.ToList() }; return(potatoResponse); }
public async Task <IHttpActionResult> Torznab([FromUri] Models.DTO.TorznabRequest request) { if (string.Equals(CurrentQuery.QueryType, "caps", StringComparison.InvariantCultureIgnoreCase)) { return(ResponseMessage(new HttpResponseMessage() { Content = new StringContent(CurrentIndexer.TorznabCaps.ToXml(), Encoding.UTF8, "application/xml") })); } if (CurrentQuery.ImdbID != null) { if (CurrentQuery.QueryType != "movie") { logger.Warn($"A non movie request with an imdbid was made from {Request.GetOwinContext().Request.RemoteIpAddress}."); return(GetErrorXML(201, "Incorrect parameter: only movie-search supports the imdbid parameter")); } if (!string.IsNullOrEmpty(CurrentQuery.SearchTerm)) { logger.Warn($"A movie-search request from {Request.GetOwinContext().Request.RemoteIpAddress} was made contining q and imdbid."); return(GetErrorXML(201, "Incorrect parameter: please specify either imdbid or q")); } CurrentQuery.ImdbID = ParseUtil.GetFullImdbID(CurrentQuery.ImdbID); // normalize ImdbID if (CurrentQuery.ImdbID == null) { logger.Warn($"A movie-search request from {Request.GetOwinContext().Request.RemoteIpAddress} was made with an invalid imdbid."); return(GetErrorXML(201, "Incorrect parameter: invalid imdbid format")); } if (!CurrentIndexer.TorznabCaps.SupportsImdbSearch) { logger.Warn($"A movie-search request with imdbid from {Request.GetOwinContext().Request.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it."); return(GetErrorXML(203, "Function Not Available: imdbid is not supported by this indexer")); } } var result = await CurrentIndexer.ResultsForQuery(CurrentQuery); // Some trackers do not support multiple category filtering so filter the releases that match manually. int?newItemCount = null; // Cache non query results if (string.IsNullOrEmpty(CurrentQuery.SanitizedSearchTerm)) { newItemCount = cacheService.GetNewItemCount(CurrentIndexer, result.Releases); cacheService.CacheRssResults(CurrentIndexer, result.Releases); } // Log info var logBuilder = new StringBuilder(); if (newItemCount != null) { logBuilder.AppendFormat("Found {0} ({1} new) releases from {2}", result.Releases.Count(), newItemCount, CurrentIndexer.DisplayName); } else { logBuilder.AppendFormat("Found {0} releases from {1}", result.Releases.Count(), CurrentIndexer.DisplayName); } if (!string.IsNullOrWhiteSpace(CurrentQuery.SanitizedSearchTerm)) { logBuilder.AppendFormat(" for: {0}", CurrentQuery.GetQueryString()); } logger.Info(logBuilder.ToString()); var serverUrl = serverService.GetServerUrl(Request); var resultPage = new ResultPage(new ChannelInfo { Title = CurrentIndexer.DisplayName, Description = CurrentIndexer.DisplayDescription, Link = new Uri(CurrentIndexer.SiteLink), ImageUrl = new Uri(serverUrl + "logos/" + CurrentIndexer.ID + ".png"), ImageTitle = CurrentIndexer.DisplayName, ImageLink = new Uri(CurrentIndexer.SiteLink), ImageDescription = CurrentIndexer.DisplayName }); var proxiedReleases = result.Releases.Select(r => AutoMapper.Mapper.Map <ReleaseInfo>(r)).Select(r => { r.Link = serverService.ConvertToProxyLink(r.Link, serverUrl, r.Origin.ID, "dl", r.Title + ".torrent"); return(r); }); resultPage.Releases = proxiedReleases.ToList(); var xml = resultPage.ToXml(new Uri(serverUrl)); // Force the return as XML return(ResponseMessage(new HttpResponseMessage() { Content = new StringContent(xml, Encoding.UTF8, "application/rss+xml") })); }
public async Task <HttpResponseMessage> Call(string indexerID) { var indexer = indexerService.GetIndexer(indexerID); var torznabQuery = TorznabQuery.FromHttpQuery(HttpUtility.ParseQueryString(Request.RequestUri.Query)); if (string.Equals(torznabQuery.QueryType, "caps", StringComparison.InvariantCultureIgnoreCase)) { return(new HttpResponseMessage() { Content = new StringContent(indexer.TorznabCaps.ToXml(), Encoding.UTF8, "application/xml") }); } torznabQuery.ExpandCatsToSubCats(); var allowBadApiDueToDebug = false; #if DEBUG allowBadApiDueToDebug = Debugger.IsAttached; #endif if (!allowBadApiDueToDebug && !string.Equals(torznabQuery.ApiKey, serverService.Config.APIKey, StringComparison.InvariantCultureIgnoreCase)) { logger.Warn(string.Format("A request from {0} was made with an incorrect API key.", Request.GetOwinContext().Request.RemoteIpAddress)); return(Request.CreateResponse(HttpStatusCode.Forbidden, "Incorrect API key")); } if (!indexer.IsConfigured) { logger.Warn(string.Format("Rejected a request to {0} which is unconfigured.", indexer.DisplayName)); return(Request.CreateResponse(HttpStatusCode.Forbidden, "This indexer is not configured.")); } if (torznabQuery.ImdbID != null) { if (torznabQuery.QueryType != "movie") { logger.Warn(string.Format("A non movie request with an imdbid was made from {0}.", Request.GetOwinContext().Request.RemoteIpAddress)); return(GetErrorXML(201, "Incorrect parameter: only movie-search supports the imdbid parameter")); } if (!string.IsNullOrEmpty(torznabQuery.SearchTerm)) { logger.Warn(string.Format("A movie-search request from {0} was made contining q and imdbid.", Request.GetOwinContext().Request.RemoteIpAddress)); return(GetErrorXML(201, "Incorrect parameter: please specify either imdbid or q")); } torznabQuery.ImdbID = ParseUtil.GetFullImdbID(torznabQuery.ImdbID); // normalize ImdbID if (torznabQuery.ImdbID == null) { logger.Warn(string.Format("A movie-search request from {0} was made with an invalid imdbid.", Request.GetOwinContext().Request.RemoteIpAddress)); return(GetErrorXML(201, "Incorrect parameter: invalid imdbid format")); } if (!indexer.TorznabCaps.SupportsImdbSearch) { logger.Warn(string.Format("A movie-search request with imdbid from {0} was made but the indexer {1} doesn't support it.", Request.GetOwinContext().Request.RemoteIpAddress, indexer.DisplayName)); return(GetErrorXML(203, "Function Not Available: imdbid is not supported by this indexer")); } } var releases = await indexer.PerformQuery(torznabQuery); releases = indexer.CleanLinks(releases); // Some trackers do not keep their clocks up to date and can be ~20 minutes out! foreach (var release in releases.Where(r => r.PublishDate > DateTime.Now)) { release.PublishDate = DateTime.Now; } // Some trackers do not support multiple category filtering so filter the releases that match manually. var filteredReleases = releases = indexer.FilterResults(torznabQuery, releases); int?newItemCount = null; // Cache non query results if (string.IsNullOrEmpty(torznabQuery.SanitizedSearchTerm)) { newItemCount = cacheService.GetNewItemCount(indexer, filteredReleases); cacheService.CacheRssResults(indexer, releases); } // Log info var logBuilder = new StringBuilder(); if (newItemCount != null) { logBuilder.AppendFormat(string.Format("Found {0} ({1} new) releases from {2}", releases.Count(), newItemCount, indexer.DisplayName)); } else { logBuilder.AppendFormat(string.Format("Found {0} releases from {1}", releases.Count(), indexer.DisplayName)); } if (!string.IsNullOrWhiteSpace(torznabQuery.SanitizedSearchTerm)) { logBuilder.AppendFormat(" for: {0}", torznabQuery.GetQueryString()); } logger.Info(logBuilder.ToString()); var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath()); var resultPage = new ResultPage(new ChannelInfo { Title = indexer.DisplayName, Description = indexer.DisplayDescription, Link = new Uri(indexer.SiteLink), ImageUrl = new Uri(serverUrl + "logos/" + indexer.ID + ".png"), ImageTitle = indexer.DisplayName, ImageLink = new Uri(indexer.SiteLink), ImageDescription = indexer.DisplayName }); foreach (var result in releases) { var clone = Mapper.Map <ReleaseInfo>(result); clone.Link = serverService.ConvertToProxyLink(clone.Link, serverUrl, indexerID, "dl", result.Title + ".torrent"); resultPage.Releases.Add(clone); } var xml = resultPage.ToXml(new Uri(serverUrl)); // Force the return as XML return(new HttpResponseMessage() { Content = new StringContent(xml, Encoding.UTF8, "application/rss+xml") }); }
public async Task <IActionResult> Torznab([FromQuery] TorznabRequest request) { if (string.Equals(CurrentQuery.QueryType, "caps", StringComparison.InvariantCultureIgnoreCase)) { return(Content(CurrentIndexer.TorznabCaps.ToXml(), "application/rss+xml", Encoding.UTF8)); } // indexers - returns a list of all included indexers (meta indexers only) if (string.Equals(CurrentQuery.QueryType, "indexers", StringComparison.InvariantCultureIgnoreCase)) { if (!(CurrentIndexer is BaseMetaIndexer)) // shouldn't be needed because CanHandleQuery should return false { logger.Warn($"A search request with t=indexers from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} isn't a meta indexer."); return(GetErrorXML(203, "Function Not Available: this isn't a meta indexer")); } var CurrentBaseMetaIndexer = (BaseMetaIndexer)CurrentIndexer; var indexers = CurrentBaseMetaIndexer.Indexers; if (string.Equals(request.configured, "true", StringComparison.InvariantCultureIgnoreCase)) { indexers = indexers.Where(i => i.IsConfigured); } else if (string.Equals(request.configured, "false", StringComparison.InvariantCultureIgnoreCase)) { indexers = indexers.Where(i => !i.IsConfigured); } var xdoc = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement("indexers", from i in indexers select new XElement("indexer", new XAttribute("id", i.ID), new XAttribute("configured", i.IsConfigured), new XElement("title", i.DisplayName), new XElement("description", i.DisplayDescription), new XElement("link", i.SiteLink), new XElement("language", i.Language), new XElement("type", i.Type), i.TorznabCaps.GetXDocument().FirstNode ) ) ); return(Content(xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString(), "application/xml", Encoding.UTF8)); } if (CurrentQuery.ImdbID != null) { if (!string.IsNullOrEmpty(CurrentQuery.SearchTerm)) { logger.Warn($"A search request from {Request.HttpContext.Connection.RemoteIpAddress} was made containing q and imdbid."); return(GetErrorXML(201, "Incorrect parameter: please specify either imdbid or q")); } CurrentQuery.ImdbID = ParseUtil.GetFullImdbID(CurrentQuery.ImdbID); // normalize ImdbID if (CurrentQuery.ImdbID == null) { logger.Warn($"A search request from {Request.HttpContext.Connection.RemoteIpAddress} was made with an invalid imdbid."); return(GetErrorXML(201, "Incorrect parameter: invalid imdbid format")); } if (!CurrentIndexer.TorznabCaps.SupportsImdbSearch) { logger.Warn($"A search request with imdbid from {Request.HttpContext.Connection.RemoteIpAddress} was made but the indexer {CurrentIndexer.DisplayName} doesn't support it."); return(GetErrorXML(203, "Function Not Available: imdbid is not supported by this indexer")); } } try { var result = await CurrentIndexer.ResultsForQuery(CurrentQuery); // Some trackers do not support multiple category filtering so filter the releases that match manually. int?newItemCount = null; // Cache non query results if (string.IsNullOrEmpty(CurrentQuery.SanitizedSearchTerm)) { newItemCount = cacheService.GetNewItemCount(CurrentIndexer, result.Releases); cacheService.CacheRssResults(CurrentIndexer, result.Releases); } // Log info var logBuilder = new StringBuilder(); if (newItemCount != null) { logBuilder.AppendFormat("Found {0} ({1} new) releases from {2}", result.Releases.Count(), newItemCount, CurrentIndexer.DisplayName); } else { logBuilder.AppendFormat("Found {0} releases from {1}", result.Releases.Count(), CurrentIndexer.DisplayName); } if (!string.IsNullOrWhiteSpace(CurrentQuery.SanitizedSearchTerm)) { logBuilder.AppendFormat(" for: {0}", CurrentQuery.GetQueryString()); } logger.Info(logBuilder.ToString()); var serverUrl = serverService.GetServerUrl(Request); var resultPage = new ResultPage(new ChannelInfo { Title = CurrentIndexer.DisplayName, Description = CurrentIndexer.DisplayDescription, Link = new Uri(CurrentIndexer.SiteLink), ImageUrl = new Uri(serverUrl + "logos/" + CurrentIndexer.ID + ".png"), ImageTitle = CurrentIndexer.DisplayName, ImageLink = new Uri(CurrentIndexer.SiteLink), ImageDescription = CurrentIndexer.DisplayName }); var proxiedReleases = result.Releases.Select(r => AutoMapper.Mapper.Map <ReleaseInfo>(r)).Select(r => { r.Link = serverService.ConvertToProxyLink(r.Link, serverUrl, r.Origin.ID, "dl", r.Title); return(r); }); resultPage.Releases = proxiedReleases.ToList(); var xml = resultPage.ToXml(new Uri(serverUrl)); // Force the return as XML return(Content(xml, "application/rss+xml", Encoding.UTF8)); } catch (Exception ex) { logger.Error(ex); return(GetErrorXML(900, ex.ToString())); } }
public ManualSearchResult Search([FromBody] AdminSearch value) { var results = new List <TrackerCacheResult>(); var stringQuery = new TorznabQuery() { SearchTerm = value.Query, Categories = value.Category == 0 ? new int[0] : new int[1] { value.Category } }; stringQuery.ExpandCatsToSubCats(); // try to build an IMDB Query var imdbID = ParseUtil.GetFullImdbID(stringQuery.SanitizedSearchTerm); TorznabQuery imdbQuery = null; if (imdbID != null) { imdbQuery = new TorznabQuery() { ImdbID = imdbID, Categories = stringQuery.Categories }; imdbQuery.ExpandCatsToSubCats(); } var trackers = indexerService.GetAllIndexers().Where(t => t.IsConfigured).ToList(); if (!string.IsNullOrWhiteSpace(value.Tracker)) { trackers = trackers.Where(t => t.ID == value.Tracker).ToList(); } if (value.Category != 0) { trackers = trackers.Where(t => t.TorznabCaps.Categories.Select(c => c.ID).Contains(value.Category)).ToList(); } Parallel.ForEach(trackers.ToList(), indexer => { try { var query = stringQuery; // use imdb Query for trackers which support it if (imdbQuery != null && indexer.TorznabCaps.SupportsImdbSearch) { query = imdbQuery; } var searchResults = indexer.PerformQuery(query).Result; searchResults = indexer.CleanLinks(searchResults); cacheService.CacheRssResults(indexer, searchResults); searchResults = indexer.FilterResults(query, searchResults); lock (results) { foreach (var result in searchResults) { var item = Mapper.Map <TrackerCacheResult>(result); item.Tracker = indexer.DisplayName; item.TrackerId = indexer.ID; item.Peers = item.Peers - item.Seeders; // Use peers as leechers results.Add(item); } } } catch (Exception e) { logger.Error(e, "An error occured during manual search on " + indexer.DisplayName + ": " + e.Message); } }); ConfigureCacheResults(results); if (trackers.Count > 1) { results = results.OrderByDescending(d => d.PublishDate).ToList(); } var manualResult = new ManualSearchResult() { Results = results, Indexers = trackers.Select(t => t.DisplayName).ToList() }; if (manualResult.Indexers.Count == 0) { manualResult.Indexers = new List <string>() { "None" } } ; logger.Info(string.Format("Manual search for \"{0}\" on {1} with {2} results.", stringQuery.GetQueryString(), string.Join(", ", manualResult.Indexers), manualResult.Results.Count)); return(manualResult); } }
protected override async Task <IEnumerable <ReleaseInfo> > PerformQuery(TorznabQuery query) { var releases = new List <ReleaseInfo>(); var searchString = query.GetQueryString(); searchString = Regex.Replace(searchString, @"(^|\s)-", " "); // remove dashes at the beginning of keywords as they exclude search strings (see issue #3096) var searchUrl = SearchUrl; var imdbId = ParseUtil.GetFullImdbID(query.ImdbID); if (imdbId != null) { searchUrl += "imdbID/" + imdbId + "/"; } else if (!string.IsNullOrWhiteSpace(searchString)) { searchUrl += "query/" + WebUtility.UrlEncode(searchString) + "/"; } string.Format(SearchUrl, WebUtility.UrlEncode(searchString)); var cats = MapTorznabCapsToTrackers(query); if (cats.Count > 0) { searchUrl += "categories/"; foreach (var cat in cats) { if (!searchUrl.EndsWith("/")) { searchUrl += ","; } searchUrl += cat; } } else { searchUrl += "newfilter/2"; // include 0day and music } var results = await RequestStringWithCookiesAndRetry(searchUrl); if (results.Content.Contains("/user/account/login")) { //Cookie appears to expire after a period of time or logging in to the site via browser await DoLogin(); results = await RequestStringWithCookiesAndRetry(searchUrl); } try { dynamic jsonObj = JsonConvert.DeserializeObject(results.Content); foreach (var torrent in jsonObj.torrentList) { var release = new ReleaseInfo(); release.MinimumRatio = 1; release.MinimumSeedTime = 172800; // 48 hours release.Guid = new Uri(SiteLink + "torrent/" + torrent.fid); release.Comments = release.Guid; release.Title = torrent.name; if (!query.MatchQueryStringAND(release.Title)) { continue; } release.Link = new Uri(SiteLink + "download/" + torrent.fid + "/" + torrent.filename); release.PublishDate = DateTime.ParseExact(torrent.addedTimestamp.ToString(), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal); release.Size = (long)torrent.size; release.Seeders = ParseUtil.CoerceInt(torrent.seeders.ToString()); release.Peers = release.Seeders + ParseUtil.CoerceInt(torrent.leechers.ToString()); release.Category = MapTrackerCatToNewznab(torrent.categoryID.ToString()); release.Grabs = ParseUtil.CoerceInt(torrent.completed.ToString()); release.Imdb = ParseUtil.GetImdbID(torrent.imdbID.ToString()); release.DownloadVolumeFactor = 1; release.UploadVolumeFactor = 1; // freeleech #6579 #6624 release.DownloadVolumeFactor = ParseUtil.CoerceInt(torrent.download_multiplier.ToString()); releases.Add(release); } } catch (Exception ex) { OnParseError(results.Content, ex); } return(releases); }