public const int MAX_LOAD_RESULTS = 61; // the max one result will have. if have at least this much then there is more. /// <summary> /// Searches Bloodcat for a string with some params /// </summary> public static async Task <SearchResultSet> Search(string query, SearchFilters.OsuRankStatus rankedFilter, SearchFilters.OsuModes modeFilter, SearchFilters.BloodcatIdFilter?bloodcatNumbersFilter, int page = 1) { // rank status filter = s // mode filter = m // when there are numebrs only special filter = c string sParam; if (rankedFilter == SearchFilters.OsuRankStatus.RankedAndApproved) { sParam = "1,2"; } else if (rankedFilter == SearchFilters.OsuRankStatus.All) { sParam = ""; // none needed for all } else if (rankedFilter == SearchFilters.OsuRankStatus.Unranked) { sParam = "0"; } else { sParam = ((int)rankedFilter).ToString(); } // build query string -- https://stackoverflow.com/questions/17096201/build-query-string-for-system-net-httpclient-get var qs = HttpUtility.ParseQueryString(string.Empty); qs["mod"] = "json"; qs["q"] = query; qs["s"] = sParam; qs["m"] = modeFilter == SearchFilters.OsuModes.All ? "" : ((int)modeFilter).ToString(); if (bloodcatNumbersFilter != null) { qs["c"] = ((int)bloodcatNumbersFilter).ToString(); } qs["p"] = page.ToString(); var data = await Web.GetJson <JArray>("http://bloodcat.com/osu/?" + qs.ToString()); var standardized = data.Select(b => StandardizeToSetStruct((JObject)b)); int count = standardized.Count(); return(new SearchResultSet(standardized, (count >= MAX_LOAD_RESULTS))); }
/// <summary> /// Searches the official beatmap listing for beatmaps. /// </summary> public static async Task <SearchResultSet> Search(string query, SearchFilters.OsuRankStatus rankedFilter = SearchFilters.OsuRankStatus.All, SearchFilters.OsuModes modeFilter = SearchFilters.OsuModes.All, int page = 1) { // hmm, this isnt exactly ideal now, but lets just roll with it i guess // there is no actual way to tell if cookies are expired are not unfortunately if (page == 1) { await CheckLoginCookie(); } // ranked filter = s // mode filter = m string rParam; if (rankedFilter == SearchFilters.OsuRankStatus.Approved) { rParam = "1"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Loved) { rParam = "8"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Pending) { rParam = "4"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Graveyard) { rParam = "5"; } else { rParam = "7"; // all } // Search time. Need to use cookies. // Same as bloodcat, construct QS var qs = HttpUtility.ParseQueryString(string.Empty); qs["q"] = query; if (rankedFilter != SearchFilters.OsuRankStatus.RankedAndApproved) { qs["s"] = rParam; } if (modeFilter != SearchFilters.OsuModes.All) { qs["m"] = ((int)modeFilter).ToString(); } if (page > 1) { qs["page"] = page.ToString(); } // p much same as bloodcat here, json apis ftw! // we cant use web.getjson as cookies. using (var handler = new HttpClientHandler() { CookieContainer = Cookies }) using (var client = new HttpClient(handler)) { var rawData = await client.GetStringAsync("https://osu.ppy.sh/beatmapsets/search?" + qs.ToString()); var data = JsonConvert.DeserializeObject <JArray>(rawData); var standardized = data.Select(b => StandardizeToSetStruct((JObject)b)); int count = standardized.Count(); return(new SearchResultSet(standardized, (count >= 50))); // 50 is the max load } }
/// <summary> /// Searches the official beatmap listing for beatmaps. /// </summary> public static async Task <SearchResultSet> Search(string query, SearchFilters.OsuRankStatus rankedFilter, SearchFilters.OsuModes modeFilter, int page = 1) { // ranked filter = r // mode filter = m string rParam; // Ranked filter into website param if (rankedFilter == SearchFilters.OsuRankStatus.RankedAndApproved) { rParam = "0"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Approved) { rParam = "6"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Qualified) { rParam = "11"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Loved) { rParam = "12"; } else { rParam = "4"; } // Search time. Need to use cookies. // Same as bloodcat, construct QS var qs = HttpUtility.ParseQueryString(string.Empty); qs["q"] = query; qs["m"] = ((int)modeFilter).ToString(); qs["r"] = rParam; if (page > 1) { qs["page"] = page.ToString(); } string rawData = await GetRawWithCookies("https://osu.ppy.sh/p/beatmaplist?" + qs.ToString()); // Check if still logged in if (rawData.Contains("Please enter your credentials")) { throw new CookiesExpiredException(); } // Parse. var htmlDoc = new HtmlAgilityPack.HtmlDocument(); htmlDoc.OptionUseIdAttribute = true; htmlDoc.LoadHtml(rawData); HtmlAgilityPack.HtmlNodeCollection beatmapNodes = htmlDoc.DocumentNode.SelectNodes("//div[@class='beatmapListing']/div[@class='beatmap']"); if (beatmapNodes == null) { return(new SearchResultSet(new List <BeatmapSet>(), false)); // empty } var sets = beatmapNodes.Select(b => { var difficulties = new Dictionary <string, string>(); try { int i = 1; foreach (var d in b.SelectNodes("div[@class='left-aligned']/div[starts-with(@class, 'difficulties')]/div")) { string _d = d.Attributes["class"].Value.Replace("diffIcon ", ""); if (_d.Contains("-t")) { _d = "1"; // taiko } else if (_d.Contains("-f")) { _d = "2"; // ctb } else if (_d.Contains("-m")) { _d = "3"; // mania } else { _d = "0"; // standard } difficulties.Add(i.ToString(), _d); i++; } } catch { } // rip // we can only base this off that green/red bar, lol -- or the search filter string rankStatus; if (rankedFilter == SearchFilters.OsuRankStatus.Loved) { rankStatus = "Loved"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Approved) { rankStatus = "Approved"; } else if (rankedFilter == SearchFilters.OsuRankStatus.Qualified) { rankStatus = "Qualified"; } else if (rankedFilter == SearchFilters.OsuRankStatus.RankedAndApproved) { rankStatus = "Ranked/Approved"; } else if (b.SelectSingleNode("div[@class='right-aligned']/div[@class='rating']") != null) { rankStatus = "Ranked/Apprv./Quali./Loved"; } else { rankStatus = "Pending/Graveyard"; } return(new BeatmapSet( b.Id, TryGetNodeText(b, "div[@class='maintext']/span[@class='artist']"), TryGetNodeText(b, "div[@class='maintext']/a[@class='title']"), TryGetNodeText(b, "div[@class='left-aligned']/div[1]/a"), rankStatus, difficulties, null )); }); bool canLoadMore = htmlDoc.DocumentNode.SelectNodes("//div[@class='pagination']") .Descendants("a") .Any(d => d.InnerText == "Next"); return(new SearchResultSet(sets, canLoadMore)); }