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-09-29 05:14:58 +00:00
|
|
|
#include "../blankie/escape.h"
|
|
|
|
#include "../blankie/unescape.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-29 08:02:31 +00:00
|
|
|
static inline Element generate_illust_tags(const httplib::Request& req, const Config& config, const Illust& illust);
|
2023-09-29 05:14:58 +00:00
|
|
|
static inline blankie::html::HTMLString fix_description_links(const httplib::Request& req, const Config& config, blankie::html::HTMLString str);
|
|
|
|
static inline std::string generate_description_text(const httplib::Request& req, const Config& config, blankie::html::HTMLString description);
|
2023-06-07 08:12:39 +00:00
|
|
|
static inline Nodes generate_ogp_nodes(const httplib::Request& req, const Config& config, const Illust& illust, bool preview);
|
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-28 08:16:10 +00:00
|
|
|
Element("br"),
|
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-09-29 05:14:58 +00:00
|
|
|
if (illust.comment_html) {
|
|
|
|
body.nodes.push_back(Element("p", {fix_description_links(req, config, blankie::html::HTMLString(*illust.comment_html))}));
|
2023-04-09 15:15:53 +00:00
|
|
|
}
|
2023-04-29 08:02:31 +00:00
|
|
|
body.nodes.push_back(generate_illust_tags(req, config, illust));
|
2023-04-09 15:15:53 +00:00
|
|
|
body.nodes.push_back(Element("p", {time_to_string(illust.upload_time)}));
|
2023-06-07 08:12:39 +00:00
|
|
|
serve(req, res, config, illust.title, std::move(body), generate_ogp_nodes(req, config, illust, preview));
|
2023-04-09 13:31:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline Element generate_user_link(const httplib::Request& req, const Config& config, const Illust& illust) {
|
2023-06-07 09:16:03 +00:00
|
|
|
std::string profile_picture = proxy_image_url(req, config, illust.user_profile_pictures.thumbnail_or_original().url);
|
2023-04-09 13:31:50 +00:00
|
|
|
std::string user_link = get_origin(req, config) + "/users/" + std::to_string(illust.user_id);
|
|
|
|
|
2023-05-12 10:13:55 +00:00
|
|
|
return Element("a", {{"class", "user_metadata"}, {"href", std::move(user_link)}}, {
|
|
|
|
Element("img", {{"class", "user_profile_picture small"}, {"loading", "lazy"}, {"src", std::move(profile_picture)}}, {}),
|
2023-04-09 13:31:50 +00:00
|
|
|
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;
|
|
|
|
|
2023-05-12 10:13:55 +00:00
|
|
|
Element div("div", {{"class", "illust-images"}}, {});
|
2023-04-09 13:31:50 +00:00
|
|
|
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];
|
2023-05-07 17:22:48 +00:00
|
|
|
const Image& thumbnail = images.thumbnail_or_original();
|
|
|
|
const Image& original = images.original_or_thumbnail();
|
2023-06-11 04:53:51 +00:00
|
|
|
std::string id = std::to_string(i + 1);
|
2023-04-09 13:31:50 +00:00
|
|
|
|
|
|
|
if (show_pages) {
|
2023-06-11 04:53:51 +00:00
|
|
|
div.nodes.push_back(Element("a", {{"class", "landmark"}, {"href", "#"s + id}}, {id, "/", std::to_string(illust.images.size())}));
|
2023-04-09 13:31:50 +00:00
|
|
|
}
|
2023-05-07 17:22:48 +00:00
|
|
|
|
2023-06-11 04:53:51 +00:00
|
|
|
Element img("img", {{"loading", "lazy"}, {"id", std::move(id)}, {"src", proxy_image_url(req, config, thumbnail.url)}}, {});
|
2023-05-07 17:22:48 +00:00
|
|
|
if (thumbnail.size) {
|
|
|
|
img.attributes.push_back({"width", std::to_string(thumbnail.size->first)});
|
|
|
|
img.attributes.push_back({"height", std::to_string(thumbnail.size->second)});
|
|
|
|
}
|
2023-06-07 09:16:03 +00:00
|
|
|
div.nodes.push_back(Element("a", {{"href", proxy_image_url(req, config, original.url)}}, {
|
2023-05-07 17:22:48 +00:00
|
|
|
std::move(img)
|
2023-04-09 13:31:50 +00:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
2023-06-07 09:16:03 +00:00
|
|
|
std::string thumbnail = proxy_image_url(req, config, images.thumbnail_or_original(1).url);
|
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-29 08:02:31 +00:00
|
|
|
static inline Element generate_illust_tags(const httplib::Request& req, const Config& config, const Illust& illust) {
|
2023-05-12 10:13:55 +00:00
|
|
|
Element div("div", {{"class", "illust-tags"}}, {});
|
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;
|
|
|
|
return i.japanese;
|
|
|
|
}();
|
|
|
|
|
2023-04-29 08:02:31 +00:00
|
|
|
div.nodes.push_back(Element("a", {{"href", get_origin(req, config) + "/tags/" + blankie::murl::escape(i.japanese)}}, {
|
|
|
|
"#", std::move(tag)
|
|
|
|
}));
|
2023-04-09 13:31:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return div;
|
|
|
|
}
|
|
|
|
|
2023-09-29 05:14:58 +00:00
|
|
|
const std::regex start_link_regex("<a href=\"([^\"]+?)\"(?: target=\"_blank\")?>");
|
|
|
|
static inline blankie::html::HTMLString fix_description_links(const httplib::Request& req, const Config& config, blankie::html::HTMLString str) {
|
|
|
|
using namespace std::string_literals;
|
|
|
|
|
|
|
|
blankie::html::HTMLString out;
|
2023-06-04 06:22:19 +00:00
|
|
|
std::smatch sm;
|
|
|
|
|
2023-09-29 05:14:58 +00:00
|
|
|
out.str.reserve(str.str.size());
|
|
|
|
while (std::regex_search(str.str, sm, start_link_regex)) {
|
|
|
|
out.str += sm.prefix();
|
2023-06-04 06:22:19 +00:00
|
|
|
|
2023-09-29 05:14:58 +00:00
|
|
|
std::string url_str;
|
|
|
|
blankie::murl::Url url(sm.str(1));
|
|
|
|
if (url.is_host_equal("pixiv.net") || url.is_host_equal("www.pixiv.net")) {
|
|
|
|
url_str = proxy_pixiv_url(req, config, std::move(url));
|
|
|
|
} else if (url.path == "/jump.php") {
|
|
|
|
url_str = blankie::murl::unescape(std::move(url.query));
|
|
|
|
} else {
|
|
|
|
url_str = url.to_string();
|
2023-06-04 06:22:19 +00:00
|
|
|
}
|
2023-09-29 05:14:58 +00:00
|
|
|
out.str += "<a href=\""s + blankie::html::escape(std::move(url_str)) + "\">";
|
|
|
|
|
|
|
|
str.str = sm.suffix();
|
|
|
|
}
|
|
|
|
out.str += std::move(str.str);
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::regex link_regex("<a href=\"([^\"]+?)\">.+?</a>");
|
|
|
|
const std::regex tag_regex("<(.+?)>");
|
|
|
|
static inline std::string generate_description_text(const httplib::Request& req, const Config& config, blankie::html::HTMLString description) {
|
|
|
|
description = fix_description_links(req, config, std::move(description));
|
2023-06-04 06:22:19 +00:00
|
|
|
|
2023-09-29 05:14:58 +00:00
|
|
|
std::string new_description;
|
|
|
|
std::smatch sm;
|
2023-06-04 06:22:19 +00:00
|
|
|
|
2023-09-29 05:14:58 +00:00
|
|
|
new_description.reserve(description.str.size());
|
|
|
|
while (std::regex_search(description.str, sm, link_regex)) {
|
|
|
|
new_description += sm.prefix();
|
|
|
|
new_description += sm.str(1);
|
|
|
|
description.str = sm.suffix();
|
|
|
|
}
|
|
|
|
new_description += std::move(description.str);
|
|
|
|
|
|
|
|
description.str = std::move(new_description);
|
|
|
|
new_description.reserve(description.str.size());
|
|
|
|
while (std::regex_search(description.str, sm, tag_regex)) {
|
|
|
|
new_description += sm.prefix();
|
|
|
|
if (sm.str(1) == "br /") {
|
|
|
|
new_description += '\n';
|
|
|
|
}
|
|
|
|
description.str = sm.suffix();
|
2023-06-04 06:22:19 +00:00
|
|
|
}
|
2023-09-29 05:14:58 +00:00
|
|
|
new_description += std::move(description.str);
|
2023-06-04 06:22:19 +00:00
|
|
|
|
2023-09-29 05:14:58 +00:00
|
|
|
return blankie::html::unescape(std::move(new_description));
|
2023-06-04 06:22:19 +00:00
|
|
|
}
|
|
|
|
|
2023-06-07 08:12:39 +00:00
|
|
|
static inline Nodes generate_ogp_nodes(const httplib::Request& req, const Config& config, const Illust& illust, bool preview) {
|
|
|
|
std::string url = get_origin(req, config) + "/artworks/" + std::to_string(illust.illust_id);
|
|
|
|
if (preview) {
|
|
|
|
url += "?preview=1";
|
|
|
|
}
|
2023-06-04 06:22:19 +00:00
|
|
|
Nodes nodes({
|
|
|
|
Element("meta", {{"property", "og:title"}, {"content", illust.title}}, {}),
|
|
|
|
Element("meta", {{"property", "og:type"}, {"content", "photo"}}, {}),
|
|
|
|
Element("meta", {{"property", "og:site_name"}, {"content", "Pixwhile"}}, {}),
|
2023-06-07 08:12:39 +00:00
|
|
|
Element("meta", {{"property", "og:url"}, {"content", std::move(url)}}, {})
|
2023-06-04 06:22:19 +00:00
|
|
|
});
|
2023-09-29 05:14:58 +00:00
|
|
|
if (illust.comment_html) {
|
|
|
|
nodes.push_back(Element("meta", {{"property", "og:description"}, {"content", generate_description_text(req, config, blankie::html::HTMLString(*illust.comment_html))}}, {}));
|
2023-06-04 06:22:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// i don't even know what multiple og:images do anymore
|
|
|
|
// https://stackoverflow.com/questions/13424780/facebook-multiple-ogimage-tags-which-is-default
|
|
|
|
nodes.reserve(nodes.size() + illust.images.size() * 3);
|
|
|
|
for (const Images& images : illust.images) {
|
|
|
|
const Image& image = images.thumbnail_or_original();
|
2023-06-07 09:16:03 +00:00
|
|
|
nodes.push_back(Element("meta", {{"property", "og:image"}, {"content", proxy_image_url(req, config, image.url)}}, {}));
|
2023-06-04 06:22:19 +00:00
|
|
|
if (image.size) {
|
|
|
|
nodes.push_back(Element("meta", {{"property", "og:image:width"}, {"content", std::to_string(image.size->first)}}, {}));
|
|
|
|
nodes.push_back(Element("meta", {{"property", "og:image:height"}, {"content", std::to_string(image.size->second)}}, {}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodes;
|
|
|
|
}
|
|
|
|
|
2023-04-09 13:31:50 +00:00
|
|
|
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));
|
|
|
|
}
|