Skip to content

Instantly share code, notes, and snippets.

@Lissy93
Last active February 27, 2024 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lissy93/444a1dcf8bb6763a8ab9da8451544321 to your computer and use it in GitHub Desktop.
Save Lissy93/444a1dcf8bb6763a8ab9da8451544321 to your computer and use it in GitHub Desktop.
API for UK food product photos (via Cloudflare Workers)
/**
* Cloudflare Worker script for displaying UK food products
* Fetches given item from Tesco.com using search endpoint
* Optionally resizes to specified dimensions
* Implements caches recently requested images
* Swaers a little bit when an error happens
*/
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request, event))
})
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36';
const TESCO_SEARCH_URL = 'https://www.tesco.com/groceries/en-GB/search?query=';
const WELCOME = 'Welcome to the Snack Champion\'s product photo API! 🥤🍬🍿😋\n' +
'To use this service, please specify a product name and optional dimensions.';
async function handleRequest(request, event) {
const cacheUrl = new URL(request.url);
const cacheKey = new Request(cacheUrl.toString(), request);
const cache = caches.default;
// Check if the response is already in the cache
let response = await cache.match(cacheKey);
if (response) {
return response; // Return the cached response if it exists
}
try {
const { productName, dimension } = extractProductAndDimension(request.url);
if (!productName) {
return new Response(WELCOME, { status: 418 });
}
const imageURL = await fetchProductImageURL(productName, dimension);
if (imageURL) {
response = await fetchAndReturnImage(imageURL);
// Store the fetched response in the cache.
event.waitUntil(cache.put(cacheKey, response.clone()));
return response;
} else {
return new Response("Image not found. Fuck sake", { status: 404 });
}
} catch (error) {
console.error(error); // Log the error for debugging
return new Response("An error occurred. Shit", { status: 500 });
}
}
function extractProductAndDimension(url) {
const pathSegments = new URL(url).pathname.split("/").filter(Boolean);
return {
productName: pathSegments[0],
dimension: pathSegments[1] // undefined if not present
};
}
async function fetchProductImageURL(product, dimension) {
const response = await fetch(`${TESCO_SEARCH_URL}${product}`, { headers: { 'User-Agent': USER_AGENT } });
const body = await response.text();
const match = body.match(/(https:\/\/digitalcontent\.api\.tesco\.com\/v2\/media\/[^?]+)/);
if (match) {
let imageURL = match[0];
if (dimension) {
imageURL += `?h=${dimension}&w=${dimension}`;
}
return imageURL;
} else {
return null;
}
}
async function fetchAndReturnImage(imageURL) {
const response = await fetch(imageURL);
const imageBlob = await response.blob();
return new Response(imageBlob, {
headers: { 'Content-Type': response.headers.get('Content-Type') }
});
}
@Lissy93
Copy link
Author

Lissy93 commented Dec 2, 2023

Docs

About
A simple script for embedding product images into a website.
E.g. <img src="https://snack-product-photo.as93.workers.dev/biscuits/128" />
Data is scraped from Tesco, and the service can be easily deployed as a serverless function.

Deployment
Log in to Cloudflare, navigate to "Workers" and then "New" / "Quick Edit".
Paste the above script in, and then hit "Save & Deploy". Done :)

Usage

  • / - Homepage (Example: /)
  • /[item-name] - Image of a given item (Example: /cheese)
  • /[item-name]/[image-width] - Image of a given item, using spacified dimensions (Example: /bread/128)

Demo
A live demo is hosted at: snack-product-photo.as93.workers.dev

License
Licensed under MIT, © Alicia Sykes 2023

@Lissy93
Copy link
Author

Lissy93 commented Feb 27, 2024

I made this in order to get product photos for github.com/lissy93/cso.
Here's a screenshot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment