Speed up timeline load with portrait thumbs, bootstrap API, and caching.
Add catalog bootstrap endpoint, portrait thumbnail pipeline, lazy queued timeline images, gzip compression, and 3D texture throttling with code-split VirtualGallery.
This commit is contained in:
+44
-2
@@ -86,6 +86,48 @@ Curator mutations are recorded in `curator_audit_log` (see [DB_structure.md](DB_
|
||||
|
||||
---
|
||||
|
||||
## `GET /api/catalog/bootstrap`
|
||||
|
||||
**Preferred for timeline first paint.** Returns bounds, eras, movements, and slim artist rows in a single response (replaces the separate `bounds` + `timeline` + `artists?timeline=1` waterfall).
|
||||
|
||||
**Query**
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `start` | int | bounds `min_year` | Window start year |
|
||||
| `end` | int | bounds `max_year` | Window end year |
|
||||
|
||||
**Response**
|
||||
|
||||
```json
|
||||
{
|
||||
"bounds": { "min_year": -800, "max_year": 2100 },
|
||||
"eras": [ … ],
|
||||
"movements": [ … ],
|
||||
"artists": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Claude Monet",
|
||||
"birth_year": 1840,
|
||||
"death_year": 1926,
|
||||
"movement_id": 12,
|
||||
"portrait_path": "portraits/Claude_Monet.jpg",
|
||||
"portrait_thumb_path": "portraits/thumbs/Claude_Monet_thumb.jpg",
|
||||
"wikipedia_title": "Claude Monet",
|
||||
"century": 19,
|
||||
"movement_name": "Impressionism",
|
||||
"movement_color": "#6B8E9F"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Caching:** `Cache-Control: public, max-age=300` with `ETag` (304 when catalog row counts unchanged).
|
||||
|
||||
The React home page loads this endpoint **once** on mount. Pan and zoom filter movements and portraits **client-side** — no refetch per view change.
|
||||
|
||||
---
|
||||
|
||||
## `GET /api/bounds`
|
||||
|
||||
Returns the overall timeline year range used to initialise the zoomable timeline.
|
||||
@@ -138,11 +180,11 @@ Artists for timeline portraits and the movement flow diagram.
|
||||
| `start` | int | Only artists alive after this year |
|
||||
| `end` | int | Only artists born before this year |
|
||||
| `movement_id` | int | Filter by movement |
|
||||
| `timeline` | bool | When `1` or `true`, return a lightweight row set for the home-page timeline (omits `bio_full` and other heavy fields) |
|
||||
| `timeline` | bool | When `1` or `true`, return a lightweight row set for the home-page timeline (omits `bio_short`, `bio_full`; includes `portrait_thumb_path`) |
|
||||
|
||||
**Response** — array of artist objects with joined `movement_name` and `movement_color`.
|
||||
|
||||
The React home page loads the timeline catalog **once** on mount via `GET /api/bounds`, `GET /api/timeline?start=…&end=…` (full range), and `GET /api/artists?timeline=1`. Pan and zoom filter movements and portraits **client-side** — no refetch per view change. Rapid pan/zoom is batched with `createViewChangeScheduler()` (one React update per animation frame).
|
||||
The React home page loads the timeline catalog **once** on mount via `GET /api/catalog/bootstrap` (or legacy: `GET /api/bounds` + `GET /api/timeline` + `GET /api/artists?timeline=1`). Pan and zoom filter movements and portraits **client-side** — no refetch per view change. Rapid pan/zoom is batched with `createViewChangeScheduler()` (one React update per animation frame).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@ How catalog content, biographies, and artwork files enter the system.
|
||||
|
||||
```text
|
||||
data/images/
|
||||
├── portraits/ # Artist headshots
|
||||
│ └── Claude_Monet.jpg
|
||||
├── portraits/ # Artist headshots (display ~900px wide)
|
||||
│ ├── Claude_Monet.jpg
|
||||
│ └── thumbs/ # Timeline thumbnails (~256px)
|
||||
│ └── Claude_Monet_thumb.jpg
|
||||
└── paintings/
|
||||
├── Claude_Monet_Water_Lilies.jpg
|
||||
└── thumbs/
|
||||
@@ -47,7 +49,8 @@ Promote dev → prod files: `npm run images:sync-to-prod` (after `net use \\192.
|
||||
| `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 |
|
||||
| `sync-images-to-prod.ps1` / `sync-images-from-prod.ps1` | `npm run images:sync-*` | Robocopy via SMB `\\192.168.10.122\Gallery` |
|
||||
| `regenerate-thumbnails.js` | `npm run regenerate-thumbnails` | Rebuild thumbs from full images via `sharp` |
|
||||
| `regenerate-thumbnails.js` | `npm run regenerate-thumbnails` | Rebuild painting thumbs from full images via `sharp` |
|
||||
| `regenerate-portrait-thumbs.js` | `npm run regenerate-portrait-thumbs` | Rebuild timeline portrait thumbs (~256px) and set `portrait_thumb_path` |
|
||||
| `audit-painting-images.js` | `npm run audit-painting-images` | Detect thumb/full aspect-ratio mismatches |
|
||||
| `find-duplicate-paintings.js` | `npm run find-duplicates` | Report exact and near-duplicate catalog rows |
|
||||
| `migrate-checkup-flags.js` | `npm run migrate:checkup-flags` | Add `checkup_checked` / `checkup_fixed` columns |
|
||||
@@ -154,6 +157,8 @@ Typical result on a full clone: ~1,000+ paintings linked from ~1,000 on-disk fil
|
||||
|
||||
## Artist portraits
|
||||
|
||||
Timeline movement flow loads **`portrait_thumb_path`** (~256px JPEG under `portraits/thumbs/{Artist}_thumb.jpg`) when available; biography and 3D exit navigation use full `portrait_path`. After adding portraits, run `npm run regenerate-portrait-thumbs` to backfill thumbs on dev.
|
||||
|
||||
`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.
|
||||
|
||||
Reference in New Issue
Block a user