#include "routes.h" #include "../numberhelper.h" #include "../servehelper.h" #include "../pixivclient.h" static inline Element generate_header(const httplib::Request& req, const Config& config, const SearchResults& search_results, const std::string& query, const std::vector& tags, const std::string& order, const std::vector& search_suggestions); static inline Element generate_search_suggestions(const httplib::Request& req, const Config& config, const std::vector& tags, const std::vector& search_suggestions, bool open_by_default); static std::string tags_to_string(const std::unordered_map& tag_translations, const std::vector& tags); static inline std::vector split(const std::string& str, char c); static inline std::string join(const std::vector& items, char c); void tags_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client) { std::string query = blankie::murl::unescape(req.matches.str(1)); std::string order = req.has_param("order") ? req.get_param_value("order") : "date_d"; unsigned long long page = req.has_param("p") ? to_ull(req.get_param_value("p")) - 1 : 0; std::vector tags = split(query, ' '); if (tags.empty()) { res.status = 400; serve_error(req, res, config, "400: Bad Request", "Empty search query"); return; } SearchResults search_results; try { search_results = pixiv_client.search_illusts(query, page, order); } catch (const std::exception& e) { res.status = 500; serve_error(req, res, config, "500: Internal server error", "Failed to search for illusts", e.what()); return; } std::vector search_suggestions; try { search_suggestions = pixiv_client.get_search_suggestions(tags.back()); } catch (const std::exception& e) { res.status = 500; serve_error(req, res, config, "500: Internal server error", "Failed to get search suggestions", e.what()); return; } Element body("body", { generate_header(req, config, search_results, query, tags, order, search_suggestions) }); if (!search_results.illusts.illusts.empty()) { body.nodes.push_back(Element("br")); body.nodes.push_back(generate_illusts_pager(req, config, search_results.illusts, page, "illusts")); } serve(req, res, config, tags_to_string(std::move(search_results.tag_translations), std::move(tags)), std::move(body)); } static inline Element generate_header(const httplib::Request& req, const Config& config, const SearchResults& search_results, const std::string& query, const std::vector& tags, const std::string& order, const std::vector& search_suggestions) { auto sort_element = [&](const char* title, const char* new_order) { std::string url = get_origin(req, config) + "/tags/" + blankie::murl::escape(query) + "/illustrations?order=" + new_order; Element ret("a", {{"href", std::move(url)}}, {title}); if (new_order == order) { ret = Element("b", {std::move(ret)}); } return ret; }; Element header("header", {{"class", "center"}}, { Element("form", {{"method", "get"}, {"action", get_origin(req, config) + "/search"}}, { Element("input", {{"name", "q"}, {"required", ""}, {"value", query}}, {}), " ", Element("button", {"Search for illustrations"}) }) }); if (!search_suggestions.empty()) { header.nodes.push_back(generate_search_suggestions(req, config, tags, search_suggestions, search_results.illusts.total_pages <= 1)); } header.nodes.push_back(Element("br")); if (search_results.illusts.total_illusts != 1) { header.nodes.push_back("There are "); header.nodes.push_back(std::to_string(search_results.illusts.total_illusts)); header.nodes.push_back(" illustrations"); } else { header.nodes.push_back("There is 1 illustration"); } header.nodes.push_back(" of "); header.nodes.push_back(Element("b", {tags_to_string(search_results.tag_translations, tags)})); header.nodes.push_back(Element("br")); header.nodes.push_back("Sort by: "); header.nodes.push_back(sort_element("Newest", "date_d")); header.nodes.push_back(" "); header.nodes.push_back(sort_element("Oldest", "date")); return header; } static inline Element generate_search_suggestions(const httplib::Request& req, const Config& config, const std::vector& tags, const std::vector& search_suggestions, bool open_by_default) { std::vector ul_nodes; ul_nodes.reserve(search_suggestions.size()); for (const SearchSuggestion& search_suggestion : search_suggestions) { std::string text = search_suggestion.tag; if (search_suggestion.english_tag) { text += " ("; text += *search_suggestion.english_tag; text += ')'; } std::vector new_tags = tags; new_tags.pop_back(); new_tags.push_back(search_suggestion.tag); std::string url = get_origin(req, config) + "/tags/" + blankie::murl::escape(join(new_tags, ' ')); ul_nodes.push_back(Element("li", { Element("a", {{"href", std::move(url)}}, {std::move(text)}) })); } Element details("details", { Element("summary", {"Search suggestions"}), Element("ul", {{"class", "searchsuggestions"}}, ul_nodes) }); if (open_by_default) { details.attributes.push_back({"open", ""}); } return details; } static std::string tags_to_string(const std::unordered_map& tag_translations, const std::vector& tags) { std::string str; for (const std::string& tag : tags) { if (!str.empty()) { str += ' '; } str += '#'; auto translated_tag = tag_translations.find(tag); str += translated_tag != tag_translations.cend() ? translated_tag->second : tag; } return str; } static inline std::vector split(const std::string& str, char c) { std::vector ret; size_t pos = 0; size_t last_pos = 0; while ((pos = str.find(c, pos)) != std::string::npos) { if (pos - last_pos > 0) { ret.push_back(str.substr(last_pos, pos - last_pos)); } pos++; last_pos = pos; } if (str.size() > last_pos) { ret.push_back(str.substr(last_pos)); } return ret; } static inline std::string join(const std::vector& items, char c) { std::string ret; for (size_t i = 0; i < items.size(); i++) { if (i) { ret += c; } ret += items[i]; } return ret; }