commit f81d977f463e91f945304388571221683ccfb868 Author: blank X Date: Thu Dec 2 22:07:37 2021 +0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2be6ae --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +builddir/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bfa3eef --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.20.2) +project(patom CXX) + +find_package(CURL REQUIRED) +find_package(nlohmann_json REQUIRED) + +add_executable(${PROJECT_NAME} main.cpp) +target_link_libraries(${PROJECT_NAME} PRIVATE curl nlohmann_json::nlohmann_json) diff --git a/build b/build new file mode 100755 index 0000000..4a0a5ed --- /dev/null +++ b/build @@ -0,0 +1,4 @@ +#!/bin/sh +test "$1" = "debug" && TYPE="Debug" || TYPE="Release" +echo "Building in $TYPE" +cmake -S . -B builddir -D CMAKE_BUILD_TYPE=$TYPE && cd builddir && cmake --build . --config "$TYPE" diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..65b07f4 --- /dev/null +++ b/main.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include + +#define MAX_FILE_SIZE 1024 * 1024 * 1024 +#define set_curl_opt_or_die(curl, option, param) do { \ + CURLcode curl_code = curl_easy_setopt(curl, option, param); \ + if (curl_code != CURLE_OK) { \ + curl_easy_cleanup(curl); \ + fprintf(stderr, "curl_easy_setopt returned %s\n", curl_easy_strerror(curl_code)); \ + curl_global_cleanup(); \ + exit(1); \ + } \ +} while(0) + +size_t write_memory_cb(char* contents, size_t size, size_t nmemb, std::string* str) { + size_t realsize = size * nmemb; + if (str->length() + realsize > MAX_FILE_SIZE) { + fprintf(stderr, "Max file size reached\n"); + return 0; + } + str->append(contents, realsize); + return realsize; +} + +// behold: the why +// oh and this is stolen from https://stackoverflow.com/a/3418285 +void escape_xml(std::string str) { + if (str.empty()) { + return; + } + size_t start = 0; + while ((start = str.find("&", start)) != std::string::npos) { + str.replace(start, 1, "&"); + start += 5; + } + start = 0; + while ((start = str.find("<", start)) != std::string::npos) { + str.replace(start, 1, "<"); + start += 4; + } + start = 0; + while ((start = str.find(">", start)) != std::string::npos) { + str.replace(start, 1, ">"); + start += 4; + } + start = 0; + while ((start = str.find("\"", start)) != std::string::npos) { + str.replace(start, 1, """); + start += 6; + } + start = 0; + while ((start = str.find("'", start)) != std::string::npos) { + str.replace(start, 1, "'"); + start += 6; + } +} + +int main(int argc, char* argv[]) { + if (argc != 2) { + if (argc > 0) { + fprintf(stderr, "Usage: %s \n", argv[0]); + } else { + fprintf(stderr, "Usage: patom \n"); + } + return 1; + } + char* pixiv_user_id = argv[1]; + if (!*pixiv_user_id) { + fprintf(stderr, "User ID is empty\n"); + return 1; + } + for (int i=0; pixiv_user_id[i]; i++) { + if (!(pixiv_user_id[i] >= '0' && pixiv_user_id[i] <= '9')) { + fprintf(stderr, "User ID is not numeric\n"); + return 1; + } + } + curl_global_init(CURL_GLOBAL_ALL); + CURL* curl = curl_easy_init(); + if (!curl) { + fprintf(stderr, "curl_easy_init returned NULL\n"); + curl_global_cleanup(); + return 1; + } + set_curl_opt_or_die(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); + std::string url = "https://www.pixiv.net/touch/ajax/user/illusts?type=illust&lang=en&id="; + url.append(argv[1]); + set_curl_opt_or_die(curl, CURLOPT_URL, url.c_str()); + std::string response_body; + set_curl_opt_or_die(curl, CURLOPT_WRITEFUNCTION, write_memory_cb); + set_curl_opt_or_die(curl, CURLOPT_WRITEDATA, &response_body); + CURLcode curl_code = curl_easy_perform(curl); + curl_easy_cleanup(curl); + if (curl_code != CURLE_OK) { + fprintf(stderr, "curl_easy_perform returned %s\n", curl_easy_strerror(curl_code)); + curl_global_cleanup(); + return 1; + } + curl_global_cleanup(); + nlohmann::json j = nlohmann::json::parse(response_body); + + if (j["error"].get()) { + fprintf(stderr, "Error returned by the API: %s\n", j["message"].get().c_str()); + return 1; + } + std::vector illusts = j["body"]["illusts"].get>(); + time_t last_updated_timestamp; + if (illusts.size()) { + last_updated_timestamp = illusts[0]["upload_timestamp"].get(); + } else { + last_updated_timestamp = time(NULL); + } + std::string ogp_title = j["body"]["meta"]["ogp"]["title"].get(); + escape_xml(ogp_title); + char tm_str[100] = ""; + strftime(tm_str, 100, "%FT%TZ", gmtime(&last_updated_timestamp)); + // wonder if pixiv is a urn namespace + // also cba to make another request to find the username so here we are + printf("\n\n urn:pixiv:user:%s\n %s\n %s\n %s\n", argv[1], argv[1], tm_str, ogp_title.c_str()); + for (size_t i=0; i < illusts.size(); i++) { + nlohmann::json illust = illusts[i]; + std::string title = illust["alt"].get(); + std::string illust_id = illust["id"].get(); + // istg if 0 isn't 1970 + time_t upload_timestamp = illust["upload_timestamp"].get(); + strftime(tm_str, 100, "%FT%TZ", gmtime(&upload_timestamp)); + escape_xml(title); + printf(" \n urn:pixiv:illust:%s\n %s\n %s\n \n \n", illust_id.c_str(), title.c_str(), tm_str, illust_id.c_str()); + } + printf("\n"); + return 0; +}