From 421bbcd6a0a5b995a407380d122bb7136e4a2a0a Mon Sep 17 00:00:00 2001 From: blankie Date: Mon, 8 May 2023 00:22:48 +0700 Subject: [PATCH] Guess the size of thumbnails --- pixivmodels.cpp | 71 +++++++++++++++++++++++++++++++---------- pixivmodels.h | 18 ++++++++--- routes/artworks.cpp | 18 +++++++---- routes/users/common.cpp | 8 ++--- servehelper.cpp | 2 +- 5 files changed, 84 insertions(+), 33 deletions(-) diff --git a/pixivmodels.cpp b/pixivmodels.cpp index 6396cf3..67fc17c 100644 --- a/pixivmodels.cpp +++ b/pixivmodels.cpp @@ -11,9 +11,10 @@ static inline std::optional get_original_profile_picture(blankie::m static inline std::optional get_360x360_illust_thumbnail(blankie::murl::Url url); static Images get_profile_pictures(const nlohmann::json& j); static Images get_profile_pictures(const std::string& url); -static Images get_illust_image(const nlohmann::json& j); +static std::optional> get_thumbnail_size(blankie::murl::Url thumbnail_url, std::optional> original_size); +static Images get_illust_images(const nlohmann::json& image, std::optional image_metadata); -const std::string& Images::original_or_thumbnail() const { +const Image& Images::original_or_thumbnail() const { if (this->original) { return *this->original; } @@ -23,7 +24,7 @@ const std::string& Images::original_or_thumbnail() const { throw std::runtime_error("Images does not contain any images"); } -const std::string& Images::thumbnail_or_original(size_t back) const { +const Image& Images::thumbnail_or_original(size_t back) const { if (this->thumbnails.size() > back) { return this->thumbnails[this->thumbnails.size() - back - 1]; } @@ -91,6 +92,7 @@ void from_json(const nlohmann::json& j, Illust& illust) { bool full_data = j.contains("illust_details"); const nlohmann::json& author_details = j.at("author_details"); const nlohmann::json& illust_details = full_data ? j.at("illust_details") : j; + const nlohmann::json& images_metadata = illust_details.at("illust_images"); author_details.at("user_account").get_to(illust.username); author_details.at("user_name").get_to(illust.user_display_name); @@ -114,11 +116,12 @@ void from_json(const nlohmann::json& j, Illust& illust) { if (illust_details.contains("manga_a")) { const nlohmann::json& manga_a = illust_details["manga_a"]; illust.images.reserve(manga_a.size()); - for (auto &[_, i] : manga_a.items()) { - illust.images.push_back(get_illust_image(i)); + + for (size_t i = 0; i < manga_a.size(); i++) { + illust.images.push_back(get_illust_images(manga_a[i], images_metadata.at(i))); } } else { - illust.images = {get_illust_image(illust_details)}; + illust.images = {get_illust_images(illust_details, images_metadata.at(0))}; } illust.page_count = to_ull(illust_details.at("page_count").get_ref()); } @@ -168,7 +171,7 @@ void from_json(const nlohmann::json& j, SearchResults& search_results) { .comment = std::nullopt, .tags = std::move(tags), - .images = {get_illust_image(i)}, + .images = {get_illust_images(i, std::nullopt)}, .page_count = i.at("pageCount").get() }; search_results.illusts.illusts.push_back(illust); @@ -247,7 +250,7 @@ static Images get_profile_pictures(const nlohmann::json& j) { images.thumbnails.push_back(j["main_s"].get()); } images.thumbnails.push_back(j.at("main").get()); - images.original = get_original_profile_picture(images.thumbnails.back()); + images.original = get_original_profile_picture(images.thumbnails.back().url); return images; } @@ -274,16 +277,50 @@ static inline std::optional get_360x360_illust_thumbnail(blankie::m return url.to_string(); } -static Images get_illust_image(const nlohmann::json& j) { +static std::regex illust_size_regex( + "/c/(\\d+)x(\\d+)[/_].+" +); +static std::optional> get_thumbnail_size(blankie::murl::Url thumbnail_url, std::optional> original_size) { + if (!original_size) { + return std::nullopt; + } + + std::smatch sm; + if (!std::regex_match(thumbnail_url.path, sm, illust_size_regex)) { + return std::nullopt; + } + + uint64_t thumbnail_width = to_ull(sm.str(1)); + // uint64_t thumbnail_height = to_ull(sm.str(2)); + + std::pair real_thumbnail_size = { + thumbnail_width, + // derived from original_size->second / (original_size->first / thumbnail_width) + // to make it more accurate without using floats + original_size->second * thumbnail_width / original_size->first + }; + return real_thumbnail_size; +} + +static Images get_illust_images(const nlohmann::json& image, std::optional image_metadata) { Images images; ssize_t add_360x360_to = -1; + std::optional> original_size; + + if (image_metadata) { + original_size = { + to_ull(image_metadata->at("illust_image_width").get_ref()), + to_ull(image_metadata->at("illust_image_height").get_ref()) + }; + } auto add_if_exists = [&](const char* key) { - if (j.contains(key) && j[key].is_string()) { - images.thumbnails.push_back(j[key].get()); - return true; + if (!image.contains(key) || !image[key].is_string()) { + return false; } - return false; + std::string url = image[key].get(); + images.thumbnails.push_back({url, get_thumbnail_size(url, original_size)}); + return true; }; add_if_exists("url_ss"); add_if_exists("url_placeholder"); @@ -292,14 +329,14 @@ static Images get_illust_image(const nlohmann::json& j) { add_360x360_to = static_cast(images.thumbnails.size()); } add_if_exists("url"); - if (j.contains("url_big") && j["url_big"].is_string()) { - images.original = j["url_big"].get(); + if (image.contains("url_big") && image["url_big"].is_string()) { + images.original = {image["url_big"].get(), original_size}; } if (add_360x360_to >= 0) { - std::optional c_360x360 = get_360x360_illust_thumbnail(images.original_or_thumbnail()); + std::optional c_360x360 = get_360x360_illust_thumbnail(images.original_or_thumbnail().url); if (c_360x360) { - images.thumbnails.insert(images.thumbnails.begin() + add_360x360_to, std::move(*c_360x360)); + images.thumbnails.insert(images.thumbnails.begin() + add_360x360_to, {*c_360x360, get_thumbnail_size(*c_360x360, original_size)}); } } diff --git a/pixivmodels.h b/pixivmodels.h index 69e5a9f..40c38a0 100644 --- a/pixivmodels.h +++ b/pixivmodels.h @@ -7,12 +7,20 @@ #include -struct Images { - std::optional original; - std::vector thumbnails; +struct Image { + std::string url; + std::optional> size; - const std::string& original_or_thumbnail() const; - const std::string& thumbnail_or_original(size_t back = 0) const; + Image(std::string url_) : url(std::move(url_)) {} + Image(std::string url_, std::optional> size_) : url(std::move(url_)), size(std::move(size_)) {} +}; + +struct Images { + std::optional original; + std::vector thumbnails; + + const Image& original_or_thumbnail() const; + const Image& thumbnail_or_original(size_t back = 0) const; }; struct User { diff --git a/routes/artworks.cpp b/routes/artworks.cpp index b11689c..8b7ddea 100644 --- a/routes/artworks.cpp +++ b/routes/artworks.cpp @@ -55,7 +55,7 @@ void artworks_route(const httplib::Request& req, httplib::Response& res, const C } 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 profile_picture = proxy_image_url(config, illust.user_profile_pictures.thumbnail_or_original().url); 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)}}, { @@ -76,15 +76,21 @@ static inline Element generate_images(const httplib::Request& req, const Config& div.nodes.reserve(div.nodes.size() + (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()); + const Image& thumbnail = images.thumbnail_or_original(); + const Image& original = 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)}}, {}) + + Element img("img", {{"loading", "lazy"}, {"src", proxy_image_url(config, thumbnail.url)}}, {}); + 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)}); + } + div.nodes.push_back(Element("a", {{"href", proxy_image_url(config, original.url)}}, { + std::move(img) })); } @@ -102,7 +108,7 @@ static inline Element generate_preview_images(const httplib::Request& req, const grid.nodes.reserve(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(1)); + std::string thumbnail = proxy_image_url(config, images.thumbnail_or_original(1).url); std::string link = no_preview_link + '#' + std::to_string(i + 1); grid.nodes.push_back(Element("a", {{"href", std::move(link)}}, { diff --git a/routes/users/common.cpp b/routes/users/common.cpp index 8efda1c..baf4c52 100644 --- a/routes/users/common.cpp +++ b/routes/users/common.cpp @@ -8,15 +8,15 @@ static inline Element generate_user_links(const User& user); Element generate_user_header(const User& user, const Config& config) { Element header("header"); if (user.cover_images) { - std::string cover_original = proxy_image_url(config, user.cover_images->original_or_thumbnail()); - std::string cover_thumbnail = proxy_image_url(config, user.cover_images->thumbnail_or_original()); + std::string cover_original = proxy_image_url(config, user.cover_images->original_or_thumbnail().url); + std::string cover_thumbnail = proxy_image_url(config, user.cover_images->thumbnail_or_original().url); header.nodes.push_back(Element("a", {{"href", std::move(cover_original)}}, { Element("img", {{"class", "profilecover"}, {"loading", "lazy"}, {"src", std::move(cover_thumbnail)}}, {}) })); } - std::string profile_picture_original = proxy_image_url(config, user.profile_pictures.original_or_thumbnail()); - std::string profile_picture_thumbnail = proxy_image_url(config, user.profile_pictures.thumbnail_or_original()); + std::string profile_picture_original = proxy_image_url(config, user.profile_pictures.original_or_thumbnail().url); + std::string profile_picture_thumbnail = proxy_image_url(config, user.profile_pictures.thumbnail_or_original().url); header.nodes.push_back(Element("div", {{"class", "usermetadata"}}, { Element("a", {{"href", std::move(profile_picture_original)}}, { Element("img", {{"class", "profilepicture"}, {"loading", "lazy"}, {"src", std::move(profile_picture_thumbnail)}}, {}) diff --git a/servehelper.cpp b/servehelper.cpp index a790f2d..af308da 100644 --- a/servehelper.cpp +++ b/servehelper.cpp @@ -147,7 +147,7 @@ static inline Element generate_illusts_grid(const httplib::Request& req, const C static inline Element generate_illusts_grid_item(const httplib::Request& req, const Config& config, const Illust& illust) { std::string illust_url = get_origin(req, config) + "/artworks/" + std::to_string(illust.illust_id); - std::string image_url = proxy_image_url(config, illust.images[0].thumbnail_or_original(1)); + std::string image_url = proxy_image_url(config, illust.images[0].thumbnail_or_original(1).url); Element div("div", {{"class", "illustsgriditem"}}, { Element("a", {{"href", illust_url}}, {