#include "sap.h" #include #include #include #include #include #include #include /* - random frames from Spongebob (using ffmpeg) * with strange text overlaid, possibly rawr'd from * spongebob transcripts combined with drug trip * experiences? either that or something with verb * frames */ sap::sap( std::string configFile, std::mt19937& rng) : rng_(rng) { // Load the config file. YAML::Node config = YAML::LoadFile(configFile); tempfile_ = config["tempfile"].as(); blog_name_ = config["blog_name"].as(); // Set up the Tumblr client. tumblr_consumer_ = std::make_unique(config["consumer_key"].as(), config["consumer_secret"].as()); tumblr_token_ = std::make_unique(config["access_token"].as(), config["access_token_secret"].as()); tumblr_client_ = std::make_unique(tumblr_consumer_.get(), tumblr_token_.get()); // Set up the text generator. for (const YAML::Node& corpusname : config["corpuses"]) { std::ifstream infile(corpusname.as()); std::string corpus; std::string line; while (getline(infile, line)) { if (line.back() == '\r') { line.pop_back(); } corpus += line + " "; } kgramstats_.addCorpus(corpus); } kgramstats_.compile(5); kgramstats_.setMinCorpora(config["corpuses"].size()); // Set up the layout designer. layout_ = std::unique_ptr( new designer(config["fonts"].as())); // Set up the frame picker. director_ = std::unique_ptr( new director(config["videos"].as())); } void sap::run() const { for (;;) { std::cout << "Generating post..." << std::endl; try { // Pick the video frame. Magick::Image image = director_->generate(rng_); // Generate the text. std::uniform_int_distribution lenDist(5, 19); std::string action = kgramstats_.randomSentence(lenDist(rng_), rng_); // Lay the text on the video frame. Magick::Image textimage = layout_->generate(image.columns(), image.rows(), action, rng_); image.composite(textimage, 0, 0, Magick::OverCompositeOp); // Post to tumblr. std::cout << "Posting to Tumblr..." << std::endl; postToTumblr(action, std::move(image)); std::cout << std::endl << "Posted!" << std::endl; // Wait. std::this_thread::sleep_for(std::chrono::hours(11)); } catch (const Magick::ErrorImage& ex) { std::cout << "Image error: " << ex.what() << std::endl; } std::cout << std::endl; } } void sap::postToTumblr(const std::string& post_text, Magick::Image image) const { image.magick("jpeg"); image.write(tempfile_); nlohmann::json jsonMedia; jsonMedia["type"] = "image/jpeg"; jsonMedia["identifier"] = "some-identifier"; jsonMedia["width"] = image.columns(); jsonMedia["height"] = image.rows(); jsonMedia["alt_text"] = post_text; jsonMedia["caption"] = post_text; nlohmann::json contentBlock; contentBlock["type"] = "image"; contentBlock["media"] = jsonMedia; nlohmann::json postBody; postBody["content"].push_back(contentBlock); postBody["tags"] = "spongebob,generated"; std::string postText = postBody.dump(); std::cout << postText << std::endl; Magick::Blob blob; image.write(&blob); std::string url = "https://api.tumblr.com/v2/blog/" + blog_name_ + "/posts"; std::string oauthHeader = tumblr_client_->getFormattedHttpHeader(OAuth::Http::Post, url, ""); struct curl_slist* header_list = NULL; header_list = curl_slist_append(header_list, oauthHeader.c_str()); CURL* easy = curl_easy_init(); curl_mime* mime = curl_mime_init(easy); curl_mimepart* json_part = curl_mime_addpart(mime); curl_mime_data(json_part, postText.c_str(), CURL_ZERO_TERMINATED); curl_mime_name(json_part, "json"); curl_mime_type(json_part, "application/json"); curl_mimepart* image_part = curl_mime_addpart(mime); curl_mime_data(image_part, reinterpret_cast(blob.data()), blob.length()); curl_mime_name(image_part, "some-identifier"); curl_mime_type(image_part, "image/jpeg"); curl_mime_filename(image_part, "image.jpg"); curl_easy_setopt(easy, CURLOPT_MIMEPOST, mime); curl_easy_setopt(easy, CURLOPT_HTTPHEADER, header_list); curl_easy_setopt(easy, CURLOPT_URL, url.c_str()); CURLcode error = curl_easy_perform(easy); if (error != CURLE_OK) { std::cout << curl_easy_strerror(error) << std::endl; } curl_easy_cleanup(easy); curl_slist_free_all(header_list); curl_mime_free(mime); }