[object Object]

← back to AsSeenInMovies

asseeninmovies: GET /api/spotted/random — JSON companion to /random/movie/person. 60s cache+SWR. Returns {spotted: {...}} with movie context. 57/57.

0f3342b268be32f4a6f00f843f111640270236a8 · 2026-05-13 06:05:17 -0700 · SteveStudio2

Files touched

Diff

commit 0f3342b268be32f4a6f00f843f111640270236a8
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 06:05:17 2026 -0700

    asseeninmovies: GET /api/spotted/random — JSON companion to /random/movie/person. 60s cache+SWR. Returns {spotted: {...}} with movie context. 57/57.
---
 server.js | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/server.js b/server.js
index fd90254..f5cc4f7 100644
--- a/server.js
+++ b/server.js
@@ -723,6 +723,26 @@ app.get('/api/spotted', async (req, res) => {
 // JSON stats — same numbers the /spotted gallery + /spotted/queue strips
 // render, exposed as a single small API. Useful for the status-loop tail
 // monitor and any external dashboard. No auth needed — public counts only.
+// /api/spotted/random — JSON companion to /random/movie /random/person.
+// Pulls a single verified SPOTTED row at random. Useful for widgets that
+// want to feature "a placement from the catalog" inline. 60s cache.
+app.get('/api/spotted/random', async (_req, res) => {
+  try {
+    const r = await pool.query(`
+      SELECT s.id, s.product_name, s.product_category, s.brand,
+             s.scene_description, s.scene_timecode, s.shop_url,
+             m.id AS movie_id, m.title AS movie_title, m.release_year, m.poster_url
+        FROM asim_spotted s JOIN asim_movies m ON m.id = s.movie_id
+       WHERE s.verified_at IS NOT NULL AND (s.verified_by IS NULL OR s.verified_by NOT LIKE 'rejected%')
+       ORDER BY random() LIMIT 1`);
+    if (!r.rows[0]) return res.status(404).json({ error: 'no-spotted-yet' });
+    res.setHeader('Cache-Control', 'public, max-age=60, stale-while-revalidate=120');
+    res.json({ spotted: r.rows[0] });
+  } catch (e) {
+    res.status(500).json({ error: e.message });
+  }
+});
+
 app.get('/api/spotted/stats', async (_req, res) => {
   // Short Cache-Control — stats don't change second-to-second. 30s lets
   // crawlers/dashboards/status-loop poll without hammering pg.

← 15d32a7 asseeninmovies: smoke +gzip header check on /movies (verifie  ·  back to AsSeenInMovies  ·  asseeninmovies: /api discovery doc — list /api/spotted/rando 11f5c83 →