2023-04-04 17:01:50 +00:00
|
|
|
#include <regex>
|
|
|
|
|
2023-04-06 12:24:09 +00:00
|
|
|
#include "blankie/murl.h"
|
2023-04-04 17:01:50 +00:00
|
|
|
#include "pixivclient.h"
|
|
|
|
|
2023-04-06 12:24:09 +00:00
|
|
|
static inline std::optional<std::string> get_1920x960_cover_image(blankie::murl::Url url);
|
|
|
|
static inline std::optional<std::string> get_original_cover_image(blankie::murl::Url url);
|
|
|
|
static inline std::optional<std::string> get_original_profile_picture(blankie::murl::Url url);
|
2023-04-04 17:01:50 +00:00
|
|
|
static inline uint64_t to_ull(const std::string& str);
|
|
|
|
|
|
|
|
PixivClient::PixivClient() {
|
|
|
|
this->_www_pixiv_net_client.set_keep_alive(true);
|
|
|
|
this->_www_pixiv_net_client.set_default_headers({
|
|
|
|
{"User-Agent", "Mozilla/5.0 (Android 12; Mobile; rv:97.0) Gecko/97.0 Firefox/97.0"}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
User PixivClient::get_user(uint64_t user_id) {
|
|
|
|
httplib::Result res = this->_www_pixiv_net_client.Get("/touch/ajax/user/details", {
|
|
|
|
{"lang", "en"}, {"id", std::to_string(user_id)}
|
|
|
|
}, httplib::Headers());
|
|
|
|
return this->_handle_result(std::move(res)).at("user_details").get<User>();
|
|
|
|
}
|
|
|
|
|
|
|
|
nlohmann::json PixivClient::_handle_result(httplib::Result res) {
|
|
|
|
if (!res) {
|
|
|
|
throw HTTPLibException(res.error());
|
|
|
|
}
|
|
|
|
|
|
|
|
nlohmann::json j = nlohmann::json::parse(std::move(res->body));
|
|
|
|
if (j.at("error")) {
|
|
|
|
throw PixivException(res->status, j.at("message").get<std::string>());
|
|
|
|
}
|
|
|
|
return j.at("body");
|
|
|
|
}
|
|
|
|
|
|
|
|
void from_json(const nlohmann::json& j, User& user) {
|
|
|
|
j.at("user_account").get_to(user.username);
|
|
|
|
j.at("user_name").get_to(user.display_name);
|
|
|
|
user.user_id = to_ull(j.at("user_id").get_ref<const nlohmann::json::string_t&>());
|
|
|
|
|
2023-04-05 16:36:20 +00:00
|
|
|
if (j.contains("cover_image") && j["cover_image"].is_object()) {
|
2023-04-04 17:01:50 +00:00
|
|
|
nlohmann::json cover_image = j["cover_image"];
|
|
|
|
std::string c_720x360 = cover_image.at("profile_cover_image").at("720x360").get<std::string>();
|
|
|
|
std::optional<std::string> original = get_original_cover_image(c_720x360);
|
2023-04-05 14:38:23 +00:00
|
|
|
std::optional<std::string> c_1920x960 = get_1920x960_cover_image(c_720x360);
|
2023-04-04 17:01:50 +00:00
|
|
|
|
|
|
|
user.cover_images = {std::move(original), {std::move(c_720x360)}};
|
2023-04-05 14:38:23 +00:00
|
|
|
if (c_1920x960) {
|
|
|
|
user.cover_images->thumbnails.push_back(std::move(*c_1920x960));
|
|
|
|
}
|
2023-04-04 17:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nlohmann::json profile_img = j.at("profile_img");
|
|
|
|
if (profile_img.contains("main_s")) {
|
|
|
|
user.profile_pictures.thumbnails.push_back(profile_img["main_s"].get<std::string>());
|
|
|
|
}
|
|
|
|
user.profile_pictures.thumbnails.push_back(profile_img.at("main").get<std::string>());
|
|
|
|
user.profile_pictures.original = get_original_profile_picture(user.profile_pictures.thumbnails.back());
|
|
|
|
|
|
|
|
std::string user_webpage = j.at("user_webpage").get<std::string>();
|
|
|
|
if (!user_webpage.empty()) {
|
2023-04-05 16:36:20 +00:00
|
|
|
user.links.push_back({"Webpage", std::move(user_webpage)});
|
2023-04-04 17:01:50 +00:00
|
|
|
}
|
2023-04-05 16:36:20 +00:00
|
|
|
auto add_social_as_needed = [&](const char* key, const char* public_name) {
|
2023-04-04 17:01:50 +00:00
|
|
|
nlohmann::json social = j.at("social");
|
|
|
|
if (!social.is_object()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!social.contains(key)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string url = social[key].at("url").get<std::string>();
|
2023-04-05 16:36:20 +00:00
|
|
|
user.links.push_back({public_name, std::move(url)});
|
2023-04-04 17:01:50 +00:00
|
|
|
};
|
2023-04-05 16:36:20 +00:00
|
|
|
add_social_as_needed("twitter", "Twitter");
|
|
|
|
add_social_as_needed("instagram", "Instagram");
|
|
|
|
add_social_as_needed("tumblr", "Tumblr");
|
|
|
|
add_social_as_needed("facebook", "Facebook");
|
|
|
|
add_social_as_needed("circlems", "Circle.ms");
|
|
|
|
add_social_as_needed("pawoo", "Pawoo");
|
2023-04-04 17:01:50 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 12:24:09 +00:00
|
|
|
static std::regex resolution_path_regex("/c/(\\d+x\\d+)(.+)");
|
|
|
|
static inline std::optional<std::string> get_1920x960_cover_image(blankie::murl::Url url) {
|
2023-04-05 14:38:23 +00:00
|
|
|
std::smatch sm;
|
2023-04-06 12:24:09 +00:00
|
|
|
if (!std::regex_match(url.path, sm, resolution_path_regex)) {
|
2023-04-05 14:38:23 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2023-04-06 12:24:09 +00:00
|
|
|
if (sm[1] == "1920x960") {
|
2023-04-05 14:38:23 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2023-04-06 12:24:09 +00:00
|
|
|
url.path = "/c/1920x960" + sm.str(2);
|
|
|
|
return url.to_string();
|
2023-04-05 14:38:23 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 12:24:09 +00:00
|
|
|
static std::regex thumbnail_path_regex("/c/[^/]+/(.+)_master\\d+(\\.\\w{3,4})?");
|
|
|
|
static inline std::optional<std::string> get_original_cover_image(blankie::murl::Url url) {
|
2023-04-04 17:01:50 +00:00
|
|
|
std::smatch sm;
|
2023-04-06 12:24:09 +00:00
|
|
|
if (!std::regex_match(url.path, sm, thumbnail_path_regex)) {
|
2023-04-04 17:01:50 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2023-04-06 12:24:09 +00:00
|
|
|
url.path = sm.str(1) + sm.str(2);
|
|
|
|
return url.to_string();
|
2023-04-04 17:01:50 +00:00
|
|
|
}
|
|
|
|
|
2023-04-06 12:24:09 +00:00
|
|
|
static std::regex profile_picture_thumbnail_path_regex("(/.+)_\\d{2,}(\\.\\w{3,4})");
|
|
|
|
static inline std::optional<std::string> get_original_profile_picture(blankie::murl::Url url) {
|
2023-04-04 17:01:50 +00:00
|
|
|
std::smatch sm;
|
2023-04-06 12:24:09 +00:00
|
|
|
if (!std::regex_match(url.path, sm, profile_picture_thumbnail_path_regex)) {
|
2023-04-04 17:01:50 +00:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2023-04-06 12:24:09 +00:00
|
|
|
url.path = sm.str(1) + sm.str(2);
|
|
|
|
return url.to_string();
|
2023-04-04 17:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint64_t to_ull(const std::string& str) {
|
|
|
|
char* endptr;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
unsigned long long res = strtoull(str.c_str(), &endptr, 10);
|
|
|
|
if (endptr[0] != '\0') {
|
|
|
|
throw std::invalid_argument(str + " contains trailing data");
|
|
|
|
} else if (res == ULLONG_MAX && errno == ERANGE) {
|
|
|
|
throw std::overflow_error(str + " overflows uint64_t");
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|