gunkiss/src/Pudding.cpp

233 lines
7.5 KiB
C++

/*
______________
//````````````\\ 😀 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]
//______________________\\
``````````````````````````
*/
#include "Pudding.hpp"
int main()
{
Pudding pudding = Pudding();
pudding.run();
pudding.quit();
return 0;
}
Pudding::Pudding()
{
load_sdl_context();
}
void Pudding::load_sdl_context()
{
super::load_sdl_context();
}
void Pudding::add_item(const std::string& barcode)
{
Item item;
item.set_upc(barcode);
incorporate_open_food_api(item);
incorporate_nutronix_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
*/
void Pudding::incorporate_open_food_api(Item& item)
{
SDL_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"))
{
if (json["product"].value("image_url", "") != "")
{
std::string url = json["product"]["image_url"];
SDL_Texture* texture = texture_from_image_url(url);
if (texture != nullptr)
{
item.add_image_texture(texture);
}
}
item.set_brand_name(json["product"].value("brands", ""));
item.set_product_name(json["product"].value("product_name", ""));
save_item_json(json, item, "Open_Food_API");
}
else
{
SDL_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
*/
void Pudding::incorporate_nutronix_api(Item& item)
{
SDL_Log("checking Nutronix API");
// Nutronix requires API keys in headers for validation
nlohmann::json json = json_from_url(
NUTRONIX_API_URL + item.get_upc(), {
"x-app-id: " + get_configuration()["api"]["nutronix-app-id"].get<std::string>(),
"x-app-key: " + get_configuration()["api"]["nutronix-app-key"].get<std::string>()
});
// test that should determine if a Nutronix response is not empty
if (!(json.contains("message") && json["message"] == NUTRONIX_NOT_FOUND))
{
nlohmann::json food = json["foods"][0];
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);
if (texture != nullptr)
{
item.add_image_texture(texture);
}
}
item.set_brand_name(food.value("brand_name", ""));
item.set_product_name(food.value("food_name", ""));
save_item_json(json, item, "Nutronix_API");
}
else
{
SDL_Log("no results from Nutronix 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)
{
if (get_configuration()["scan"]["json-save"])
{
fs::path path = get_configuration()["scan"]["json-save-directory"];
if (!fs::exists(path))
{
fs::create_directories(path);
}
std::string prefix = api_name;
if (item.get_full_name() != "")
{
prefix += "_" + item.get_full_name();
}
else
{
prefix += "_Unknown";
}
std::replace_if(prefix.begin(), prefix.end(), [](char c) { return !std::isalnum(c); }, '_');
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());
}
else
{
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "not saving JSON, saving disabled by configuration");
}
}
nlohmann::json Pudding::json_from_url(const std::string& url, const std::vector<std::string>& headers)
{
std::vector<std::uint8_t> storage;
curl_get_bytes(url, storage, headers);
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());
return json;
}
void Pudding::curl_get_bytes(const std::string& url, std::vector<std::uint8_t>& storage, const std::vector<std::string>& headers)
{
CURL *curl;
CURLcode result;
result = curl_global_init(CURL_GLOBAL_DEFAULT);
if (result != CURLE_OK)
{
std::cout << "curl initialization failed " << curl_easy_strerror(result) << std::endl;
}
else
{
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Pudding::curl_write_response);
std::vector<std::uint8_t> food_barcode_response;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &storage);
curl_easy_setopt(curl, CURLOPT_USERAGENT, get_configuration()["api"]["user-agent"].get<std::string>().c_str());
struct curl_slist* list = nullptr;
if (headers.size() > 0)
{
for (const std::string& header : headers)
{
list = curl_slist_append(list, header.c_str());
}
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
result = curl_easy_perform(curl);
curl_slist_free_all(list);
if (result != CURLE_OK)
{
std::cout << "curl request failed " << curl_easy_strerror(result) << std::endl;
}
}
else
{
std::cout << "curl initialization failed" << std::endl;
}
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
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;
storage->insert(storage->end(), buffer, buffer + total_size);
return total_size;
}
SDL_Texture* Pudding::texture_from_image_url(const std::string& url)
{
SDL_Log("looking up image at %s", url.c_str());
std::vector<std::uint8_t> storage;
curl_get_bytes(url, storage);
if (!storage.empty())
{
SDL_RWops* rw = SDL_RWFromConstMem(storage.data(), storage.size());
return IMG_LoadTexture_RW(get_renderer(), rw, 0);
}
else
{
return nullptr;
}
}
void Pudding::update()
{
get_root()->configuration.load("config.json");
get_root()->configuration.merge();
if (current_barcode != get_configuration()["scan"]["barcode"])
{
current_barcode = get_configuration()["scan"]["barcode"];
add_item(current_barcode);
}
if (items.size() > 0)
{
if (items.front().get_image_textures().size() > 0)
{
SDL_RenderCopyF(get_renderer(), items.front().get_image_textures().front().get(), nullptr, nullptr);
}
}
}