Add /users/:id/illustrations
This commit is contained in:
parent
f6447d9ec5
commit
5c594b6301
|
@ -24,7 +24,7 @@ add_link_options(${FLAGS})
|
||||||
|
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp pixivclient.cpp blankie/serializer.cpp blankie/escape.cpp blankie/murl.cpp
|
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp pixivclient.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/home.cpp routes/css.cpp routes/users/common.cpp routes/users/users.cpp routes/users/illustrations.cpp)
|
||||||
set_target_properties(${PROJECT_NAME}
|
set_target_properties(${PROJECT_NAME}
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
CXX_STANDARD 20
|
CXX_STANDARD 20
|
||||||
|
|
|
@ -52,5 +52,5 @@ std::string Element::serialize() const {
|
||||||
}; // namespace blankie
|
}; // namespace blankie
|
||||||
|
|
||||||
static inline bool is_autoclosing_tag(const char* tag) {
|
static inline bool is_autoclosing_tag(const char* tag) {
|
||||||
return !strncmp(tag, "link", 5) || !strncmp(tag, "meta", 5) || !strncmp(tag, "img", 4);
|
return !strncmp(tag, "link", 5) || !strncmp(tag, "meta", 5) || !strncmp(tag, "img", 4) || !strncmp(tag, "br", 3);
|
||||||
}
|
}
|
||||||
|
|
15
main.cpp
15
main.cpp
|
@ -31,6 +31,9 @@ int main(int argc, char** argv) {
|
||||||
server.Get("/users/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) {
|
server.Get("/users/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) {
|
||||||
users_route(req, res, config, pixiv_client);
|
users_route(req, res, config, pixiv_client);
|
||||||
});
|
});
|
||||||
|
server.Get("/users/(\\d+)/illustrations", [&](const httplib::Request& req, httplib::Response& res) {
|
||||||
|
user_illustrations_route(req, res, config, pixiv_client);
|
||||||
|
});
|
||||||
|
|
||||||
server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) {
|
server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) {
|
||||||
std::string id = req.get_param_value("id");
|
std::string id = req.get_param_value("id");
|
||||||
|
@ -52,18 +55,6 @@ int main(int argc, char** argv) {
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// TODO remove
|
|
||||||
server.Get("/debug/userillusts", [&](const httplib::Request& req, httplib::Response& res) {
|
|
||||||
std::vector<uint64_t> illusts = pixiv_client.get_illusts(2583663);
|
|
||||||
std::string output;
|
|
||||||
|
|
||||||
for (uint64_t i : illusts) {
|
|
||||||
output += std::to_string(i);
|
|
||||||
output += '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
res.set_content(std::move(output), "text/plain");
|
|
||||||
});
|
|
||||||
server.Get("/debug/exception/known", [](const httplib::Request& req, httplib::Response& res) {
|
server.Get("/debug/exception/known", [](const httplib::Request& req, httplib::Response& res) {
|
||||||
throw std::runtime_error("awoo");
|
throw std::runtime_error("awoo");
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,6 +51,10 @@ void css_route(const httplib::Request& req, httplib::Response& res) {
|
||||||
margin-left: .5em;
|
margin-left: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: var(--error-background-color);
|
background-color: var(--error-background-color);
|
||||||
|
|
|
@ -8,3 +8,4 @@ class PixivClient; // forward declaration from ../pixivclient.h
|
||||||
void home_route(const httplib::Request& req, httplib::Response& res, const Config& config);
|
void home_route(const httplib::Request& req, httplib::Response& res, const Config& config);
|
||||||
void css_route(const httplib::Request& req, httplib::Response& res);
|
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 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);
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "../routes.h"
|
||||||
|
#include "../../servehelper.h"
|
||||||
|
#include "../../pixivclient.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
static inline uint64_t to_ull(const std::string& str);
|
||||||
|
static Element generate_pager(size_t page, size_t items, size_t items_per_page);
|
||||||
|
static inline Element generate_content(const std::vector<uint64_t>& illust_ids, size_t page, size_t items_per_page);
|
||||||
|
|
||||||
|
static inline size_t page_count(size_t items, size_t items_per_page);
|
||||||
|
static inline std::pair<size_t, size_t> page_to_offsets(size_t page, size_t items, size_t items_per_page);
|
||||||
|
|
||||||
|
void user_illustrations_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client) {
|
||||||
|
uint64_t user_id = to_ull(req.matches[1].str());
|
||||||
|
uint64_t page = req.has_param("p") ? to_ull(req.get_param_value("p")) - 1 : 0;
|
||||||
|
User user;
|
||||||
|
std::vector<uint64_t> illust_ids;
|
||||||
|
|
||||||
|
try {
|
||||||
|
user = pixiv_client.get_user(user_id);
|
||||||
|
illust_ids = pixiv_client.get_illusts(user_id);
|
||||||
|
} catch (const PixivException& e) {
|
||||||
|
if (e.status == 404) {
|
||||||
|
res.status = 404;
|
||||||
|
serve_error(req, res, config, "404: User not found", e.what());
|
||||||
|
} else {
|
||||||
|
res.status = 500;
|
||||||
|
serve_error(req, res, config, "500: Internal server error", "Failed to fetch user information", e.what());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
res.status = 500;
|
||||||
|
serve_error(req, res, config, "500: Internal server error", "Failed to fetch user information", e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const constexpr size_t items_per_page = 18; // on pixiv touch
|
||||||
|
Element body("body", {
|
||||||
|
generate_user_header(std::move(user), config),
|
||||||
|
generate_pager(page, illust_ids.size(), items_per_page),
|
||||||
|
Element("br"),
|
||||||
|
generate_content(std::move(illust_ids), page, items_per_page),
|
||||||
|
generate_pager(page, illust_ids.size(), items_per_page)
|
||||||
|
});
|
||||||
|
(void)page_to_offsets;
|
||||||
|
serve(req, res, config, user.display_name + " illustrations", std::move(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t to_ull(const std::string& str) {
|
||||||
|
char* endptr;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
uint64_t res = strtoull(str.c_str(), &endptr, 10);
|
||||||
|
if (res == ULLONG_MAX && errno == ERANGE) {
|
||||||
|
throw std::overflow_error(str + " is too big");
|
||||||
|
} else if (endptr[0] != '\0') {
|
||||||
|
throw std::invalid_argument(str + " contains trailing text or is not an integer");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Element generate_pager(size_t page, size_t items, size_t items_per_page) {
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
size_t total_pages = page_count(items, items_per_page);
|
||||||
|
auto link = [](std::string href, const char* text, bool add_link) {
|
||||||
|
Element b("b");
|
||||||
|
if (add_link) {
|
||||||
|
b.nodes.push_back(Element("a", {{"href", std::move(href)}}, {text}));
|
||||||
|
} else {
|
||||||
|
b.nodes.push_back(text);
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
return Element("div", {{"class", "center"}}, {
|
||||||
|
link("?p=1", "First", page != 0), " ",
|
||||||
|
link("?p="s + std::to_string(page), "Prev", page != 0), " ",
|
||||||
|
std::to_string(page + 1), "/", std::to_string(total_pages), " ",
|
||||||
|
link("?p="s + std::to_string(page + 2), "Next", page + 1 < total_pages), " ",
|
||||||
|
link("?p="s + std::to_string(total_pages), "Last", page + 1 < total_pages)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Element generate_content(const std::vector<uint64_t>& illust_ids, size_t page, size_t items_per_page) {
|
||||||
|
// TODO be real
|
||||||
|
Element ul("ul");
|
||||||
|
std::pair<size_t, size_t> illust_ids_offsets = page_to_offsets(page, illust_ids.size(), items_per_page);
|
||||||
|
|
||||||
|
ul.nodes.reserve(items_per_page);
|
||||||
|
for (size_t i = illust_ids_offsets.first; i < illust_ids_offsets.second; i++) {
|
||||||
|
ul.nodes.push_back(Element("li", {
|
||||||
|
std::to_string(illust_ids[i])
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ul;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t page_count(size_t items, size_t items_per_page) {
|
||||||
|
size_t ret = items / items_per_page;
|
||||||
|
if (items % items_per_page != 0) {
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::pair<size_t, size_t> page_to_offsets(size_t page, size_t items, size_t items_per_page) {
|
||||||
|
size_t start_offset = page * items_per_page;
|
||||||
|
size_t end_offset = start_offset + items_per_page;
|
||||||
|
return {items > start_offset ? start_offset : items, items > end_offset ? end_offset : items};
|
||||||
|
}
|
Loading…
Reference in New Issue