← back to Quadrille House Site

scripts/build-lookbook.js

68 lines

#!/usr/bin/env node
/**
 * Build the lookbook manifest from the harvested marketing assets:
 *   - 32 lookbook PDFs  (data/assets/lookbooks/*.pdf)
 *   - ~873 editorial/room images (data/assets/rooms/*.jpg)
 * Copies them into public/lookbooks (PDFs) + public/rooms (a curated subset of room shots),
 * and writes data/lookbook.json that the microsite renders.
 *
 *   node scripts/build-lookbook.js
 */
const fs = require('fs');
const path = require('path');

const SRC = '/Users/macstudio3/Projects/Designer-Wallcoverings/vendor-scrapers/quadrille-house/data/assets';
const PUB = path.join(__dirname, '..', 'public');
const OUT = path.join(__dirname, '..', 'data', 'lookbook.json');

const pdfDir = path.join(PUB, 'lookbooks');
const roomDir = path.join(PUB, 'rooms');
fs.mkdirSync(pdfDir, { recursive: true });
fs.mkdirSync(roomDir, { recursive: true });

// pretty title from a filename
const titleize = f => f.replace(/\.[^.]+$/, '').replace(/[-_]+/g, ' ')
  .replace(/\bLookbook\b/i, '')
  .replace(/Wallpapers/g, 'Wallcoverings').replace(/wallpapers/gi, 'wallcoverings')
  .replace(/Wallpaper/g, 'Wallcovering').replace(/wallpaper/gi, 'wallcovering')
  .replace(/\s{2,}/g, ' ').trim();

// --- PDFs ---
const pdfs = fs.existsSync(path.join(SRC, 'lookbooks'))
  ? fs.readdirSync(path.join(SRC, 'lookbooks')).filter(f => /\.pdf$/i.test(f)) : [];
const lookbooks = pdfs.map(f => {
  fs.copyFileSync(path.join(SRC, 'lookbooks', f), path.join(pdfDir, f));
  return { title: titleize(f), file: `/lookbooks/${encodeURIComponent(f)}` };
}).sort((a, b) => a.title.localeCompare(b.title));

// --- room imagery: curate a gallery subset (cap to keep deploy light) ---
const allRooms = fs.existsSync(path.join(SRC, 'rooms'))
  ? fs.readdirSync(path.join(SRC, 'rooms')).filter(f => /\.(jpe?g|png)$/i.test(f)) : [];
// de-dupe by pattern prefix so the gallery shows variety, not 8 shots of one pattern
const seenPattern = new Set();
const curated = [];
for (const f of allRooms) {
  const pat = f.split('-').slice(0, 2).join('-').toLowerCase();
  if (seenPattern.has(pat)) continue;
  seenPattern.add(pat);
  curated.push(f);
  if (curated.length >= 120) break;       // gallery cap
}
const rooms = curated.map(f => {
  fs.copyFileSync(path.join(SRC, 'rooms', f), path.join(roomDir, f));
  // designer credit = trailing token group, pattern = leading
  const base = f.replace(/\.[^.]+$/, '');
  // DW banned-word rule: "Wallpaper" → "Wallcovering" in all customer-facing text
  const caption = base.replace(/[-_]+/g, ' ')
    .replace(/wallpapers/gi, 'wallcoverings').replace(/wallpaper/gi, 'wallcovering');
  return { src: `/rooms/${encodeURIComponent(f)}`, caption };
});

fs.writeFileSync(OUT, JSON.stringify({
  lookbook_count: lookbooks.length,
  room_count: rooms.length,
  lookbooks, rooms,
}, null, 2));
console.log(`lookbook: ${lookbooks.length} PDFs + ${rooms.length} room images → ${OUT}`);
console.log(`  (from ${allRooms.length} total room shots harvested)`);