← back to Nineoh Guide

scripts/seed-recurring-cast.mjs

72 lines

// Promote recurring guest actors (>= MIN_EPS episodes) into cast_people +
// characters + credits(credit_type='recurring'), so the Cast tab lists them
// alongside the 14 mains. Bios are ORIGINAL and grounded only in our own
// episode-count data (no fabricated external facts). Idempotent by name.
//
//   node scripts/seed-recurring-cast.mjs
//
import { readFileSync } from "node:fs";
import { Pool } from "pg";

const MIN_EPS = 4;
const env = readFileSync(new URL("../apps/web/.env.local", import.meta.url), "utf8");
const DB = env.match(/^DATABASE_URL=(.+)$/m)?.[1]?.replace(/^["']|["']$/g, "");
if (!DB) throw new Error("DATABASE_URL not found in apps/web/.env.local");
const pool = new Pool({ connectionString: DB, max: 4 });

// show_id (all characters/credits hang off it)
const { rows: showRows } = await pool.query(`select id from shows limit 1`);
const showId = showRows[0].id;

const { rows: people } = await pool.query(
  `select person_name,
          (array_agg(character_name order by ord))[1] as character,
          count(distinct episode_id) as eps
     from episode_guest_cast
    where person_name not in (select name from cast_people)
    group by person_name
   having count(distinct episode_id) >= $1
    order by eps desc`,
  [MIN_EPS]
);
console.log(`recurring actors to add (>= ${MIN_EPS} eps): ${people.length}`);

let added = 0;
for (const p of people) {
  const bio = `Recurring guest star who appears as ${p.character ?? "a recurring character"} in ${p.eps} episodes of the reboot. Part of the extended 90210 ensemble beyond the core cast.`;

  // cast_people (skip if somehow already there)
  const cp = await pool.query(
    `insert into cast_people (name, biography_original, bio_status, publicity_risk_level)
     values ($1,$2,'ai-draft','editorial-only')
     on conflict do nothing
     returning id`,
    [p.person_name, bio]
  );
  let personId = cp.rows[0]?.id;
  if (!personId) {
    const ex = await pool.query(`select id from cast_people where name=$1`, [p.person_name]);
    personId = ex.rows[0].id;
  }

  // character
  let charId = null;
  if (p.character) {
    const ch = await pool.query(
      `insert into characters (name, show_id) values ($1,$2) returning id`,
      [p.character, showId]
    );
    charId = ch.rows[0].id;
  }

  // recurring credit — billing_order negative-eps so higher counts sort first
  await pool.query(
    `insert into credits (person_id, character_id, credit_type, billing_order)
     values ($1,$2,'recurring',$3)`,
    [personId, charId, -p.eps]
  );
  added++;
}
console.log(`added ${added} recurring cast members`);
await pool.end();