AsSeenInMovies

IMDb-class catalog with the "SPOTTED" community layer — what fictional decor / fashion / cars are real, and where to buy them.

repo: ~/Projects/asseeninmovies · 122 commits · 0 in last 24h, 0 in last 7d ·

Search the build

122 commits indexed

  1. c27bdce 2026-06-23 Replace pagination with infinite scrolling
  2. 11799d1 2026-05-31 Add per-site favicon (kills /favicon.ico 404)
  3. 466b16e 2026-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.
  4. 694e783 2026-05-19 Add rel=noreferrer to every target=_blank link
  5. 2cee761 2026-05-19 Guard .bak/.pre-/.orig paths in static middleware and gitignore
  6. d70a112 2026-05-13 Makefile: audit target (fleet-status + fleet-smoke)
  7. 5bc6c50 2026-05-13 smoke: /api/health perf budget bumped 500→1000ms (flaky cold-cache spike)
  8. aea2c77 2026-05-13 smoke: assert /api/spotted/featured SWR Cache-Control
  9. 8243fbe 2026-05-13 sitemap: extract buildSitemapXml() helper — coalesce concurrent cold-cache builds; /sitemap.txt no longer returns 'warming' stub
  10. 888e73e 2026-05-13 smoke: cover /healthz (200, ok JSON, no-store, HEAD method) + harness opts.method
  11. 32e313c 2026-05-13 sitemap.xml: preserve Last-Modified+Cache-Control on cache-hit fast path + smoke
  12. a7da201 2026-05-13 smoke: home perf budget <1500ms (cold-cache ceiling; regression guard)
  13. ed68d9d 2026-05-13 Makefile: add help target listing all available commands
  14. 57d42ae 2026-05-13 smoke: /api/health perf budget (<500ms) — regression guard for N+1/pool
  15. 755754e 2026-05-13 Makefile: add fleet-status target
  16. bf54e4d 2026-05-13 .gitignore: add .env.*, tmp/, dist/, build/, .next/ (Steve's MEMORY standard)
  17. e68674f 2026-05-13 smoke: assert /people sort + density (grid standard)
  18. 211c824 2026-05-13 smoke: assert Vary: Accept-Encoding (cache-safety regression guard)
  19. c79cbbc 2026-05-13 smoke: assert /api/version git !== 'unknown' (catches missing git on PATH at boot)
  20. db4fdf7 2026-05-13 smoke: assert /api/version Cache-Control no-store
  21. e3ecee0 2026-05-13 api/spotted/featured: N (default 6, max 24) verified spotted placements with movie context
  22. d1d1f7a 2026-05-13 smoke: 4 assertions for /api/version (200, name, git sha, node)
  23. 3f7e5d7 2026-05-13 api/version: deploy fingerprint endpoint (name, version, git SHA, node, started_at)
  24. b8ab370 2026-05-13 smoke: uptime_s assertion on /api/health
  25. 9dd8cca 2026-05-13 api/health: include uptime_s for ops dashboards
  26. 0f94cb4 2026-05-13 sitemap.txt: emit Last-Modified header for conditional GET (304)
  27. 98f890a 2026-05-13 sitemap.xml: emit Last-Modified header for conditional GET (304)
  28. e25dd59 2026-05-13 asim /api/credits/stats — document why department=0 (TMDB ingest gap, future enrichment).
  29. f10bd92 2026-05-13 asim robots.txt: also reference /sitemap.txt (multiple Sitemap directives allowed). 69/69.
  30. a1260ae 2026-05-13 asim Makefile — common dev targets (start, smoke, smoke-quiet, smoke-json, fleet-smoke, restart, logs)
  31. 0d70bcf 2026-05-13 snapshot: 1 file(s) changed, ~1 modified
  32. c6662ec 2026-05-13 asim sitemap.xml + .txt Cache-Control 1h→6h + SWR 24h (catalog rarely changes that fast). 69/69.
  33. 1df1a75 2026-05-13 asim smoke --json suppresses header so output is parseable.
  34. ba2ce08 2026-05-13 asim smoke +--json flag (monitoring/dashboard output)
  35. 7d3963c 2026-05-13 asim +.deploy.conf — wire into /deploy skill (rsync→Kamatera /root/Projects/asseeninmovies, healthcheck /healthz). Mirrors sod template.
  36. 2c2ee45 2026-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.
  37. e8ce9a1 2026-05-13 asim nav dice icon — +aria-label for screen readers ("Show a random movie"). a11y polish. 69/69.
  38. a43054b 2026-05-13 asim smoke +2 /api/credits/stats checks. 69/69.
  39. 08a8678 2026-05-13 asseeninmovies: GET /api/credits/stats — aggregate counts on asim_credits (363k rows). 5min Cache-Control+SWR. /api discovery doc updated. 67/67.
  40. f7d2a51 2026-05-13 asim /api/health Cache-Control: no-store (timestamped liveness/status, always fresh). 67/67.
  41. e88f11a 2026-05-13 asim smoke +2 /sitemap.txt checks. 67/67.
  42. c819393 2026-05-13 asseeninmovies: GET /sitemap.txt — plain-text sitemap (Google + Bing supported, simpler than XML). Reuses _sitemapCache.xml; 1h Cache-Control. 65/65.
  43. 4cd8acf 2026-05-13 asim /spotted/queue Cache-Control: no-store, must-revalidate (admin token-gated, never share cache). 65/65.
  44. e67d0fe 2026-05-13 asim /healthz Cache-Control: no-store (liveness should always be fresh)
  45. 0898544 2026-05-13 asseeninmovies: GET /api/movies/random + /api/people/random — JSON random-row companions to the /random/* redirects. 60s cache. 65/65.
  46. 6257501 2026-05-13 asim smoke +2 ItemList checks (/movies + /people). 65/65.
  47. 914d939 2026-05-13 asseeninmovies: smoke +2 home checks (og:image + color-scheme meta). 63/63.
  48. 9e1c6cf 2026-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.
  49. 406c98c 2026-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.
  50. 7253508 2026-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.
  51. 070a207 2026-05-13 asseeninmovies: smoke +2 /random/spotted checks. 61/61.
  52. 921ea62 2026-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.
  53. ee14e9d 2026-05-13 asseeninmovies: smoke +2 /api/spotted/random checks. 59/59.
  54. 11f5c83 2026-05-13 asseeninmovies: /api discovery doc — list /api/spotted/random alongside the rest.
  55. 0f3342b 2026-05-13 asseeninmovies: GET /api/spotted/random — JSON companion to /random/movie/person. 60s cache+SWR. Returns {spotted: {...}} with movie context. 57/57.
  56. 15d32a7 2026-05-13 asseeninmovies: smoke +gzip header check on /movies (verifies compression middleware is wired). 57/57.
  57. c212984 2026-05-13 asseeninmovies: smoke --quiet flag prints only failures + final tally. CI/cron friendly.
  58. d17d0a5 2026-05-13 asseeninmovies: home stat-row — show credits count (363,323 asim_credits rows) alongside movies/people/spotted. 56/56.
  59. 4996930 2026-05-13 asseeninmovies: home-cache + /api/health expose credits_total (asim_credits row count). 56/56.
  60. 391cd93 2026-05-13 asseeninmovies: smoke +4 security-header checks (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy). 56/56.
  61. 999138e 2026-05-13 asseeninmovies: pin engines.node>=20 in package.json. Mirrors sod, prevents accidental run on legacy node.
  62. cc62349 2026-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.
  63. e810f9c 2026-05-13 asseeninmovies: home cta-row — add SPOTTED browse-link alongside Movies and People. Surfaces the most distinctive feature directly from the hero.
  64. 0878efc 2026-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.
  65. 4de56c1 2026-05-13 asim-healthz
  66. 7197fa3 2026-05-12 asseeninmovies: /api/{movie,person,search,spotted} Cache-Control + parallelize /api/person/:id
  67. f78067e 2026-05-12 asseeninmovies: /api/spotted/stats Cache-Control max-age=30 SWR=60
  68. 9f98777 2026-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.
  69. cd952cb 2026-05-12 asseeninmovies: trust proxy=1 + Cache-Control on sitemap.xml (3600s) and robots.txt (86400s). Cloudflare-friendly. Smoke 52/52.
  70. 5aadd3a 2026-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.
  71. 2c7ee2c 2026-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.
  72. 018b503 2026-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.
  73. a471a71 2026-05-12 asseeninmovies: smoke — add /api discovery doc check (+2 assertions). 52/52.
  74. ea5c081 2026-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.
  75. 4648ead 2026-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.
  76. 2d5ec29 2026-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.
  77. 210edd9 2026-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.
  78. b7aaa6e 2026-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.
  79. b8d4281 2026-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.
  80. fd70620 2026-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.
  81. 56476af 2026-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.
  82. b8cbc44 2026-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.
  83. 592a2e7 2026-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.
  84. 1910794 2026-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.
  85. 880a1dd 2026-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.
  86. f353476 2026-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
  87. f89066d 2026-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.
  88. f4b860b 2026-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).
  89. 2261023 2026-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.
  90. b59fa66 2026-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.
  91. bbaae4b 2026-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.
  92. 08671ef 2026-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)
  93. 40fa8dd 2026-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.
  94. 992f471 2026-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
  95. ae66c63 2026-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
  96. b7e1d47 2026-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.
  97. dbd4763 2026-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)
  98. d6451f3 2026-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)
  99. 25508fb 2026-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
  100. 17d9d2d 2026-05-12 asseeninmovies: /spotted gallery — 4-tile stats strip (placements/films/brands/categories) with responsive @media; mirrors /spotted/queue header pattern
  101. 3d60e9b 2026-05-12 asseeninmovies: /spotted/queue stats header (4-tile strip — pending/live/rejected/submitted-7d) with parallel Promise.all query and responsive @media
  102. 0206a11 2026-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
  103. 4f8629c 2026-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)
  104. 3a3c2c7 2026-05-12 asseeninmovies: home page — third rail 'Recent SPOTTED' (6 verified placements) + 'spotted' added to stat-row; cards deep-link to /movie/:id#spotted
  105. c22b6be 2026-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
  106. 6ed5341 2026-05-12 asseeninmovies: /spotted HTML gallery — verified placements paginated, category chip-bar filter, shop-button CTA per row, submit banner up top
  107. 1cdcaf5 2026-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
  108. 562f3b6 2026-05-12 asseeninmovies: /search HTML page — combined movies+people grid, replaces nav link to JSON endpoint
  109. 57e1b20 2026-05-12 asseeninmovies: live home page — stats + 2 random-sampled rails (movies + people) + cleaner blurb grid
  110. 8205ad7 2026-05-12 wikidata-enrich: add --byCast (filter to role=actor) to target high-yield enrichment pool
  111. 67784fe 2026-05-12 asseeninmovies: /movies + /people sort dropdowns + density slider (per standing grid rule); localStorage-persisted density
  112. 96cf4db 2026-05-12 scaffold wikidata-commons-fallback.js — search Commons for headshots when wikidata P18 missing (CC-licenses only)
  113. 40aa91f 2026-05-12 wikidata-enrich: add --byCredits to rank people by credit count (target high-recall enrichment)
  114. bdd362c 2026-05-12 feat(asseeninmovies): /movies + /people paginated index pages with filters (year/genre/q)
  115. 2b42577 2026-05-12 feat(asseeninmovies): inject Big Red widget into htmlShell footer
  116. 2b1ecd3 2026-05-12 feat(asseeninmovies): fan-art credit policy — Commons artist + license + source on every poster
  117. f69197c 2026-05-12 feat(asseeninmovies/tick3): Wikidata SPARQL enrichment
  118. dcdead0 2026-05-12 feat(asseeninmovies/tick8): /movie/:id + /person/:id HTML pages
  119. 9a12577 2026-05-12 tick7: SPOTTED seed import — 14 placements from thesetdecorator film-pieces
  120. 6c82c50 2026-05-12 tick2: thesetdecorator roster importer + uniq idx on imdb_id (running in bg)
  121. 01bf78d 2026-05-12 tick1: TMDB Phase A complete — 6.46M stubs across 7 sources, awaiting API key for Phase B
  122. 92aef22 2026-05-12 feat(asseeninmovies/tick0): scaffold + schema + AI placeholder + plan

Authors

Agents used

  • none detected

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

Commits with substantial prose (≥120 chars) — the rationale behind each move.

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

22 files tracked. Click any to browse the source at HEAD.

Other build journals

← Butlr  ·  all 4 projects  ·  Stars of Design →

Export

commits.csv · feed.atom · project.json · commits.json

rendered in 1ms