Docs/Skills and code/skill video-visual-production
.claude/skills/video-visual-production/scripts/review.js
What the file says about itself
Writes REVIEW-<beat>.md beside the beat sheet: one row per scene with its time, what the voice says, what is on screen and which sounds fire, so the user can point at "B1.06" and say what to change. Rebuilt on every build.
#!/usr/bin/env node
// Writes REVIEW-<beat>.md beside the beat sheet: one row per scene with its time, what the voice says, what is on screen
// and which sounds fire, so the user can point at "B1.06" and say what to change. Rebuilt on every build.
// node review.js --sheet <beat-sheet.json>
const fs = require('fs');
const path = require('path');
const U = require('./lib/util');
function build(sheet, tl, words) {
const L = [];
L.push(`# Review sheet: ${sheet.beat} ${sheet.title}`, '',
`Video: \`${path.join(U.buildDir(sheet.slug, sheet.beat), sheet.beat + '.mp4')}\` (${(tl.total_frames / tl.fps).toFixed(1)} s). Contact sheet next to it. Scene ids below are what you cite.`, '',
'## How to send notes (any of these is enough)', '',
'- `B1.06 at 40 s: the highlight comes too late` (scene id and a time or word is all I need).',
'- `B1.08: change the line to ...` (new wording).',
'- `B1.12: swap this scene for a plain statement` (a different scene type).',
'- `sounds: swipe too loud` or `drop the tick in B1.02` (sound).',
'- `everything: gold is too orange` (style).', '',
'Each note becomes one small change to the beat sheet (`edit.js`), is linted before it is kept, and only the scenes it touches are re-rendered. Nothing is rewritten from scratch.', '',
'## Scenes', '', '| Scene | Time (s) | Type | Voice says | On screen | Sounds |', '|---|---|---|---|---|---|');
tl.scenes.forEach((sc, i) => {
const s = sheet.scenes[i];
const says = words.slice(s.span[0], s.span[1] + 1).map((w) => w.text);
const voice = says.length > 16 ? says.slice(0, 14).join(' ') + ' ...' : says.join(' ');
const shown = (s.text || []).filter((t) => t.role !== 'source' && t.role !== 'chapter').map((t) => t.value).join(' / ');
const snd = tl.sfx.filter((e) => e.scene === sc.id && !e.texture).map((e) => `${e.sound} @${(e.frame / tl.fps).toFixed(1)}`).concat(tl.sfx.some((e) => e.scene === sc.id && e.texture) ? ['clock ticks'] : []).join(', ');
L.push(`| **${sc.id}** | ${(sc.start / tl.fps).toFixed(1)}-${(sc.end / tl.fps).toFixed(1)} | ${sc.type} (${sc.class}) | ${voice.replace(/\|/g, '/')} | ${shown.replace(/\|/g, '/') || '-'} | ${snd || '-'} |`);
});
L.push('', '## What is cheap to change', '',
'| You want | Change | Cost |', '|---|---|---|',
'| different wording on a card or slam | `props` text of that scene | re-render that scene only |',
'| a highlight, label or number to appear on another word | that scene\'s `cues` | that scene only |',
'| a different phrase highlighted in an article passage | `props.highlights` (phrase must exist in the capture) | that scene only |',
'| a sound louder, quieter, removed or moved | `levels`, the scene\'s `sfx`, or its cue | sound re-mix (about 15 s), no video re-render |',
'| bed louder or off | `bed.level_db` / `bed.enabled` | sound re-mix only |',
'| colours | `style` (accent, good, ink, muted, bg0, bg1 ...) | all scenes (one full render, about 2.5 min) |',
'| a scene of another kind | change `type` and `props` (see `references/scene-catalogue.md`) | that scene only |',
'| a new scene type or a motion change | code in `render/scenes.js` | all scenes that use it |', '');
return L.join('\n');
}
module.exports = { build };
if (require.main === module) {
const sheetPath = U.fromRoot(U.arg('sheet', ''));
const sheet = U.readJSON(sheetPath);
const tl = U.readJSON(path.join(path.dirname(sheetPath), `timeline-${sheet.beat}.json`));
const words = U.loadWords(sheet).filter((w) => w.beat === sheet.beat);
const out = path.join(path.dirname(sheetPath), `REVIEW-${sheet.beat}.md`);
fs.writeFileSync(out, build(sheet, tl, words));
U.say('review sheet -> ' + out);
}
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.