← 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)`);