pixwhile/routes/artworks.cpp

212 lines
8.1 KiB
C++
Raw Normal View History

2023-04-09 17:08:01 +00:00
#include <regex>
2023-04-09 13:31:50 +00:00
#include "routes.h"
2023-04-09 17:08:01 +00:00
#include "../blankie/murl.h"
2023-04-09 13:31:50 +00:00
#include "../servehelper.h"
#include "../numberhelper.h"
#include "../pixivclient.h"
static inline Element generate_user_link(const httplib::Request& req, const Config& config, const Illust& illust);
static inline Element generate_images(const httplib::Request& req, const Config& config, const Illust& illust);
2023-04-09 13:50:44 +00:00
static inline Element generate_preview_images(const httplib::Request& req, const Config& config, const Illust& illust);
2023-04-09 17:34:13 +00:00
static inline std::vector<blankie::html::Node> parse_description_line(const httplib::Request& req, const Config& config, std::string str);
static inline Element generate_description(const httplib::Request& req, const Config& config, const std::string& description);
2023-04-09 14:30:07 +00:00
static inline Element generate_illust_tags(const Illust& illust);
2023-04-09 13:31:50 +00:00
2023-04-09 17:08:01 +00:00
static inline bool is_true(const std::string& str);
static inline std::string time_to_string(time_t time);
2023-04-10 12:55:32 +00:00
static inline std::string proxy_pixiv_url(const httplib::Request& req, const Config& config, blankie::murl::Url url);
2023-04-09 17:08:01 +00:00
2023-04-09 13:31:50 +00:00
void artworks_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client) {
uint64_t illust_id = to_ull(req.matches.str(1));
2023-04-09 13:50:44 +00:00
bool preview = is_true(req.get_param_value("preview"));
2023-04-09 13:31:50 +00:00
Illust illust;
try {
illust = pixiv_client.get_illust(illust_id);
} catch (const PixivException& e) {
if (e.status == 404) {
res.status = 404;
serve_error(req, res, config, "404: Illust not found", e.what());
} else {
res.status = 500;
serve_error(req, res, config, "500: Internal server error", "Failed to fetch illust information", e.what());
}
return;
} catch (const std::exception& e) {
res.status = 500;
serve_error(req, res, config, "500: Internal server error", "Failed to fetch illust information", e.what());
return;
}
Element body("body", {
Element("h2", {illust.title}),
generate_user_link(req, config, illust),
2023-04-09 13:50:44 +00:00
!preview ? generate_images(req, config, illust) : generate_preview_images(req, config, illust),
2023-04-09 15:15:53 +00:00
Element("br")
2023-04-09 13:31:50 +00:00
});
2023-04-09 15:15:53 +00:00
if (illust.comment) {
2023-04-09 17:34:13 +00:00
body.nodes.push_back(generate_description(req, config, *illust.comment));
2023-04-09 15:15:53 +00:00
}
body.nodes.push_back(generate_illust_tags(illust));
body.nodes.push_back(Element("p", {time_to_string(illust.upload_time)}));
2023-04-09 13:31:50 +00:00
serve(req, res, config, std::move(illust.title), std::move(body));
}
static inline Element generate_user_link(const httplib::Request& req, const Config& config, const Illust& illust) {
std::string profile_picture = proxy_image_url(config, illust.user_profile_pictures.thumbnail_or_original());
std::string user_link = get_origin(req, config) + "/users/" + std::to_string(illust.user_id);
return Element("a", {{"class", "usermetadata"}, {"href", std::move(user_link)}}, {
Element("img", {{"class", "smallprofilepicture"}, {"loading", "lazy"}, {"src", std::move(profile_picture)}}, {}),
Element("b", {illust.user_display_name})
});
}
static inline Element generate_images(const httplib::Request& req, const Config& config, const Illust& illust) {
using namespace std::string_literals;
Element div("div", {{"class", "illust"}}, {});
bool show_pages = illust.images.size() > 1;
2023-04-09 14:10:51 +00:00
if (show_pages) {
div.nodes.push_back(Element("a", {{"href", "?preview=1"}}, {"Preview Images"}));
}
div.nodes.reserve(div.nodes.size() + (show_pages ? illust.images.size() * 2 : illust.images.size()));
2023-04-09 13:31:50 +00:00
for (size_t i = 0; i < illust.images.size(); i++) {
const Images& images = illust.images[i];
std::string thumbnail = proxy_image_url(config, images.thumbnail_or_original());
std::string original = proxy_image_url(config, images.original_or_thumbnail());
if (show_pages) {
std::string id = std::to_string(i + 1);
div.nodes.push_back(Element("a", {{"class", "landmark"}, {"id", id}, {"href", "#"s + id}}, {id, "/", std::to_string(illust.images.size())}));
}
div.nodes.push_back(Element("a", {{"href", std::move(original)}}, {
Element("img", {{"loading", "lazy"}, {"src", std::move(thumbnail)}}, {})
}));
}
return div;
}
2023-04-09 13:50:44 +00:00
static inline Element generate_preview_images(const httplib::Request& req, const Config& config, const Illust& illust) {
2023-04-09 14:10:51 +00:00
std::string no_preview_link = get_origin(req, config) + "/artworks/" + std::to_string(illust.illust_id);
Element div("div", {
Element("a", {{"class", "center"}, {"href", no_preview_link}}, {"Go back"}),
Element("br")
});
Element grid("div", {{"class", "grid"}}, {});
2023-04-09 13:50:44 +00:00
2023-04-09 14:10:51 +00:00
grid.nodes.reserve(illust.images.size());
2023-04-09 13:50:44 +00:00
for (size_t i = 0; i < illust.images.size(); i++) {
const Images& images = illust.images[i];
std::string thumbnail = proxy_image_url(config, images.thumbnail_or_original(1));
2023-04-09 14:10:51 +00:00
std::string link = no_preview_link + '#' + std::to_string(i + 1);
2023-04-09 13:50:44 +00:00
2023-04-09 14:10:51 +00:00
grid.nodes.push_back(Element("a", {{"href", std::move(link)}}, {
2023-04-09 13:50:44 +00:00
Element("img", {{"loading", "lazy"}, {"src", std::move(thumbnail)}}, {})
}));
}
2023-04-09 14:10:51 +00:00
div.nodes.push_back(std::move(grid));
2023-04-09 13:50:44 +00:00
return div;
}
2023-04-09 17:34:13 +00:00
static inline std::vector<blankie::html::Node> parse_description_line(const httplib::Request& req, const Config& config, std::string str) {
2023-04-09 17:08:01 +00:00
std::vector<blankie::html::Node> nodes;
std::smatch sm;
while (std::regex_search(str, sm, blankie::murl::full_url_regex)) {
if (sm.prefix().length()) {
nodes.push_back(sm.prefix());
}
2023-04-09 17:34:13 +00:00
blankie::murl::Url url(sm.str(0));
std::string url_str = url.is_host_equal("pixiv.net") || url.is_host_equal("www.pixiv.net")
2023-04-10 12:55:32 +00:00
? proxy_pixiv_url(req, config, std::move(url))
2023-04-09 17:34:13 +00:00
: url.to_string();
nodes.push_back(Element("a", {{"href", url_str}}, {url_str}));
2023-04-09 17:08:01 +00:00
str = sm.suffix();
}
if (!str.empty()) {
nodes.push_back(std::move(str));
}
return nodes;
}
2023-04-09 17:34:13 +00:00
static inline Element generate_description(const httplib::Request& req, const Config& config, const std::string& description) {
2023-04-09 15:15:53 +00:00
Element p("p");
size_t pos = 0;
size_t last_pos = 0;
auto add = [&](std::string str) {
if (!p.nodes.empty()) {
p.nodes.push_back(Element("br"));
}
2023-04-09 17:34:13 +00:00
std::vector<blankie::html::Node> nodes = parse_description_line(req, config, std::move(str));
2023-04-09 17:08:01 +00:00
p.nodes.insert(p.nodes.end(), nodes.begin(), nodes.end());
2023-04-09 15:15:53 +00:00
};
while ((pos = description.find('\n', pos)) != std::string::npos) {
2023-04-09 17:08:01 +00:00
add(description.substr(last_pos, pos - last_pos));
2023-04-09 15:15:53 +00:00
last_pos = ++pos;
}
if (description.size() > last_pos) {
add(description.substr(last_pos));
}
return p;
}
2023-04-09 14:30:07 +00:00
static inline Element generate_illust_tags(const Illust& illust) {
Element div("div", {{"class", "illusttags"}}, {});
2023-04-09 13:31:50 +00:00
if (illust.ai_generated) {
div.nodes.push_back(Element("b", {"AI-Generated "}));
}
div.nodes.reserve(div.nodes.size() + illust.tags.size());
for (const Tag& i : illust.tags) {
std::string tag = [&]() {
if (i.english) return *i.english;
if (i.romaji) return *i.romaji;
return i.japanese;
}();
div.nodes.push_back(Element("span", {"#", std::move(tag)}));
}
return div;
}
static inline bool is_true(const std::string& str) {
return !str.empty() && str != "0" && str != "false" && str != "no";
}
2023-04-09 14:30:07 +00:00
static inline std::string time_to_string(time_t time) {
struct tm tm;
char str[1024] = {0};
if (!gmtime_r(&time, &tm)) {
return std::to_string(time);
}
size_t len = strftime(str, 1024, "%F %H:%M:%S %z", &tm);
if (len == 0) {
return std::to_string(time);
}
return std::string(str, len);
}
2023-04-10 12:55:32 +00:00
static inline std::string proxy_pixiv_url(const httplib::Request& req, const Config& config, blankie::murl::Url url) {
if (url.path.size() >= 4 && url.path.starts_with("/en/")) {
url.path.erase(0, 3);
} else if (url.path.size() == 3 && url.path == "/en") {
url.path = '/';
}
// TODO strip the lang query parameter
return proxy_url(get_origin(req, config), std::move(url));
}