added best buy api; create shared ptr immediately from sdl texture return val

This commit is contained in:
frank 2021-06-24 17:53:58 -04:00
parent 1a090d26d5
commit 60101b7bbc
6 changed files with 145 additions and 76 deletions

View File

@ -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"
}
}

@ -1 +1 @@
Subproject commit 4ece64442fc12be150be217d71dab9afd2d2f8e4
Subproject commit fff9e13562d63ccd0b478e15d1953d1c7e3c08f9

View File

@ -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<SDL_Texture> 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<SDL_Texture>(texture, Item::destroy_texture));
}
const std::vector<std::shared_ptr<SDL_Texture>>& 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());
}

View File

@ -2,23 +2,34 @@
#define Item_h_
#include <vector>
#include <sstream>
#include <string>
#include <memory>
#include <functional>
#include <SDL.h>
#include <extension.hpp>
#include "Node.hpp"
class Item
class Item : public Node
{
private:
std::vector<std::shared_ptr<SDL_Texture>> 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> SDL_Texture);
const std::vector<std::shared_ptr<SDL_Texture>>& get_image_textures() const;
const std::shared_ptr<SDL_Texture>& 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();
};

View File

@ -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<SDL_Texture> 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<SDL_Texture> 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<SDL_Texture> 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<std::string>();
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<int>() > 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<SDL_Texture> 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<std::string>& headers)
{
std::vector<std::uint8_t> 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<std::uint8_t>& storage, const std::vector<std::string>& headers)
{
CURL *curl;
@ -248,6 +282,9 @@ void Pudding::curl_get_bytes(const std::string& url, std::vector<std::uint8_t>&
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<std::uint8_t>* 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<SDL_Texture> 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<std::uint8_t> 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<SDL_Texture>(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<int>(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");

View File

@ -3,6 +3,7 @@
#include <string>
#include <iostream>
#include <memory>
#include <type_traits>
#include <filesystem>
#include <curl/curl.h>
@ -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<Item> 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<std::string>& = {});
void curl_get_bytes(const std::string& url, std::vector<std::uint8_t>&, const std::vector<std::string>& = {});
static size_t curl_write_response(std::uint8_t*, size_t, size_t, std::vector<std::uint8_t>*);
SDL_Texture* texture_from_image_url(const std::string&);
std::shared_ptr<SDL_Texture> 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"; }
};