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.
- package/README.md +184 -30
- package/dist/actions.d.ts +7 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +5 -0
- package/dist/actions.js.map +1 -1
- package/dist/audio/track.d.ts +10 -0
- package/dist/audio/track.d.ts.map +1 -1
- package/dist/audio/track.js +19 -0
- package/dist/audio/track.js.map +1 -1
- package/dist/compose/boundaries.d.ts +13 -0
- package/dist/compose/boundaries.d.ts.map +1 -0
- package/dist/compose/boundaries.js +49 -0
- package/dist/compose/boundaries.js.map +1 -0
- package/dist/compose/mix.d.ts +3 -2
- package/dist/compose/mix.d.ts.map +1 -1
- package/dist/compose/mix.js +8 -3
- package/dist/compose/mix.js.map +1 -1
- package/dist/compose/recordable.d.ts +24 -2
- package/dist/compose/recordable.d.ts.map +1 -1
- package/dist/compose/recordable.js +56 -11
- package/dist/compose/recordable.js.map +1 -1
- package/dist/compose/session.d.ts +19 -8
- package/dist/compose/session.d.ts.map +1 -1
- package/dist/compose/session.js +99 -28
- package/dist/compose/session.js.map +1 -1
- package/dist/ffmpeg.d.ts.map +1 -1
- package/dist/ffmpeg.js +8 -3
- package/dist/ffmpeg.js.map +1 -1
- package/dist/formats/json.d.ts +2 -1
- package/dist/formats/json.d.ts.map +1 -1
- package/dist/formats/json.js.map +1 -1
- package/dist/fs.d.ts +16 -0
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +33 -4
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/result.d.ts +30 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +8 -0
- package/dist/result.js.map +1 -0
- package/dist/video/recorder.d.ts +26 -3
- package/dist/video/recorder.d.ts.map +1 -1
- package/dist/video/recorder.js +43 -19
- package/dist/video/recorder.js.map +1 -1
- package/package.json +19 -6
- package/recordable.schema.json +78 -0
package/dist/fs.js
CHANGED
|
@@ -17,14 +17,43 @@ export function moveFile(src, dest) {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
/** The optional `-YYYYMMDDhhmmss` suffix shared across a run's output files. */
|
|
21
|
+
function stamp(outputTimestamp) {
|
|
22
|
+
return outputTimestamp
|
|
23
|
+
? "-" + new Date().toISOString().replace(/\D/g, "").slice(0, 14)
|
|
24
|
+
: "";
|
|
25
|
+
}
|
|
20
26
|
/** Build the timestamped output path and ensure its directory exists. */
|
|
21
27
|
export function getOutputPath(opts) {
|
|
22
28
|
const { outputDir, outputName, outputTimestamp } = opts;
|
|
23
|
-
const
|
|
24
|
-
? "-" + new Date().toISOString().replace(/\D/g, "").slice(0, 14)
|
|
25
|
-
: "";
|
|
26
|
-
const out = `${outputDir}/${outputName}${timestamp}.mp4`;
|
|
29
|
+
const out = `${outputDir}/${outputName}${stamp(outputTimestamp)}.mp4`;
|
|
27
30
|
mkdirSync(dirname(out), { recursive: true });
|
|
28
31
|
return out;
|
|
29
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the output path for each file of a multi-file run (ROADMAP §6),
|
|
35
|
+
* ensuring the directory exists. A single unlabelled file stays
|
|
36
|
+
* `<name>.mp4`; otherwise each file is suffixed — a label always wins
|
|
37
|
+
* (`<name>-intro.mp4`), an unlabelled file falls back to its 1-based position
|
|
38
|
+
* (`<name>-2.mp4`). One run-wide timestamp (when enabled) is shared by all.
|
|
39
|
+
* Collisions (e.g. a label equal to another's index) are disambiguated by
|
|
40
|
+
* appending the position.
|
|
41
|
+
*/
|
|
42
|
+
export function resolveOutputPaths(opts, files) {
|
|
43
|
+
const { outputDir, outputName, outputTimestamp } = opts;
|
|
44
|
+
const ts = stamp(outputTimestamp);
|
|
45
|
+
const single = files.length === 1 && files[0].label == null;
|
|
46
|
+
const used = new Set();
|
|
47
|
+
const paths = files.map((f, i) => {
|
|
48
|
+
const suffix = single ? "" : `-${f.label ?? i + 1}`;
|
|
49
|
+
let name = `${outputName}${suffix}${ts}.mp4`;
|
|
50
|
+
if (used.has(name))
|
|
51
|
+
name = `${outputName}${suffix}-${i + 1}${ts}.mp4`;
|
|
52
|
+
used.add(name);
|
|
53
|
+
return `${outputDir}/${name}`;
|
|
54
|
+
});
|
|
55
|
+
if (paths.length)
|
|
56
|
+
mkdirSync(dirname(paths[0]), { recursive: true });
|
|
57
|
+
return paths;
|
|
58
|
+
}
|
|
30
59
|
//# sourceMappingURL=fs.js.map
|
package/dist/fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,6EAA6E;AAC7E,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAY;IAChD,IAAI,CAAC;QACH,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;QACnE,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,IAI7B;IACC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,SAAS,GAAG,eAAe;
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../src/fs.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,6EAA6E;AAC7E,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAY;IAChD,IAAI,CAAC;QACH,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;QACnE,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,SAAS,KAAK,CAAC,eAAwB;IACrC,OAAO,eAAe;QACpB,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAChE,CAAC,CAAC,EAAE,CAAC;AACT,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,IAI7B;IACC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,GAAG,GAAG,GAAG,SAAS,IAAI,UAAU,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC;IACtE,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAyE,EACzE,KAA0C;IAE1C,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IACxD,MAAM,EAAE,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;IAElC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;IAC5D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACpD,IAAI,IAAI,GAAG,GAAG,UAAU,GAAG,MAAM,GAAG,EAAE,MAAM,CAAC;QAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,GAAG,UAAU,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC;QACtE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,OAAO,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,MAAM;QAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,5 @@ export { buildSchema } from "./schema.js";
|
|
|
7
7
|
export { parseMarkdown } from "./formats/markdown/parse.js";
|
|
8
8
|
export type { ParsedMarkdown, MarkdownBlock, NarrationBlock, ActionsBlock, } from "./formats/markdown/parse.js";
|
|
9
9
|
export type { RecordableConfig, VoiceoverConfig, AudioOptions, InsertOptions, WaitForOptions, ResolvedConfig, } from "./config.js";
|
|
10
|
+
export type { RecordableResult, RecordableFile } from "./result.js";
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrE,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,GACb,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrE,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EACV,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,GACb,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/result.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** One written output file. */
|
|
2
|
+
export interface RecordableFile {
|
|
3
|
+
/** Absolute path to the .mp4. */
|
|
4
|
+
path: string;
|
|
5
|
+
/** The `start`/`split` label, or null when the file fell back to its position. */
|
|
6
|
+
label: string | null;
|
|
7
|
+
/** 1-based position among the written files. */
|
|
8
|
+
index: number;
|
|
9
|
+
/** Recorded length (ms), off-camera gaps excluded. */
|
|
10
|
+
durationMs: number;
|
|
11
|
+
/** File size in bytes. */
|
|
12
|
+
bytes: number;
|
|
13
|
+
}
|
|
14
|
+
/** What a successful `run()` resolves to. */
|
|
15
|
+
export interface RecordableResult {
|
|
16
|
+
/** `"completed"` when at least one file was written; `"empty"` when nothing
|
|
17
|
+
* was captured (no frames anywhere) and no file was written. */
|
|
18
|
+
status: "completed" | "empty";
|
|
19
|
+
/** The written files, in timeline order. Empty only when `status` is "empty". */
|
|
20
|
+
files: RecordableFile[];
|
|
21
|
+
/** The directory the files were written to. */
|
|
22
|
+
outputDir: string;
|
|
23
|
+
/** Total recorded length (ms) across all files. */
|
|
24
|
+
durationMs: number;
|
|
25
|
+
/** Wall-clock time the run took (ms). */
|
|
26
|
+
elapsedMs: number;
|
|
27
|
+
/** Non-fatal notes (skipped empty files, trimmed audio overruns). */
|
|
28
|
+
warnings: string[];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAOA,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B;qEACiE;IACjE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC;IAC9B,iFAAiF;IACjF,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB"}
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// ─── Run result ──────────────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// What a successful run() resolves to. A run produces one or more output files
|
|
4
|
+
// (one per start/end/split boundary; one for a plain top-to-bottom script). Hard
|
|
5
|
+
// failures throw a RecordableError instead — so on the result `files` is always
|
|
6
|
+
// real and there is never two ways to model the same failure.
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,iFAAiF;AACjF,gFAAgF;AAChF,8DAA8D"}
|
package/dist/video/recorder.d.ts
CHANGED
|
@@ -2,6 +2,18 @@ import { type Page } from "puppeteer";
|
|
|
2
2
|
import { type Logger } from "../logger.js";
|
|
3
3
|
import { type InsertOptions, type ResolvedConfig } from "../config.js";
|
|
4
4
|
import { type Segment } from "./stitch.js";
|
|
5
|
+
/** One output file's captured + inserted segments, with its place on the global
|
|
6
|
+
* recorded-time line (so audio clips can be partitioned and rebased per file). */
|
|
7
|
+
export interface OutputFile {
|
|
8
|
+
/** The `start`/`split` label, or null for an unlabelled / implicit file. */
|
|
9
|
+
label: string | null;
|
|
10
|
+
/** Segments in timeline order, for stitching. */
|
|
11
|
+
segments: Segment[];
|
|
12
|
+
/** Global recorded-time position (ms) where this file opened. */
|
|
13
|
+
startMs: number;
|
|
14
|
+
/** Recorded length (ms); set when the file closes. */
|
|
15
|
+
durationMs: number;
|
|
16
|
+
}
|
|
5
17
|
/**
|
|
6
18
|
* Recorded-time position now: finalised segment time plus the in-flight segment's
|
|
7
19
|
* elapsed time (`frames / fps`). Off-camera (paused) stretches capture no frames,
|
|
@@ -12,7 +24,9 @@ export declare class Recorder {
|
|
|
12
24
|
private readonly getCfg;
|
|
13
25
|
private readonly log;
|
|
14
26
|
private tmpDirPath;
|
|
15
|
-
private
|
|
27
|
+
private files;
|
|
28
|
+
private current;
|
|
29
|
+
private segCount;
|
|
16
30
|
private currentSegment;
|
|
17
31
|
private cdp;
|
|
18
32
|
private cdpPage;
|
|
@@ -26,10 +40,19 @@ export declare class Recorder {
|
|
|
26
40
|
constructor(getCfg: () => ResolvedConfig, log: Logger);
|
|
27
41
|
/** True while a segment is actively capturing frames. */
|
|
28
42
|
get capturing(): boolean;
|
|
29
|
-
/**
|
|
30
|
-
get
|
|
43
|
+
/** True while an output file is open (capturing or paused). */
|
|
44
|
+
get fileOpen(): boolean;
|
|
45
|
+
/** The closed output files, in timeline order (for finalising). */
|
|
46
|
+
get outputFiles(): readonly OutputFile[];
|
|
31
47
|
/** The temp working directory holding segment files. */
|
|
32
48
|
get tmpDir(): string;
|
|
49
|
+
/** Open a new output file at the current recorded-time position. Segments
|
|
50
|
+
* captured/inserted from here on land in it until `closeFile`. */
|
|
51
|
+
openFile(label: string | null): void;
|
|
52
|
+
/** Seal the active segment and close the open file (recording its duration).
|
|
53
|
+
* An empty file (no captured frames) is still recorded so finalisation can
|
|
54
|
+
* report and skip it. No-op if no file is open. */
|
|
55
|
+
closeFile(): Promise<void>;
|
|
33
56
|
/** Recorded-time position now: finalised segments + the in-flight segment. */
|
|
34
57
|
currentTimelineMs(): number;
|
|
35
58
|
/** Create the temp working directory for segment files. Call once before use. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/video/recorder.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,IAAI,EAAmB,MAAM,WAAW,CAAC;AAQvD,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../src/video/recorder.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,IAAI,EAAmB,MAAM,WAAW,CAAC;AAQvD,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAmB3C;mFACmF;AACnF,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iDAAiD;IACjD,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,OAAO,GACjB,MAAM,CAGR;AAED,qBAAa,QAAQ;IAwBjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAxBtB,OAAO,CAAC,UAAU,CAAM;IAGxB,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,OAAO,CAA2B;IAE1C,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,GAAG,CAA2B;IAEtC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,YAAY,CAAsB;IAG1C,OAAO,CAAC,WAAW,CAAK;gBAGL,MAAM,EAAE,MAAM,cAAc,EAC5B,GAAG,EAAE,MAAM;IAG9B,yDAAyD;IACzD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,+DAA+D;IAC/D,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,mEAAmE;IACnE,IAAI,WAAW,IAAI,SAAS,UAAU,EAAE,CAEvC;IAED,wDAAwD;IACxD,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;uEACmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAcpC;;wDAEoD;IAC9C,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAQhC,8EAA8E;IAC9E,iBAAiB,IAAI,MAAM;IAU3B,iFAAiF;IACjF,IAAI,IAAI,IAAI;IAIZ;;+EAE2E;YAC7D,UAAU;IAkBxB,wEAAwE;IAClE,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoEtC;;iFAE6E;IACvE,GAAG,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CxC;;;;;;OAMG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CtE,sEAAsE;IAChE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B,0EAA0E;IAC1E,SAAS,IAAI,IAAI;CAIlB"}
|
package/dist/video/recorder.js
CHANGED
|
@@ -4,16 +4,6 @@ import { join } from "node:path";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { FFMPEG_PATH, getDuration, runFfmpeg, videoEncodeArgs, } from "../ffmpeg.js";
|
|
6
6
|
import { RecordableError } from "../errors.js";
|
|
7
|
-
// ─── Video layer: capture ────────────────────────────────────────────────────
|
|
8
|
-
//
|
|
9
|
-
// Captures the page via CDP `Page.startScreencast`, pipes JPEG frames into ffmpeg
|
|
10
|
-
// at a steady fps to produce one MP4 per captured stretch, and tracks the
|
|
11
|
-
// recorded-time clock the audio layer positions clips against. Stitching the
|
|
12
|
-
// segments into the final video lives in `./stitch.ts`.
|
|
13
|
-
//
|
|
14
|
-
// Segments are started lazily and ended on pause, so off-camera gaps (page loads,
|
|
15
|
-
// logins, data setup) leave no trace. Config is read through `getCfg` on each call
|
|
16
|
-
// so runtime changes take effect.
|
|
17
7
|
/**
|
|
18
8
|
* Recorded-time position now: finalised segment time plus the in-flight segment's
|
|
19
9
|
* elapsed time (`frames / fps`). Off-camera (paused) stretches capture no frames,
|
|
@@ -27,7 +17,12 @@ export class Recorder {
|
|
|
27
17
|
getCfg;
|
|
28
18
|
log;
|
|
29
19
|
tmpDirPath = "";
|
|
30
|
-
|
|
20
|
+
// Output files, in timeline order, plus the one currently open (null in an
|
|
21
|
+
// off-camera gap). Segments land in `current`; start/end/split manage the set.
|
|
22
|
+
files = [];
|
|
23
|
+
current = null;
|
|
24
|
+
// Monotonic segment counter for unique temp filenames across all files.
|
|
25
|
+
segCount = 0;
|
|
31
26
|
currentSegment = "";
|
|
32
27
|
cdp = null;
|
|
33
28
|
// The page `cdp` is attached to — a new tab needs a fresh session (per-target).
|
|
@@ -49,14 +44,41 @@ export class Recorder {
|
|
|
49
44
|
get capturing() {
|
|
50
45
|
return this.ffmpegProc !== null;
|
|
51
46
|
}
|
|
52
|
-
/**
|
|
53
|
-
get
|
|
54
|
-
return this.
|
|
47
|
+
/** True while an output file is open (capturing or paused). */
|
|
48
|
+
get fileOpen() {
|
|
49
|
+
return this.current !== null;
|
|
50
|
+
}
|
|
51
|
+
/** The closed output files, in timeline order (for finalising). */
|
|
52
|
+
get outputFiles() {
|
|
53
|
+
return this.files;
|
|
55
54
|
}
|
|
56
55
|
/** The temp working directory holding segment files. */
|
|
57
56
|
get tmpDir() {
|
|
58
57
|
return this.tmpDirPath;
|
|
59
58
|
}
|
|
59
|
+
/** Open a new output file at the current recorded-time position. Segments
|
|
60
|
+
* captured/inserted from here on land in it until `closeFile`. */
|
|
61
|
+
openFile(label) {
|
|
62
|
+
if (this.current)
|
|
63
|
+
throw new RecordableError("CONFIG_INVALID", "openFile: a recording is already open");
|
|
64
|
+
this.current = {
|
|
65
|
+
label,
|
|
66
|
+
segments: [],
|
|
67
|
+
startMs: this.completedMs,
|
|
68
|
+
durationMs: 0,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Seal the active segment and close the open file (recording its duration).
|
|
72
|
+
* An empty file (no captured frames) is still recorded so finalisation can
|
|
73
|
+
* report and skip it. No-op if no file is open. */
|
|
74
|
+
async closeFile() {
|
|
75
|
+
await this.end(true); // seal the in-flight segment, no "pause" log
|
|
76
|
+
if (!this.current)
|
|
77
|
+
return;
|
|
78
|
+
this.current.durationMs = this.completedMs - this.current.startMs;
|
|
79
|
+
this.files.push(this.current);
|
|
80
|
+
this.current = null;
|
|
81
|
+
}
|
|
60
82
|
/** Recorded-time position now: finalised segments + the in-flight segment. */
|
|
61
83
|
currentTimelineMs() {
|
|
62
84
|
const fps = this.segmentFps || this.getCfg().fps;
|
|
@@ -92,7 +114,7 @@ export class Recorder {
|
|
|
92
114
|
if (this.ffmpegProc)
|
|
93
115
|
return;
|
|
94
116
|
const cfg = this.getCfg();
|
|
95
|
-
const idx = this.
|
|
117
|
+
const idx = this.segCount++;
|
|
96
118
|
const file = join(this.tmpDirPath, `seg-${String(idx).padStart(3, "0")}.mp4`);
|
|
97
119
|
const { width, height } = cfg.viewport;
|
|
98
120
|
const fps = cfg.fps;
|
|
@@ -174,8 +196,8 @@ export class Recorder {
|
|
|
174
196
|
throw new RecordableError("FFMPEG_FAILED", `recording capture failed: ${e.message}`, { cause: e });
|
|
175
197
|
}
|
|
176
198
|
// Only keep segments that actually captured frames (avoids empty/invalid mp4).
|
|
177
|
-
if (this.currentSegment && frames > 0) {
|
|
178
|
-
this.
|
|
199
|
+
if (this.currentSegment && frames > 0 && this.current) {
|
|
200
|
+
this.current.segments.push({
|
|
179
201
|
path: this.currentSegment,
|
|
180
202
|
fadeIn: 0,
|
|
181
203
|
fadeOut: 0,
|
|
@@ -199,8 +221,10 @@ export class Recorder {
|
|
|
199
221
|
if (!existsSync(path))
|
|
200
222
|
throw new RecordableError("FILE_NOT_FOUND", `insert: file not found: ${path}`);
|
|
201
223
|
await this.end(true);
|
|
224
|
+
if (!this.current)
|
|
225
|
+
throw new RecordableError("CONFIG_INVALID", "insert: no open recording — call start() first");
|
|
202
226
|
const cfg = this.getCfg();
|
|
203
|
-
const idx = this.
|
|
227
|
+
const idx = this.segCount++;
|
|
204
228
|
const file = join(this.tmpDirPath, `seg-${String(idx).padStart(3, "0")}.mp4`);
|
|
205
229
|
const { width, height } = cfg.viewport;
|
|
206
230
|
// Letterbox-fit to the viewport and conform fps/codec/pixel format so the clip
|
|
@@ -218,7 +242,7 @@ export class Recorder {
|
|
|
218
242
|
"yuv420p",
|
|
219
243
|
file,
|
|
220
244
|
]);
|
|
221
|
-
this.
|
|
245
|
+
this.current.segments.push({
|
|
222
246
|
path: file,
|
|
223
247
|
fadeIn: Math.max(0, options.fadeIn ?? 0) / 1000,
|
|
224
248
|
fadeOut: Math.max(0, options.fadeOut ?? 0) / 1000,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/video/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EACL,WAAW,EACX,WAAW,EACX,SAAS,EACT,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../src/video/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EACL,WAAW,EACX,WAAW,EACX,SAAS,EACT,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAmC/C;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,WAAmB,EACnB,aAAqB,EACrB,GAAW,EACX,SAAkB;IAElB,MAAM,OAAO,GAAG,SAAS,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,WAAW,GAAG,OAAO,CAAC;AAC/B,CAAC;AAED,MAAM,OAAO,QAAQ;IAwBA;IACA;IAxBX,UAAU,GAAG,EAAE,CAAC;IACxB,2EAA2E;IAC3E,+EAA+E;IACvE,KAAK,GAAiB,EAAE,CAAC;IACzB,OAAO,GAAsB,IAAI,CAAC;IAC1C,wEAAwE;IAChE,QAAQ,GAAG,CAAC,CAAC;IACb,cAAc,GAAG,EAAE,CAAC;IACpB,GAAG,GAAsB,IAAI,CAAC;IACtC,gFAAgF;IACxE,OAAO,GAAgB,IAAI,CAAC;IAC5B,UAAU,GAAwB,IAAI,CAAC;IACvC,WAAW,GAA0C,IAAI,CAAC;IAC1D,WAAW,GAAkB,IAAI,CAAC;IAClC,aAAa,GAAG,CAAC,CAAC;IAClB,UAAU,GAAG,CAAC,CAAC;IACvB,4EAA4E;IACpE,YAAY,GAAiB,IAAI,CAAC;IAE1C,qEAAqE;IAC7D,WAAW,GAAG,CAAC,CAAC;IAExB,YACmB,MAA4B,EAC5B,GAAW;QADX,WAAM,GAAN,MAAM,CAAsB;QAC5B,QAAG,GAAH,GAAG,CAAQ;IAC3B,CAAC;IAEJ,yDAAyD;IACzD,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC;IAClC,CAAC;IAED,+DAA+D;IAC/D,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC/B,CAAC;IAED,mEAAmE;IACnE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,wDAAwD;IACxD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;uEACmE;IACnE,QAAQ,CAAC,KAAoB;QAC3B,IAAI,IAAI,CAAC,OAAO;YACd,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,uCAAuC,CACxC,CAAC;QACJ,IAAI,CAAC,OAAO,GAAG;YACb,KAAK;YACL,QAAQ,EAAE,EAAE;YACZ,OAAO,EAAE,IAAI,CAAC,WAAW;YACzB,UAAU,EAAE,CAAC;SACd,CAAC;IACJ,CAAC;IAED;;wDAEoD;IACpD,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,6CAA6C;QACnE,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAClE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,8EAA8E;IAC9E,iBAAiB;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC;QACjD,OAAO,UAAU,CACf,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,aAAa,EAClB,GAAG,EACH,IAAI,CAAC,SAAS,CACf,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,IAAI;QACF,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED;;+EAE2E;IACnE,KAAK,CAAC,UAAU,CAAC,IAAU;QACjC,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC;QACvD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1C,GAAG,CAAC,EAAE,CAAC,sBAAsB,EAAE,CAAC,KAAK,EAAE,EAAE;YACvC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACrD,GAAG;iBACA,IAAI,CAAC,yBAAyB,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;iBAC/D,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,KAAK,CAAC,IAAU;QACpB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CACf,IAAI,CAAC,UAAU,EACf,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAC1C,CAAC;QACF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;QACvC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;QAEpB,qEAAqE;QACrE,MAAM,IAAI,GAAG,KAAK,CAChB,WAAW,EACX;YACE,IAAI;YACJ,IAAI;YACJ,YAAY;YACZ,YAAY;YACZ,MAAM,CAAC,GAAG,CAAC;YACX,IAAI;YACJ,QAAQ;YACR,IAAI;YACJ,MAAM,CAAC,GAAG,CAAC;YACX,GAAG,eAAe,CAAC,GAAG,CAAC;YACvB,UAAU;YACV,SAAS;YACT,KAAK;YACL,+BAA+B,EAAE,gCAAgC;YACjE,IAAI;SACL,EACD,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,CACxC,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACrB,yEAAyE;YACzE,uDAAuD;YACvD,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAExB,6EAA6E;QAC7E,4DAA4D;QAC5D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,MAAM;YACjB,aAAa,EAAE,CAAC;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,WAAW,CAC5B,GAAG,EAAE;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;YAC1B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,OAAO;YAC1D,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAChC,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CACpC,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1E,CAAC;IAED;;iFAE6E;IAC7E,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK;QACtB,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAC7B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAChC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,qBAAqB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAE5D,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;QAElC,8DAA8D;QAC9D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;YAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,MAAM,IAAI,eAAe,CACvB,eAAe,EACf,6BAA6B,CAAC,CAAC,OAAO,EAAE,EACxC,EAAE,KAAK,EAAE,CAAC,EAAE,CACb,CAAC;QACJ,CAAC;QAED,+EAA+E;QAC/E,IAAI,IAAI,CAAC,cAAc,IAAI,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACzB,IAAI,EAAE,IAAI,CAAC,cAAc;gBACzB,MAAM,EAAE,CAAC;gBACT,OAAO,EAAE,CAAC;aACX,CAAC,CAAC;YACH,IAAI,CAAC,WAAW;gBACd,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,UAAyB,EAAE;QACpD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YACnB,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,2BAA2B,IAAI,EAAE,CAClC,CAAC;QACJ,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,OAAO;YACf,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,gDAAgD,CACjD,CAAC;QAEJ,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CACf,IAAI,CAAC,UAAU,EACf,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAC1C,CAAC;QACF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC;QAEvC,+EAA+E;QAC/E,mDAAmD;QACnD,MAAM,SAAS,CAAC;YACd,IAAI;YACJ,IAAI;YACJ,IAAI;YACJ,KAAK;YACL,KAAK;YACL,SAAS,KAAK,IAAI,MAAM,wCAAwC;gBAC9D,OAAO,KAAK,IAAI,MAAM,qCAAqC,GAAG,CAAC,GAAG,EAAE;YACtE,GAAG,eAAe,CAAC,GAAG,CAAC;YACvB,UAAU;YACV,SAAS;YACT,IAAI;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;YACzB,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI;YAC/C,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI;SAClD,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,IAAI,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;IACvD,CAAC;IAED,sEAAsE;IACtE,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,0EAA0E;IAC1E,SAAS;QACP,IAAI,IAAI,CAAC,UAAU;YACjB,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recordable",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Programmatic, repeatable browser screen recording. Script a walkthrough as a fluent chain of actions — clicks, typing, smooth zoom/scroll, an animated cursor, and manual steps like logins — and capture it to MP4.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,15 +36,17 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc",
|
|
38
38
|
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
39
|
-
"test": "tsx --test test
|
|
40
|
-
"test:e2e": "tsx --test test/e2e
|
|
39
|
+
"test": "tsx --test test/*.test.ts",
|
|
40
|
+
"test:e2e": "tsx --test test/e2e/*.e2e.ts",
|
|
41
41
|
"clean": "rm -rf dist",
|
|
42
42
|
"gen:schema": "tsx scripts/gen-schema.ts",
|
|
43
43
|
"lint": "eslint .",
|
|
44
44
|
"lint:fix": "eslint . --fix",
|
|
45
45
|
"format": "prettier --write .",
|
|
46
46
|
"format:check": "prettier --check .",
|
|
47
|
-
"prepublishOnly": "npm run clean && npm run gen:schema && npm run build"
|
|
47
|
+
"prepublishOnly": "npm run clean && npm run gen:schema && npm run build",
|
|
48
|
+
"commitlint": "commitlint",
|
|
49
|
+
"prepare": "husky && git config commit.template .gitmessage 2>/dev/null || true"
|
|
48
50
|
},
|
|
49
51
|
"keywords": [
|
|
50
52
|
"screen-recording",
|
|
@@ -61,10 +63,10 @@
|
|
|
61
63
|
"author": "Cam Parry <hello@camparry.com>",
|
|
62
64
|
"license": "MIT",
|
|
63
65
|
"engines": {
|
|
64
|
-
"node": ">=
|
|
66
|
+
"node": ">=20"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|
|
67
|
-
"
|
|
69
|
+
"ffmpeg-static": "^5.3.0",
|
|
68
70
|
"gray-matter": "^4.0.3",
|
|
69
71
|
"json5": "^2.2.3",
|
|
70
72
|
"markdown-it": "^14.2.0",
|
|
@@ -72,11 +74,15 @@
|
|
|
72
74
|
"zod": "^4.4.3"
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
77
|
+
"@commitlint/cli": "^19.8.1",
|
|
78
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
75
79
|
"@eslint/js": "^10.0.1",
|
|
76
80
|
"@types/markdown-it": "^14.1.2",
|
|
77
81
|
"@types/node": "^20.0.0",
|
|
78
82
|
"eslint": "^10.5.0",
|
|
79
83
|
"eslint-config-prettier": "^10.1.8",
|
|
84
|
+
"husky": "^9.1.7",
|
|
85
|
+
"lint-staged": "^15.5.2",
|
|
80
86
|
"prettier": "^3.8.4",
|
|
81
87
|
"tsx": "^4.0.0",
|
|
82
88
|
"typescript": "^5.0.0",
|
|
@@ -84,5 +90,12 @@
|
|
|
84
90
|
},
|
|
85
91
|
"optionalDependencies": {
|
|
86
92
|
"@elevenlabs/elevenlabs-js": "^2.0.0"
|
|
93
|
+
},
|
|
94
|
+
"lint-staged": {
|
|
95
|
+
"*.{ts,js,mjs,cjs}": [
|
|
96
|
+
"eslint --fix --no-warn-ignored",
|
|
97
|
+
"prettier --write"
|
|
98
|
+
],
|
|
99
|
+
"*.{json,md,yml,yaml,css,html}": "prettier --write"
|
|
87
100
|
}
|
|
88
101
|
}
|
package/recordable.schema.json
CHANGED
|
@@ -132,6 +132,9 @@
|
|
|
132
132
|
"properties": {
|
|
133
133
|
"action": {
|
|
134
134
|
"enum": [
|
|
135
|
+
"start",
|
|
136
|
+
"end",
|
|
137
|
+
"split",
|
|
135
138
|
"pause",
|
|
136
139
|
"resume",
|
|
137
140
|
"waitForPlay",
|
|
@@ -157,6 +160,81 @@
|
|
|
157
160
|
}
|
|
158
161
|
},
|
|
159
162
|
"allOf": [
|
|
163
|
+
{
|
|
164
|
+
"if": {
|
|
165
|
+
"properties": {
|
|
166
|
+
"action": {
|
|
167
|
+
"const": "start"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"required": [
|
|
171
|
+
"action"
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
"then": {
|
|
175
|
+
"properties": {
|
|
176
|
+
"action": {
|
|
177
|
+
"const": "start"
|
|
178
|
+
},
|
|
179
|
+
"name": {
|
|
180
|
+
"type": "string"
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"required": [
|
|
184
|
+
"action"
|
|
185
|
+
],
|
|
186
|
+
"additionalProperties": false
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"if": {
|
|
191
|
+
"properties": {
|
|
192
|
+
"action": {
|
|
193
|
+
"const": "end"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"required": [
|
|
197
|
+
"action"
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
"then": {
|
|
201
|
+
"properties": {
|
|
202
|
+
"action": {
|
|
203
|
+
"const": "end"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"required": [
|
|
207
|
+
"action"
|
|
208
|
+
],
|
|
209
|
+
"additionalProperties": false
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"if": {
|
|
214
|
+
"properties": {
|
|
215
|
+
"action": {
|
|
216
|
+
"const": "split"
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"required": [
|
|
220
|
+
"action"
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"then": {
|
|
224
|
+
"properties": {
|
|
225
|
+
"action": {
|
|
226
|
+
"const": "split"
|
|
227
|
+
},
|
|
228
|
+
"name": {
|
|
229
|
+
"type": "string"
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
"required": [
|
|
233
|
+
"action"
|
|
234
|
+
],
|
|
235
|
+
"additionalProperties": false
|
|
236
|
+
}
|
|
237
|
+
},
|
|
160
238
|
{
|
|
161
239
|
"if": {
|
|
162
240
|
"properties": {
|