[object Object]

← back to Nineoh Guide

ingest factual cast + character names (TVmaze, names-only, no photos/bios) — populates /cast

fc018158f9b330a0d7827775a33bd7e1cbe0da9c · 2026-07-25 10:28:56 -0700 · Steve

Files touched

Diff

commit fc018158f9b330a0d7827775a33bd7e1cbe0da9c
Author: Steve <steve@designerwallcoverings.com>
Date:   Sat Jul 25 10:28:56 2026 -0700

    ingest factual cast + character names (TVmaze, names-only, no photos/bios) — populates /cast
---
 db/ingest-cast.mjs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/db/ingest-cast.mjs b/db/ingest-cast.mjs
new file mode 100644
index 0000000..14cf4a3
--- /dev/null
+++ b/db/ingest-cast.mjs
@@ -0,0 +1,59 @@
+// Ingests FACTUAL cast data only — actor names + character names (both facts,
+// not copyrightable). Deliberately does NOT store TVmaze person photos (publicity
+// rights + image licensing are gated) or any biography text (we write our own).
+// Run: node db/ingest-cast.mjs
+import pg from "pg";
+
+const DATABASE_URL =
+  process.env.DATABASE_URL ?? "postgresql://localhost/nineoh_guide?host=/tmp";
+const pool = new pg.Pool({ connectionString: DATABASE_URL });
+
+const show = (await pool.query(`select id from shows where canonical_title='90210'`)).rows[0];
+if (!show) {
+  console.error("Run db/seed.mjs first.");
+  process.exit(1);
+}
+const showId = show.id;
+
+// TVmaze show id 578 = 90210 (2008 CW). Cast endpoint returns main cast: person + character.
+const res = await fetch("https://api.tvmaze.com/shows/578/cast");
+const cast = await res.json();
+
+let people = 0;
+let characters = 0;
+for (const c of cast) {
+  const personName = c.person?.name;
+  const charName = c.character?.name;
+  if (!personName) continue;
+
+  // Upsert person by name (names are facts; no photo, no bio stored).
+  const p = await pool.query(
+    `insert into cast_people (name, publicity_risk_level)
+       select $1, 'editorial-only'
+       where not exists (select 1 from cast_people where name = $1)
+       returning id`,
+    [personName]
+  );
+  if (p.rowCount) people++;
+
+  if (charName) {
+    const ch = await pool.query(
+      `insert into characters (name, show_id)
+         select $1, $2
+         where not exists (select 1 from characters where name = $1 and show_id = $2)
+         returning id`,
+      [charName, showId]
+    );
+    if (ch.rowCount) characters++;
+  }
+}
+
+const totals = await pool.query(
+  `select (select count(*) from cast_people) as people,
+          (select count(*) from characters where show_id=$1) as chars`,
+  [showId]
+);
+console.log(`inserted this run — people:${people} characters:${characters}`);
+console.log(`totals — cast_people:${totals.rows[0].people} characters:${totals.rows[0].chars}`);
+console.log("Names only. No photos (image-license gated), no bios (we write our own original).");
+await pool.end();

← 1b02ed5 contrarian fixes: correct Expo API-base wiring (expo-constan  ·  back to Nineoh Guide  ·  write 6 original spoiler-safe S1 recaps (our wording, ground c260ce8 →