Improve timeline UX and add catalog restore scripts for paintings and portraits.

Load the home-page catalog once with a lightweight artists API, batch pan/zoom updates per frame, use dynamic year labels, and speed up movement-flow zoom. Add sync-image-paths and fetch-artist-images plus docs for the post-seed pipeline.
This commit is contained in:
Danila Khodjaef
2026-07-03 18:33:16 +03:00
parent 7f26696328
commit 02d238b043
26 changed files with 865 additions and 195 deletions
+57 -12
View File
@@ -26,11 +26,14 @@ File names are sanitised `{Artist}_{Title}.{ext}`. The image service can redisco
| Script | npm command | Role |
|--------|-------------|------|
| `seed-wikipedia.js` | `npm run seed` | Initial eras, movements, artists, paintings, influences |
| `seed-wikipedia.js` | `npm run seed` | Initial eras, movements, artists, one flagship painting per artist |
| `seed-catalog-data.js` | *(data only)* | Eras, movements, artist metadata consumed by seed |
| `sync-image-paths.js` | `npm run sync-image-paths` | Import painting rows from disk; set `image_path` / `thumbnail_path` |
| `fetch-artist-images.js` | `npm run fetch-artist-images` | Download or link artist portraits under `data/images/portraits/` |
| `fetch-artist-bios.js` | `npm run fetch-artist-bios` | Wikipedia intros → `bio_short` / `bio_full` |
| `famous-paintings-data.js` | *(data only)* | Curated list of notable works per artist |
| `expand-paintings.js` | `npm run expand-catalog` | Inserts works from data file for thin catalogs |
| `art-influences-data.js` | *(data only)* | Curated painting-to-painting influence edges |
| `art-influences-data.js` | *(data only)* | Curated influence edges (painting / artist / movement) |
| `update-influences.js` | `npm run update-influences` | Applies influence graph; creates missing artists/works |
| `fetch-missing-images.js` | `npm run fetch-images` | Downloads files for paintings missing on disk |
| `image-fetcher.js` | *(library)* | Wikimedia / museum resolution used by fetch scripts and API |
@@ -42,23 +45,26 @@ File names are sanitised `{Artist}_{Title}.{ext}`. The image service can redisco
## Typical workflow
```text
migrate → seed → fetch-artist-bios → expand-catalog → update-influences → fetch-images (per artist or batch) → build client
migrate → seed → sync-image-paths → fetch-artist-images → fetch-artist-bios → expand-catalog → update-influences → fetch-images (per artist or batch) → build client
```
1. **Seed** creates the base catalog (often one flagship painting per modern artist).
2. **fetch-artist-bios** fills biography fields for every artist with a `wikipedia_title`.
3. **expand-catalog** brings each artist up to at least **6** notable works (configurable via `MIN_PAINTINGS`).
4. **fetch-images** downloads artwork files; the 3D gallery needs local files for reliable textures.
1. **Seed** creates the base catalog (one flagship painting per artist; ~100 artists).
2. **sync-image-paths** imports additional paintings when `data/images/paintings/` already contains files from a full clone (filename pattern `{Artist}_{Title}.jpg`).
3. **fetch-artist-images** sets `portrait_path` from local files or Wikipedia.
4. **fetch-artist-bios** fills biography fields for every artist with a `wikipedia_title`.
5. **expand-catalog** brings each artist up to at least **6** notable works (configurable via `MIN_PAINTINGS`).
6. **update-influences** loads the influence graph (*Influenced By* / *Influenced* panels, 3D hall lamps, exit navigation).
7. **fetch-images** downloads artwork files still missing on disk; the 3D gallery needs local files for reliable textures.
## Seeding pipeline
`npm run seed` runs `scripts/seed-wikipedia.js`, which:
1. Inserts **historical eras** and **art movements** (curated date ranges and colours).
1. Inserts **historical eras** and **art movements** (curated date ranges and colours from `seed-catalog-data.js`).
2. For each curated **artist**:
- Creates **artist periods** and **paintings**.
- May download portraits and painting images (depending on seed script version).
3. Writes **painting_influences** edges from curated scholarship references (mirrored into `painting_influence_sources` when you run `npm run migrate:influence-sources` and `npm run update-influences`).
- Creates **artist periods** and one **flagship painting**.
- May download portraits and painting images when run with `--fetch-images`.
3. Does **not** insert influence edges — run `npm run update-influences` after seed (see [Painting influence graph](#painting-influence-graph)).
Those influence edges power **3D hall navigation** and painting detail panels via **`painting_influence_sources`** (see `GET /api/artists/:id/navigation` and `GET /api/paintings/:id` in [API.md](API.md)).
@@ -111,7 +117,44 @@ To add more works, append entries to `famous-paintings-data.js`:
`wikipedia_title` is optional; it defaults to `title`. Use it when the Wikipedia article name differs from the display title.
Renaissance and medieval masters with large museum catalog dumps (e.g. Raphael, Dürer) are usually above the minimum already; expansion targets Impressionists, modernists, and other artists who had only a single seed painting.
Renaissance and medieval masters with large museum catalog dumps (e.g. Raphael, Dürer) are usually above the minimum already when **`sync-image-paths`** has imported files from disk; expansion targets Impressionists, modernists, and other artists who had only a single seed painting.
## Importing paintings from disk
When the repository includes a full `data/images/paintings/` tree but the database was seeded fresh (one row per artist), run:
```bash
npm run sync-image-paths
```
`scripts/sync-image-paths.js`:
1. Scans `data/images/paintings/` for full-size files (not `thumbs/`).
2. Matches filenames to artists using the same `{Artist}_{Title}` sanitisation as `server/image-service.js`.
3. **Updates** `image_path` / `thumbnail_path` on existing rows when files are found.
4. **Inserts** missing painting rows for files not yet in the catalog.
Flags:
- `--dry-run` — report counts only, no DB writes.
Safe to re-run; already-imported works are skipped by normalized title matching.
Typical result on a full clone: ~1,000+ paintings linked from ~1,000 on-disk files.
## Artist portraits
`npm run fetch-artist-images` runs `scripts/fetch-artist-images.js`:
1. For each artist, checks `data/images/portraits/{Artist}.jpg` (or other extensions) and sets `portrait_path` when a local file exists.
2. Otherwise downloads from Wikipedia / search fallbacks via `image-fetcher.js`.
Flags:
- `--force` — re-fetch even when `portrait_path` is already set.
- `--limit=N` — process only the first N artists needing portraits.
Run after seed when portrait files exist on disk but the DB still has null `portrait_path` values.
## Painting influence graph
@@ -216,6 +259,8 @@ Wing layout (up to 55 works per wing, side-wall-only hang, window gap placement)
| Storage | TypeScript module in the client — **not** a database table |
| Format | `{ id, name, startYear, endYear?, shortLabel? }` — omit `endYear` for a single-year pin |
| Interaction | Click a marker to zoom the shared timeline/movement view to that period |
| Year axis labels | Dynamic density in `Timeline.tsx` via `chooseTimelineTickInterval()` — fewer labels when zoomed out |
| Pan/zoom batching | `createViewChangeScheduler()` in `timelineView.ts` — one React update per animation frame |
| Vertical guides | `TimelineEventGuides.tsx` draws faint gold lines (or shaded spans) from the marker row down through the movement flow, aligned to the same year scale |
Edit `HISTORICAL_EVENTS` and rebuild the client to extend the set.