← back to Greenland Onboard

scripts/full-01-localize.mjs

47 lines

#!/usr/bin/env node
// Step 1: LOCALIZE — download each SKU thumb (fallback coverImg) → public/img/<dwSku>.jpg
// Skip if exists >1KB. $0 (local network fetch). Reversible.
import fs from 'node:fs';
import path from 'node:path';

const ROOT = path.resolve(import.meta.dirname, '..');
const IMG_DIR = path.join(ROOT, 'public', 'img');
fs.mkdirSync(IMG_DIR, { recursive: true });

const map = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', 'full-map.json'), 'utf8'));

let downloaded = 0, cached = 0, fail = 0;
const failures = [];

async function fetchTo(url, dest) {
  const res = await fetch(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 < 1024) throw new Error('too small ' + buf.length);
  fs.writeFileSync(dest, buf);
  return buf.length;
}

const CONC = 8;
let idx = 0;
async function worker() {
  while (idx < map.length) {
    const r = map[idx++];
    const dest = path.join(IMG_DIR, `${r.dwSku}.jpg`);
    try {
      if (fs.existsSync(dest) && fs.statSync(dest).size > 1024) { cached++; continue; }
    } catch {}
    let ok = false;
    for (const url of [r.thumb, r.coverImg].filter(Boolean)) {
      try { await fetchTo(url, dest); downloaded++; ok = true; break; }
      catch (e) { /* try fallback */ }
    }
    if (!ok) { fail++; failures.push({ dwSku: r.dwSku, thumb: r.thumb, coverImg: r.coverImg }); }
  }
}

await Promise.all(Array.from({ length: CONC }, worker));

console.log(JSON.stringify({ total: map.length, downloaded, cached, fail, failures: failures.slice(0, 20) }, null, 2));
if (fail) fs.writeFileSync(path.join(ROOT, 'data', 'localize-failures.json'), JSON.stringify(failures, null, 2));