← back to NationalPaperHangers
lib/segment-image.js
90 lines
// Per-installer deterministic public-domain image picker.
// Strategy: pick the installer's primary market_segment, fall through to
// related segments, and finally to 'generic'. Index = installer.id mod
// pool_size so the same studio always gets the same photo across renders.
// PD-only — every entry in manifest.json is Wikimedia Commons public-domain
// or CC0 (the fetcher rejects anything else).
const path = require('path');
const fs = require('fs');
let _manifest = null;
let _byPath = null;
const FALLBACK = {
silk: ['hand_painted', 'mural', 'luxury_residential', 'generic'],
hospitality: ['luxury_residential', 'museum', 'generic'],
museum: ['luxury_residential', 'hand_painted', 'generic'],
hand_painted: ['mural', 'luxury_residential', 'generic'],
grasscloth: ['hand_painted', 'luxury_residential', 'generic'],
mural: ['hand_painted', 'luxury_residential', 'generic'],
luxury_residential: ['hand_painted', 'mural', 'museum', 'generic'],
retail: ['hospitality', 'luxury_residential', 'generic'],
yacht: ['luxury_residential', 'hospitality', 'generic'],
generic: []
};
function loadManifest() {
if (_manifest) return _manifest;
const p = path.join(__dirname, '..', 'public', 'img', 'segments', 'manifest.json');
try {
const txt = fs.readFileSync(p, 'utf8');
_manifest = JSON.parse(txt);
_byPath = {};
for (const it of (_manifest.items || [])) _byPath[it.file] = it;
} catch {
_manifest = { items: [] };
_byPath = {};
}
return _manifest;
}
function imagesIn(segment) {
const m = loadManifest();
return (m.items || []).filter(it => it.segment === segment);
}
function pickSegmentImage(installer) {
if (!installer) return null;
const segs = Array.isArray(installer.market_segments) ? installer.market_segments : [];
const candidates = [];
if (segs.length) candidates.push(segs[0]);
if (segs[0] && FALLBACK[segs[0]]) candidates.push(...FALLBACK[segs[0]]);
if (!candidates.length) candidates.push('generic');
// Always end with 'generic' as final fallback.
if (!candidates.includes('generic')) candidates.push('generic');
for (const seg of candidates) {
const pool = imagesIn(seg);
if (pool.length) {
const idx = Math.abs(Number(installer.id) || 0) % pool.length;
return pool[idx];
}
}
return null;
}
// Public-facing license/attribution string for a manifest item.
// Cooper Hewitt + Met + Brooklyn Museum entries usually carry a creator;
// generic site dumps may not. We render this minimally: "Photo: <creator> ·
// <license> · Wikimedia Commons" — link to source_url for users who want
// to verify.
function attributionFor(file) {
const m = loadManifest();
if (!_byPath) return null;
const it = _byPath[file];
if (!it) return null;
return {
creator: it.creator || null,
license: it.license || 'Public domain',
source_url: it.source_url || null,
source: 'Wikimedia Commons'
};
}
module.exports = {
loadManifest,
imagesIn,
pickSegmentImage,
attributionFor
};