154 lines
5.8 KiB
C++
154 lines
5.8 KiB
C++
#include <FastHash.h>
|
|
|
|
#include "blankie/murl.h"
|
|
#include "hiredis_wrapper.h"
|
|
#include "numberhelper.h"
|
|
#include "pixivclient.h"
|
|
|
|
using namespace std::string_literals;
|
|
|
|
static const constexpr char* touch_user_agent = "Mozilla/5.0 (Android 12; Mobile; rv:97.0) Gecko/97.0 Firefox/97.0";
|
|
static const constexpr char* desktop_user_agent = "Mozilla/5.0 (Windows NT 10.0; rv:111.0) Gecko/20100101 Firefox/111.0";
|
|
static void to_lower(std::string& str);
|
|
|
|
PixivClient::PixivClient(Redis* redis) : _redis(redis) {
|
|
this->_www_pixiv_net_client.set_keep_alive(true);
|
|
this->_www_pixiv_net_client.set_default_headers({
|
|
{"Cookie", "webp_available=1"}
|
|
});
|
|
this->_i_pximg_net_client.set_keep_alive(true);
|
|
this->_i_pximg_net_client.set_default_headers({
|
|
{"Referer", "https://www.pixiv.net/"}
|
|
});
|
|
}
|
|
|
|
User PixivClient::get_user(uint64_t user_id) {
|
|
nlohmann::json j = this->_call_api("pixwhile:user:"s + std::to_string(user_id), std::nullopt, 24 * 60 * 60,
|
|
"/touch/ajax/user/details", {{"lang", "en"}, {"id", std::to_string(user_id)}}, {{"User-Agent", touch_user_agent}});
|
|
return j.at("user_details").get<User>();
|
|
}
|
|
|
|
Illusts PixivClient::get_illusts(uint64_t user_id, size_t page) {
|
|
httplib::Params params = {{"lang", "en"}, {"id", std::to_string(user_id)}};
|
|
if (page != 0) {
|
|
params.insert({"p", std::to_string(page + 1)});
|
|
}
|
|
|
|
nlohmann::json j = this->_call_api("pixwhile:illusts:"s + std::to_string(user_id), std::to_string(page), 60 * 60,
|
|
"/touch/ajax/user/illusts", std::move(params), {{"User-Agent", touch_user_agent}});
|
|
return j.get<Illusts>();
|
|
}
|
|
|
|
Illust PixivClient::get_illust(uint64_t illust_id) {
|
|
nlohmann::json j = this->_call_api("pixwhile:illust:"s + std::to_string(illust_id), std::nullopt, 24 * 60 * 60,
|
|
"/touch/ajax/illust/details", {{"lang", "en"}, {"illust_id", std::to_string(illust_id)}}, {{"User-Agent", touch_user_agent}});
|
|
return j.get<Illust>();
|
|
}
|
|
|
|
SearchResults PixivClient::search_illusts(std::string query, size_t page, const std::string& order) {
|
|
to_lower(query);
|
|
httplib::Params params = {
|
|
{"lang", "en"},
|
|
{"mode", "all"},
|
|
{"p", std::to_string(page + 1)},
|
|
{"s_mode", "s_tag_full"},
|
|
{"type", "illust_and_ugoira"},
|
|
{"order", order},
|
|
{"word", query}
|
|
};
|
|
|
|
std::string cache_key = "pixwhile:search:"s + order + ':' + std::to_string(FastHash(query.data(), query.size(), 0));
|
|
nlohmann::json j = this->_call_api(std::move(cache_key), std::to_string(page), 60 * 60,
|
|
"/ajax/search/illustrations/"s + blankie::murl::escape(std::move(query)), std::move(params), {{"User-Agent", desktop_user_agent}});
|
|
return j.get<SearchResults>();
|
|
}
|
|
|
|
std::vector<SearchSuggestion> PixivClient::get_search_suggestions(std::string query) {
|
|
to_lower(query);
|
|
std::string body = [&]() {
|
|
std::string cache_key = "pixwhile:searchsugg:"s + std::to_string(FastHash(query.data(), query.size(), 0));
|
|
if (this->_redis) {
|
|
std::optional<std::string> cached_body = this->_redis->get(cache_key);
|
|
if (cached_body) {
|
|
return std::move(*cached_body);
|
|
}
|
|
}
|
|
|
|
httplib::Result res = this->_www_pixiv_net_client.Get("/rpc/cps.php", {
|
|
{"lang", "en"}, {"keyword", query}
|
|
}, {{"User-Agent", desktop_user_agent}, {"Referer", "https://www.pixiv.net/"}});
|
|
if (!res) {
|
|
throw HTTPLibException(res.error());
|
|
}
|
|
|
|
if (this->_redis) {
|
|
this->_redis->set(std::move(cache_key), res->body, 24 * 60 * 60);
|
|
}
|
|
return std::move(res->body);
|
|
}();
|
|
nlohmann::json j = nlohmann::json::parse(std::move(body)).at("candidates");
|
|
|
|
std::vector<SearchSuggestion> search_suggestions;
|
|
search_suggestions.reserve(j.size());
|
|
|
|
for (const nlohmann::json& i : j) {
|
|
SearchSuggestion search_suggestion = i.get<SearchSuggestion>();
|
|
if (search_suggestion.tag != query) {
|
|
search_suggestions.push_back(std::move(search_suggestion));
|
|
}
|
|
}
|
|
|
|
return search_suggestions;
|
|
}
|
|
|
|
nlohmann::json PixivClient::_call_api(std::string cache_key, std::optional<std::string> cache_field, time_t expiry,
|
|
std::string path, httplib::Params params, httplib::Headers headers) {
|
|
if (this->_redis) {
|
|
std::optional<std::string> success_body = !cache_field
|
|
? this->_redis->get(cache_key)
|
|
: this->_redis->hget(cache_key, *cache_field);
|
|
if (success_body) {
|
|
return nlohmann::json::parse(std::move(*success_body));
|
|
}
|
|
}
|
|
|
|
httplib::Result res = this->_www_pixiv_net_client.Get(std::move(path), std::move(params), std::move(headers));
|
|
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>());
|
|
}
|
|
|
|
j = j.at("body");
|
|
// trim data sent to redis without making our own serialization of our objects
|
|
j.erase("ads");
|
|
if (this->_redis) {
|
|
if (!cache_field) {
|
|
this->_redis->set(std::move(cache_key), j.dump(), expiry);
|
|
} else {
|
|
this->_redis->hset(cache_key, std::move(*cache_field), j.dump());
|
|
this->_redis->expire_nx(std::move(cache_key), expiry);
|
|
}
|
|
}
|
|
return j;
|
|
}
|
|
|
|
bool PixivClient::i_pximg_url_valid(const std::string& path) {
|
|
httplib::Result res = this->_i_pximg_net_client.Head(path, {{"User-Agent", touch_user_agent}});
|
|
if (!res) {
|
|
throw HTTPLibException(res.error());
|
|
}
|
|
return res->status >= 200 && res->status <= 200;
|
|
}
|
|
|
|
|
|
static void to_lower(std::string& str) {
|
|
for (size_t i = 0; i < str.size(); i++) {
|
|
if (str[i] >= 'A' && str[i] <= 'Z') {
|
|
str[i] = str[i] - 'A' + 'a';
|
|
}
|
|
}
|
|
}
|