2023-05-01 16:40:33 +00:00
|
|
|
#include "routes.h"
|
|
|
|
#include "../numberhelper.h"
|
|
|
|
#include "../servehelper.h"
|
|
|
|
#include "../pixivclient.h"
|
|
|
|
|
|
|
|
static inline Element generate_header(const httplib::Request& req, const Config& config,
|
2023-05-02 08:24:27 +00:00
|
|
|
const SearchResults& search_results, const std::string& query, const std::vector<std::string>& tags, const std::string& order,
|
|
|
|
const std::vector<SearchSuggestion>& search_suggestions);
|
|
|
|
static inline Element generate_search_suggestions(const httplib::Request& req, const Config& config,
|
|
|
|
const std::vector<std::string>& tags, const std::vector<SearchSuggestion>& search_suggestions, bool open_by_default);
|
2023-05-01 16:40:33 +00:00
|
|
|
|
|
|
|
static std::string tags_to_string(const std::unordered_map<std::string, std::string>& tag_translations, const std::vector<std::string>& tags);
|
|
|
|
static inline std::vector<std::string> split(const std::string& str, char c);
|
2023-05-02 08:24:27 +00:00
|
|
|
static inline std::string join(const std::vector<std::string>& items, char c);
|
2023-05-01 16:40:33 +00:00
|
|
|
|
|
|
|
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<std::string> 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;
|
|
|
|
}
|
2023-05-02 08:24:27 +00:00
|
|
|
std::vector<SearchSuggestion> 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;
|
|
|
|
}
|
2023-05-01 16:40:33 +00:00
|
|
|
|
|
|
|
Element body("body", {
|
2023-05-02 08:48:45 +00:00
|
|
|
generate_header(req, config, search_results, query, tags, order, search_suggestions)
|
2023-05-01 16:40:33 +00:00
|
|
|
});
|
2023-05-02 08:48:45 +00:00
|
|
|
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"));
|
|
|
|
}
|
2023-05-01 16:40:33 +00:00
|
|
|
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,
|
2023-05-02 08:24:27 +00:00
|
|
|
const SearchResults& search_results, const std::string& query, const std::vector<std::string>& tags, const std::string& order,
|
|
|
|
const std::vector<SearchSuggestion>& search_suggestions) {
|
2023-05-01 16:40:33 +00:00
|
|
|
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"})
|
2023-05-02 08:24:27 +00:00
|
|
|
})
|
2023-05-01 16:40:33 +00:00
|
|
|
});
|
2023-05-02 08:24:27 +00:00
|
|
|
if (!search_suggestions.empty()) {
|
2023-05-02 08:50:18 +00:00
|
|
|
header.nodes.push_back(generate_search_suggestions(req, config, tags, search_suggestions, search_results.illusts.total_pages <= 1));
|
2023-05-02 08:24:27 +00:00
|
|
|
}
|
|
|
|
header.nodes.push_back(Element("br"));
|
|
|
|
|
2023-05-01 16:40:33 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-05-02 08:24:27 +00:00
|
|
|
static inline Element generate_search_suggestions(const httplib::Request& req, const Config& config,
|
|
|
|
const std::vector<std::string>& tags, const std::vector<SearchSuggestion>& search_suggestions, bool open_by_default) {
|
|
|
|
std::vector<blankie::html::Node> 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<std::string> 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"}),
|
2023-05-12 10:13:55 +00:00
|
|
|
Element("ul", {{"class", "search_results-suggestions"}}, ul_nodes)
|
2023-05-02 08:24:27 +00:00
|
|
|
});
|
|
|
|
if (open_by_default) {
|
|
|
|
details.attributes.push_back({"open", ""});
|
|
|
|
}
|
|
|
|
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
2023-05-01 16:40:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
static std::string tags_to_string(const std::unordered_map<std::string, std::string>& tag_translations, const std::vector<std::string>& 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<std::string> split(const std::string& str, char c) {
|
|
|
|
std::vector<std::string> 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;
|
|
|
|
}
|
2023-05-02 08:24:27 +00:00
|
|
|
|
|
|
|
static inline std::string join(const std::vector<std::string>& items, char c) {
|
|
|
|
std::string ret;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < items.size(); i++) {
|
|
|
|
if (i) {
|
|
|
|
ret += c;
|
|
|
|
}
|
|
|
|
ret += items[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|