Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        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")
            }));
        }
Ejemplo n.º 5
0
        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")
            });
        }
Ejemplo n.º 6
0
        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()));
            }
        }
Ejemplo n.º 7
0
        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);
        }
    }
Ejemplo n.º 8
0
        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);
        }