diff --git a/config.json b/config.json index 04e46a1..26b0716 100644 --- a/config.json +++ b/config.json @@ -26,17 +26,18 @@ "any-key-ignore-commands": ["up", "right", "down", "left"], "suppress-any-key-on-mods": true }, - "logging": + "log": { "enabled": true, "output-directory": "local", - "debug": true + "debug-to-stdout": false, + "debug-to-file": true }, "scan": { "json-save": true, "json-save-directory": "local/scans", - "barcode": "645175525233" + "barcode": "400063314395" }, "api": { @@ -45,7 +46,7 @@ "nutronix-app-key": "39218dde526dd3349daa028deda518ae", "edamam-app-id": "c23b139f", "edamam-app-key": "c54cf8c997534caf7ee92b1ccc7d95a3", - "best-buy-api-key": "cFshpD7C2LtMq07GqlBVpYtY", + "best-buy-api-key": "vAC23XA5YWBzaYiGtOkoNlXZ", "giantbomb-api-key": "91a395231f4e1fd9f9ba8840c52a61cda343cd70" } } diff --git a/lib/sfw b/lib/sfw index 4ece644..fff9e13 160000 --- a/lib/sfw +++ b/lib/sfw @@ -1 +1 @@ -Subproject commit 4ece64442fc12be150be217d71dab9afd2d2f8e4 +Subproject commit fff9e13562d63ccd0b478e15d1953d1c7e3c08f9 diff --git a/src/Item.cpp b/src/Item.cpp index 01c2734..76a80c8 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -1,5 +1,7 @@ #include "Item.hpp" +Item::Item(Node* parent) : Node(parent) {}; + void Item::set_text_property(const std::string& value, std::string& property, const std::string& property_name) { if (property == "") @@ -7,32 +9,30 @@ void Item::set_text_property(const std::string& value, std::string& property, co if (value != "") { property = value; - SDL_Log("set %s to %s in %s", property_name.c_str(), property.c_str(), get_full_name().c_str()); + log("set " + property_name + " to " + property + " in " + get_full_name()); } else { - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "empty string passed, not setting %s in %s", - property_name.c_str(), get_full_name().c_str()); + debug("empty string passed, not setting " + property_name + " in " + get_full_name()); } } else { - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s already set to %s in %s, not setting", - property_name.c_str(), property.c_str(), get_full_name().c_str()); + debug(property_name + " already set to " + property + " in " + get_full_name() + ", not setting"); } } +void Item::add_image_texture(std::shared_ptr texture) +{ + image_textures.push_back(texture); +} + void Item::destroy_texture(SDL_Texture* texture) { SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "destroying texture %p", texture); SDL_DestroyTexture(texture); } -void Item::add_image_texture(SDL_Texture* texture) -{ - image_textures.push_back(std::shared_ptr(texture, Item::destroy_texture)); -} - const std::vector>& Item::get_image_textures() const { return image_textures; @@ -91,5 +91,7 @@ void Item::increment_image_index(int increment) Item::~Item() { - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "destroying item %p", this); + std::stringstream message; + message << "destroying item " << this; + debug(message.str()); } diff --git a/src/Item.hpp b/src/Item.hpp index a3db4de..1c01a8c 100644 --- a/src/Item.hpp +++ b/src/Item.hpp @@ -2,23 +2,34 @@ #define Item_h_ #include +#include #include #include +#include #include #include +#include "Node.hpp" -class Item +class Item : public Node { private: + std::vector> image_textures; std::string brand_name = "", product_name = "", upc = ""; int current_image_index = 0; void set_text_property(const std::string&, std::string&, const std::string&); static void destroy_texture(SDL_Texture*); + /* + * add properties: ingredients, protein weight, nutrition grade, popularity, "serving unit", keywords, + * allergens, calories, fat, saturated fat, cholesterol, sodium, carbohydrates, fiber, sugar, potassium + */ + public: - void add_image_texture(SDL_Texture*); + + Item(Node*); + void add_image_texture(std::shared_ptr SDL_Texture); const std::vector>& get_image_textures() const; const std::shared_ptr& get_active_image_texture() const; void set_brand_name(const std::string&); @@ -29,7 +40,7 @@ public: const std::string& get_upc() const; std::string get_full_name() const; void increment_image_index(int = 1); - ~Item(); + virtual ~Item(); }; diff --git a/src/Pudding.cpp b/src/Pudding.cpp index ee98499..89f37e4 100644 --- a/src/Pudding.cpp +++ b/src/Pudding.cpp @@ -1,17 +1,18 @@ /* - * ______________ - * //````````````\\ 😀 Thank you for choosing Pudding Customs for your business 😀 - * //~~~~~~~~~~~~~~\\ - * //================\\ Custom pudding code provided by 0eggxactly ........... [http://0eggxactly.itch.io] - * // \\ Available for copy, modification and redistribution .. [http://git.shampoo.ooo/pudding] - * // ''CUSTOM PUDDING'' \\ Updates .............................................. [http://twitter.com/diskmem] - * //______________________\\ - * `````````````````````````` - * - * Use this application to generate a custom pudding from a series of UPC scans, helping a pair of rats leverage their - * extraterrestrial trash symbiosis, using it to take over the video game industry with performance enhancing drugged - * puddings that enable business professionals to predict the stock market with supernatural accuracy. - */ + ______________ + //````````````\\ + by @ohsqueezy [http://ohsqueezy.itch.io] & @sleepin [http://instagram.com/sleepin] + //~~~~~~~~~~~~~~\\ + custom pudding code provided by Nuggets Select [http://nugget.fun] + //================\\ + available for copy, modification and redistribution [http://git.nugget.fun/pudding] + // \\ + // ''CUSTOM PUDDING'' \\ 😀 Thank you for choosing Pudding Customs for your business 😀 + //______________________\\ + `````````````````````````` + + Generate a custom pudding from food product UPC codes and help a pair of rats take over the video game industry, using + their extraterrestrial ability to turn trash into performance enhancing drug puddings that enable business professionals + to predict the stock market with supernatural accuracy. + +*/ #include "Pudding.hpp" @@ -29,6 +30,7 @@ Pudding::Pudding() load_sdl_context(); } +// Respond to key press events void Pudding::respond(SDL_Event& event) { if (get_delegate().compare(event, "up")) @@ -49,30 +51,26 @@ void Pudding::respond(SDL_Event& event) } } -void Pudding::load_sdl_context() -{ - super::load_sdl_context(); -} - -/* Build an Item object by submitting upc parameter to various APIs and incorporating +/* Build an Item object by submitting the upc parameter to multiple APIs and taking * relevant results from each. Result JSON will be saved if saving is enabled in the global - * configuration. + * configuration */ void Pudding::add_item(const std::string& upc) { - Item item; + Item item(this); item.set_upc(upc); incorporate_open_food_api(item); incorporate_nutronix_api(item); incorporate_edamam_api(item); + incorporate_best_buy_api(item); items.push_back(item); } -/* Look for item upc in the Open Food API, and use result to fill out item properties if found +/* Look for item upc in the Open Food API, and use the result to fill out item properties if found */ void Pudding::incorporate_open_food_api(Item& item) { - SDL_Log("checking Open Food API"); + log("checking Open Food API"); nlohmann::json json = json_from_url(OPEN_FOOD_API_URL + item.get_upc()); // test that should determine if an Open Food API response is not empty if (json.value("status", 0) && json.contains("product")) @@ -80,7 +78,7 @@ void Pudding::incorporate_open_food_api(Item& item) if (json["product"].value("image_url", "") != "") { std::string url = json["product"]["image_url"]; - SDL_Texture* texture = texture_from_image_url(url); + std::shared_ptr texture = texture_from_image_url(url); if (texture != nullptr) { item.add_image_texture(texture); @@ -92,15 +90,15 @@ void Pudding::incorporate_open_food_api(Item& item) } else { - SDL_Log("no results from Open Food API"); + log("no results from Open Food API"); } } -/* Look for item upc in the Nutronix API, and use result to fill out item properties if found +/* Look for item upc in the Nutronix API, and use the result to fill out item properties if found */ void Pudding::incorporate_nutronix_api(Item& item) { - SDL_Log("checking Nutronix API"); + log("checking Nutronix API"); // Nutronix requires API keys in headers for validation nlohmann::json json = json_from_url( NUTRONIX_API_URL + item.get_upc(), { @@ -114,8 +112,8 @@ void Pudding::incorporate_nutronix_api(Item& item) if (food.contains("photo") && food["photo"].value("thumb", "") != "") { std::string url = food["photo"]["thumb"]; - SDL_Log("adding image listed in Nutronix API at %s", url.c_str()); - SDL_Texture* texture = texture_from_image_url(url); + log("adding image listed in Nutronix API at " + url); + std::shared_ptr texture = texture_from_image_url(url); if (texture != nullptr) { item.add_image_texture(texture); @@ -127,7 +125,7 @@ void Pudding::incorporate_nutronix_api(Item& item) } else { - SDL_Log("no results from Nutronix API"); + log("no results from Nutronix API"); } } @@ -135,7 +133,7 @@ void Pudding::incorporate_nutronix_api(Item& item) */ void Pudding::incorporate_edamam_api(Item& item) { - SDL_Log("checking Edamam API"); + log("checking Edamam API"); // build API url by concatenating relevant values into query string std::stringstream url; url << "https://api.edamam.com/api/food-database/v2/parser?upc=" << item.get_upc() << "&app_id=" << @@ -149,7 +147,7 @@ void Pudding::incorporate_edamam_api(Item& item) if (food.value("image", "") != "") { std::string url = food["image"]; - SDL_Texture* texture = texture_from_image_url(url); + std::shared_ptr texture = texture_from_image_url(url); if (texture != nullptr) { item.add_image_texture(texture); @@ -160,10 +158,42 @@ void Pudding::incorporate_edamam_api(Item& item) } } +/* Submit a query to the Best Buy API and insert relevant results into supplied Item object + */ +void Pudding::incorporate_best_buy_api(Item& item) +{ + log("checking Best Buy API"); + // build API url by concatenating relevant values into query string + std::stringstream url; + url << "https://api.bestbuy.com/v1/products(upc=" << item.get_upc() << ")?format=json&apiKey=" << + get_configuration()["api"]["best-buy-api-key"].get(); + nlohmann::json json = json_from_url(url.str()); + // test that should determine if a Best Buy response has a result + if (json.contains("total") && json["total"].get() > 0) + { + nlohmann::json product = json["products"][0]; + // look up image (for games this is box art) and "alternate views image" (for games this is a screen shot) + for (std::string key : {"image", "alternateViewsImage"}) + { + if (product.value(key, "") != "") + { + std::string url = product[key]; + std::shared_ptr texture = texture_from_image_url(url); + if (texture != nullptr) + { + item.add_image_texture(texture); + } + } + } + item.set_product_name(product.value("name", "")); + save_item_json(json, item, "Best_Buy_API"); + } +} + /* Write submitted JSON to file, creating parent directories if necessary, and using item and * api_name to determine file name prefix */ -void Pudding::save_item_json(const nlohmann::json& json, const Item& item, const std::string& api_name) +void Pudding::save_item_json(const nlohmann::json& json, const Item& item, const std::string& api_name) const { if (get_configuration()["scan"]["json-save"]) { @@ -185,14 +215,16 @@ void Pudding::save_item_json(const nlohmann::json& json, const Item& item, const path /= prefix + "_" + item.get_upc() + ".json"; std::ofstream out(path); out << std::setw(4) << json << std::endl; - SDL_Log("Saved JSON to %s", path.c_str()); + log("Saved JSON to " + path.string()); } else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "not saving JSON, saving disabled by configuration"); + SDL_LogWarn(SDL_LOG_CATEGORY_CUSTOM, "not saving JSON, saving disabled by configuration"); } } +/* Download the JSON data at the submitted URL, and return it as a JSON object + */ nlohmann::json Pudding::json_from_url(const std::string& url, const std::vector& headers) { std::vector storage; @@ -200,10 +232,12 @@ nlohmann::json Pudding::json_from_url(const std::string& url, const std::vector< nlohmann::json json = nlohmann::json::parse(storage); std::stringstream json_formatted; json_formatted << std::setw(4) << json << std::endl; - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", json_formatted.str().c_str()); + debug(json_formatted.str()); return json; } +/* Store the byte buffer from the submitted URL downloaded by cURL into the supplied storage vector + */ void Pudding::curl_get_bytes(const std::string& url, std::vector& storage, const std::vector& headers) { CURL *curl; @@ -248,6 +282,9 @@ void Pudding::curl_get_bytes(const std::string& url, std::vector& curl_global_cleanup(); } +/* This callback will be called by cURL when it has a response char buffer. The chars will be inserted into the storage + * vector pointed to by the storage parameter. + */ size_t Pudding::curl_write_response(std::uint8_t* buffer, size_t size, size_t count, std::vector* storage) { size_t total_size = size * count; @@ -255,24 +292,38 @@ size_t Pudding::curl_write_response(std::uint8_t* buffer, size_t size, size_t co return total_size; } -SDL_Texture* Pudding::texture_from_image_url(const std::string& url) +/* Get an image at the submitted URL as a pointer to SDL_Texture memory + */ +std::shared_ptr Pudding::texture_from_image_url(const std::string& url) { - SDL_Log("looking up image at %s", url.c_str()); + log("looking up image at " + url); std::vector storage; curl_get_bytes(url, storage); if (!storage.empty()) { SDL_RWops* rw = SDL_RWFromConstMem(storage.data(), storage.size()); - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "received image data"); - return IMG_LoadTexture_RW(get_renderer(), rw, 0); + debug("received image data"); + return std::shared_ptr(IMG_LoadTexture_RW(get_renderer(), rw, 0), Pudding::destroy_texture); } else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "image url returned no data"); + SDL_LogWarn(SDL_LOG_CATEGORY_CUSTOM, "image url returned no data"); return nullptr; } } +/* Call SDL's destroy texture function, and print a debug statement for testing. This is defined as a static member + * function and uses the SDL function instead of the inherited logging functions from Node since the object may not + * be allocated at destruction time (?) + */ +void Pudding::destroy_texture(SDL_Texture* texture) +{ + // not sure why SDL_Log works here but SDL_LogDebug and SDL_LogInfo don't + SDL_Log("destroying texture %p", texture); + SDL_DestroyTexture(texture); +} + +/* Change the currently selected item */ void Pudding::increment_item_index(int increment) { current_item_index = sfw::mod(current_item_index + increment, static_cast(items.size())); @@ -283,6 +334,7 @@ Item& Pudding::get_current_item() return items[current_item_index]; } +/* Update parameters and draw the screen */ void Pudding::update() { get_root()->configuration.load("config.json"); diff --git a/src/Pudding.hpp b/src/Pudding.hpp index 8c29cb3..d09d0d7 100644 --- a/src/Pudding.hpp +++ b/src/Pudding.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -15,41 +16,43 @@ #include "extension.hpp" #include "Item.hpp" -struct Pudding : Game +class Pudding : public Game { +private: + + typedef Game super; const std::string OPEN_FOOD_API_URL = "https://world.openfoodfacts.org/api/v0/product/"; const std::string NUTRONIX_API_URL = "https://trackapi.nutritionix.com/v2/search/item?upc="; const std::string BARCODE_MONSTER_API_URL = "https://barcode.monster/api/"; - const std::string BEST_BUY_API_URL = "https://api.bestbuy.com/v1/products(upc=)?format=json&apiKey="; + const std::string BEST_BUY_API_URL_1 = "https://api.bestbuy.com/v1/products(upc="; + const std::string BEST_BUY_API_URL_2 = ")?format=json&apiKey="; const std::string NUTRONIX_NOT_FOUND = "resource not found"; - - /* - * ingredients, protein weight, nutrition grade, popularity, "serving unit", keywords, - * allergens, calories, fat, saturated fat, cholesterol, sodium, carbohydrates, fiber, sugar, potassium - */ - - typedef Game super; + const std::string GIANTBOMB_API_URL = "https://www.giantbomb.com/api/release/?api_key="; std::string current_barcode; std::vector items; int current_item_index; - Pudding(); - void load_sdl_context(); - void respond(SDL_Event&); - void add_item(const std::string&); void incorporate_open_food_api(Item&); void incorporate_nutronix_api(Item&); void incorporate_edamam_api(Item&); - void save_item_json(const nlohmann::json&, const Item&, const std::string&); + void incorporate_best_buy_api(Item&); + void save_item_json(const nlohmann::json&, const Item&, const std::string&) const; nlohmann::json json_from_url(const std::string&, const std::vector& = {}); void curl_get_bytes(const std::string& url, std::vector&, const std::vector& = {}); static size_t curl_write_response(std::uint8_t*, size_t, size_t, std::vector*); - SDL_Texture* texture_from_image_url(const std::string&); + std::shared_ptr texture_from_image_url(const std::string&); + static void destroy_texture(SDL_Texture*); + +public: + + Pudding(); + void respond(SDL_Event&); + void add_item(const std::string&); void increment_item_index(int = 1); Item& get_current_item(); void update(); - std::string get_class_name() { return "Pudding"; } + virtual std::string get_class_name() const { return "Pudding"; } };