# Art Gallery — data and images How catalog content and artwork files enter the system. ## Principles 1. **No runtime hot-linking** — the UI reads from `/images/…` (local disk). External URLs are used only during ingest. 2. **No AI-generated art or text** — biographies and descriptions come from Wikipedia; influence notes from curated art-history sources. 3. **Local copies** — every displayed image should exist under `data/images/` after seeding or fetch. ## Directory layout ```text data/images/ ├── portraits/ # Artist headshots │ └── Claude_Monet.jpg └── paintings/ ├── Claude_Monet_Water_Lilies.jpg └── thumbs/ └── Claude_Monet_Water_Lilies_thumb.jpg ``` File names are sanitised `{Artist}_{Title}.{ext}`. The image service can rediscover files on disk even when DB paths are empty (`server/image-service.js` → `syncPaintingFromDisk`). ## Seeding pipeline `npm run seed` runs `scripts/seed-wikipedia.js`, which: 1. Inserts **historical eras** and **art movements** (curated date ranges and colours). 2. For each curated **artist**: - Fetches Wikipedia intro text for `bio_short` / `bio_full`. - Downloads a Commons portrait into `portraits/`. - Creates **artist periods** and **paintings**. - Downloads painting images into `paintings/`. 3. Writes **painting_influences** edges from curated scholarship references. Those influence edges also power **3D hall navigation**: predecessors and successors at each artist’s exit doorway are computed from this table (see `GET /api/artists/:id/navigation` in [API.md](API.md)). Artists are grouped by movement and century; the seed list targets at most ~100 artists per century. ## On-demand image resolution When a painting has no local file, `GET /api/paintings/:id/image` triggers `ensurePaintingImages()`: 1. Check DB paths → verify file on disk. 2. Scan disk by `{artist}_{title}` pattern. 3. If still missing and `wikipedia_title` is set, call `scripts/image-fetcher.js`: - Wikidata → Wikimedia Commons → Wikipedia page image - Fallbacks: Met Museum, Art Institute of Chicago, Rijksmuseum APIs 4. Save full + thumbnail, update DB, serve file. Requests are deduplicated (`inflight` map) and timeout after 15 seconds. A 2.5 s delay between external requests reduces rate-limit risk. ## Preload before 3D gallery `POST /api/artists/:id/preload-images` runs **local-only** linking — no network. Call this when entering an artist’s 3D hall so textures use files already on disk. The 3D scene uses `galleryImageUrl()`, which never hits the on-demand API (remote latency breaks WebGL texture loading). ## Placeholders When no image is available: - `/placeholder-portrait.svg` — timeline / movement band portraits - `/placeholder-art.svg` — paintings in lists and detail view These live in `client/public/` (and `client/dist/` after build). ## Adding new artists manually 1. Insert rows into `artists`, `artist_periods`, `paintings` (or extend the `ARTISTS` array in the seed script). 2. Place image files under `data/images/` using the naming convention. 3. Run `npm run sync-image-paths` if that script is available, or rely on preload / on-demand sync. 4. Add influence rows to `painting_influences` with citation fields where possible. ## Image fetcher overrides `scripts/image-fetcher.js` includes hand-maintained overrides for ambiguous Wikipedia titles and direct URLs (e.g. works whose Commons name does not match the article title). Extend `PAINTING_WIKI_OVERRIDES` and `DIRECT_IMAGE_OVERRIDES` when automated resolution fails.