diff --git a/CMakeLists.txt b/CMakeLists.txt index 06338f4..75f0959 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ add_link_options(${FLAGS}) add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp numberhelper.cpp pixivclient.cpp pixivmodels.cpp blankie/serializer.cpp blankie/escape.cpp blankie/murl.cpp - routes/home.cpp routes/css.cpp routes/users/common.cpp routes/users/users.cpp routes/users/illustrations.cpp) + routes/home.cpp routes/css.cpp routes/artworks.cpp routes/users/common.cpp routes/users/users.cpp routes/users/illustrations.cpp) set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 20 diff --git a/main.cpp b/main.cpp index b67e80f..156f61f 100644 --- a/main.cpp +++ b/main.cpp @@ -34,6 +34,9 @@ int main(int argc, char** argv) { server.Get("/users/(\\d+)/illustrations", [&](const httplib::Request& req, httplib::Response& res) { user_illustrations_route(req, res, config, pixiv_client); }); + server.Get("/artworks/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) { + artworks_route(req, res, config, pixiv_client); + }); server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) { std::string id = req.get_param_value("id"); diff --git a/pixivmodels.cpp b/pixivmodels.cpp index 4e0ae08..65ffea8 100644 --- a/pixivmodels.cpp +++ b/pixivmodels.cpp @@ -91,7 +91,7 @@ void from_json(const nlohmann::json& j, Illust& illust) { const nlohmann::json& illust_details = full_data ? j.at("illust_details") : j; author_details.at("user_account").get_to(illust.username); - author_details.at("user_name").get_to(illust.display_name); + author_details.at("user_name").get_to(illust.user_display_name); illust.user_id = to_ull(author_details.at("user_id").get_ref()); if (full_data) { illust.user_profile_pictures = get_profile_pictures(author_details.at("profile_img")); @@ -99,7 +99,7 @@ void from_json(const nlohmann::json& j, Illust& illust) { illust.illust_id = to_ull(illust_details.at("id").get_ref()); illust_details.at("title").get_to(illust.title); - illust.ai_generated = illust_details.at("ai_type").get() != 0; + illust.ai_generated = illust_details.at("ai_type").get() == 2; illust_details.at("upload_timestamp").get_to(illust.upload_time); if (full_data) { @@ -193,12 +193,7 @@ static Images get_illust_image(const nlohmann::json& j) { add_if_exists("url_s"); add_if_exists("url"); if (j.contains("url_big") && j["url_big"].is_string()) { - std::string url_big = j["url_big"].get(); - if (url_big.starts_with("/img-original/")) { - images.original = std::move(url_big); - } else { - images.thumbnails.push_back(std::move(url_big)); - } + images.original = j["url_big"].get(); } return images; diff --git a/pixivmodels.h b/pixivmodels.h index defa9f1..9538698 100644 --- a/pixivmodels.h +++ b/pixivmodels.h @@ -33,7 +33,7 @@ struct Tag { struct Illust { std::string username; - std::string display_name; + std::string user_display_name; uint64_t user_id; Images user_profile_pictures; diff --git a/routes/artworks.cpp b/routes/artworks.cpp new file mode 100644 index 0000000..1ec812a --- /dev/null +++ b/routes/artworks.cpp @@ -0,0 +1,98 @@ +#include "routes.h" +#include "../servehelper.h" +#include "../numberhelper.h" +#include "../pixivclient.h" + +static inline bool is_true(const std::string& str); +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); +static inline Element generate_illust_metadata(const Illust& illust); + +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)); + 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), + generate_images(req, config, illust), + Element("br"), + generate_illust_metadata(illust) + }); + 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; + + div.nodes.reserve(show_pages ? illust.images.size() * 2 : illust.images.size()); + 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; +} + +static inline Element generate_illust_metadata(const Illust& illust) { + Element div("div", {{"class", "illustmetadata"}}, {}); + + 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"; +} diff --git a/routes/css.cpp b/routes/css.cpp index 32248cc..1cac9c2 100644 --- a/routes/css.cpp +++ b/routes/css.cpp @@ -3,6 +3,7 @@ void css_route(const httplib::Request& req, httplib::Response& res) { res.set_content(R"EOF( + /* GENERAL */ :root { --background-color: black; --text-color: white; @@ -35,15 +36,22 @@ void css_route(const httplib::Request& req, httplib::Response& res) { object-fit: cover; } + /* USER PAGE (and a tiny bit for illustrations page) */ .cover { width: 100%; height: 50vh; margin-bottom: 1em; } + .profilepicture, .smallprofilepicture { + margin-right: .5em; + } .profilepicture { width: 5em; height: 5em; - margin-right: .5em; + } + .smallprofilepicture { + width: 2.5em; + height: 2.5em; } .usermetadata { display: flex; @@ -51,6 +59,7 @@ void css_route(const httplib::Request& req, httplib::Response& res) { margin-left: .5em; } + /* USER ILLUSTRATIONS PAGE */ .center { text-align: center; } @@ -68,6 +77,22 @@ void css_route(const httplib::Request& req, httplib::Response& res) { width: 15em; } + /* ILLUSTRATIONS PAGE */ + .illustmetadata { + display: flex; + flex-wrap: wrap; + gap: 1em; + } + .illust { + display: grid; + text-align: center; + } + .illust .landmark { + padding-top: 1em; + padding-bottom: 1em; + } + + /* ERROR PAGE */ .error { text-align: center; background-color: var(--error-background-color); diff --git a/routes/routes.h b/routes/routes.h index d93cc35..92d24c7 100644 --- a/routes/routes.h +++ b/routes/routes.h @@ -9,3 +9,4 @@ void home_route(const httplib::Request& req, httplib::Response& res, const Confi void css_route(const httplib::Request& req, httplib::Response& res); void users_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client); void user_illustrations_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client); +void artworks_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client);