From 24bdcba4747f0225f66ced4ecd753584de412b28 Mon Sep 17 00:00:00 2001 From: dotnjet Date: Mon, 8 Dec 2025 20:15:12 +0100 Subject: [PATCH] Adding two new plugins, one for OpenBeautyFacts and one a combination of the existing OpenFoodFact and OpenBeautyFact --- .../OpenBeautyFactsBarcodeLookupPlugin.php | 74 +++++++++++++ ...nFoodAndBeautyFactsBarcodeLookupPlugin.php | 100 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 plugins/OpenBeautyFactsBarcodeLookupPlugin.php create mode 100644 plugins/OpenFoodAndBeautyFactsBarcodeLookupPlugin.php diff --git a/plugins/OpenBeautyFactsBarcodeLookupPlugin.php b/plugins/OpenBeautyFactsBarcodeLookupPlugin.php new file mode 100644 index 00000000..68189c89 --- /dev/null +++ b/plugins/OpenBeautyFactsBarcodeLookupPlugin.php @@ -0,0 +1,74 @@ + false]); + $response = $webClient->request('GET', 'https://world.openbeautyfacts.org/api/v2/product/' . preg_replace('/[^0-9]/', '', $barcode) . '?fields=product_name,image_url,' . $productNameFieldLocalized, ['headers' => ['User-Agent' => 'GrocyOpenFoodFactsBarcodeLookupPlugin/1.0 (https://grocy.info)']]); + $statusCode = $response->getStatusCode(); + + // Guzzle throws exceptions for connection errors, so nothing to do on that here + + $data = json_decode($response->getBody()); + if ($statusCode == 404 || $data->status != 1) + { + // Nothing found for the given barcode + return null; + } + else + { + $imageUrl = ''; + if (isset($data->product->image_url) && !empty($data->product->image_url)) + { + $imageUrl = $data->product->image_url; + } + + // Take the preset user setting or otherwise simply the first existing location + $locationId = $this->Locations[0]->id; + if ($this->UserSettings['product_presets_location_id'] != -1) + { + $locationId = $this->UserSettings['product_presets_location_id']; + } + + // Take the preset user setting or otherwise simply the first existing quantity unit + $quId = $this->QuantityUnits[0]->id; + if ($this->UserSettings['product_presets_qu_id'] != -1) + { + $quId = $this->UserSettings['product_presets_qu_id']; + } + + // Use the localized product name, if provided + $name = $data->product->product_name; + if (isset($data->product->$productNameFieldLocalized) && !empty($data->product->$productNameFieldLocalized)) + { + $name = $data->product->$productNameFieldLocalized; + } + + // Remove non-ASCII characters in product name (whyever a product name should have them at all) + $name = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß ]/', '', $name); + + return [ + 'name' => $name, + 'location_id' => $locationId, + 'qu_id_purchase' => $quId, + 'qu_id_stock' => $quId, + '__qu_factor_purchase_to_stock' => 1, + '__barcode' => $barcode, + '__image_url' => $imageUrl + ]; + } + } +} diff --git a/plugins/OpenFoodAndBeautyFactsBarcodeLookupPlugin.php b/plugins/OpenFoodAndBeautyFactsBarcodeLookupPlugin.php new file mode 100644 index 00000000..5c269d61 --- /dev/null +++ b/plugins/OpenFoodAndBeautyFactsBarcodeLookupPlugin.php @@ -0,0 +1,100 @@ +request( + 'GET', + $apiUrl . preg_replace('/[^0-9]/', '', $barcode) . '?fields=product_name,image_url,' . $productNameFieldLocalized, + ['headers' => ['User-Agent' => 'GrocyOpenFoodFactsBarcodeLookupPlugin/1.0 (https://grocy.info)']] + ); + + // Parse the response + $statusCode = $response->getStatusCode(); + $data = json_decode($response->getBody()); + + // Check if the product was not found + if ($statusCode == 404 || $data->status != 1) + { + return null; + } + + // Return the response data + return $data; + } + + $productNameFieldLocalized = 'product_name_' . substr(GROCY_LOCALE, 0, 2); + $webClient = new Client(['http_errors' => false]); + + // First lookup on Open Food Facts + $data = performLookup('https://world.openfoodfacts.org/api/v2/product/', $barcode, $productNameFieldLocalized, $webClient); + + // If the first lookup fails, try the Open Beauty Facts + if ($data === null) + { + $data = performLookup('https://world.openbeautyfacts.org/api/v2/product/', $barcode, $productNameFieldLocalized, $webClient); + + // If the second lookup fails too, return null + if ($data === null) + { + return null; + } + } + + // Initialize an empty image URL + $imageUrl = ''; + if (isset($data->product->image_url) && !empty($data->product->image_url)) + { + $imageUrl = $data->product->image_url; + } + + // Retrieve the location ID from user settings or set to the first location in the list + $locationId = $this->Locations[0]->id; + if ($this->UserSettings['product_presets_location_id'] != -1) + { + $locationId = $this->UserSettings['product_presets_location_id']; + } + + // Retrieve the quantity unit ID from user settings or set to the first quantity unit in the list + $quId = $this->QuantityUnits[0]->id; + if ($this->UserSettings['product_presets_qu_id'] != -1) + { + $quId = $this->UserSettings['product_presets_qu_id']; + } + + // Use the localized product name if provided + $name = $data->product->product_name; + if (isset($data->product->$productNameFieldLocalized) && !empty($data->product->$productNameFieldLocalized)) + { + $name = $data->product->$productNameFieldLocalized; + } + + // Remove non-ASCII characters from the product name + $name = preg_replace('/[^a-zA-Z0-9äöüÄÖÜß ]/', '', $name); + + // Return the structured product data + return [ + 'name' => $name, + 'location_id' => $locationId, + 'qu_id_purchase' => $quId, + 'qu_id_stock' => $quId, + '__qu_factor_purchase_to_stock' => 1, + '__barcode' => $barcode, + '__image_url' => $imageUrl + ]; + } +} \ No newline at end of file