← back to Nineoh Guide
recaps: complete Season 1 (24 original spoiler-safe drafts) via scalable db/recaps/sN.json + apply-recaps.mjs; add recap_status ai-draft review gate
c7d73bc6192eeb49f8b8272af815e7ac4afc5190 · 2026-07-25 10:37:09 -0700 · Steve
Files touched
A db/apply-recaps.mjsA db/recaps/s1.jsonD db/seed-recaps-s1.mjs
Diff
commit c7d73bc6192eeb49f8b8272af815e7ac4afc5190
Author: Steve <steve@designerwallcoverings.com>
Date: Sat Jul 25 10:37:09 2026 -0700
recaps: complete Season 1 (24 original spoiler-safe drafts) via scalable db/recaps/sN.json + apply-recaps.mjs; add recap_status ai-draft review gate
---
db/apply-recaps.mjs | 44 ++++++++++++++++++++++++++++++++++++++++++++
db/recaps/s1.json | 27 +++++++++++++++++++++++++++
db/seed-recaps-s1.mjs | 35 -----------------------------------
3 files changed, 71 insertions(+), 35 deletions(-)
diff --git a/db/apply-recaps.mjs b/db/apply-recaps.mjs
new file mode 100644
index 0000000..957a143
--- /dev/null
+++ b/db/apply-recaps.mjs
@@ -0,0 +1,44 @@
+// Generic season-by-season recap applier. Reads db/recaps/s{N}.json (keys =
+// episode numbers, values = ORIGINAL recap text) and writes them into
+// episodes.summary_short_original with recap_status='ai-draft' (human review gate).
+// Run: node db/apply-recaps.mjs (all seasons present)
+// node db/apply-recaps.mjs 2 (just season 2)
+import pg from "pg";
+import { readdirSync, readFileSync } from "node:fs";
+import { fileURLToPath } from "node:url";
+import { dirname, join } from "node:path";
+
+const DATABASE_URL =
+ process.env.DATABASE_URL ?? "postgresql://localhost/nineoh_guide?host=/tmp";
+const pool = new pg.Pool({ connectionString: DATABASE_URL });
+const dir = join(dirname(fileURLToPath(import.meta.url)), "recaps");
+
+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 only = process.argv[2] ? Number(process.argv[2]) : null;
+const files = readdirSync(dir).filter((f) => /^s\d+\.json$/.test(f));
+
+let total = 0;
+for (const f of files) {
+ const season = Number(f.match(/^s(\d+)\.json$/)[1]);
+ if (only && season !== only) continue;
+ const data = JSON.parse(readFileSync(join(dir, f), "utf8"));
+ let n = 0;
+ for (const [ep, text] of Object.entries(data)) {
+ if (ep.startsWith("_")) continue; // skip _note
+ const res = await pool.query(
+ `update episodes set summary_short_original=$1, recap_status='ai-draft', spoiler_rating=0
+ where show_id=$2 and season_number=$3 and episode_number=$4`,
+ [text, show.id, season, Number(ep)]
+ );
+ n += res.rowCount;
+ }
+ console.log(`season ${season}: ${n} recaps applied`);
+ total += n;
+}
+const tally = await pool.query(`select recap_status, count(*) from episodes group by recap_status order by 1`);
+console.log("total applied:", total);
+console.table(tally.rows);
+console.log("All recaps are ai-draft — review before marking published.");
+await pool.end();
diff --git a/db/recaps/s1.json b/db/recaps/s1.json
new file mode 100644
index 0000000..f3b3d24
--- /dev/null
+++ b/db/recaps/s1.json
@@ -0,0 +1,27 @@
+{
+ "_note": "ORIGINAL spoiler-safe recaps in our own editorial voice, grounded in factual episode events (not copied/closely paraphrased). AI-drafted starters — human review before publish.",
+ "1": "The series opens as the Wilson family trades small-town Kansas for the 90210 ZIP code after Harry takes the principal's chair at West Beverly Hills High. Siblings Annie and Dixon get a same-day crash course in Beverly Hills social politics while the family settles in near a sharp-tongued grandmother.",
+ "2": "Dixon works to win over territorial new teammates while a long-distance breakup leaves Annie unexpectedly open to a first spark in her adopted city — the back half of a two-part premiere that sets the ensemble in motion.",
+ "3": "A well-meaning family bowling night goes sideways once the kids' new friends turn up uninvited, and Annie finds herself pulled between two very different love interests as the group's dynamics start to harden.",
+ "4": "West Beverly's musical loses its director at the worst possible moment, and talking a familiar alum into stepping in reopens old rivalries over the school's drama scene.",
+ "5": "With opening night of the school production closing in, Annie's whirlwind romance with an older guy has her friends worried it's all moving far too fast.",
+ "6": "A magazine fashion shoot doubles as a stage for family drama: Annie spins it into a school project while a well-intentioned scheme to patch up a marriage quietly backfires.",
+ "7": "A parenting-class assignment forces Annie and Ethan into close quarters and shifts how they see each other, while a half-birthday party stirs up fresh dynamics across the group.",
+ "8": "As homecoming nears, Adrianna returns and tries to repair a fractured friendship that her ex-best-friend isn't ready to forgive — and an unexpected new connection quietly takes root.",
+ "9": "A long-buried family secret finally surfaces for Annie and Dixon, while what starts as an easygoing girls' night takes a sharply tense turn.",
+ "10": "Jealousy boils over around Annie's sweet-sixteen as old rivalries sharpen, and campus gossip swirls around a classmate's sudden change of fortune.",
+ "11": "Old friendships strain toward a breaking point, a houseguest moves in with the Wilsons, and shifting social alliances shake up the whole group.",
+ "12": "Life-changing news lands for one of the show's veterans as budding romances and buried secrets threaten to upend everyone's plans.",
+ "13": "An extravagant gift sparks a clash between the Wilson women, long-held secrets inch toward the surface, and a day at the beach brings two friends closer.",
+ "14": "Hard decisions weigh on the group as play tryouts loom, and leaning on friends for support turns out to be more complicated than anyone expects.",
+ "15": "Difficult truths come out at home while a new friendship stokes insecurity inside an established couple.",
+ "16": "A chance encounter intrigues Naomi, a Valentine's plan falls apart, and simmering jealousy starts to spill into the open.",
+ "17": "Naomi is drawn to a magnetic, volatile newcomer even as an established relationship hits a fresh and painful snag.",
+ "18": "When one couple tries to slow things down, a solo road trip to Kansas becomes an emotionally fraught detour.",
+ "19": "A familiar face arrives in Beverly Hills as road trips, spring-break commitments, and old ties pull the group in very different directions.",
+ "20": "The Wilsons close the door on Kansas with a Beverly Hills-style yard sale, while old friends navigate the start of new chapters.",
+ "21": "The unexpected return of Naomi's older sister upends her world just as a double date and a big reveal come to a head.",
+ "22": "Bachelor and bachelorette celebrations split the group for a night out that goes nowhere near as planned.",
+ "23": "Prom prep glams up the girls, and a decidedly mismatched pairing sets the stage for an eventful night.",
+ "24": "The season builds to a prom night where long-held secrets, tangled feelings, and a dramatic turn all collide as summer arrives."
+}
diff --git a/db/seed-recaps-s1.mjs b/db/seed-recaps-s1.mjs
deleted file mode 100644
index c667c6d..0000000
--- a/db/seed-recaps-s1.mjs
+++ /dev/null
@@ -1,35 +0,0 @@
-// ORIGINAL, spoiler-safe Season 1 recaps — written in our own editorial voice,
-// grounded in FACTUAL episode events (not copied or closely paraphrased from any
-// synopsis). These are AI-drafted editorial starters for human review, not
-// authoritative. Copyright protects expression, not plot facts.
-// Run: node db/seed-recaps-s1.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 RECAPS = {
- 1: "The series opens as the Wilson family trades small-town Kansas for the 90210 ZIP code after Harry takes the principal's chair at West Beverly Hills High. Siblings Annie and Dixon get a same-day crash course in Beverly Hills social politics while the family settles in near a sharp-tongued grandmother.",
- 2: "Dixon works to win over territorial new teammates while a long-distance breakup leaves Annie unexpectedly open to a first spark in her adopted city — the back half of a two-part premiere that sets the ensemble in motion.",
- 3: "A well-meaning family bowling night goes sideways once the kids' new friends turn up uninvited, and Annie finds herself pulled between two very different love interests as the group's dynamics start to harden.",
- 4: "West Beverly's musical loses its director at the worst possible moment, and talking a familiar alum into stepping in reopens old rivalries over the school's drama scene.",
- 5: "With opening night of the school production closing in, Annie's whirlwind romance with an older guy has her friends worried it's all moving far too fast.",
- 6: "A magazine fashion shoot doubles as a stage for family drama: Annie spins it into a school project while a well-intentioned scheme to patch up a marriage quietly backfires.",
-};
-
-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); }
-
-let updated = 0;
-for (const [num, text] of Object.entries(RECAPS)) {
- const res = await pool.query(
- `update episodes set summary_short_original = $1, spoiler_rating = 0
- where show_id = $2 and season_number = 1 and episode_number = $3`,
- [text, show.id, Number(num)]
- );
- updated += res.rowCount;
-}
-console.log(`original S1 recaps written: ${updated}`);
-console.log("These are ORIGINAL editorial drafts grounded in factual events — review before publish.");
-await pool.end();
← c260ce8 write 6 original spoiler-safe S1 recaps (our wording, ground
·
back to Nineoh Guide
·
recaps: complete Season 2 (22 original spoiler-safe drafts) 2cc6c11 →