← back to Fentucci Naturals

scripts/download-images.js

42 lines

#!/usr/bin/env node
// Download original full-res wixstatic images to images/<safe mfr_sku>.jpg
// Verifies every file is a real image > 5KB (guards the WallQuest image-collapse gotcha).
const fs = require('fs');
const path = require('path');

const ROOT = path.join(__dirname, '..');
const products = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', 'products.json')));

function isImage(buf) {
  if (buf.length < 4) return false;
  if (buf[0] === 0xFF && buf[1] === 0xD8) return true;               // JPEG
  if (buf[0] === 0x89 && buf[1] === 0x50) return true;               // PNG
  if (buf.slice(0, 4).toString() === 'RIFF') return true;            // WebP
  return false;
}

(async () => {
  const results = [];
  let ok = 0, failed = 0, skipped = 0;
  for (const p of products) {
    const dest = path.join(ROOT, p.image_local);
    if (fs.existsSync(dest) && fs.statSync(dest).size > 5120) { skipped++; continue; }
    try {
      const res = await fetch(p.image_url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const buf = Buffer.from(await res.arrayBuffer());
      if (buf.length <= 5120) throw new Error(`too small: ${buf.length}B`);
      if (!isImage(buf)) throw new Error('not an image (magic bytes)');
      fs.writeFileSync(dest, buf);
      ok++;
    } catch (e) {
      failed++;
      results.push({ mfr_sku: p.mfr_sku, image_url: p.image_url, error: e.message });
      console.error(`${p.mfr_sku}: ${e.message}`);
    }
    await new Promise(r => setTimeout(r, 150));
  }
  fs.writeFileSync(path.join(ROOT, 'data', 'image-failures.json'), JSON.stringify(results, null, 2));
  console.log(`downloaded: ${ok}, cached: ${skipped}, failed: ${failed}`);
})();