Initial commit
This commit is contained in:
		
						commit
						f81d977f46
					
				| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					builddir/
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					@ -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"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,134 @@
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <curl/curl.h>
 | 
				
			||||||
 | 
					#include <nlohmann/json.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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 <pixiv user id>\n", argv[0]);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            fprintf(stderr, "Usage: patom <pixiv user id>\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<bool>()) {
 | 
				
			||||||
 | 
					        fprintf(stderr, "Error returned by the API: %s\n", j["message"].get<std::string>().c_str());
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::vector<nlohmann::json> illusts = j["body"]["illusts"].get<std::vector<nlohmann::json>>();
 | 
				
			||||||
 | 
					    time_t last_updated_timestamp;
 | 
				
			||||||
 | 
					    if (illusts.size()) {
 | 
				
			||||||
 | 
					        last_updated_timestamp = illusts[0]["upload_timestamp"].get<time_t>();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        last_updated_timestamp = time(NULL);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    std::string ogp_title = j["body"]["meta"]["ogp"]["title"].get<std::string>();
 | 
				
			||||||
 | 
					    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("<?xml version=\"1.0\"?>\n<feed xmlns=\"http://www.w3.org/2005/Atom\">\n    <id>urn:pixiv:user:%s</id>\n    <author><name>%s</name></author>\n    <updated>%s</updated>\n    <title type=\"text\">%s</title>\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>();
 | 
				
			||||||
 | 
					        std::string illust_id = illust["id"].get<std::string>();
 | 
				
			||||||
 | 
					        // istg if 0 isn't 1970
 | 
				
			||||||
 | 
					        time_t upload_timestamp = illust["upload_timestamp"].get<time_t>();
 | 
				
			||||||
 | 
					        strftime(tm_str, 100, "%FT%TZ", gmtime(&upload_timestamp));
 | 
				
			||||||
 | 
					        escape_xml(title);
 | 
				
			||||||
 | 
					        printf("    <entry>\n        <id>urn:pixiv:illust:%s</id>\n        <title type=\"text\">%s</title>\n        <updated>%s</updated>\n        <link rel=\"alternate\" type=\"text/html\" href=\"https://www.pixiv.net/en/artworks/%s\"></link>\n    </entry>\n", illust_id.c_str(), title.c_str(), tm_str, illust_id.c_str());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    printf("</feed>\n");
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue