#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; }