recordable 0.4.0 → 0.5.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.
Files changed (47) hide show
  1. package/README.md +184 -30
  2. package/dist/actions.d.ts +7 -0
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +5 -0
  5. package/dist/actions.js.map +1 -1
  6. package/dist/audio/track.d.ts +10 -0
  7. package/dist/audio/track.d.ts.map +1 -1
  8. package/dist/audio/track.js +19 -0
  9. package/dist/audio/track.js.map +1 -1
  10. package/dist/compose/boundaries.d.ts +13 -0
  11. package/dist/compose/boundaries.d.ts.map +1 -0
  12. package/dist/compose/boundaries.js +49 -0
  13. package/dist/compose/boundaries.js.map +1 -0
  14. package/dist/compose/mix.d.ts +3 -2
  15. package/dist/compose/mix.d.ts.map +1 -1
  16. package/dist/compose/mix.js +8 -3
  17. package/dist/compose/mix.js.map +1 -1
  18. package/dist/compose/recordable.d.ts +24 -2
  19. package/dist/compose/recordable.d.ts.map +1 -1
  20. package/dist/compose/recordable.js +56 -11
  21. package/dist/compose/recordable.js.map +1 -1
  22. package/dist/compose/session.d.ts +19 -8
  23. package/dist/compose/session.d.ts.map +1 -1
  24. package/dist/compose/session.js +99 -28
  25. package/dist/compose/session.js.map +1 -1
  26. package/dist/ffmpeg.d.ts.map +1 -1
  27. package/dist/ffmpeg.js +8 -3
  28. package/dist/ffmpeg.js.map +1 -1
  29. package/dist/formats/json.d.ts +2 -1
  30. package/dist/formats/json.d.ts.map +1 -1
  31. package/dist/formats/json.js.map +1 -1
  32. package/dist/fs.d.ts +16 -0
  33. package/dist/fs.d.ts.map +1 -1
  34. package/dist/fs.js +33 -4
  35. package/dist/fs.js.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/result.d.ts +30 -0
  39. package/dist/result.d.ts.map +1 -0
  40. package/dist/result.js +8 -0
  41. package/dist/result.js.map +1 -0
  42. package/dist/video/recorder.d.ts +26 -3
  43. package/dist/video/recorder.d.ts.map +1 -1
  44. package/dist/video/recorder.js +43 -19
  45. package/dist/video/recorder.js.map +1 -1
  46. package/package.json +19 -6
  47. package/recordable.schema.json +78 -0
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # recordable
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/recordable.svg)](https://www.npmjs.com/package/recordable)
4
+
3
5
  Programmatic, repeatable browser screen recording. Describe a session as a fluent
4
6
  chain of actions — `visit`, `click`, `type`, `zoom`, `scroll` — and `recordable`
5
7
  drives a real [Puppeteer](https://pptr.dev/) browser and captures a clean MP4,
@@ -22,12 +24,14 @@ await new Recordable({ typingSpeed: 120 })
22
24
  .scroll("bottom")
23
25
  .resetZoom()
24
26
  .wait(1500)
25
- .run(); // finalises automatically — no start()/stop()
27
+ .run(); // finalises automatically — bookends are optional
26
28
  ```
27
29
 
28
- Recording is **on by default** and finalises when `.run()` ends there's no
29
- `start()`/`stop()`. Use `pause()` / `resume()` to carve out anything you don't
30
- want on camera; every captured segment is stitched into one seamless MP4.
30
+ Recording is **on by default** and finalises when `.run()` ends. Use `pause()` /
31
+ `resume()` to carve out anything you don't want on camera; every captured segment
32
+ is stitched into one seamless MP4. Need explicit bookends or several output files?
33
+ `start()` / `end()` / `split()` move the file boundaries (see
34
+ [Multiple output files](#multiple-output-files-start--end--split)).
31
35
 
32
36
  ## Features
33
37
 
@@ -45,9 +49,10 @@ want on camera; every captured segment is stitched into one seamless MP4.
45
49
  - **Manual steps / logins** — `resumeOnPlay()` waits for an in-page ▶ Play button
46
50
  (see below), so you can sign in by hand before recording.
47
51
  - **Auto-scroll** to bring elements into view before interacting.
48
- - **Declarative JSON scripts + CLI** — author a recording as JSON (with a published
49
- schema for editor autocomplete) and run it with `npx recordable demo.json`, no
50
- install or TypeScript required.
52
+ - **Declarative scripts (JSON _or_ Markdown) + CLI** — author a recording as data,
53
+ not code, and run it with `npx recordable demo.json` / `demo.md`, no install or
54
+ TypeScript required. JSON ships a published schema for editor autocomplete;
55
+ Markdown adds prose narration for voiceover.
51
56
 
52
57
  ## Install
53
58
 
@@ -57,7 +62,7 @@ npm install recordable
57
62
 
58
63
  Frames are captured via the Chrome DevTools Protocol and encoded with **FFmpeg** —
59
64
  there's no external screen-recorder dependency. The ffmpeg binary ships via
60
- [`@ffmpeg-installer/ffmpeg`](https://www.npmjs.com/package/@ffmpeg-installer/ffmpeg),
65
+ [`ffmpeg-static`](https://www.npmjs.com/package/ffmpeg-static),
61
66
  so there's nothing else to install (a system `ffmpeg` on your `PATH` is used as a
62
67
  fallback).
63
68
 
@@ -93,27 +98,132 @@ local copy) and your editor gives you autocomplete, required-key checking, and
93
98
  typo catching for every action — no TypeScript needed. The schema is published as
94
99
  `recordable.schema.json`.
95
100
 
96
- Run a script from code with `runScript` (or `fromJSON` to build without running):
101
+ Run a JSON script from code by handing it to a `Recordable` a parsed object or
102
+ the raw file string both work:
103
+
104
+ ```ts
105
+ import { readFileSync } from "node:fs";
106
+ import { Recordable } from "recordable";
107
+
108
+ const script = readFileSync("./demo.json", "utf8");
109
+ await new Recordable({ baseDir: "." }).fromJSON(script).run();
110
+ ```
111
+
112
+ `baseDir` is the script's folder — `recordable` resolves relative `visit` URLs
113
+ and a relative `outputDir` against it. (Standalone `runScript` / `fromJSON`
114
+ helpers are also exported if you prefer a single call.)
115
+
116
+ ## Declarative scripts (Markdown)
117
+
118
+ Markdown is the richest authoring surface — the **same actions as JSON**, written
119
+ as backtick method-call spans, with optional narration prose woven around them for
120
+ voiceover. YAML frontmatter carries the [config](#configuration); an optional
121
+ `voiceover` block opts into narration audio (`voiceover: true` reads provider /
122
+ voice from the environment, or pass an object to set them inline).
123
+
124
+ Two flavours, mixable in one document:
125
+
126
+ **1. A fenced action list** — one call per line, no prose. The closest Markdown
127
+ gets to JSON; compiles to the exact same actions:
128
+
129
+ ````md
130
+ ---
131
+ viewport: { width: 1280, height: 800 }
132
+ ---
133
+
134
+ ```
135
+ pause()
136
+ visit("./index.html")
137
+ resume()
138
+ zoom(1.4, { origin: "#email" })
139
+ type("#email", "hello@example.com")
140
+ click("text:Sign up", { waitForNav: true })
141
+ waitFor("text:Thanks", { state: "visible" })
142
+ resetZoom()
143
+ ```
144
+ ````
145
+
146
+ **2. Inline markers in prose** — drop call spans into narration; each fires at its
147
+ position in the spoken line. With `voiceover` on, the prose is read aloud and waits
148
+ are timed to the narration:
149
+
150
+ ```md
151
+ ---
152
+ typingSpeed: 16
153
+ voiceover: true
154
+ ---
155
+
156
+ `visit("./signin.html")` Welcome — first we sign in with our work account
157
+ `type("#email", "maya@example.com")` then our password
158
+ `type("#password", "•••••")` `click("#signInBtn", { waitForNav: true })` — and
159
+ we're straight into the dashboard.
160
+ ```
161
+
162
+ Each backtick span holds exactly one call; its arguments are the method's
163
+ arguments, identical to the chainable API and the JSON `action` keys. Whole-line
164
+ `//` comments are stripped before parsing, so toggle-comment in your editor is
165
+ safe. Run a Markdown file through the [CLI](#cli) (`npx recordable demo.md`) or
166
+ from code:
97
167
 
98
168
  ```ts
99
- import { runScript } from "recordable";
100
- import demo from "./demo.json" with { type: "json" };
169
+ import { readFileSync } from "node:fs";
170
+ import { Recordable } from "recordable";
101
171
 
102
- await runScript(demo);
172
+ const md = readFileSync("./demo.md", "utf8");
173
+ await new Recordable({ baseDir: "." }).fromMarkdown(md).run();
103
174
  ```
104
175
 
105
- ### CLI
176
+ ## Voiceover
106
177
 
107
- Or run a JSON file directly **no install required** via `npx`:
178
+ A Markdown script can narrate itself. The prose around your inline markers becomes
179
+ spoken audio (text-to-speech), and the markers are **timed to the narration** — each
180
+ action fires at its position in the spoken line, so the demo and the voice stay in
181
+ sync without hand-tuned `wait`s.
182
+
183
+ Opt in from frontmatter. With credentials in the environment, `voiceover: true` is
184
+ all a document needs; spell out a `voiceover` object to set provider / voice / model
185
+ inline (it overrides the environment):
186
+
187
+ ```yaml
188
+ voiceover: true
189
+ ```
190
+
191
+ ```yaml
192
+ voiceover:
193
+ provider: elevenlabs # or `mock` for silent, offline audio
194
+ voiceId: EXAVITQu4vr4xnSDxMaL
195
+ modelId: eleven_multilingual_v2
196
+ ```
197
+
198
+ **Credentials & defaults** come from a `.env` loaded automatically from **beside the
199
+ document** (copy [`.env.example`](.env.example)):
200
+
201
+ ```sh
202
+ ELEVENLABS_API_KEY=... # required for real synthesis
203
+ RECORDABLE_TTS_PROVIDER=elevenlabs # or `mock` for silent, offline audio
204
+ RECORDABLE_VOICE_ID=... # default voice when frontmatter omits it
205
+ RECORDABLE_MODEL_ID=eleven_multilingual_v2
206
+ ```
207
+
208
+ Generated audio is written to the `assetsDir` (default `assets/`, beside the output)
209
+ and cached, so re-running an unchanged script doesn't re-synthesize. Validate a
210
+ voiceover script without hitting the TTS API — or a browser — with `recordable
211
+ demo.md --check`. For a music bed or a hand-recorded narration file, drop it straight
212
+ onto the timeline with `audio(path, opts?)` (see the [API](#recording)).
213
+
214
+ ## CLI
215
+
216
+ Run a JSON **or** Markdown file directly — **no install required** via `npx`:
108
217
 
109
218
  ```sh
110
219
  npx recordable demo.json
220
+ npx recordable demo.md
111
221
  ```
112
222
 
113
223
  ```
114
- recordable <script.json> [options]
224
+ recordable <script.json | script.md> [options]
115
225
 
116
- --check Validate the script and exit (no browser, no recording)
226
+ --check Validate the script and exit (no browser, no audio, no recording)
117
227
  --headless Run without a visible browser window
118
228
  --silent Suppress recorder console output
119
229
  --out-dir <dir> Output directory (overrides the script's config)
@@ -195,8 +305,9 @@ new Recordable()
195
305
  ```
196
306
 
197
307
  No `pause()`/`resume()` needed — `insert` seals the current segment and recording
198
- resumes into a fresh one on the next action automatically. (Audio on the clip is
199
- currently dropped; voiceover support is on the roadmap.)
308
+ resumes into a fresh one on the next action automatically. (Audio on the inserted
309
+ clip itself is currently dropped — narration voiceover is a separate, supported
310
+ feature, authored in [Markdown](#declarative-scripts-markdown).)
200
311
 
201
312
  **Cross-fades.** Pass `fadeIn` / `fadeOut` (ms) to dissolve rather than hard-cut.
202
313
  A fade blends the clip with the **neighbouring recorded footage** (a true
@@ -206,6 +317,39 @@ on `fadeOut`, while an outro's `fadeOut` dissolves down to black. Omit them for
206
317
  hard cut. A cross-fade of _d_ ms overlaps the two pieces by _d_, shortening the
207
318
  timeline by that much at each faded boundary.
208
319
 
320
+ ## Multiple output files (`start` / `end` / `split`)
321
+
322
+ `pause()`/`resume()` carve off-camera gaps **within one file**. To produce
323
+ **separate files**, move the file boundaries with `start()` / `end()` / `split()`:
324
+
325
+ ```ts
326
+ await new Recordable({ outputName: "demo" })
327
+ .start("intro") // open the first file (content before it is off-camera)
328
+ .visit("https://example.com")
329
+ .click("text:Get started")
330
+ .split("checkout") // close "intro", open the next — camera keeps rolling
331
+ .click("text:Buy")
332
+ .end() // close "checkout"; the teardown below runs off-camera
333
+ .click("text:Sign out")
334
+ .run(); // → demo-intro.mp4, demo-checkout.mp4
335
+ ```
336
+
337
+ - **Boundaries default to the script edges.** With no `start()`, recording opens
338
+ at the top; with no `end()`, it closes at the bottom — so a plain script is one
339
+ file, exactly as before. Add only the bookend you need.
340
+ - **`pause`/`resume` ≠ `start`/`end`.** `resume()` continues the _same_ file (the
341
+ gap is stitched out); `start()`/`split()` open a _new_ file. `split() ≡ end() +
342
+ start()` fused with no gap; for two files _with_ an off-camera gap between them,
343
+ use `end()` … `start()`.
344
+ - **Naming.** Each file is `${outputName}-${label ?? index}.mp4`; a label always
345
+ wins. A single unlabelled file stays `${outputName}.mp4`.
346
+ - **Audio is per-file** — each output is standalone with its own zero-based
347
+ timeline; a clip is assigned to the file containing its start.
348
+
349
+ `run()` resolves to a `RecordableResult` — `{ status, files: [{ path, label,
350
+ index, durationMs, bytes }], outputDir, durationMs, elapsedMs, warnings }` — so
351
+ you can find every file that was written. Hard failures throw instead.
352
+
209
353
  ## API
210
354
 
211
355
  Create an instance with optional [config](#configuration), chain actions, then
@@ -213,16 +357,22 @@ Create an instance with optional [config](#configuration), chain actions, then
213
357
 
214
358
  ### Recording
215
359
 
216
- Recording is on by default and finalises automatically on `.run()`. These control
217
- what lands on camera:
218
-
219
- | Method | Description |
220
- | ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
221
- | `pause()` | Stop capturing; the chain keeps running off-camera. |
222
- | `resume()` | Resume capturing in a fresh segment, immediately. |
223
- | `waitForPlay(message?)` | Block until the user clicks the in-page ▶ Play button (or presses Enter); leaves recording state untouched. |
224
- | `resumeOnPlay(message?)` | Wait for Play, then resume capturing — `waitForPlay().resume()`. |
225
- | `insert(path, opts?)` | Splice an external clip (intro / outro / mid-roll) into the timeline; `opts.fadeIn`/`fadeOut` (ms) cross-fade it. |
360
+ Recording is on by default and finalises automatically on `.run()`, which resolves
361
+ to a [`RecordableResult`](#multiple-output-files-start--end--split). `pause`/`resume`
362
+ control what lands on camera _within_ a file; `start`/`end`/`split` move the file
363
+ boundaries to produce [separate files](#multiple-output-files-start--end--split):
364
+
365
+ | Method | Description |
366
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
367
+ | `pause()` | Stop capturing; the chain keeps running off-camera. |
368
+ | `resume()` | Resume capturing in a fresh segment, immediately. |
369
+ | `start(name?)` | Open an output file (opening boundary); content before the first `start()` is off-camera. `name` labels the file. |
370
+ | `end()` | Close the current output file (closing boundary); content after it runs off-camera. |
371
+ | `split(name?)` | Close the current file and open the next in one move, camera still rolling — `end()` + `start()` with no gap. |
372
+ | `waitForPlay(message?)` | Block until the user clicks the in-page ▶ Play button (or presses Enter); leaves recording state untouched. |
373
+ | `resumeOnPlay(message?)` | Wait for ▶ Play, then resume capturing — `waitForPlay().resume()`. |
374
+ | `insert(path, opts?)` | Splice an external clip (intro / outro / mid-roll) into the timeline; `opts.fadeIn`/`fadeOut` (ms) cross-fade it. |
375
+ | `audio(path, opts?)` | Lay an existing audio file (mp3/wav) onto the timeline here — narration, music bed, SFX. Blocks until the clip ends by default (`opts.wait: false` plays it over following actions); `opts.volume` gains it. |
226
376
 
227
377
  ### Navigation & waiting
228
378
 
@@ -305,11 +455,13 @@ new Recordable({
305
455
  viewport: { width: 1920, height: 1080 },
306
456
  pageZoom: 1, // browser page zoom (Ctrl +/−); <1 reflows to fit more on screen
307
457
  fps: 30,
308
- outputDir: "./output",
458
+ outputDir: "output", // relative paths resolve against baseDir
309
459
  outputName: "recordable",
310
460
  outputTimestamp: true, // prepend an ISO timestamp to the filename
461
+ assetsDir: "assets", // where generated voiceover audio is written (relative to baseDir)
311
462
  headless: false,
312
- language: "", // BCP-47 locale, e.g. "fr-FR" (--lang + Accept-Language); "" = system
463
+ launchArgs: [], // extra Chromium flags, e.g. ["--no-sandbox"] for CI/containers
464
+ language: "", // BCP-47 locale, e.g. "fr-FR" (--lang + --accept-lang + Accept-Language); "" = system
313
465
  typingSpeed: 7, // characters per second
314
466
  videoCrf: 18, // lower = better quality, larger file
315
467
  videoCodec: "libx264",
@@ -323,6 +475,7 @@ new Recordable({
323
475
  scrollDuration: 1200, // ms for the scroll action's transition
324
476
  cursor: true, // show the animated cursor overlay
325
477
  visitTimeout: 30_000, // ms for navigation / waitFor
478
+ baseDir: "", // dir that relative visit URLs, outputDir & assetsDir resolve against; "" = cwd
326
479
  });
327
480
  ```
328
481
 
@@ -336,6 +489,7 @@ npm test # unit + ffmpeg I/O tests
336
489
  npm run test:e2e # opt-in end-to-end pipeline run (launches a browser)
337
490
  npx tsx my-script.ts # run a recording script directly
338
491
  node dist/cli.js demo.json # run a JSON script through the CLI locally
492
+ node dist/cli.js demo.md # run a Markdown script through the CLI locally
339
493
  ```
340
494
 
341
495
  The JSON action set and its schema are both generated from one manifest in
package/dist/actions.d.ts CHANGED
@@ -4,6 +4,13 @@ import * as z from "zod";
4
4
  * discriminator). strictObject so an unknown key (a typo) fails validation.
5
5
  */
6
6
  declare const ACTIONS: {
7
+ start: z.ZodObject<{
8
+ name: z.ZodOptional<z.ZodString>;
9
+ }, z.core.$strict>;
10
+ end: z.ZodObject<{}, z.core.$strict>;
11
+ split: z.ZodObject<{
12
+ name: z.ZodOptional<z.ZodString>;
13
+ }, z.core.$strict>;
7
14
  pause: z.ZodObject<{}, z.core.$strict>;
8
15
  resume: z.ZodObject<{}, z.core.$strict>;
9
16
  waitForPlay: z.ZodObject<{
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAezB;;;GAGG;AACH,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgE0B,CAAC;AAYxC,sEAAsE;AACtE,MAAM,MAAM,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAuChE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAgBjD;AAED;;;;;;GAMG;AACH,iBAAS,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAcxD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAkD3E;AAED,wEAAwE;AACxE,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB,iFAAiF;AACjF,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAezB;;;GAGG;AACH,QAAA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmE0B,CAAC;AAcxC,sEAAsE;AACtE,MAAM,MAAM,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAuChE;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAgBjD;AAED;;;;;;GAMG;AACH,iBAAS,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAcxD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAkD3E;AAED,wEAAwE;AACxE,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB,iFAAiF;AACjF,OAAO,EAAE,SAAS,EAAE,CAAC"}
package/dist/actions.js CHANGED
@@ -16,6 +16,9 @@ const XY = z.strictObject({ x: z.number(), y: z.number() });
16
16
  */
17
17
  const ACTIONS = {
18
18
  // Recording control
19
+ start: z.strictObject({ name: z.string().optional() }),
20
+ end: z.strictObject({}),
21
+ split: z.strictObject({ name: z.string().optional() }),
19
22
  pause: z.strictObject({}),
20
23
  resume: z.strictObject({}),
21
24
  waitForPlay: z.strictObject({ message: z.string().optional() }),
@@ -81,6 +84,8 @@ const ACTIONS = {
81
84
  * fact not derivable from the schema.
82
85
  */
83
86
  const POSITIONAL_OPTIONAL = {
87
+ start: ["name"],
88
+ split: ["name"],
84
89
  waitForPlay: ["message"],
85
90
  resumeOnPlay: ["message"],
86
91
  };
@@ -1 +1 @@
1
- {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,16 @@ export declare class AudioTrack {
24
24
  durationMs: number;
25
25
  }>;
26
26
  }
27
+ /**
28
+ * Partition clips across a run's output files (ROADMAP §6: per-file audio). A
29
+ * clip is assigned to the file *containing its start* — the last file whose
30
+ * global `startMs` is at or before the clip — then rebased to that file's own
31
+ * zero-based timeline. A clip overrunning its file's end is left for the mixer
32
+ * to trim (and warn). Returns one clip list per file, aligned to `files`.
33
+ */
34
+ export declare function partitionAudioByFiles(clips: readonly AudioClip[], files: readonly {
35
+ startMs: number;
36
+ }[]): AudioClip[][];
27
37
  /**
28
38
  * Build the `filter_complex` chain that delays each clip to its `startMs`,
29
39
  * applies volume, and mixes them. Input index `i+1` (input 0 is the video).
@@ -1 +1 @@
1
- {"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/audio/track.ts"],"names":[],"mappings":"AAYA,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;uEACuE;AACvE,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,oCAAoC;IACpC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,kEAAkE;IAClE,IAAI,IAAI,SAAS,SAAS,EAAE;IAI5B;;;OAGG;IACG,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChC,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAUpD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAeA;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,GACT;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAOpC"}
1
+ {"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/audio/track.ts"],"names":[],"mappings":"AAYA,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;uEACuE;AACvE,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,oCAAoC;IACpC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,kEAAkE;IAClE,IAAI,IAAI,SAAS,SAAS,EAAE;IAI5B;;;OAGG;IACG,GAAG,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChC,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAUpD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,KAAK,EAAE,SAAS;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,GACpC,SAAS,EAAE,EAAE,CAUf;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAeA;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,GACT;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,CAOpC"}
@@ -25,6 +25,25 @@ export class AudioTrack {
25
25
  return { startMs, durationMs };
26
26
  }
27
27
  }
28
+ /**
29
+ * Partition clips across a run's output files (ROADMAP §6: per-file audio). A
30
+ * clip is assigned to the file *containing its start* — the last file whose
31
+ * global `startMs` is at or before the clip — then rebased to that file's own
32
+ * zero-based timeline. A clip overrunning its file's end is left for the mixer
33
+ * to trim (and warn). Returns one clip list per file, aligned to `files`.
34
+ */
35
+ export function partitionAudioByFiles(clips, files) {
36
+ const groups = files.map(() => []);
37
+ for (const c of clips) {
38
+ let idx = 0;
39
+ for (let i = 0; i < files.length; i++)
40
+ if (files[i].startMs <= c.startMs)
41
+ idx = i;
42
+ if (groups[idx])
43
+ groups[idx].push({ ...c, startMs: c.startMs - files[idx].startMs });
44
+ }
45
+ return groups;
46
+ }
28
47
  /**
29
48
  * Build the `filter_complex` chain that delays each clip to its `startMs`,
30
49
  * applies volume, and mixes them. Input index `i+1` (input 0 is the video).
@@ -1 +1 @@
1
- {"version":3,"file":"track.js","sourceRoot":"","sources":["../../src/audio/track.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAkB/C;uEACuE;AACvE,MAAM,OAAO,UAAU;IACJ,KAAK,GAAgB,EAAE,CAAC;IAEzC,oCAAoC;IACpC,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,kEAAkE;IAClE,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,CACP,IAAY,EACZ,OAAe,EACf,UAA+B,EAAE;QAEjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YACnB,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,0BAA0B,IAAI,EAAE,CACjC,CAAC;QACJ,MAAM,UAAU,GAAG,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACpD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IACjC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAA2B;IAI1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxD,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAE3D,OAAO,CAAC,IAAI,CACV,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,KAAK,CAAC,MAAM,oBAAoB,CAClE,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,KAA2B,EAC3B,OAAe,EACf,KAAK,GAAG,EAAE;IAEV,MAAM,IAAI,GAAuC,EAAE,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC;QAC9D,IAAI,MAAM,GAAG,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"track.js","sourceRoot":"","sources":["../../src/audio/track.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** Control actions whose ordering the state machine cares about. Plain actions
2
+ * (click, type, visit, …) are legal anywhere and aren't tracked. */
3
+ export type QueueKind = "start" | "end" | "split" | "pause" | "resume" | "insert" | "audio";
4
+ /**
5
+ * Walk the ordered control `kinds` and throw on the first illegal transition.
6
+ *
7
+ * Boundaries default to the script edges: with no explicit `start()` the file is
8
+ * open from the top (records top-to-bottom as before); an explicit `start()`
9
+ * means content before it is off-camera, so the file opens closed. An unmatched
10
+ * `start()` gets an implicit end at the bottom — finalisation closes it, no error.
11
+ */
12
+ export declare function validateBoundaries(kinds: readonly QueueKind[]): void;
13
+ //# sourceMappingURL=boundaries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaries.d.ts","sourceRoot":"","sources":["../../src/compose/boundaries.ts"],"names":[],"mappings":"AAWA;qEACqE;AACrE,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,KAAK,GACL,OAAO,GACP,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,OAAO,CAAC;AAIZ;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAwCpE"}
@@ -0,0 +1,49 @@
1
+ import { RecordableError } from "../errors.js";
2
+ const err = (message) => new RecordableError("CONFIG_INVALID", message);
3
+ /**
4
+ * Walk the ordered control `kinds` and throw on the first illegal transition.
5
+ *
6
+ * Boundaries default to the script edges: with no explicit `start()` the file is
7
+ * open from the top (records top-to-bottom as before); an explicit `start()`
8
+ * means content before it is off-camera, so the file opens closed. An unmatched
9
+ * `start()` gets an implicit end at the bottom — finalisation closes it, no error.
10
+ */
11
+ export function validateBoundaries(kinds) {
12
+ // The only state that gates legality is whether a file is open. pause/resume
13
+ // toggle the camera *within* an open file, so they never change it; split/end
14
+ // are allowed even while paused — so paused-ness needs no separate tracking.
15
+ let fileOpen = !kinds.includes("start"); // implicit start at top when none given
16
+ for (const k of kinds) {
17
+ switch (k) {
18
+ case "start":
19
+ if (fileOpen)
20
+ throw err("start() while a recording is already open — call end() or split() first");
21
+ fileOpen = true;
22
+ break;
23
+ case "end":
24
+ if (!fileOpen)
25
+ throw err("end() with no open recording — call start() first");
26
+ fileOpen = false; // sealed; allowed while paused
27
+ break;
28
+ case "split":
29
+ if (!fileOpen)
30
+ throw err("split() with no open recording — call start() first");
31
+ break; // end+start fused: stays open, new file rolls (allowed while paused)
32
+ case "pause":
33
+ if (!fileOpen)
34
+ throw err("pause() with no open recording to pause");
35
+ break; // redundant pause is a no-op, not an error
36
+ case "resume":
37
+ if (!fileOpen)
38
+ throw err("resume() with no open recording to resume");
39
+ break; // redundant resume is a no-op, not an error
40
+ case "insert":
41
+ case "audio":
42
+ if (!fileOpen)
43
+ throw err(`${k}() needs an open recording — it can't run in an off-camera gap; ` +
44
+ `call start() first`);
45
+ break;
46
+ }
47
+ }
48
+ }
49
+ //# sourceMappingURL=boundaries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../../src/compose/boundaries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAsB/C,MAAM,GAAG,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAI,eAAe,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B;IAC5D,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,IAAI,QAAQ,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,wCAAwC;IAEjF,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,QAAQ,CAAC,EAAE,CAAC;YACV,KAAK,OAAO;gBACV,IAAI,QAAQ;oBACV,MAAM,GAAG,CACP,yEAAyE,CAC1E,CAAC;gBACJ,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM;YACR,KAAK,KAAK;gBACR,IAAI,CAAC,QAAQ;oBACX,MAAM,GAAG,CAAC,mDAAmD,CAAC,CAAC;gBACjE,QAAQ,GAAG,KAAK,CAAC,CAAC,+BAA+B;gBACjD,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,QAAQ;oBACX,MAAM,GAAG,CAAC,qDAAqD,CAAC,CAAC;gBACnE,MAAM,CAAC,qEAAqE;YAC9E,KAAK,OAAO;gBACV,IAAI,CAAC,QAAQ;oBAAE,MAAM,GAAG,CAAC,yCAAyC,CAAC,CAAC;gBACpE,MAAM,CAAC,2CAA2C;YACpD,KAAK,QAAQ;gBACX,IAAI,CAAC,QAAQ;oBAAE,MAAM,GAAG,CAAC,2CAA2C,CAAC,CAAC;gBACtE,MAAM,CAAC,4CAA4C;YACrD,KAAK,QAAQ,CAAC;YACd,KAAK,OAAO;gBACV,IAAI,CAAC,QAAQ;oBACX,MAAM,GAAG,CACP,GAAG,CAAC,kEAAkE;wBACpE,oBAAoB,CACvB,CAAC;gBACJ,MAAM;QACV,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -7,7 +7,8 @@ import type { Logger } from "../logger.js";
7
7
  *
8
8
  * Per the timing contract the video defines the length: a clip that runs past
9
9
  * the end is the author's cue to add a trailing `wait()`, so we warn and let
10
- * `-t` truncate it rather than padding the video.
10
+ * `-t` truncate it rather than padding the video. Returns the overrun warnings
11
+ * (also logged) so the caller can surface them on the run result.
11
12
  */
12
- export declare function addAudio(videoPath: string, clips: readonly AudioClip[], out: string, log: Logger): Promise<void>;
13
+ export declare function addAudio(videoPath: string, clips: readonly AudioClip[], out: string, log: Logger): Promise<string[]>;
13
14
  //# sourceMappingURL=mix.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mix.d.ts","sourceRoot":"","sources":["../../src/compose/mix.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAU3C;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAkCf"}
1
+ {"version":3,"file":"mix.d.ts","sourceRoot":"","sources":["../../src/compose/mix.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAU3C;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCnB"}
@@ -14,13 +14,17 @@ import { audioFilterGraph, audioOverruns, } from "../audio/track.js";
14
14
  *
15
15
  * Per the timing contract the video defines the length: a clip that runs past
16
16
  * the end is the author's cue to add a trailing `wait()`, so we warn and let
17
- * `-t` truncate it rather than padding the video.
17
+ * `-t` truncate it rather than padding the video. Returns the overrun warnings
18
+ * (also logged) so the caller can surface them on the run result.
18
19
  */
19
20
  export async function addAudio(videoPath, clips, out, log) {
20
21
  const videoMs = (await getDuration(videoPath)) * 1000;
22
+ const warnings = [];
21
23
  for (const { path, overMs } of audioOverruns(clips, videoMs)) {
22
- log("Audio", `warning: "${path}" runs ${overMs}ms past the video end and will be cut — ` +
23
- `add a trailing wait() to give it room`);
24
+ const msg = `"${path}" runs ${overMs}ms past the video end and will be cut — ` +
25
+ `add a trailing wait() to give it room`;
26
+ warnings.push(msg);
27
+ log("Audio", `warning: ${msg}`);
24
28
  }
25
29
  const { filters, mapLabel } = audioFilterGraph(clips);
26
30
  await runFfmpeg([
@@ -46,5 +50,6 @@ export async function addAudio(videoPath, clips, out, log) {
46
50
  out,
47
51
  ]);
48
52
  log("Audio", `mixed ${clips.length} audio clip(s)`);
53
+ return warnings;
49
54
  }
50
55
  //# sourceMappingURL=mix.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mix.js","sourceRoot":"","sources":["../../src/compose/mix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,gBAAgB,EAChB,aAAa,GAEd,MAAM,mBAAmB,CAAC;AAG3B,gFAAgF;AAChF,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,gFAAgF;AAChF,+EAA+E;AAC/E,aAAa;AAEb;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAiB,EACjB,KAA2B,EAC3B,GAAW,EACX,GAAW;IAEX,MAAM,OAAO,GAAG,CAAC,MAAM,WAAW,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC;IACtD,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAC7D,GAAG,CACD,OAAO,EACP,aAAa,IAAI,UAAU,MAAM,0CAA0C;YACzE,uCAAuC,CAC1C,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,SAAS,CAAC;QACd,IAAI;QACJ,IAAI;QACJ,SAAS;QACT,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,iBAAiB;QACjB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QACjB,MAAM;QACN,KAAK;QACL,MAAM;QACN,IAAI,QAAQ,GAAG;QACf,MAAM;QACN,MAAM;QACN,MAAM;QACN,KAAK;QACL,6EAA6E;QAC7E,2EAA2E;QAC3E,+DAA+D;QAC/D,IAAI;QACJ,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3B,GAAG;KACJ,CAAC,CAAC;IACH,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,CAAC,MAAM,gBAAgB,CAAC,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"mix.js","sourceRoot":"","sources":["../../src/compose/mix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,gBAAgB,EAChB,aAAa,GAEd,MAAM,mBAAmB,CAAC;AAG3B,gFAAgF;AAChF,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,gFAAgF;AAChF,+EAA+E;AAC/E,aAAa;AAEb;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAiB,EACjB,KAA2B,EAC3B,GAAW,EACX,GAAW;IAEX,MAAM,OAAO,GAAG,CAAC,MAAM,WAAW,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC;IACtD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAC7D,MAAM,GAAG,GACP,IAAI,IAAI,UAAU,MAAM,0CAA0C;YAClE,uCAAuC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,SAAS,CAAC;QACd,IAAI;QACJ,IAAI;QACJ,SAAS;QACT,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,iBAAiB;QACjB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QACjB,MAAM;QACN,KAAK;QACL,MAAM;QACN,IAAI,QAAQ,GAAG;QACf,MAAM;QACN,MAAM;QACN,MAAM;QACN,KAAK;QACL,6EAA6E;QAC7E,2EAA2E;QAC3E,+DAA+D;QAC/D,IAAI;QACJ,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3B,GAAG;KACJ,CAAC,CAAC;IACH,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,CAAC,MAAM,gBAAgB,CAAC,CAAC;IACpD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { type GoToOptions } from "puppeteer";
2
2
  import { type AudioOptions, type ClickOptions, type InsertOptions, type RecordableConfig, type WaitForOptions } from "../config.js";
3
3
  import { type Script } from "../script.js";
4
+ import { type RecordableResult } from "../result.js";
4
5
  export declare class Recordable {
5
6
  private cfg;
6
7
  private readonly userConfig;
@@ -28,6 +29,26 @@ export declare class Recordable {
28
29
  private _stageVoiceover;
29
30
  /** Merge config mid-sequence — enqueued so it takes effect at this point. */
30
31
  setConfig(config: RecordableConfig): this;
32
+ /**
33
+ * Open an output file (the opening boundary). Content *before* the first
34
+ * `start()` runs off-camera; absent, recording opens at the top. Pass an
35
+ * optional `name` to label the file (`start("intro")` → `…-intro.mp4`).
36
+ * Pair with `end()`, or leave it to close implicitly at the bottom.
37
+ */
38
+ start(name?: string): this;
39
+ /**
40
+ * Close the current output file (the closing boundary). Content *after* `end()`
41
+ * runs off-camera (cleanup is common, so no warning); absent, recording closes
42
+ * at the bottom. Open another file with `start()` for a second output.
43
+ */
44
+ end(): this;
45
+ /**
46
+ * Split the output here: close the current file and open the next in one move,
47
+ * camera still rolling (`split() ≡ end() + start()` fused, no gap). Pass an
48
+ * optional `name` to label the new file. For two files with an off-camera gap
49
+ * between them, use `end()` … `start()` instead.
50
+ */
51
+ split(name?: string): this;
31
52
  /**
32
53
  * Stop capturing. The chain keeps running — anything between `pause()` and the
33
54
  * next resume executes off-camera (page loads, logins, data setup) and is
@@ -151,8 +172,9 @@ export declare class Recordable {
151
172
  }): this;
152
173
  /** Pause the sequence for `ms` milliseconds. */
153
174
  wait(ms: number): this;
154
- /** Execute the queued action sequence, then finalise the recording. */
155
- run(): Promise<void>;
175
+ /** Execute the queued action sequence, then finalise the recording. Resolves to
176
+ * a {@link RecordableResult} describing the written file(s). */
177
+ run(): Promise<RecordableResult>;
156
178
  /** Resolve a local asset path (insert/audio clip) against `baseDir` so a
157
179
  * Markdown/JSON script and its clips travel together; absolute paths pass
158
180
  * through. `baseDir` empty → resolve against cwd (the programmatic default). */