explainer-video 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +12 -0
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/cordis.patch.yml +7 -0
- package/lib/index.js +35 -0
- package/package.json +56 -4
- package/plugin.json +13 -0
- package/skills/coursework-author/.synced-from +5 -0
- package/skills/coursework-author/SKILL.md +209 -0
- package/skills/craft-codeflow-flows/SKILL.md +116 -0
- package/skills/craft-codeflow-mine/SKILL.md +207 -0
- package/skills/craft-codeflow-plan/SKILL.md +65 -0
- package/skills/craft-codeflow-scene/SKILL.md +168 -0
- package/skills/craft-video-align/SKILL.md +109 -0
- package/skills/craft-video-assemble/SKILL.md +69 -0
- package/skills/craft-video-audio/SKILL.md +59 -0
- package/skills/craft-video-enrich/SKILL.md +46 -0
- package/skills/craft-video-plan/SKILL.md +57 -0
- package/skills/craft-video-publish/SKILL.md +79 -0
- package/skills/craft-video-render/SKILL.md +68 -0
- package/skills/craft-video-scene/SKILL.md +112 -0
- package/skills/craft-video-script/SKILL.md +80 -0
- package/skills/explainer-video-from-codebase/FLOWS.md +577 -0
- package/skills/explainer-video-from-codebase/SKILL.md +659 -0
- package/skills/explainer-video-from-codebase/tools/apply-flows.mjs +531 -0
- package/skills/explainer-video-from-codebase/tools/approve-flows.mjs +56 -0
- package/skills/explainer-video-from-codebase/tools/codemap-md.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/config/dotenv.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/json.mjs +14 -0
- package/skills/explainer-video-from-codebase/tools/config/redact.mjs +16 -0
- package/skills/explainer-video-from-codebase/tools/config/toml-lite.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/yaml-lite.mjs +18 -0
- package/skills/explainer-video-from-codebase/tools/excerpt-check.mjs +186 -0
- package/skills/explainer-video-from-codebase/tools/excerpt.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/flow-diagram.mjs +168 -0
- package/skills/explainer-video-from-codebase/tools/flow-plan.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/flows-md.mjs +87 -0
- package/skills/explainer-video-from-codebase/tools/lang/c.mjs +684 -0
- package/skills/explainer-video-from-codebase/tools/lang/go.mjs +673 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-config.mjs +143 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-deps.mjs +62 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-stdlib.mjs +24 -0
- package/skills/explainer-video-from-codebase/tools/lang/python.mjs +609 -0
- package/skills/explainer-video-from-codebase/tools/lang/typescript.mjs +963 -0
- package/skills/explainer-video-from-codebase/tools/lib/decompose.mjs +102 -0
- package/skills/explainer-video-from-codebase/tools/lib/groups.mjs +77 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-coverage.mjs +68 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-v8.mjs +232 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe.mjs +162 -0
- package/skills/explainer-video-from-codebase/tools/lib/redact.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/sandbox.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/lib/test-links.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/toml.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-c.mjs +95 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-go.mjs +101 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-js.mjs +136 -0
- package/skills/explainer-video-from-codebase/tools/lib/tree-sitter.mjs +19 -0
- package/skills/explainer-video-from-codebase/tools/lib/walk.mjs +138 -0
- package/skills/explainer-video-from-codebase/tools/mine.mjs +409 -0
- package/skills/explainer-video-from-codebase/tools/propose.mjs +127 -0
- package/skills/explainer-video-from-codebase/tools/rank.mjs +119 -0
- package/skills/explainer-video-from-codebase/tools/ranking-md.mjs +76 -0
- package/skills/explainer-video-from-codebase/tools/rules/c-handlers.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/celery-taskiq.mjs +48 -0
- package/skills/explainer-video-from-codebase/tools/rules/click-typer.mjs +6 -0
- package/skills/explainer-video-from-codebase/tools/rules/express.mjs +72 -0
- package/skills/explainer-video-from-codebase/tools/rules/fastapi.mjs +99 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-http.mjs +173 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-test.mjs +108 -0
- package/skills/explainer-video-from-codebase/tools/rules/grpc.mjs +57 -0
- package/skills/explainer-video-from-codebase/tools/rules/gtest.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/rules/haystack.mjs +55 -0
- package/skills/explainer-video-from-codebase/tools/rules/jest-vitest.mjs +100 -0
- package/skills/explainer-video-from-codebase/tools/rules/js-http-client.mjs +111 -0
- package/skills/explainer-video-from-codebase/tools/rules/nextjs.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/process-spawn.mjs +49 -0
- package/skills/explainer-video-from-codebase/tools/rules/py-http-client.mjs +26 -0
- package/skills/explainer-video-from-codebase/tools/rules/pytest.mjs +107 -0
- package/skills/explainer-video-from-codebase/tools/rules/react-router.mjs +17 -0
- package/skills/explainer-video-from-codebase/tools/rules/registry.mjs +31 -0
- package/skills/explainer-video-from-codebase/tools/trace/c/fmtrace-sh +14 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-net.cjs +33 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-srcmap.cjs +41 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/fmtrace_plugin.py +87 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/sitecustomize.py +330 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/linux-bwrap.sh +73 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/macos-nonet.sb +41 -0
- package/skills/explainer-video-from-codebase/tools/trace.mjs +577 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-c.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-cpp.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-go.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-javascript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-python.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-typescript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/web-tree-sitter.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/MANIFEST.json +172 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-c.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-cpp.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-go.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-python.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.js +4081 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.wasm +0 -0
- package/skills/explainer-video-from-coursework/ARCHITECTURE.md +59 -0
- package/skills/explainer-video-from-coursework/PIPELINE.md +343 -0
- package/skills/explainer-video-from-coursework/ROADMAP.md +725 -0
- package/skills/explainer-video-from-coursework/SERIES.md +232 -0
- package/skills/explainer-video-from-coursework/SETUP.md +105 -0
- package/skills/explainer-video-from-coursework/SKILL.md +858 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-chart.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-equation.js +10 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-hubspoke.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-loopflow.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-sequence.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-timeline.js +11 -0
- package/skills/explainer-video-from-coursework/src/scene-code.html +127 -0
- package/skills/explainer-video-from-coursework/src/scene-code.js +379 -0
- package/skills/explainer-video-from-coursework/src/scene-core.js +1250 -0
- package/skills/explainer-video-from-coursework/src/scene-diagram.html +50 -0
- package/skills/explainer-video-from-coursework/src/scene-svg.html +50 -0
- package/skills/explainer-video-from-coursework/src/timeline.js +297 -0
- package/skills/explainer-video-from-coursework/src/timing-chart.js +160 -0
- package/skills/explainer-video-from-coursework/tools/align.mjs +419 -0
- package/skills/explainer-video-from-coursework/tools/apply-enrichment.mjs +206 -0
- package/skills/explainer-video-from-coursework/tools/assemble.mjs +223 -0
- package/skills/explainer-video-from-coursework/tools/build-audio.mjs +234 -0
- package/skills/explainer-video-from-coursework/tools/chromium.mjs +73 -0
- package/skills/explainer-video-from-coursework/tools/debug-page.mjs +75 -0
- package/skills/explainer-video-from-coursework/tools/env.mjs +30 -0
- package/skills/explainer-video-from-coursework/tools/frame-dedup.mjs +131 -0
- package/skills/explainer-video-from-coursework/tools/key-source.mjs +151 -0
- package/skills/explainer-video-from-coursework/tools/model.mjs +21 -0
- package/skills/explainer-video-from-coursework/tools/naming.mjs +38 -0
- package/skills/explainer-video-from-coursework/tools/onset-audit.mjs +51 -0
- package/skills/explainer-video-from-coursework/tools/pacing-check.mjs +350 -0
- package/skills/explainer-video-from-coursework/tools/planner.mjs +616 -0
- package/skills/explainer-video-from-coursework/tools/publish.mjs +230 -0
- package/skills/explainer-video-from-coursework/tools/render.mjs +205 -0
- package/skills/explainer-video-from-coursework/tools/resolve-beats.mjs +239 -0
- package/skills/explainer-video-from-coursework/tools/script-check.mjs +174 -0
- package/skills/explainer-video-from-coursework/tools/stamp.mjs +71 -0
- package/skills/explainer-video-from-coursework/tools/timing-report.mjs +299 -0
- package/skills/explainer-video-from-coursework/tools/transcribe-gemini.mjs +96 -0
- package/skills/explainer-video-from-coursework/tools/tts-gemini.mjs +394 -0
- package/skills/explainer-video-from-coursework/tools/verify-audio.mjs +88 -0
- package/skills/explainer-video-from-coursework/tools/workdir.mjs +124 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js +8 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js.LICENSE.txt +16 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-align
|
|
3
|
+
description: Measure per-word and per-line timings for one video's narration, so scene beats can be anchored to the words they illustrate. Stage 4b of explainer-video-from-coursework.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-align — word and line timings
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 4b of 8**, between
|
|
9
|
+
> audio (4) and scene (5).
|
|
10
|
+
>
|
|
11
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
12
|
+
|
|
13
|
+
> **Timing.** Bracket the stage with the stamp pair so the series accrues a per-video audit;
|
|
14
|
+
> `end` must run before the stage counts as done (see *Timing* in the main skill):
|
|
15
|
+
>
|
|
16
|
+
> ```bash
|
|
17
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 4b
|
|
18
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs end --out out/<name> --video <NN> --stage 4b
|
|
19
|
+
> ```
|
|
20
|
+
|
|
21
|
+
**Prerequisites:** `out/<name>/<NN>/narration/<key>.wav` and `timeline.json` — both
|
|
22
|
+
final once the audio stage exits.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node ../explainer-video-from-coursework/tools/align.mjs --script out/<name>/scripts/<NN>-*.json \
|
|
26
|
+
--work out/<name>/<NN>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Why this stage exists
|
|
30
|
+
|
|
31
|
+
The audio stage measures each **segment**. Everything *inside* a segment was left to
|
|
32
|
+
guesswork, and a guess is where sync dies: on the module-07 pilot the "April" reveal
|
|
33
|
+
fired at 6.6s while the words "In April" begin at 12.6s — a 6-second error that every
|
|
34
|
+
check passed, because no check could see the narration's shape.
|
|
35
|
+
|
|
36
|
+
This stage measures the narration's shape. It emits the span of every **line**
|
|
37
|
+
(sentence) of the script, in audio time, so the scene stage can bind a beat to a line
|
|
38
|
+
instead of inventing a number.
|
|
39
|
+
|
|
40
|
+
## What it does
|
|
41
|
+
|
|
42
|
+
Transcribe the **whole narration track in one request** with `gemini-3.5-transcribe`
|
|
43
|
+
(requesting `wordTimestamp`), cut the returned words into segments using the measured
|
|
44
|
+
`timeline.json` boundaries, align each segment's words back onto that segment's script
|
|
45
|
+
words, then compute each line's span from the aligned words.
|
|
46
|
+
|
|
47
|
+
**Why one request, not one per segment.** The model is capped at **100 requests/day
|
|
48
|
+
per project per model**, the Batch API is not supported for it, and the cap is per
|
|
49
|
+
project — so it cannot be spread across keys. Per-segment alignment spent ~30 requests
|
|
50
|
+
on a 13-minute video (≈3 videos/day); a whole-track pass spends **one**.
|
|
51
|
+
|
|
52
|
+
The model allows 30 minutes per request *when word timestamps are enabled*, so a track
|
|
53
|
+
longer than that is cut at **segment boundaries** into multiple passes (never
|
|
54
|
+
mid-sentence). `--per-segment` keeps the old path as a fallback, and is chosen
|
|
55
|
+
automatically when only a few segments are missing. Already-cached segments are never
|
|
56
|
+
re-requested, so a full pass only fills what is absent and cannot overwrite good
|
|
57
|
+
measurements with worse ones.
|
|
58
|
+
|
|
59
|
+
On HTTP 429 the run **exits 3** rather than falling back — a daily-cap failure would
|
|
60
|
+
fail identically for every segment, so cascading would just burn the run.
|
|
61
|
+
|
|
62
|
+
**The alignment step is not optional.** The transcriber is not a faithful echo — it
|
|
63
|
+
rewrites punctuation and applies inverse text normalisation. Measured on real
|
|
64
|
+
narration it returned "In March, it pushes…" (inserted comma), "spends **40** minutes"
|
|
65
|
+
(we wrote *forty*), "**failed**" (we wrote *fail*), and merged two sentences into one.
|
|
66
|
+
So the transcript never decides a boundary: **our punctuation decides boundaries, the
|
|
67
|
+
aligned words supply times.**
|
|
68
|
+
|
|
69
|
+
**Word offsets can contain outliers.** One plan returned "A delta" at 3.7s in a
|
|
70
|
+
sentence whose neighbours were at 12.4s and 14.2s. Offsets are forced monotonic and
|
|
71
|
+
non-overlapping (`clampMonotonic`), which pulls an early outlier to the end of the
|
|
72
|
+
previous word — within a word or two of the truth. Repairs are reported.
|
|
73
|
+
|
|
74
|
+
## Outputs
|
|
75
|
+
|
|
76
|
+
- `narration/<key>.words.json` — per segment: every word with its offsets, the script
|
|
77
|
+
index it maps to, and a cache stamp (wav size+mtime, text hash). Re-runs are free
|
|
78
|
+
until the audio or the script text changes.
|
|
79
|
+
- `lines.json` — per video: for every segment, each line's `{ line, text, start, end }`
|
|
80
|
+
plus `lead` and the window bounds.
|
|
81
|
+
|
|
82
|
+
**Line times are relative to the narration WAV.** A scene renders in window time,
|
|
83
|
+
which starts `lead` seconds earlier, so the conversion is:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
beat time (scene) = line.start + lead
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`lines.json` records `lead` per segment so nothing has to assume 0.6.
|
|
90
|
+
|
|
91
|
+
## Done when
|
|
92
|
+
|
|
93
|
+
`align` exits **0** and prints `✓ N segment(s) · M lines · written`. Any error means a
|
|
94
|
+
line could not be placed in the audio (usually a garbled transcript) — re-run with
|
|
95
|
+
`--force` for that segment, and if it persists treat the segment's narration as the
|
|
96
|
+
thing to fix.
|
|
97
|
+
|
|
98
|
+
Two things to watch in the report:
|
|
99
|
+
|
|
100
|
+
- **`only NN% of script words aligned`** — below `--min-match` (default 0.8) the
|
|
101
|
+
transcript probably diverged; the line spans may still be right, but check them.
|
|
102
|
+
- **`NN word offset(s) came back out of order and were clamped`** — expected
|
|
103
|
+
occasionally; a large number suggests the audio and the script text disagree.
|
|
104
|
+
|
|
105
|
+
## Maintenance
|
|
106
|
+
|
|
107
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
108
|
+
> main `explainer-video-from-coursework` skill's **"Stage 4b — /align"** section must be
|
|
109
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-assemble
|
|
3
|
+
description: Assemble one video from its segments — render each window silent, concatenate, mux the narration once, and verify. Stage 8 of explainer-video-from-coursework.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-assemble — the finished video
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 8 of 8**. The
|
|
9
|
+
> **orchestrator** over the render stage.
|
|
10
|
+
>
|
|
11
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
12
|
+
|
|
13
|
+
> **Timing.** Bracket the stage with the stamp pair so the series accrues a per-video audit;
|
|
14
|
+
> `end` must run before the stage counts as done (see *Timing* in the main skill):
|
|
15
|
+
>
|
|
16
|
+
> ```bash
|
|
17
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 8
|
|
18
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs end --out out/<name> --video <NN> --stage 8
|
|
19
|
+
> ```
|
|
20
|
+
|
|
21
|
+
**Prerequisites:** the script, `out/<name>/<NN>/timeline.json`, `manifest.json`, and
|
|
22
|
+
`narration.wav`; on the machine, `ffmpeg` + headless Chromium (see `SETUP.md`).
|
|
23
|
+
|
|
24
|
+
> A `[setup]` / `Playwright cache not found` failure means ffmpeg or Chromium is absent —
|
|
25
|
+
> relay the matching `SETUP.md` step to the user and re-run.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node ../explainer-video-from-coursework/tools/assemble.mjs --script out/<name>/scripts/<NN>-*.json \
|
|
29
|
+
--work out/<name>/<NN> --name <output> \
|
|
30
|
+
--dest <destination> [--port 9333]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`--port` (default 9333) is forwarded to every per-segment `render.mjs`, so **two videos can
|
|
34
|
+
assemble concurrently** when each is given its own port — one port per concurrent agent.
|
|
35
|
+
The run aborts before segment one if the port is already listening, because a shared CDP
|
|
36
|
+
port does not fail cleanly: the second assembly attaches to the first browser's page and
|
|
37
|
+
encodes the *other* video's scene under its own filename, exit 0. Verified — byte-identical
|
|
38
|
+
frames from the wrong video. This is what lets a per-video agent own stage 8 end to end.
|
|
39
|
+
|
|
40
|
+
Per segment: render its own window through its own host + config, **silent** — parts
|
|
41
|
+
are silent on purpose, because muxing narration onto each would restart the whole track
|
|
42
|
+
at every boundary. Then concatenate (`-c copy`), mux the single measured narration
|
|
43
|
+
track **once**, and verify. `--only <key>` renders a single segment, for a smoke test.
|
|
44
|
+
|
|
45
|
+
Asserts: every segment has a timeline window **and** a `manifest.json` entry · the
|
|
46
|
+
windows tile the timeline with no gaps · the finished audio matches the narration at
|
|
47
|
+
**sample level**.
|
|
48
|
+
|
|
49
|
+
**Done when:** the run exits **0** and prints `[done] …<output>.mp4` with `audio ok`.
|
|
50
|
+
The MP4 is copied into the video's own folder, `--dest/<NN>-<slug>/`, alongside the
|
|
51
|
+
`description.md` and `captions.srt` that publish wrote.
|
|
52
|
+
|
|
53
|
+
<!-- sync:codeflow-publish-render -->
|
|
54
|
+
**Code-flow videos only.** A code-flow video's plan, `plan.json` or `plan.enriched.json`, carries
|
|
55
|
+
`"kind": "codebase"`, and its `forms` are `overview`, `flow` and `core`. A coursework plan has no `kind`; nothing
|
|
56
|
+
in this block applies to its videos. A code-flow video adds these rules at stages 6–8:
|
|
57
|
+
|
|
58
|
+
- **Render.** A code segment renders through `src/scene-code.html?scene=<key>`. Pass `--work out/<name>/<NN>` so
|
|
59
|
+
the host reads that video's `timeline.js`; `assemble` passes it for every segment.
|
|
60
|
+
- **Chapters.** Write each chapter title from that chapter's narration, as for any video.
|
|
61
|
+
- **Upload title.** The `youtubeTitle` names the flow in the viewer's terms, such as what a request does or what
|
|
62
|
+
a command runs. Never use a module path.
|
|
63
|
+
<!-- /sync:codeflow-publish-render -->
|
|
64
|
+
|
|
65
|
+
## Maintenance
|
|
66
|
+
|
|
67
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
68
|
+
> main `explainer-video-from-coursework` skill's **"Stage 8 — /assemble"** section must be
|
|
69
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-audio
|
|
3
|
+
description: Synthesize narration and build the measured timeline for one video. Stage 4 of explainer-video-from-coursework — the one-shot TTS primitive.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-audio — TTS and the measured timeline
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 4 of 8**. This is the
|
|
9
|
+
> **primitive**: it builds audio once and stops. This skill itself never loops.
|
|
10
|
+
>
|
|
11
|
+
> The scene stage may re-invoke it, but **padding is not the expected path any more**.
|
|
12
|
+
> Beat dwells are measured from the narration (stage 4b), so a shortfall now means the
|
|
13
|
+
> scene over-asked — the fix is to widen the beat's line range, not to insert silence.
|
|
14
|
+
> Only if a visual genuinely needs more screen time than its words take is a `pad`
|
|
15
|
+
> justified; see stage 5's over-ask ladder.
|
|
16
|
+
>
|
|
17
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
18
|
+
|
|
19
|
+
> **Timing.** Bracket the stage with the stamp pair so the series accrues a per-video audit;
|
|
20
|
+
> `end` must run before the stage counts as done (see *Timing* in the main skill):
|
|
21
|
+
>
|
|
22
|
+
> ```bash
|
|
23
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 4
|
|
24
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs end --out out/<name> --video <NN> --stage 4
|
|
25
|
+
> ```
|
|
26
|
+
|
|
27
|
+
**Prerequisites:** `out/<name>/scripts/<NN>-*.json` from the script stage; on the machine, the Gemini key in the OS secret store (see `SETUP.md`).
|
|
28
|
+
|
|
29
|
+
> A `No API key found` failure means the key isn't in the OS secret store — relay the
|
|
30
|
+
> matching `SETUP.md` step to the user and re-run.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node ../explainer-video-from-coursework/tools/build-audio.mjs --script out/<name>/scripts/<NN>-*.json \
|
|
34
|
+
--out out/<name>/<NN> --voice Aoede
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Writes `narration/<seg>.wav`, `narration.wav`, `timeline.json`, and `timeline.js` into
|
|
38
|
+
the per-video work dir. This is **the only network stage** — it spends the Gemini TTS
|
|
39
|
+
key.
|
|
40
|
+
|
|
41
|
+
Next: **stage 4b (`/align`)** measures the words *inside* each of those windows —
|
|
42
|
+
`node ../explainer-video-from-coursework/tools/align.mjs --script out/<name>/scripts/<NN>-*.json --work out/<name>/<NN>`.
|
|
43
|
+
Without it, scene beats have nothing measured to anchor to.
|
|
44
|
+
|
|
45
|
+
**Done when:** the run exits **0** and prints one window per segment, ending in
|
|
46
|
+
`[out] …/timeline.json`.
|
|
47
|
+
|
|
48
|
+
- **Durations are measured, not estimated** — each WAV is measured with `ffprobe`; the
|
|
49
|
+
plan's `Min` was a guardrail, this is the real number later stages bind to.
|
|
50
|
+
- **The cache is text-keyed** — `narration/<key>.src` stores the exact spoken text, so a
|
|
51
|
+
re-run after a wording tweak costs 0 calls. It does **not** cover `--voice`, `style`,
|
|
52
|
+
or `--model`: change one and the old audio is silently reused. Pass `--force` to
|
|
53
|
+
re-synthesize.
|
|
54
|
+
|
|
55
|
+
## Maintenance
|
|
56
|
+
|
|
57
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
58
|
+
> main `explainer-video-from-coursework` skill's **"Stage 4 — /audio"** section must be
|
|
59
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-enrich
|
|
3
|
+
description: Retitle and curate the videos in an explainer-video plan — write enrichment.json and merge it. Stage 2 of explainer-video-from-coursework; run it alone to retitle an existing plan.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-enrich — retitle and curate
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 2 of 8**, the second
|
|
9
|
+
> once-per-series stage. Self-contained: it operates on an existing `plan.json`. For a
|
|
10
|
+
> full run, use the main skill instead.
|
|
11
|
+
>
|
|
12
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
13
|
+
|
|
14
|
+
**Prerequisites:** `out/<name>/plan.json` from the plan stage.
|
|
15
|
+
|
|
16
|
+
The agent writes `out/<name>/enrichment.json` — one entry per video — then validates
|
|
17
|
+
and merges:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
node ../explainer-video-from-coursework/tools/apply-enrichment.mjs --plan out/<name>/plan.json \
|
|
21
|
+
--enrichment out/<name>/enrichment.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Writes `plan.enriched.json` and `plan.enriched.md`.
|
|
25
|
+
|
|
26
|
+
**Done when:** the run exits **0** (no validation problems), `plan.enriched.md` shows
|
|
27
|
+
the final video list, **and that full list has been shown to the user** — every
|
|
28
|
+
surviving video plus the removals with their reasons. Never summarise it into a sample:
|
|
29
|
+
`apply-enrichment` prints the whole table, and the whole table is what gets reviewed.
|
|
30
|
+
|
|
31
|
+
**Title contract.** The planner's titles are placeholders. Replace each with a real
|
|
32
|
+
title: descriptive, **unique**, written for a human scrolling YouTube — never a
|
|
33
|
+
directory slug. Title from the content, not the name: read the source at the video's
|
|
34
|
+
`sectionSpans` before naming it. The tool rejects empty titles, `Part N` placeholders,
|
|
35
|
+
ellipses, slugs, and duplicates.
|
|
36
|
+
|
|
37
|
+
**Curation.** The planner cannot tell an overview page from real content, so it plans
|
|
38
|
+
both — the agent decides. An entry may carry `skip: true` or `mergeInto: <index>`, both
|
|
39
|
+
requiring a `why`. Every skip/merge lands in `plan.removed` with its reason and
|
|
40
|
+
sections, so coverage stays auditable.
|
|
41
|
+
|
|
42
|
+
## Maintenance
|
|
43
|
+
|
|
44
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
45
|
+
> main `explainer-video-from-coursework` skill's **"Stage 2 — /enrich"** section must be
|
|
46
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-plan
|
|
3
|
+
description: Decompose a coursework notes directory into an explainer-video plan and prove no content-bearing section is dropped. Stage 1 of explainer-video-from-coursework; run it alone to (re)plan a directory.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-plan — decompose and prove coverage
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 1 of 8**, the first of
|
|
9
|
+
> the two once-per-series stages. This skill is self-contained: it plans a directory
|
|
10
|
+
> from scratch. For a full run, use the main skill instead.
|
|
11
|
+
>
|
|
12
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, defaulting beside your notes — so `out/` scratch lands where Stage 0 said it would and inside a repo's ignore rules.
|
|
13
|
+
|
|
14
|
+
**Prerequisites:** a directory of markdown notes (`<notesDir>`).
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node ../explainer-video-from-coursework/tools/planner.mjs <notesDir> --out out/<name> --max-min 25 \
|
|
18
|
+
[--exclude <notesDir>/videos]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Writes `out/<name>/plan.json` and `out/<name>/plan.md`. No network, no key, safe to
|
|
22
|
+
re-run. Read `plan.md`, not `plan.json`.
|
|
23
|
+
|
|
24
|
+
> **Exclude a destination that lives inside the source.** `<notesDir>/videos/` is the
|
|
25
|
+
> usual destination, and the planner walks all of `<notesDir>`: a published
|
|
26
|
+
> `description.md` comes back in as a note and turns into a phantom video. Pass
|
|
27
|
+
> `--exclude` for it (repeatable, one per old output folder). The planner warns when
|
|
28
|
+
> it finds `description.md` inputs, because that filename is this pipeline's own
|
|
29
|
+
> output — but the flag is the fix.
|
|
30
|
+
|
|
31
|
+
> **`--max-min` is part of the plan.** Video indices are keyed by the split, so
|
|
32
|
+
> re-running with a different cap reshuffles them and invalidates any enrichment
|
|
33
|
+
> already written. The planner refuses (exit 4) to overwrite an enriched plan whose
|
|
34
|
+
> model differs; pass `--force` only when you intend to re-enrich from scratch.
|
|
35
|
+
|
|
36
|
+
**Done when:** the run exits **0**, `plan.md`'s coverage line reads
|
|
37
|
+
`N assigned · M excluded as navigation · 0 orphaned`, **and every planned video has
|
|
38
|
+
been shown to the user** (index, title, length — all of them, no sample and no
|
|
39
|
+
"…and N more"). A non-zero exit, or any orphan, means the plan is incomplete — stop and
|
|
40
|
+
fix it. Never enrich an incomplete plan.
|
|
41
|
+
|
|
42
|
+
Two facts that shape how the plan is read:
|
|
43
|
+
|
|
44
|
+
- **The coverage proof is the point.** The planner proves every content-bearing section
|
|
45
|
+
is assigned to a video or is a navigation heading. `plan.json` also carries
|
|
46
|
+
`sectionSpans` (each section's `src`, `title`, and 1-based `line`/`endLine`) — the raw
|
|
47
|
+
material for the next stage.
|
|
48
|
+
- **The `Min` column is a guardrail, not a duration** — a planning estimate
|
|
49
|
+
(`wpm 153 × expansion 1.10`), not measured audio. Use it to judge boundaries.
|
|
50
|
+
|
|
51
|
+
Only `.md` files are parsed; non-markdown content is invisible to the planner.
|
|
52
|
+
|
|
53
|
+
## Maintenance
|
|
54
|
+
|
|
55
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
56
|
+
> main `explainer-video-from-coursework` skill's **"Stage 1 — /plan"** section must be
|
|
57
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-publish
|
|
3
|
+
description: Generate the upload title, YouTube chapters and English captions for one finished video. Stage 6 of explainer-video-from-coursework.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-publish — title, chapters and captions
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 6 of 8**.
|
|
9
|
+
>
|
|
10
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
11
|
+
|
|
12
|
+
> **Timing.** Bracket the stage with the stamp pair so the series accrues a per-video audit;
|
|
13
|
+
> `end` must run before the stage counts as done (see *Timing* in the main skill):
|
|
14
|
+
>
|
|
15
|
+
> ```bash
|
|
16
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 6
|
|
17
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs end --out out/<name> --video <NN> --stage 6
|
|
18
|
+
> ```
|
|
19
|
+
|
|
20
|
+
**Prerequisites:** `out/<name>/<NN>/timeline.json` and `narration/<key>.src` — both
|
|
21
|
+
final once the scene stage exits.
|
|
22
|
+
|
|
23
|
+
The agent writes `out/<name>/<NN>/publish.json` — a `youtubeTitle`, a chapter list
|
|
24
|
+
(`segments` keys + `title`) and a `blurb`. The tool resolves timecodes, validates, and
|
|
25
|
+
emits **three files** into the video's own folder, `--dest/<NN>-<slug>/`: `title.txt`
|
|
26
|
+
(one line, what YouTube shows), `description.md` (the blurb, then the chapter block,
|
|
27
|
+
paste-ready plain text) and `captions.srt`. The folder name is derived from the script's
|
|
28
|
+
`videoIndex` + title — do not pick it by hand.
|
|
29
|
+
|
|
30
|
+
- **Title** — `title.txt` is the upload title, and it is also the file `upload_yt_video`
|
|
31
|
+
reads out of that folder, so a missing one is an incomplete upload. It is *not* the
|
|
32
|
+
same thing as the video's `title`: that one is structural (set at stage 2, it names the
|
|
33
|
+
folder and must not move); this one is written here because only now does the video
|
|
34
|
+
exist to be described honestly. Keep it a real search phrase — the subject someone
|
|
35
|
+
would type — and never promise what the video does not deliver: retention past the
|
|
36
|
+
first seconds is measured, and a hook the video does not pay off is the one thing that
|
|
37
|
+
gets actively suppressed.
|
|
38
|
+
|
|
39
|
+
- **Chapters** — `M:SS Title`, start-time only, one per segment by default; grouping is
|
|
40
|
+
two keys in one list, and a chapter's time is the first segment's window start.
|
|
41
|
+
Titles are authored from the narration — never the scene `stageNames` (uppercase,
|
|
42
|
+
one missing, near-duplicate pairs).
|
|
43
|
+
- **Captions** — one cue per segment, text from `narration/<key>.src` (the exact text
|
|
44
|
+
the WAV was built from — the audio is the truth, not `script.json`).
|
|
45
|
+
- **Hard checks** — first timecode `0:00` · ≥3 chapters · each ≥10s · ascending · every
|
|
46
|
+
segment key resolves · titles ≤40 chars, not duplicated/near-duplicated · cues
|
|
47
|
+
ascend and don't overlap · `youtubeTitle` ≤100 chars, one line.
|
|
48
|
+
- **Soft checks** (warnings, not failures) — title past ~70 chars (mobile truncation) ·
|
|
49
|
+
all-uppercase · stacked `!!`/`??` · a near-duplicate of a sibling video's title, which
|
|
50
|
+
buries a series' own parts. The tool can police the shape of a title, never its
|
|
51
|
+
appeal; that judgement stays yours.
|
|
52
|
+
|
|
53
|
+
**Chapters need channel eligibility.** They are a YouTube advanced feature: a channel that
|
|
54
|
+
has not qualified for advanced-feature eligibility renders **no** chapters, however valid
|
|
55
|
+
the list — its timestamps still work as jump-to-section links, but the segmented progress
|
|
56
|
+
bar and Google key moments do not appear. Nothing in `description.md` affects this, so
|
|
57
|
+
absent chapters are not a formatting fault: `0:00` (one digit), a `-` after the timecode, a
|
|
58
|
+
blurb above the block and a 2:39 runtime were each verified live on eligible channels, and
|
|
59
|
+
the hard checks above already mirror YouTube's rules.
|
|
60
|
+
|
|
61
|
+
**Done when:** `publish` exits 0 and both files land in `--dest/<NN>-<slug>/` beside the MP4.
|
|
62
|
+
|
|
63
|
+
<!-- sync:codeflow-publish-render -->
|
|
64
|
+
**Code-flow videos only.** A code-flow video's plan, `plan.json` or `plan.enriched.json`, carries
|
|
65
|
+
`"kind": "codebase"`, and its `forms` are `overview`, `flow` and `core`. A coursework plan has no `kind`; nothing
|
|
66
|
+
in this block applies to its videos. A code-flow video adds these rules at stages 6–8:
|
|
67
|
+
|
|
68
|
+
- **Render.** A code segment renders through `src/scene-code.html?scene=<key>`. Pass `--work out/<name>/<NN>` so
|
|
69
|
+
the host reads that video's `timeline.js`; `assemble` passes it for every segment.
|
|
70
|
+
- **Chapters.** Write each chapter title from that chapter's narration, as for any video.
|
|
71
|
+
- **Upload title.** The `youtubeTitle` names the flow in the viewer's terms, such as what a request does or what
|
|
72
|
+
a command runs. Never use a module path.
|
|
73
|
+
<!-- /sync:codeflow-publish-render -->
|
|
74
|
+
|
|
75
|
+
## Maintenance
|
|
76
|
+
|
|
77
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
78
|
+
> main `explainer-video-from-coursework` skill's **"Stage 6 — /publish"** section must be
|
|
79
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-render
|
|
3
|
+
description: Render one scene config over one time window to an MP4 (or a handful of QA stills). Stage 7 of explainer-video-from-coursework — the render primitive.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-render — frames to MP4
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 7 of 8**. The render
|
|
9
|
+
> **primitive**; the assemble stage drives it per segment. It can also run alone for QA
|
|
10
|
+
> stills during scene authoring.
|
|
11
|
+
>
|
|
12
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
13
|
+
|
|
14
|
+
> **Timing.** Bracket the stage with the stamp pair so the series accrues a per-video audit;
|
|
15
|
+
> `end` must run before the stage counts as done (see *Timing* in the main skill):
|
|
16
|
+
>
|
|
17
|
+
> ```bash
|
|
18
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 7
|
|
19
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs end --out out/<name> --video <NN> --stage 7
|
|
20
|
+
> ```
|
|
21
|
+
|
|
22
|
+
**Prerequisites:** a scene config and, for windowed renders, `out/<name>/<NN>/timeline.json`; on the machine, `ffmpeg` + headless Chromium (see `SETUP.md`).
|
|
23
|
+
|
|
24
|
+
> A `[node]` / `[setup]` / `Playwright cache not found` failure means a prerequisite is
|
|
25
|
+
> absent — relay the matching `SETUP.md` step to the user and re-run.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node ../explainer-video-from-coursework/tools/render.mjs --scene "src/scene-diagram.html?scene=<name>" \
|
|
29
|
+
--range <t0>,<t1> --outdir out/<name>/<NN>/parts --name part-<key>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Renders one scene config over one window — headless Chromium via CDP → PNG sequence →
|
|
33
|
+
ffmpeg H.264. It is not a whole-video renderer: `--scene` takes a single
|
|
34
|
+
`host?scene=<config>`, so a multi-scene video is rendered window by window. A window
|
|
35
|
+
rendered with no `--narration` comes out silent, which is what assembly wants.
|
|
36
|
+
|
|
37
|
+
**Each distinct frame is captured once** (no flag; it is the only capture path). Because
|
|
38
|
+
`renderAt(t)` is pure, consecutive frames are often byte-identical — 82–99% measured — and
|
|
39
|
+
a screenshot costs ~63 ms, so the loop hashes the serialized `#stage` SVG after each
|
|
40
|
+
`renderAt(t)` and hardlinks the previous PNG when the markup is unchanged. Guards: scenes
|
|
41
|
+
declaring CSS/web animations take the plain capture path from frame 0, and every 30 frames
|
|
42
|
+
a real screenshot is diffed against the frame written (on disagreement it re-captures,
|
|
43
|
+
rewinds since the last audit, and falls back to plain capture). Verified at SSIM 1.000000
|
|
44
|
+
against every-frame renders on two full videos, 3.3×–4.3× faster. Read the closing
|
|
45
|
+
`[dedup]` line — screenshots vs total, reuse %, audit mismatches.
|
|
46
|
+
|
|
47
|
+
`--shots 6,20,34` renders QA stills instead of a full sequence (stills bypass the dedup
|
|
48
|
+
loop — each is a different instant); `--keep-frames` retains the PNG sequence.
|
|
49
|
+
|
|
50
|
+
**Done when:** the run exits **0** and prints `[done] …<name>.mp4`.
|
|
51
|
+
|
|
52
|
+
<!-- sync:codeflow-publish-render -->
|
|
53
|
+
**Code-flow videos only.** A code-flow video's plan, `plan.json` or `plan.enriched.json`, carries
|
|
54
|
+
`"kind": "codebase"`, and its `forms` are `overview`, `flow` and `core`. A coursework plan has no `kind`; nothing
|
|
55
|
+
in this block applies to its videos. A code-flow video adds these rules at stages 6–8:
|
|
56
|
+
|
|
57
|
+
- **Render.** A code segment renders through `src/scene-code.html?scene=<key>`. Pass `--work out/<name>/<NN>` so
|
|
58
|
+
the host reads that video's `timeline.js`; `assemble` passes it for every segment.
|
|
59
|
+
- **Chapters.** Write each chapter title from that chapter's narration, as for any video.
|
|
60
|
+
- **Upload title.** The `youtubeTitle` names the flow in the viewer's terms, such as what a request does or what
|
|
61
|
+
a command runs. Never use a module path.
|
|
62
|
+
<!-- /sync:codeflow-publish-render -->
|
|
63
|
+
|
|
64
|
+
## Maintenance
|
|
65
|
+
|
|
66
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
67
|
+
> main `explainer-video-from-coursework` skill's **"Stage 7 — /render"** section must be
|
|
68
|
+
> reconciled to match, and name that section in your reply.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: craft-video-scene
|
|
3
|
+
description: Author and iterate the animated visuals for one video's segments. Stage 5 of explainer-video-from-coursework; beats anchor to measured narration lines, not hand-written seconds.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# craft-video-scene — author the visuals
|
|
7
|
+
|
|
8
|
+
> Part of the `explainer-video-from-coursework` pipeline — **stage 5 of 8**. This is the
|
|
9
|
+
> **orchestrator** over the audio stage: it loops, and it re-invokes `build-audio` when a
|
|
10
|
+
> beat cannot fit. The audio stage itself is one-shot and never loops back.
|
|
11
|
+
>
|
|
12
|
+
> **A shortfall is no longer routine.** Beat dwells default to the measured span of
|
|
13
|
+
> the lines they cover, so `at + min` is inside the window by construction and a
|
|
14
|
+
> line-anchored scene should report none. If `pacing-check` does report one, the scene
|
|
15
|
+
> **over-asked**: widen the beat's `lines` range first, redesign the beat second, and
|
|
16
|
+
> `pad` the segment only as a last resort.
|
|
17
|
+
>
|
|
18
|
+
> If you do pad, line times survive it (a pad widens the window, never the segment
|
|
19
|
+
> WAV) — but *rewriting the narration* invalidates every line time for that segment, so
|
|
20
|
+
> re-run `/audio` → `/align` → `/resolve-beats`. `resolve-beats --script …` enforces
|
|
21
|
+
> that by comparing the text hash `align` recorded.
|
|
22
|
+
>
|
|
23
|
+
> The pipeline's tools and scene hosts live in the sibling `explainer-video-from-coursework` skill: run them as `node ../explainer-video-from-coursework/tools/<tool>.mjs`, resolved against this skill's folder. Run from the **scratch root** — the directory holding `out/`, which Stage 0 fixes (default: beside your notes) — so every `out/<name>/…` path in this stage resolves.
|
|
24
|
+
|
|
25
|
+
> **Timing.** Bracket the stage with the stamp pair so the series accrues a per-video audit;
|
|
26
|
+
> `end` must run before the stage counts as done (see *Timing* in the main skill):
|
|
27
|
+
>
|
|
28
|
+
> ```bash
|
|
29
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 5
|
|
30
|
+
> node ../explainer-video-from-coursework/tools/stamp.mjs end --out out/<name> --video <NN> --stage 5
|
|
31
|
+
> ```
|
|
32
|
+
|
|
33
|
+
**Prerequisites:** `out/<name>/<NN>/timeline.json` (from the audio stage) and the
|
|
34
|
+
script for the video; headless Chromium for the `--shots` look (see `SETUP.md`).
|
|
35
|
+
|
|
36
|
+
> A `Playwright cache not found` failure on the `--shots` render means Chromium is absent —
|
|
37
|
+
> relay the `SETUP.md` step to the user and re-run.
|
|
38
|
+
|
|
39
|
+
Each segment is a **beat**: narration plus the visual that illustrates it. Author the
|
|
40
|
+
visual as **visual beats** — steps, each with a minimum dwell and an alignment to the
|
|
41
|
+
narration — never as raw seconds.
|
|
42
|
+
|
|
43
|
+
**Hosts, per segment** (`out/<name>/<NN>/manifest.json`, `key` → `{host, scene}`):
|
|
44
|
+
|
|
45
|
+
- **`svg`** (`src/scene-svg.html`) — the open lane; expose `renderAt(t, svg, timeline)`
|
|
46
|
+
plus `SCENE_BEATS`.
|
|
47
|
+
- **`code`** (`src/scene-code.html`) — code walkthroughs.
|
|
48
|
+
- **`diagram`** (`src/scene-diagram.html`) — declarative `nodes`/`edges`/`reveal`/
|
|
49
|
+
`banners` configs built on `scene-core.js`.
|
|
50
|
+
|
|
51
|
+
**The beat anchors to a line, never to a second** — declare per segment:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
window.SCENE_BEATS = { beats: [
|
|
55
|
+
{ id: 'march', line: 1 }, // index into lines.json for this segment
|
|
56
|
+
{ id: 'caption', lines: [3, 4], hold: true }, // holds across those lines
|
|
57
|
+
]};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`line` indexes that segment's lines in `lines.json` (stage 4b). `lines: [from, to]`
|
|
61
|
+
claims a range, and `hold: true` lets a final beat run to the cut.
|
|
62
|
+
|
|
63
|
+
**Do not write `at`.** A hand-written second cannot be validated and will not be
|
|
64
|
+
caught: the module-07 pilot shipped `at: 6.6` for a phrase that begins at 12.6s and
|
|
65
|
+
passed every check. `resolve-beats` rejects a beat with `at` but no `line`, and
|
|
66
|
+
rejects a baked `at` that disagrees with the measured onset by more than 60 ms.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
node ../explainer-video-from-coursework/tools/resolve-beats.mjs --work out/<name>/<NN>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Run this before `pacing-check`: it resolves each `line` to seconds, writes `beats.json`,
|
|
73
|
+
and fails when a line does not exist, beats run out of order, or a narration line has
|
|
74
|
+
no beat (narration playing with nothing changing on screen).
|
|
75
|
+
|
|
76
|
+
A beat's dwell should be **how long its lines are spoken**. Only add `min` when the
|
|
77
|
+
visual genuinely needs longer than its line — and then reach for a wider `lines` range
|
|
78
|
+
before reaching for `pad` (stage 5's over-ask ladder).
|
|
79
|
+
|
|
80
|
+
### The loop, expressed as tool invocations
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
write / edit a scene
|
|
84
|
+
→ node ../explainer-video-from-coursework/tools/pacing-check.mjs --scene out/<name>/<NN>/scenes/<name>.js \
|
|
85
|
+
--timeline out/<name>/<NN>/timeline.json --segment <key>
|
|
86
|
+
(a shortfall means the scene over-asked — widen the beat's `lines` range,
|
|
87
|
+
or redesign the beat. Only then consider a pad.)
|
|
88
|
+
→ last resort, if the visual really needs more screen time than its words take:
|
|
89
|
+
set `pad` on the script segment and `hold: true` on the beat, then:
|
|
90
|
+
node ../explainer-video-from-coursework/tools/build-audio.mjs --script out/<name>/scripts/<NN>-*.json \
|
|
91
|
+
--out out/<name>/<NN> --voice Aoede
|
|
92
|
+
(free — TTS is text-cached — and it re-tiles the timeline wider)
|
|
93
|
+
line times stay valid: a pad widens the window, never the segment WAV
|
|
94
|
+
→ if you REWROTE the narration instead (extending it):
|
|
95
|
+
re-run build-audio, then align.mjs, then resolve-beats.mjs — a new WAV
|
|
96
|
+
invalidates every line time. resolve-beats --script … catches the omission.
|
|
97
|
+
→ node ../explainer-video-from-coursework/tools/render.mjs --shots 6,20,34 --work out/<name>/<NN> \
|
|
98
|
+
--scene "src/scene-diagram.html?scene=<name>"
|
|
99
|
+
→ LOOK at the stills ← the step nothing automates
|
|
100
|
+
→ fix → repeat
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The checkers catch timing and structure, never a visual fault — a missing chip, an
|
|
104
|
+
orphaned connector, a wrong colour. Only looking at a rendered frame catches those.
|
|
105
|
+
|
|
106
|
+
**Done when:** `pacing-check` exits 0 **and** every still has been eyeballed.
|
|
107
|
+
|
|
108
|
+
## Maintenance
|
|
109
|
+
|
|
110
|
+
> If you modify this file, or the user asks to change this stage, tell the user that the
|
|
111
|
+
> main `explainer-video-from-coursework` skill's **"Stage 5 — /scene"** section must be
|
|
112
|
+
> reconciled to match, and name that section in your reply.
|