← back to Beverlyhillsvideos
filmgen/gen_store_scripts.mjs
50 lines
#!/usr/bin/env node
// gen_store_scripts.mjs — draft safe editorial script+tagline per remaining store via
// local Ollama ($0). HARD constraints: general facts only, no invented products/prices/
// dates, no protected product-design names. Output reviewed + compliance-filtered after.
import fs from 'node:fs';
const MODEL = process.env.OLLAMA_MODEL || 'gemma3:12b';
const remaining = JSON.parse(fs.readFileSync('/tmp/remaining-stores.json', 'utf8'));
const SCRIPTS = 'data/store-scripts.json';
const TAGS = '/tmp/store-taglines.json';
const scripts = JSON.parse(fs.readFileSync(SCRIPTS, 'utf8'));
const tags = fs.existsSync(TAGS) ? JSON.parse(fs.readFileSync(TAGS, 'utf8')) : {};
const prompt = (name, cat) => `You write short, elegant editorial copy for "Beverly Hills Videos", an independent city guide. Write about the ${cat} house "${name}" on Rodeo Drive in Beverly Hills.
STRICT RULES:
- State ONLY safe, widely-known general facts: country/city of origin, that it is a ${cat} house, its presence on Rodeo Drive, its general character/reputation.
- Do NOT invent or state specific products, product names, prices, founding dates, or people's names unless universally known and certain.
- Do NOT name any specific trademarked product design (e.g. a specific bag/watch/jewelry model).
- If unsure of any fact, stay general. Never guess.
- Tone: refined, warm, understated luxury. American English.
Return JSON only: {"tagline":"<one short line, max 90 chars, no period needed>","script":"<2 sentences, ~40 words, for a voiceover>"}`;
async function gen(name, cat) {
const res = await fetch('http://127.0.0.1:11434/api/generate', {
method: 'POST',
body: JSON.stringify({ model: MODEL, prompt: prompt(name, cat), stream: false, format: 'json', options: { temperature: 0.6 } }),
});
const j = await res.json();
const o = JSON.parse(j.response);
if (!o.tagline || !o.script) throw new Error('missing fields');
return { tagline: String(o.tagline).slice(0, 90).replace(/\s+/g, ' ').trim(), script: String(o.script).replace(/\s+/g, ' ').trim() };
}
let ok = 0, fail = 0;
for (const s of remaining) {
if (scripts[s.name]) { ok++; continue; }
try {
const { tagline, script } = await gen(s.name, s.category);
scripts[s.name] = script; tags[s.name] = tagline;
ok++; console.log(`✓ ${s.name}`);
} catch (e) {
fail++; console.log(`✗ ${s.name}: ${String(e.message).slice(0, 60)}`);
}
fs.writeFileSync(SCRIPTS, JSON.stringify(scripts, null, 1) + '\n');
fs.writeFileSync(TAGS, JSON.stringify(tags, null, 1) + '\n');
}
console.log(`\nDONE: ${ok} scripts, ${fail} failed. Total in file: ${Object.keys(scripts).length}`);