AsSeenInMovies
IMDb-class catalog with the "SPOTTED" community layer — what fictional decor / fashion / cars are real, and where to buy them.
Search the build
-
c27bdce2026-06-23 Replace pagination with infinite scrolling -
11799d12026-05-31 Add per-site favicon (kills /favicon.ico 404) -
466b16e2026-05-28 card grid: universal hover-preview — wire ::after 3×3 tile-repeat on /movies grid, /people grid, and home rails (movies/people/spotted). --card-bg was already stamped on every grid + rail card; this completes the pattern by adding the hover ::after CSS on .grid-poster, .grid-headshot, .rail-poster, .rail-headshot. Matches wallco-ai pattern (reference: wallco-ai/public/css/site.css:347-385). Skipped hero .poster at line 1267 (movie detail hero) + line 1396 (person detail hero) per non-card rule. -
694e7832026-05-19 Add rel=noreferrer to every target=_blank link -
2cee7612026-05-19 Guard .bak/.pre-/.orig paths in static middleware and gitignore -
d70a1122026-05-13 Makefile: audit target (fleet-status + fleet-smoke) -
5bc6c502026-05-13 smoke: /api/health perf budget bumped 500→1000ms (flaky cold-cache spike) -
aea2c772026-05-13 smoke: assert /api/spotted/featured SWR Cache-Control -
8243fbe2026-05-13 sitemap: extract buildSitemapXml() helper — coalesce concurrent cold-cache builds; /sitemap.txt no longer returns 'warming' stub -
888e73e2026-05-13 smoke: cover /healthz (200, ok JSON, no-store, HEAD method) + harness opts.method -
32e313c2026-05-13 sitemap.xml: preserve Last-Modified+Cache-Control on cache-hit fast path + smoke -
a7da2012026-05-13 smoke: home perf budget <1500ms (cold-cache ceiling; regression guard) -
ed68d9d2026-05-13 Makefile: add help target listing all available commands -
57d42ae2026-05-13 smoke: /api/health perf budget (<500ms) — regression guard for N+1/pool -
755754e2026-05-13 Makefile: add fleet-status target -
bf54e4d2026-05-13 .gitignore: add .env.*, tmp/, dist/, build/, .next/ (Steve's MEMORY standard) -
e68674f2026-05-13 smoke: assert /people sort + density (grid standard) -
211c8242026-05-13 smoke: assert Vary: Accept-Encoding (cache-safety regression guard) -
c79cbbc2026-05-13 smoke: assert /api/version git !== 'unknown' (catches missing git on PATH at boot) -
db4fdf72026-05-13 smoke: assert /api/version Cache-Control no-store -
e3ecee02026-05-13 api/spotted/featured: N (default 6, max 24) verified spotted placements with movie context -
d1d1f7a2026-05-13 smoke: 4 assertions for /api/version (200, name, git sha, node) -
3f7e5d72026-05-13 api/version: deploy fingerprint endpoint (name, version, git SHA, node, started_at) -
b8ab3702026-05-13 smoke: uptime_s assertion on /api/health -
9dd8cca2026-05-13 api/health: include uptime_s for ops dashboards -
0f94cb42026-05-13 sitemap.txt: emit Last-Modified header for conditional GET (304) -
98f890a2026-05-13 sitemap.xml: emit Last-Modified header for conditional GET (304) -
e25dd592026-05-13 asim /api/credits/stats — document why department=0 (TMDB ingest gap, future enrichment). -
f10bd922026-05-13 asim robots.txt: also reference /sitemap.txt (multiple Sitemap directives allowed). 69/69. -
a1260ae2026-05-13 asim Makefile — common dev targets (start, smoke, smoke-quiet, smoke-json, fleet-smoke, restart, logs) -
0d70bcf2026-05-13 snapshot: 1 file(s) changed, ~1 modified -
c6662ec2026-05-13 asim sitemap.xml + .txt Cache-Control 1h→6h + SWR 24h (catalog rarely changes that fast). 69/69. -
1df1a752026-05-13 asim smoke --json suppresses header so output is parseable. -
ba2ce082026-05-13 asim smoke +--json flag (monitoring/dashboard output) -
7d3963c2026-05-13 asim +.deploy.conf — wire into /deploy skill (rsync→Kamatera /root/Projects/asseeninmovies, healthcheck /healthz). Mirrors sod template. -
2c2ee452026-05-13 asseeninmovies: GET /spotted/queue.csv — admin CSV export of pending submissions (token-gated, no-store). Attachment download with date in filename. 69/69. -
e8ce9a12026-05-13 asim nav dice icon — +aria-label for screen readers ("Show a random movie"). a11y polish. 69/69. -
a43054b2026-05-13 asim smoke +2 /api/credits/stats checks. 69/69. -
08a86782026-05-13 asseeninmovies: GET /api/credits/stats — aggregate counts on asim_credits (363k rows). 5min Cache-Control+SWR. /api discovery doc updated. 67/67. -
f7d2a512026-05-13 asim /api/health Cache-Control: no-store (timestamped liveness/status, always fresh). 67/67. -
e88f11a2026-05-13 asim smoke +2 /sitemap.txt checks. 67/67. -
c8193932026-05-13 asseeninmovies: GET /sitemap.txt — plain-text sitemap (Google + Bing supported, simpler than XML). Reuses _sitemapCache.xml; 1h Cache-Control. 65/65. -
4cd8acf2026-05-13 asim /spotted/queue Cache-Control: no-store, must-revalidate (admin token-gated, never share cache). 65/65. -
e67d0fe2026-05-13 asim /healthz Cache-Control: no-store (liveness should always be fresh) -
08985442026-05-13 asseeninmovies: GET /api/movies/random + /api/people/random — JSON random-row companions to the /random/* redirects. 60s cache. 65/65. -
62575012026-05-13 asim smoke +2 ItemList checks (/movies + /people). 65/65. -
914d9392026-05-13 asseeninmovies: smoke +2 home checks (og:image + color-scheme meta). 63/63. -
9e1c6cf2026-05-13 asseeninmovies: default og:image+twitter:image on baseline htmlShell + home — share previews now show the brand mark. +tabular-nums on stat-row counts. 61/61. -
406c98c2026-05-13 asseeninmovies: +<meta name=color-scheme content=dark> so form controls/scrollbars use the dark theme on Chrome+Safari. Paired with existing theme-color #0d0c0a. 61/61. -
72535082026-05-13 asseeninmovies: home stat-row — make movies/people/spotted counts clickable links to their index pages. Dotted underline on hover. Discreet but discoverable. 61/61. -
070a2072026-05-13 asseeninmovies: smoke +2 /random/spotted checks. 61/61. -
921ea622026-05-13 asseeninmovies: GET /random/spotted — 302 to a random verified placement on its parent /movie/:id#spotted page. Completes the /random/{movie,person,spotted} trio. 59/59. -
ee14e9d2026-05-13 asseeninmovies: smoke +2 /api/spotted/random checks. 59/59. -
11f5c832026-05-13 asseeninmovies: /api discovery doc — list /api/spotted/random alongside the rest. -
0f3342b2026-05-13 asseeninmovies: GET /api/spotted/random — JSON companion to /random/movie/person. 60s cache+SWR. Returns {spotted: {...}} with movie context. 57/57. -
15d32a72026-05-13 asseeninmovies: smoke +gzip header check on /movies (verifies compression middleware is wired). 57/57. -
c2129842026-05-13 asseeninmovies: smoke --quiet flag prints only failures + final tally. CI/cron friendly. -
d17d0a52026-05-13 asseeninmovies: home stat-row — show credits count (363,323 asim_credits rows) alongside movies/people/spotted. 56/56. -
49969302026-05-13 asseeninmovies: home-cache + /api/health expose credits_total (asim_credits row count). 56/56. -
391cd932026-05-13 asseeninmovies: smoke +4 security-header checks (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy). 56/56. -
999138e2026-05-13 asseeninmovies: pin engines.node>=20 in package.json. Mirrors sod, prevents accidental run on legacy node. -
cc623492026-05-13 asseeninmovies: minimal security headers (X-Content-Type-Options nosniff, X-Frame-Options SAMEORIGIN, Referrer-Policy strict-origin-when-cross-origin, Permissions-Policy default-deny on geo/mic/camera/payment). 52/52. -
e810f9c2026-05-13 asseeninmovies: home cta-row — add SPOTTED browse-link alongside Movies and People. Surfaces the most distinctive feature directly from the hero. -
0878efc2026-05-13 asseeninmovies: home cache background refresh every 45s (unref-timer) so quiet periods never let the cache go stale. Eliminates the post-quiet cold-cache penalty. -
4de56c12026-05-13 asim-healthz -
7197fa32026-05-12 asseeninmovies: /api/{movie,person,search,spotted} Cache-Control + parallelize /api/person/:id -
f78067e2026-05-12 asseeninmovies: /api/spotted/stats Cache-Control max-age=30 SWR=60 -
9f987772026-05-12 asseeninmovies: GA4 gtag wiring (env-gated via GA_ID) on both home + htmlShell. Only injects when GA_ID is set so dev/local doesn't ping analytics. Smoke 52/52. -
cd952cb2026-05-12 asseeninmovies: trust proxy=1 + Cache-Control on sitemap.xml (3600s) and robots.txt (86400s). Cloudflare-friendly. Smoke 52/52. -
5aadd3a2026-05-12 asseeninmovies: gzip compression middleware (threshold 1KB) — JSON-LD-heavy HTML pages drop dramatically over the wire. /movies 37KB uncompressed → gzip. Smoke 52/52. -
2c7ee2c2026-05-12 asseeninmovies: <meta name='theme-color' content='#0d0c0a'> on home + htmlShell — mobile browser chrome (Safari iOS, Chrome Android) tints to the dark site palette instead of white. Matches sod's existing theme-color convention. -
018b5032026-05-12 asseeninmovies: footer links — add Privacy/Terms/API to home page footer (htmlShell footer already had About/Privacy/Terms, now adds API too). Closes the cross-link discoverability gap on the inline home template. -
a471a712026-05-12 asseeninmovies: smoke — add /api discovery doc check (+2 assertions). 52/52. -
ea5c0812026-05-12 asseeninmovies: GET /api — JSON index of every public API endpoint with one-line descriptions + public-data-only declaration + TMDB license note. Integrators don't have to read source to discover what's available. -
4648ead2026-05-12 asseeninmovies: ItemList JSON-LD on /movies + /people index pages — each visible row emitted as schema.org/Movie or /Person ListItem with position + url. Closes the schema-coverage gap on listing pages. numberOfItems reflects total filter result count, not page slice, so crawlers know there's more behind pagination. -
2d5ec292026-05-12 asseeninmovies: smoke test harness — test/smoke.js (zero deps, pure node) hits every public route and asserts 200/3xx + key DOM markers (canonical, JSON-LD type, OG tags, ItemList, BreadcrumbList). 50 checks across home, movies, people, spotted, queue, detail pages, search, api/health, api/spotted/stats, random redirects, sitemap, robots, favicon, about/privacy/terms, and the 404 paths. `npm test` wired up. 50/50 pass on initial run. -
210edd92026-05-12 asseeninmovies: GET /privacy + /terms — minimal legal pages (no-collect privacy notice, public-data terms, imagery + liability). Linked from footer + sitemap. Closes the production-readiness gap on legal pages. -
b7aaa6e2026-05-12 asseeninmovies: GET /about — editorial explainer page (what this is, who runs it, submit a placement, public-data policy) with custom meta_desc + canonical + footer link. Added to sitemap. Closes the asim-vs-sod /about parity gap. -
b8d42812026-05-12 asseeninmovies: BreadcrumbList JSON-LD on /movie/:id (Home > Movies > Title) and /person/:id (Home > People > Name) — packaged with Movie/Person schema in a schema.org @graph so it's one script tag. Enables Google breadcrumb-trail rich snippets. -
fd706202026-05-12 asseeninmovies: /spotted ItemList JSON-LD — each placement emitted as schema.org/Product (name/brand/category/url anchored to #spotted-N on the parent movie page) inside an ItemList with position + numberOfItems. Eligible for Google rich-snippet 'list of products' carousels in search results. -
56476af2026-05-12 asseeninmovies: branded 404 — was leaking stock Express 'Cannot GET /…' plain-text page. New handler uses htmlShell (theme+nav+footer consistent) with 6 chip-style recovery links (Home/Movies/People/Spotted/Search/🎲 random). /api/* paths get JSON 404 instead of HTML. -
b8cbc442026-05-12 asseeninmovies: GET /random/movie and /random/person — 302 to a random poster-having movie or headshot-having person, fast (random() over partial idx_*_has_poster) + 🎲 dice icon in nav header. 'I'm feeling lucky' for the catalog. -
592a2e72026-05-12 asseeninmovies: /api/health perf — pull counts from _homeCache instead of running count(*) on 290k rows per request. 505ms → 1.6ms warm. Returns null counts + as_of=null on cold-cache (request fires before warmup completes); SELECT 1 liveness stays no matter what. -
19107942026-05-12 asseeninmovies: /search perf — gate ILIKE search to poster/headshot-having rows only (the visually-rich subset users actually want). Was full-scanning 290k movies + 61k people on every keystroke (5s+ timeout); now lands on the partial idx_asim_movies_has_poster index. /search?q=hotel 5s → 877ms cold, 10-14ms warm. -
880a1dd2026-05-12 asseeninmovies: htmlShell — auto-emit baseline meta description + og:title/description/url + twitter:card when caller didn't inject OG via headExtras. Now every htmlShell route (/movies, /people, /spotted, /search etc) gets share preview cards; /movie/:id + /person/:id keep their richer routed tags. -
f3534762026-05-12 asseeninmovies: canonical URLs on all 5 remaining htmlShell routes (/spotted, /spotted/submit, /movies, /people, /search) — full canonical coverage across the public site, matches sod's head.ejs convention -
f89066d2026-05-12 asseeninmovies: canonical URLs + WebSite/Organization JSON-LD — htmlShell now takes optional canonical param; /movie/:id + /person/:id pass theirs; home page gets WebSite schema with SearchAction (Google sitelinks search box) + Organization → Designer Wallcoverings + OG + Twitter Card. Closes the asim-vs-sod canonical parity gap. -
f4b860b2026-05-12 asseeninmovies: home cache — true stale-while-revalidate. Stale hits now return cached data immediately + trigger background refresh, instead of blocking on the next 2-3s count(*). Empty-cache first hit still awaits (warmup covers it on startup). -
22610232026-05-12 asseeninmovies: GET /favicon.ico fallback — same inline SVG served at the conventional path so bots/legacy clients that ignore <link rel=icon> get 200 + content, not 404. 1-day Cache-Control. -
b59fa662026-05-12 asseeninmovies: SEO surface — inline SVG favicon (data: URI, kills /favicon.ico 404s), JSON-LD Movie schema on /movie/:id, JSON-LD Person schema on /person/:id, Open Graph + Twitter Card meta tags on both, meta description on home page. htmlShell now accepts optional headExtras fragment. -
bbaae4b2026-05-12 asseeninmovies: GET /sitemap.xml + /robots.txt — XML sitemap with 6 static pages + top-10k movies (popularity-ranked) + top-10k people + all verified SPOTTED anchors; 10-min in-memory cache; robots disallows /spotted/queue + /api/. Closes the asim-vs-sod sitemap parity gap. -
08671ef2026-05-12 asseeninmovies: /movie/:id + /person/:id — parallelize credits + spotted queries via Promise.all (was sequential after the lookup-row await; ~2x speedup on cold cache; ~5ms warm now) -
40fa8dd2026-05-12 asseeninmovies: /spotted — full parallelization of all 5 queries (rows / total / cats / stats / top-films) via Promise.all; was 4 sequential awaits before Promise.all on the last 2. Cold 1.3s → 543ms, warm 3-7ms. -
992f4712026-05-12 asseeninmovies: /spotted — 'Most-Spotted Films' rail (top 6 by placement count, with poster + count badge); parallelized against the stats CTE; only renders when >1 unique film has placements -
ae66c632026-05-12 asseeninmovies: GET /api/spotted/stats — JSON counts (pending/live/rejected/submitted_7d/films/brands/categories) consumed by status-loop and external dashboards; public, no auth -
b7e1d472026-05-12 asseeninmovies: /movies perf — 3 partial indexes (has_poster / popularity_w_poster / has_headshot) + drop the (popularity IS NOT NULL) expression prefix that blocked index-order traversal. /movies?sort=popular cold 3.9s → 494ms warm 110ms. Plus migrations/2026-05-12_perf_indexes.sql for reproducibility. -
dbd47632026-05-12 asseeninmovies: /movies + /people — parallelize page query and count(*) via Promise.all (was sequential, double-wall-time); /people warm now ~20ms, /movies warm ~900ms (count + ORDER BY popularity on 290k rows remains the long pole) -
d6451f32026-05-12 asseeninmovies: home page — 60s in-memory cache with inflight dedup + startup warmup (count(*) on 290k movies stays slow cold but every user request hits cached data; concurrent home hits no longer stack up in pg_stat_activity) -
25508fb2026-05-12 asseeninmovies: home page perf — parallelize 4 queries via Promise.all (3.9s sequential → ~100ms warm); rejected TABLESAMPLE due to cold-cache cost + empty samples on filtered WHERE -
17d9d2d2026-05-12 asseeninmovies: /spotted gallery — 4-tile stats strip (placements/films/brands/categories) with responsive @media; mirrors /spotted/queue header pattern -
3d60e9b2026-05-12 asseeninmovies: /spotted/queue stats header (4-tile strip — pending/live/rejected/submitted-7d) with parallel Promise.all query and responsive @media -
0206a112026-05-12 asseeninmovies: /spotted/queue admin moderation — token-gated approve/reject pending submissions; reject sets verified_by='rejected' (reversible) and filters out of /spotted, /api/spotted, /movie/:id, /person/:id, home rail, and stats; ASIM_ADMIN_TOKEN persisted to .env -
4f8629c2026-05-12 asseeninmovies: /person/:id — surface 'SPOTTED in their films' section (person ⇄ movie ⇄ spotted graph; renders only when at least one verified placement matches person's filmography) -
3a3c2c72026-05-12 asseeninmovies: home page — third rail 'Recent SPOTTED' (6 verified placements) + 'spotted' added to stat-row; cards deep-link to /movie/:id#spotted -
c22b6be2026-05-12 asseeninmovies: cross-link /movie/:id ↔ /spotted/submit?movie_id= — render Spotted section even with zero rows + CTA chip in header + invitation copy when empty + brand+timecode in row body -
6ed53412026-05-12 asseeninmovies: /spotted HTML gallery — verified placements paginated, category chip-bar filter, shop-button CTA per row, submit banner up top -
1cdcaf52026-05-12 asseeninmovies: SPOTTED submission form — public /spotted/submit + POST /api/spotted/submit with rate-limit (1/30s), autocomplete movie picker, validation, queues unverified rows for admin review -
562f3b62026-05-12 asseeninmovies: /search HTML page — combined movies+people grid, replaces nav link to JSON endpoint -
57e1b202026-05-12 asseeninmovies: live home page — stats + 2 random-sampled rails (movies + people) + cleaner blurb grid -
8205ad72026-05-12 wikidata-enrich: add --byCast (filter to role=actor) to target high-yield enrichment pool -
67784fe2026-05-12 asseeninmovies: /movies + /people sort dropdowns + density slider (per standing grid rule); localStorage-persisted density -
96cf4db2026-05-12 scaffold wikidata-commons-fallback.js — search Commons for headshots when wikidata P18 missing (CC-licenses only) -
40aa91f2026-05-12 wikidata-enrich: add --byCredits to rank people by credit count (target high-recall enrichment) -
bdd362c2026-05-12 feat(asseeninmovies): /movies + /people paginated index pages with filters (year/genre/q) -
2b425772026-05-12 feat(asseeninmovies): inject Big Red widget into htmlShell footer -
2b1ecd32026-05-12 feat(asseeninmovies): fan-art credit policy — Commons artist + license + source on every poster -
f69197c2026-05-12 feat(asseeninmovies/tick3): Wikidata SPARQL enrichment -
dcdead02026-05-12 feat(asseeninmovies/tick8): /movie/:id + /person/:id HTML pages -
9a125772026-05-12 tick7: SPOTTED seed import — 14 placements from thesetdecorator film-pieces -
6c82c502026-05-12 tick2: thesetdecorator roster importer + uniq idx on imdb_id (running in bg) -
01bf78d2026-05-12 tick1: TMDB Phase A complete — 6.46M stubs across 7 sources, awaiting API key for Phase B -
92aef222026-05-12 feat(asseeninmovies/tick0): scaffold + schema + AI placeholder + plan
Authors
- Steve121
- Claude1
Agents used
Skills used
- /spotted33
- /movie18
- /person17
- /movies14
- /people14
- /random12
- /health8
- /stats6
- /queue6
- /sitemap5
- /search5
- /version4
- /submit4
- /favicon3
- /healthz3
- /credits3
- /span3
- /featured2
- /privacy2
- /terms2
- /about2
- /url2
- /live2
- /rejected2
- /films2
- /brands2
- /categories2
- /next1
- /site1
- /index1
Creative ideas + design notes
c27bdce · 2026-06-23 · Replace pagination with infinite scrolling
- Remove prev/next pagination nav - Add scroll sentinel at bottom - Load next page dynamically when user scrolls near sentinel - Append product rows incrementally to grid Complies with infinite scrolling requirement.
694e783 · 2026-05-19 · Add rel=noreferrer to every target=_blank link
Eight inline anchor tags across SPOTTED rows, queue review, image credits, movie-detail shop CTAs, and the TMDB attribution were opening in new tabs with rel missing 'noreferrer'. Adding it (alongside the existing 'noopener') closes the window.opener leak AND blanks the Referer header sent to the target — matches the fleet standard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2cee761 · 2026-05-19 · Guard .bak/.pre-/.orig paths in static middleware and gitignore
A 404-guard middleware now sits before express.static so any future accidentally-committed snapshot file (server.js.bak, public/index.html.pre-fix, etc.) cannot leak via a direct URL hit. .gitignore broadened to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2b42577 · 2026-05-12 · feat(asseeninmovies): inject Big Red widget into htmlShell footer
Same lower-right avatar launcher Steve has on the rest of the fleet. Tag goes before </body> in every page rendered through htmlShell(), which covers /, /movie/:id, /person/:id. No CSP whitelist needed — this server doesn't run helmet.
2b1ecd3 · 2026-05-12 · feat(asseeninmovies): fan-art credit policy — Commons artist + license + source on every poster
Per Steve's fan-art rule: every non-PD image must render artist + license
+ source-link on its page, or it can't be used. CC0/PD images get a
'Public domain · source' pill. Made-with-AI placeholder keeps the
existing pill.
Schema (idempotent ALTER):
ALTER TABLE asim_movies ADD COLUMN poster_credit TEXT;
ALTER TABLE asim_movies ADD COLUMN poster_license TEXT;
ALTER TABLE asim_movies ADD COLUMN poster_source_url TEXT;
ALTER TABLE asim_people ADD COLUMN headshot_credit TEXT;
ALTER TABLE asim_people ADD COLUMN headshot_license TEXT;
ALTER TABLE asim_people ADD COLUMN headshot_source_url TEXT;
scrapers/wikimedia-credit-backfill.js — batched Commons API
(prop=imageinfo, iiprop=extmetadata) fetcher that pulls Artist /
LicenseShortName / LicenseUrl + the canonical descriptionurl for every
poster_url we already imported from Wikimedia. 50 files per request,
1.1s sleep between batches, polite User-Agent.
Backfill result on this pass:
movies 783/783 ok (100%, no misses)
people 5/5 ok
All carry Artist + License + Commons File-page URL now.
server.js — imageCreditHtml(credit, license, sourceUrl, sourceKind):
CC0 / public-domain-mark / PD → '<span class="img-credit pd">Public domain · source</span>'
CC-BY / CC-BY-SA / anything else → '<span class="img-credit">Photo: {artist} / {license} / source</span>'
made-with-ai → existing '<span class="ai-pill">made with AI</span>'
No source URL → render nothing (safe default)
Rendered on both /movie/:id (poster chip) and /person/:id (headshot chip).
CSS chip uses a thin gold border for PD, line-color border for credited.
Smoke-tested live:
GET /movie/40 (Living in Oblivion) → 200
HTML contains: 'Photo: Machete kills / CC BY-SA 4.0 / <a ...>source</a>'
Memory codified: feedback_fan_art_credit_policy.md.
Hooks into existing IMDb-no-assets policy: fan art on Commons is the
legitimate substitute for IMDb posters.
f69197c · 2026-05-12 · feat(asseeninmovies/tick3): Wikidata SPARQL enrichment
scrapers/wikidata-enrich.js — joins our IMDb-keyed rows to Wikidata via
P345 (IMDb-ID property) and pulls back:
movies : wikidata_id, poster_url (P18 = Wikimedia Commons image),
director cross-ref via P57.
people : wikidata_id, headshot_url (P18), birth_year (P569),
death_year (P570), birth_place (P19).
Why this matters now — every /movie/:id and /person/:id page was rendering
the made-with-ai placeholder, even though Wikidata covers a huge slice of
our roster's IMDb IDs for free, CC0/CC-BY-SA-licensed via Commons.
Implementation notes:
- SPARQL endpoint https://query.wikidata.org/sparql, polite UA string,
80 IDs per batch, 1.2s sleep between batches (well under Wikidata's
guidance). Backs off 5s on errors and continues.
- Commons URL form: Special:FilePath/<file>?width=600 — the
Wikidata-recommended embed resolver. poster_source set to
'wikimedia-cc' so per-asset license is auditable downstream.
- Priority mode (--priority) restricts to rows already linked from
asim_credits / asim_spotted — cheap fast pass for the movies a real
user would browse first.
- birth_place via SERVICE wikibase:label so we get the English name
instead of a Q-id.
Tick 3 priority pass results:
movies (priority, 200 cands) matched=120/200 posters=12 noMatch=80
people (priority, 400 cands) matched=57/400 headshots=5 noMatch=343
→ 'Now You See Me', 'Hobbs & Shaw', 'Living in Oblivion', 'Scouts vs
Zombies', 'I, Robot', 'American Gods', 'Phenomena', '3 Generations',
'Opera', 'Johns', 'The Newsroom', 'September 11' now render real
Commons posters instead of the made-with-AI placeholder.
Background drain (PID 74821) started for next 10k popularity-ordered
movies; ~2.5h run; logs/wikidata-movies-bg.log.
dcdead0 · 2026-05-12 · feat(asseeninmovies/tick8): /movie/:id + /person/:id HTML pages
Renders dark luxe-magazine aesthetic (Cormorant Garamond + Inter, gold on near-black) so the 56k people / 280k movies / 347k credits / 14 SPOTTED rows are actually browsable, not just /api/* JSON. - /movie/:id: poster + meta + tagline + overview + credit table grouped by department (Directing/Acting/Writing/Art/Camera/Editing/Sound) + SPOTTED row with category badges + shop link. - /person/:id: headshot + name + birth/death + profession chips + union membership pills + bio + filmography table grouped by role. VERIFIED badge if claimed_at set. Claim CTA otherwise. - All images fall back to /img/made-with-ai.svg when poster_source is 'made-with-ai' or the URL is empty. Never sourced from IMDb. - TMDB attribution in footer (required). Smoke tested: /person/1 = Matthew Versteeg (set decorator, Goosebumps), /movie/1 = Goosebumps with 5 SPOTTED rows + 12 credits.
9a12577 · 2026-05-12 · tick7: SPOTTED seed import — 14 placements from thesetdecorator film-pieces
14 of 66 seed rows imported into asim_spotted. 52 skipped (missing imdb_id or piece_description — those are 'planned' entries without firm cites yet). Now /api/spotted returns real data: - The Girl with the Dragon Tattoo · Martin Vanger's abstract art - The Royal Tenenbaums · Margot's zebra-wallpaper bedroom (wallcovering!) - Forgetting Sarah Marshall · Turtle Bay Resort suite Categorizer heuristic buckets into wallcovering / art / furniture / lighting / rug / window-treatment / books / plants / prop / tableware / set / dressing. The wallcovering bucket is exactly where DW SKU cross-refs will land. Each row also enriches asim_people + asim_credits — set decorator, production designer, director — so /api/person/:id works once we add the HTML page (tick 8). 30 credits added across the 14 placements.
File tree
- .deploy.conf
- .env.example
- .gitignore
- Makefile
- data/schema-ingest.sql
- data/schema.sql
- docs/PLAN.md
- migrations/2026-05-12_perf_indexes.sql
- package-lock.json
- package.json
- public/favicon.ico
- public/favicon.svg
- public/img/made-with-ai.svg
- scrapers/import-film-pieces-seed.js
- scrapers/import-thesetdecorator-roster.js
- scrapers/tmdb-download-ids.js
- scrapers/tmdb-fetch-worker.js
- scrapers/wikidata-commons-fallback.js
- scrapers/wikidata-enrich.js
- scrapers/wikimedia-credit-backfill.js
- server.js
- test/smoke.js