videowright 0.1.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 +91 -0
- package/dist/cli/argv.d.ts +28 -0
- package/dist/cli/argv.d.ts.map +1 -0
- package/dist/cli/argv.js +115 -0
- package/dist/cli/argv.js.map +1 -0
- package/dist/cli/bin.d.ts +7 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +10 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/dev.d.ts +19 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +104 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/discover.d.ts +29 -0
- package/dist/cli/discover.d.ts.map +1 -0
- package/dist/cli/discover.js +104 -0
- package/dist/cli/discover.js.map +1 -0
- package/dist/cli/discover_project.d.ts +29 -0
- package/dist/cli/discover_project.d.ts.map +1 -0
- package/dist/cli/discover_project.js +108 -0
- package/dist/cli/discover_project.js.map +1 -0
- package/dist/cli/errors.d.ts +10 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +13 -0
- package/dist/cli/errors.js.map +1 -0
- package/dist/cli/ffmpeg.d.ts +57 -0
- package/dist/cli/ffmpeg.d.ts.map +1 -0
- package/dist/cli/ffmpeg.js +122 -0
- package/dist/cli/ffmpeg.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +152 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/playwright_check.d.ts +44 -0
- package/dist/cli/playwright_check.d.ts.map +1 -0
- package/dist/cli/playwright_check.js +20 -0
- package/dist/cli/playwright_check.js.map +1 -0
- package/dist/cli/prompt.d.ts +13 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +47 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/cli/render.d.ts +60 -0
- package/dist/cli/render.d.ts.map +1 -0
- package/dist/cli/render.js +471 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/script_cmd.d.ts +26 -0
- package/dist/cli/script_cmd.d.ts.map +1 -0
- package/dist/cli/script_cmd.js +88 -0
- package/dist/cli/script_cmd.js.map +1 -0
- package/dist/cli/time_shim.d.ts +44 -0
- package/dist/cli/time_shim.d.ts.map +1 -0
- package/dist/cli/time_shim.js +390 -0
- package/dist/cli/time_shim.js.map +1 -0
- package/dist/cli/ts_loader.d.ts +28 -0
- package/dist/cli/ts_loader.d.ts.map +1 -0
- package/dist/cli/ts_loader.js +95 -0
- package/dist/cli/ts_loader.js.map +1 -0
- package/dist/cli/vite_helpers.d.ts +62 -0
- package/dist/cli/vite_helpers.d.ts.map +1 -0
- package/dist/cli/vite_helpers.js +273 -0
- package/dist/cli/vite_helpers.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/player/hash_router.d.ts +23 -0
- package/dist/player/hash_router.d.ts.map +1 -0
- package/dist/player/hash_router.js +49 -0
- package/dist/player/hash_router.js.map +1 -0
- package/dist/player/hud.d.ts +33 -0
- package/dist/player/hud.d.ts.map +1 -0
- package/dist/player/hud.js +357 -0
- package/dist/player/hud.js.map +1 -0
- package/dist/player/index.d.ts +123 -0
- package/dist/player/index.d.ts.map +1 -0
- package/dist/player/index.js +848 -0
- package/dist/player/index.js.map +1 -0
- package/dist/player/input.d.ts +14 -0
- package/dist/player/input.d.ts.map +1 -0
- package/dist/player/input.js +90 -0
- package/dist/player/input.js.map +1 -0
- package/dist/player/slot.d.ts +22 -0
- package/dist/player/slot.d.ts.map +1 -0
- package/dist/player/slot.js +43 -0
- package/dist/player/slot.js.map +1 -0
- package/dist/player/transitions/cut.d.ts +7 -0
- package/dist/player/transitions/cut.d.ts.map +1 -0
- package/dist/player/transitions/cut.js +9 -0
- package/dist/player/transitions/cut.js.map +1 -0
- package/dist/player/transitions/fade.d.ts +7 -0
- package/dist/player/transitions/fade.d.ts.map +1 -0
- package/dist/player/transitions/fade.js +18 -0
- package/dist/player/transitions/fade.js.map +1 -0
- package/dist/player/transitions/index.d.ts +4 -0
- package/dist/player/transitions/index.d.ts.map +1 -0
- package/dist/player/transitions/index.js +4 -0
- package/dist/player/transitions/index.js.map +1 -0
- package/dist/player/transitions/slide.d.ts +6 -0
- package/dist/player/transitions/slide.d.ts.map +1 -0
- package/dist/player/transitions/slide.js +35 -0
- package/dist/player/transitions/slide.js.map +1 -0
- package/dist/script/index.d.ts +2 -0
- package/dist/script/index.d.ts.map +1 -0
- package/dist/script/index.js +2 -0
- package/dist/script/index.js.map +1 -0
- package/dist/script/script.d.ts +10 -0
- package/dist/script/script.d.ts.map +1 -0
- package/dist/script/script.js +41 -0
- package/dist/script/script.js.map +1 -0
- package/dist/segment/SegmentRunner.d.ts +52 -0
- package/dist/segment/SegmentRunner.d.ts.map +1 -0
- package/dist/segment/SegmentRunner.js +187 -0
- package/dist/segment/SegmentRunner.js.map +1 -0
- package/dist/segment/defineConfig.d.ts +6 -0
- package/dist/segment/defineConfig.d.ts.map +1 -0
- package/dist/segment/defineConfig.js +7 -0
- package/dist/segment/defineConfig.js.map +1 -0
- package/dist/segment/defineSegment.d.ts +7 -0
- package/dist/segment/defineSegment.d.ts.map +1 -0
- package/dist/segment/defineSegment.js +25 -0
- package/dist/segment/defineSegment.js.map +1 -0
- package/dist/segment/index.d.ts +5 -0
- package/dist/segment/index.d.ts.map +1 -0
- package/dist/segment/index.js +4 -0
- package/dist/segment/index.js.map +1 -0
- package/dist/timeline/index.d.ts +73 -0
- package/dist/timeline/index.d.ts.map +1 -0
- package/dist/timeline/index.js +142 -0
- package/dist/timeline/index.js.map +1 -0
- package/dist/timeline/loadAudioTrack.d.ts +18 -0
- package/dist/timeline/loadAudioTrack.d.ts.map +1 -0
- package/dist/timeline/loadAudioTrack.js +44 -0
- package/dist/timeline/loadAudioTrack.js.map +1 -0
- package/dist/timeline/loadVoiceover.d.ts +18 -0
- package/dist/timeline/loadVoiceover.d.ts.map +1 -0
- package/dist/timeline/loadVoiceover.js +38 -0
- package/dist/timeline/loadVoiceover.js.map +1 -0
- package/dist/timeline/resolveTiming.d.ts +28 -0
- package/dist/timeline/resolveTiming.d.ts.map +1 -0
- package/dist/timeline/resolveTiming.js +63 -0
- package/dist/timeline/resolveTiming.js.map +1 -0
- package/dist/timeline/validateTiming.d.ts +29 -0
- package/dist/timeline/validateTiming.d.ts.map +1 -0
- package/dist/timeline/validateTiming.js +62 -0
- package/dist/timeline/validateTiming.js.map +1 -0
- package/dist/types.d.ts +216 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
- package/skill/SKILL.md +64 -0
- package/skill/assets/hello_world/PLAN.md +31 -0
- package/skill/assets/hello_world/README.md +27 -0
- package/skill/assets/hello_world/audio/audio_plan.md +14 -0
- package/skill/assets/hello_world/segments/hello_intro.ts +69 -0
- package/skill/assets/hello_world/segments/hello_outro.ts +71 -0
- package/skill/assets/hello_world/timeline.ts +15 -0
- package/skill/assets/hello_world/voiceover_script/script.md +10 -0
- package/skill/assets/install/package.json +10 -0
- package/skill/assets/install/tsconfig.json +23 -0
- package/skill/assets/styles/editorial-mono/STYLE.md +124 -0
- package/skill/assets/styles/editorial-mono/brand.md +85 -0
- package/skill/assets/styles/editorial-mono/reference/animations.jsx +752 -0
- package/skill/assets/styles/editorial-mono/reference/scenes.html +563 -0
- package/skill/assets/styles/editorial-mono/sample/bullet.ts +101 -0
- package/skill/assets/styles/editorial-mono/sample/content.ts +104 -0
- package/skill/assets/styles/editorial-mono/sample/cta.ts +113 -0
- package/skill/assets/styles/editorial-mono/sample/feature.ts +111 -0
- package/skill/assets/styles/editorial-mono/sample/grid.ts +97 -0
- package/skill/assets/styles/editorial-mono/sample/kinetic.ts +96 -0
- package/skill/assets/styles/editorial-mono/sample/section.ts +101 -0
- package/skill/assets/styles/editorial-mono/sample/stat.ts +128 -0
- package/skill/assets/styles/editorial-mono/sample/title.ts +97 -0
- package/skill/assets/styles/editorial-mono/sample/ui-showcase.ts +159 -0
- package/skill/assets/styles/editorial-mono/tokens.css +44 -0
- package/skill/assets/styles/iso-diagram/STYLE.md +109 -0
- package/skill/assets/styles/iso-diagram/brand.md +32 -0
- package/skill/assets/styles/iso-diagram/reference/animations.jsx +673 -0
- package/skill/assets/styles/iso-diagram/reference/scenes.html +427 -0
- package/skill/assets/styles/iso-diagram/sample/bullet.ts +144 -0
- package/skill/assets/styles/iso-diagram/sample/content.ts +192 -0
- package/skill/assets/styles/iso-diagram/sample/cta.ts +162 -0
- package/skill/assets/styles/iso-diagram/sample/feature.ts +205 -0
- package/skill/assets/styles/iso-diagram/sample/grid.ts +181 -0
- package/skill/assets/styles/iso-diagram/sample/kinetic.ts +102 -0
- package/skill/assets/styles/iso-diagram/sample/section.ts +149 -0
- package/skill/assets/styles/iso-diagram/sample/stat.ts +164 -0
- package/skill/assets/styles/iso-diagram/sample/title.ts +173 -0
- package/skill/assets/styles/iso-diagram/sample/ui-showcase.ts +162 -0
- package/skill/assets/styles/iso-diagram/tokens.css +40 -0
- package/skill/assets/styles/motion-engineering/STYLE.md +106 -0
- package/skill/assets/styles/motion-engineering/brand.md +29 -0
- package/skill/assets/styles/motion-engineering/reference/animations.jsx +673 -0
- package/skill/assets/styles/motion-engineering/reference/scenes.html +513 -0
- package/skill/assets/styles/motion-engineering/sample/bullet.ts +176 -0
- package/skill/assets/styles/motion-engineering/sample/content.ts +228 -0
- package/skill/assets/styles/motion-engineering/sample/cta.ts +209 -0
- package/skill/assets/styles/motion-engineering/sample/feature.ts +299 -0
- package/skill/assets/styles/motion-engineering/sample/grid.ts +190 -0
- package/skill/assets/styles/motion-engineering/sample/kinetic.ts +159 -0
- package/skill/assets/styles/motion-engineering/sample/section.ts +196 -0
- package/skill/assets/styles/motion-engineering/sample/stat.ts +230 -0
- package/skill/assets/styles/motion-engineering/sample/title.ts +219 -0
- package/skill/assets/styles/motion-engineering/sample/ui-showcase.ts +267 -0
- package/skill/assets/styles/motion-engineering/tokens.css +40 -0
- package/skill/assets/styles/neon-terminal/STYLE.md +105 -0
- package/skill/assets/styles/neon-terminal/brand.md +27 -0
- package/skill/assets/styles/neon-terminal/reference/animations.jsx +673 -0
- package/skill/assets/styles/neon-terminal/reference/scenes.html +387 -0
- package/skill/assets/styles/neon-terminal/sample/bullet.ts +113 -0
- package/skill/assets/styles/neon-terminal/sample/content.ts +117 -0
- package/skill/assets/styles/neon-terminal/sample/cta.ts +131 -0
- package/skill/assets/styles/neon-terminal/sample/feature.ts +112 -0
- package/skill/assets/styles/neon-terminal/sample/grid.ts +128 -0
- package/skill/assets/styles/neon-terminal/sample/kinetic.ts +105 -0
- package/skill/assets/styles/neon-terminal/sample/section.ts +96 -0
- package/skill/assets/styles/neon-terminal/sample/stat.ts +123 -0
- package/skill/assets/styles/neon-terminal/sample/title.ts +122 -0
- package/skill/assets/styles/neon-terminal/sample/ui-showcase.ts +127 -0
- package/skill/assets/styles/neon-terminal/tokens.css +39 -0
- package/skill/assets/styles/risograph/STYLE.md +110 -0
- package/skill/assets/styles/risograph/brand.md +26 -0
- package/skill/assets/styles/risograph/reference/animations.jsx +673 -0
- package/skill/assets/styles/risograph/reference/scenes.html +403 -0
- package/skill/assets/styles/risograph/sample/bullet.ts +124 -0
- package/skill/assets/styles/risograph/sample/content.ts +135 -0
- package/skill/assets/styles/risograph/sample/cta.ts +149 -0
- package/skill/assets/styles/risograph/sample/feature.ts +152 -0
- package/skill/assets/styles/risograph/sample/grid.ts +123 -0
- package/skill/assets/styles/risograph/sample/kinetic.ts +125 -0
- package/skill/assets/styles/risograph/sample/section.ts +130 -0
- package/skill/assets/styles/risograph/sample/stat.ts +145 -0
- package/skill/assets/styles/risograph/sample/title.ts +132 -0
- package/skill/assets/styles/risograph/sample/ui-showcase.ts +147 -0
- package/skill/assets/styles/risograph/tokens.css +39 -0
- package/skill/assets/styles/swiss-console/STYLE.md +107 -0
- package/skill/assets/styles/swiss-console/brand.md +37 -0
- package/skill/assets/styles/swiss-console/reference/animations.jsx +673 -0
- package/skill/assets/styles/swiss-console/reference/scenes.html +420 -0
- package/skill/assets/styles/swiss-console/sample/bullet.ts +122 -0
- package/skill/assets/styles/swiss-console/sample/content.ts +137 -0
- package/skill/assets/styles/swiss-console/sample/cta.ts +109 -0
- package/skill/assets/styles/swiss-console/sample/feature.ts +163 -0
- package/skill/assets/styles/swiss-console/sample/grid.ts +145 -0
- package/skill/assets/styles/swiss-console/sample/kinetic.ts +117 -0
- package/skill/assets/styles/swiss-console/sample/section.ts +127 -0
- package/skill/assets/styles/swiss-console/sample/stat.ts +148 -0
- package/skill/assets/styles/swiss-console/sample/title.ts +148 -0
- package/skill/assets/styles/swiss-console/sample/ui-showcase.ts +198 -0
- package/skill/assets/styles/swiss-console/tokens.css +39 -0
- package/skill/install/INSTALL.md +400 -0
- package/skill/references/audio/audio_plan.md +199 -0
- package/skill/references/audio/build.md +208 -0
- package/skill/references/audio/cue_template.md +219 -0
- package/skill/references/audio/ffmpeg_cookbook.md +267 -0
- package/skill/references/audio/music/music.md +171 -0
- package/skill/references/audio/music/providers/elevenlabs.md +170 -0
- package/skill/references/audio/music/providers/manual.md +140 -0
- package/skill/references/audio/music/providers/openverse.md +265 -0
- package/skill/references/audio/sfx/providers/elevenlabs.md +152 -0
- package/skill/references/audio/sfx/providers/manual.md +117 -0
- package/skill/references/audio/sfx/providers/openverse.md +243 -0
- package/skill/references/audio/sfx/sfx.md +149 -0
- package/skill/references/audio/styles.md +102 -0
- package/skill/references/audio/sync.md +237 -0
- package/skill/references/audio/voiceover/animation_sync.md +142 -0
- package/skill/references/audio/voiceover/provider_script.md +153 -0
- package/skill/references/audio/voiceover/providers/elevenlabs.md +288 -0
- package/skill/references/audio/voiceover/providers/manual.md +100 -0
- package/skill/references/audio/voiceover/script_writing.md +100 -0
- package/skill/references/audio/voiceover/style_intake.md +56 -0
- package/skill/references/audio/voiceover/sync_algorithm.md +167 -0
- package/skill/references/audio/voiceover.md +296 -0
- package/skill/references/audio.md +135 -0
- package/skill/references/authoring_segment.md +446 -0
- package/skill/references/create_or_edit_video.md +232 -0
- package/skill/references/dev_server.md +157 -0
- package/skill/references/export.md +145 -0
- package/skill/references/new_video.md +117 -0
- package/skill/references/project_structure.md +144 -0
- package/skill/references/setup.md +109 -0
- package/skill/references/setup_new_style.md +158 -0
- package/skill/references/styles.md +154 -0
- package/skill/references/testing.md +115 -0
- package/skill/references/types.md +240 -0
- package/src/cli/entry/components/copy_button.ts +42 -0
- package/src/cli/entry/components/download_modal.ts +204 -0
- package/src/cli/entry/components/empty_state.ts +55 -0
- package/src/cli/entry/components/hide_hud_tab.ts +37 -0
- package/src/cli/entry/components/icons.ts +31 -0
- package/src/cli/entry/components/top_bar.ts +69 -0
- package/src/cli/entry/components/video_card.ts +57 -0
- package/src/cli/entry/dev_frame.ts +189 -0
- package/src/cli/entry/entry_index.ts +16 -0
- package/src/cli/entry/entry_video.ts +24 -0
- package/src/cli/entry/index.html +12 -0
- package/src/cli/entry/parse_slug.ts +14 -0
- package/src/cli/entry/render.html +17 -0
- package/src/cli/entry/render_entry.ts +121 -0
- package/src/cli/entry/styles/base.css +45 -0
- package/src/cli/entry/styles/components.css +605 -0
- package/src/cli/entry/styles/tokens.css +44 -0
- package/src/cli/entry/video.html +22 -0
- package/src/cli/entry/views/homepage.ts +66 -0
- package/src/cli/entry/views/video_view.ts +286 -0
- package/src/cli/entry/virtual.d.ts +8 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `videowright render` -- deterministic JS-injection-driven frame export.
|
|
3
|
+
*
|
|
4
|
+
* The player runs in render mode with a JS time shim injected into the page
|
|
5
|
+
* BEFORE navigation via `page.addInitScript()`. The shim virtualizes:
|
|
6
|
+
* - Date, Date.now, performance.now
|
|
7
|
+
* - setTimeout, setInterval, clearTimeout, clearInterval
|
|
8
|
+
* - requestAnimationFrame, cancelAnimationFrame
|
|
9
|
+
* - Element.prototype.animate (WAAPI)
|
|
10
|
+
*
|
|
11
|
+
* The render driver advances the virtual clock by `frameMs` per frame,
|
|
12
|
+
* firing timers and RAF callbacks deterministically. WAAPI animations
|
|
13
|
+
* are paused and driven by explicit `currentTime` updates.
|
|
14
|
+
*
|
|
15
|
+
* Frames are byte-identical across runs because:
|
|
16
|
+
* - All time APIs return virtual time controlled by the driver
|
|
17
|
+
* - hold() uses setTimeout, which is virtualized
|
|
18
|
+
* - clock() uses performance.now, which is virtualized
|
|
19
|
+
* - RAF callbacks fire exactly once per render frame
|
|
20
|
+
* - WAAPI animations advance by exactly frameMs per frame
|
|
21
|
+
*/
|
|
22
|
+
import type { Config, Timeline, VideoSummary } from "../types.js";
|
|
23
|
+
export interface RenderOptions {
|
|
24
|
+
cwd: string;
|
|
25
|
+
positional?: string;
|
|
26
|
+
width?: number;
|
|
27
|
+
height?: number;
|
|
28
|
+
fps?: number;
|
|
29
|
+
output?: string;
|
|
30
|
+
verbose?: boolean;
|
|
31
|
+
audioTrack?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface RenderResult {
|
|
34
|
+
outputPath: string;
|
|
35
|
+
frames: number;
|
|
36
|
+
duration: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve width, height, and fps using four-level precedence:
|
|
40
|
+
* 1. CLI args (opts.width/height/fps)
|
|
41
|
+
* 2. timeline.meta.resolution / timeline.meta.fps
|
|
42
|
+
* 3. config.defaults.resolution / config.defaults.fps
|
|
43
|
+
* 4. Hardcoded fallback (1920x1080 @ 60fps)
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveRenderParams(opts: Pick<RenderOptions, "width" | "height" | "fps">, timeline: Timeline, config: Config): {
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
fps: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Resolve which timeline to render when no positional arg is given.
|
|
52
|
+
* Exported for testability -- the branching logic depends on video count and TTY state.
|
|
53
|
+
*
|
|
54
|
+
* @returns The absolute timelinePath to render.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveRenderTarget(project: {
|
|
57
|
+
videos: VideoSummary[];
|
|
58
|
+
}, isTTY: boolean): Promise<string>;
|
|
59
|
+
export declare function runRender(opts: RenderOptions): Promise<RenderResult>;
|
|
60
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/cli/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,OAAO,KAAK,EAAc,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA2C9E,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAwCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC,EACrD,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACZ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAQhD;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE;IAAE,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,EACnC,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA4b1E"}
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `videowright render` -- deterministic JS-injection-driven frame export.
|
|
3
|
+
*
|
|
4
|
+
* The player runs in render mode with a JS time shim injected into the page
|
|
5
|
+
* BEFORE navigation via `page.addInitScript()`. The shim virtualizes:
|
|
6
|
+
* - Date, Date.now, performance.now
|
|
7
|
+
* - setTimeout, setInterval, clearTimeout, clearInterval
|
|
8
|
+
* - requestAnimationFrame, cancelAnimationFrame
|
|
9
|
+
* - Element.prototype.animate (WAAPI)
|
|
10
|
+
*
|
|
11
|
+
* The render driver advances the virtual clock by `frameMs` per frame,
|
|
12
|
+
* firing timers and RAF callbacks deterministically. WAAPI animations
|
|
13
|
+
* are paused and driven by explicit `currentTime` updates.
|
|
14
|
+
*
|
|
15
|
+
* Frames are byte-identical across runs because:
|
|
16
|
+
* - All time APIs return virtual time controlled by the driver
|
|
17
|
+
* - hold() uses setTimeout, which is virtualized
|
|
18
|
+
* - clock() uses performance.now, which is virtualized
|
|
19
|
+
* - RAF callbacks fire exactly once per render frame
|
|
20
|
+
* - WAAPI animations advance by exactly frameMs per frame
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync } from "node:fs";
|
|
23
|
+
import { dirname, resolve } from "node:path";
|
|
24
|
+
import { applyMetaDefaults, validateSegmentAdvances } from "../timeline/index.js";
|
|
25
|
+
import { loadAudioTrack } from "../timeline/loadAudioTrack.js";
|
|
26
|
+
import { resolveTiming } from "../timeline/resolveTiming.js";
|
|
27
|
+
import { validateAudioTrack, validateTiming } from "../timeline/validateTiming.js";
|
|
28
|
+
import { findConfig, resolveSlugOrPath } from "./discover.js";
|
|
29
|
+
import { discoverAllVideos } from "./discover_project.js";
|
|
30
|
+
import { UserError } from "./errors.js";
|
|
31
|
+
import { buildFfmpegArgs, findFfmpeg, spawnFfmpeg, writeWithBackpressure } from "./ffmpeg.js";
|
|
32
|
+
import { ensurePlaywright } from "./playwright_check.js";
|
|
33
|
+
import { promptVideoSelection } from "./prompt.js";
|
|
34
|
+
import { TIME_SHIM_SOURCE } from "./time_shim.js";
|
|
35
|
+
import { loadModule } from "./ts_loader.js";
|
|
36
|
+
/**
|
|
37
|
+
* Inline helper: render a progress bar string for the frame-capture loop.
|
|
38
|
+
* When stdout is a TTY, returns a single-line `\r`-overwritable bar.
|
|
39
|
+
* Otherwise returns a plain "Rendered X / Y" line.
|
|
40
|
+
*/
|
|
41
|
+
function formatProgress(frameCount, totalFrames, fps, isTTY) {
|
|
42
|
+
const renderedSec = (frameCount / fps).toFixed(1);
|
|
43
|
+
const totalSec = (totalFrames / fps).toFixed(1);
|
|
44
|
+
if (!isTTY) {
|
|
45
|
+
return `Rendered ${renderedSec}s / ${totalSec}s`;
|
|
46
|
+
}
|
|
47
|
+
const pct = Math.min(Math.round((frameCount / totalFrames) * 100), 100);
|
|
48
|
+
const suffix = `${String(pct).padStart(3)}% ${renderedSec}s / ${totalSec}s`;
|
|
49
|
+
// "[] " + suffix + 2 bracket chars
|
|
50
|
+
const chrome = 2 + 1 + suffix.length; // "[" + "]" + " " + suffix
|
|
51
|
+
const columns = process.stdout.columns ?? 80;
|
|
52
|
+
const barWidth = Math.max(10, Math.min(50, columns - chrome));
|
|
53
|
+
const filled = Math.round((frameCount / totalFrames) * barWidth);
|
|
54
|
+
const empty = barWidth - filled;
|
|
55
|
+
const bar = `[${"█".repeat(filled)}${"░".repeat(empty)}] ${suffix}`;
|
|
56
|
+
// Pad to full terminal width so shorter strings overwrite stale chars
|
|
57
|
+
return `\r${bar.padEnd(columns)}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build a flat schedule of frame indices at which to fire triggerNext().
|
|
61
|
+
* Each entry is { globalFrame, segmentId, advanceIndex }.
|
|
62
|
+
*/
|
|
63
|
+
function buildFrameSchedule(timeline, segmentAdvances, fps) {
|
|
64
|
+
const schedule = [];
|
|
65
|
+
let cumulativeFrames = 0;
|
|
66
|
+
for (const entry of timeline.segments) {
|
|
67
|
+
const advances = segmentAdvances.get(entry.id);
|
|
68
|
+
if (!advances)
|
|
69
|
+
continue;
|
|
70
|
+
for (let i = 0; i < advances.length; i++) {
|
|
71
|
+
const advanceFrame = cumulativeFrames + Math.round(advances[i] * fps);
|
|
72
|
+
schedule.push({
|
|
73
|
+
globalFrame: advanceFrame,
|
|
74
|
+
segmentId: entry.id,
|
|
75
|
+
advanceIndex: i,
|
|
76
|
+
isLast: i === advances.length - 1,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Advance cumulative by the last advance value (total segment duration)
|
|
80
|
+
cumulativeFrames += Math.round(advances[advances.length - 1] * fps);
|
|
81
|
+
}
|
|
82
|
+
return schedule;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve width, height, and fps using four-level precedence:
|
|
86
|
+
* 1. CLI args (opts.width/height/fps)
|
|
87
|
+
* 2. timeline.meta.resolution / timeline.meta.fps
|
|
88
|
+
* 3. config.defaults.resolution / config.defaults.fps
|
|
89
|
+
* 4. Hardcoded fallback (1920x1080 @ 60fps)
|
|
90
|
+
*/
|
|
91
|
+
export function resolveRenderParams(opts, timeline, config) {
|
|
92
|
+
const resolvedMeta = applyMetaDefaults(timeline, config).meta;
|
|
93
|
+
const [resolvedW, resolvedH] = resolvedMeta.resolution;
|
|
94
|
+
return {
|
|
95
|
+
width: opts.width ?? resolvedW,
|
|
96
|
+
height: opts.height ?? resolvedH,
|
|
97
|
+
fps: opts.fps ?? resolvedMeta.fps,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve which timeline to render when no positional arg is given.
|
|
102
|
+
* Exported for testability -- the branching logic depends on video count and TTY state.
|
|
103
|
+
*
|
|
104
|
+
* @returns The absolute timelinePath to render.
|
|
105
|
+
*/
|
|
106
|
+
export async function resolveRenderTarget(project, isTTY) {
|
|
107
|
+
if (project.videos.length === 0) {
|
|
108
|
+
throw new UserError("No videos found.", "Ask your coding agent to create one (e.g., /videowright new video).");
|
|
109
|
+
}
|
|
110
|
+
if (project.videos.length === 1) {
|
|
111
|
+
return project.videos[0].timelinePath;
|
|
112
|
+
}
|
|
113
|
+
if (!isTTY) {
|
|
114
|
+
const slugList = project.videos.map((v) => ` npx videowright render ${v.slug}`).join("\n");
|
|
115
|
+
throw new UserError(`Multiple videos found. Specify one explicitly:\n${slugList}`);
|
|
116
|
+
}
|
|
117
|
+
return promptVideoSelection(project.videos);
|
|
118
|
+
}
|
|
119
|
+
export async function runRender(opts) {
|
|
120
|
+
const { cwd, positional, verbose } = opts;
|
|
121
|
+
const output = opts.output ?? "output.mp4";
|
|
122
|
+
// 1. Validate dependencies
|
|
123
|
+
const ffmpegPath = findFfmpeg();
|
|
124
|
+
if (verbose)
|
|
125
|
+
console.log(`ffmpeg: ${ffmpegPath}`);
|
|
126
|
+
const pw = await ensurePlaywright();
|
|
127
|
+
// 2. Find config + timeline
|
|
128
|
+
const configPath = findConfig(cwd);
|
|
129
|
+
if (!configPath) {
|
|
130
|
+
throw new UserError(`No videowright.config.ts found at ${cwd}`, "The Videowright skill can scaffold one. Run setup first.");
|
|
131
|
+
}
|
|
132
|
+
// Discover all videos up front. This is reused for both render-target
|
|
133
|
+
// selection (when no positional arg) and the Vite virtual module plugin.
|
|
134
|
+
const projectInfo = await discoverAllVideos(cwd);
|
|
135
|
+
let timelinePath;
|
|
136
|
+
if (positional) {
|
|
137
|
+
const resolved = resolveSlugOrPath(positional, cwd);
|
|
138
|
+
if (!resolved) {
|
|
139
|
+
throw new UserError(`No video matching "${positional}" — not a slug under videos/, not a valid path.`);
|
|
140
|
+
}
|
|
141
|
+
timelinePath = resolved;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
timelinePath = await resolveRenderTarget(projectInfo, !!process.stdin.isTTY);
|
|
145
|
+
}
|
|
146
|
+
// Load config and timeline
|
|
147
|
+
const configMod = await loadModule(configPath);
|
|
148
|
+
const config = configMod.default;
|
|
149
|
+
if (!config) {
|
|
150
|
+
throw new UserError("Config file has no default export", "Use `export default defineConfig({ ... })` in videowright.config.ts.");
|
|
151
|
+
}
|
|
152
|
+
const timelineMod = await loadModule(timelinePath);
|
|
153
|
+
const timeline = timelineMod.default;
|
|
154
|
+
// Resolve resolution and fps using four-level precedence
|
|
155
|
+
const { width, height, fps } = resolveRenderParams(opts, timeline, config);
|
|
156
|
+
const frameMs = 1000 / fps;
|
|
157
|
+
// 2b. Resolve audio track if requested
|
|
158
|
+
const videoFolder = dirname(timelinePath);
|
|
159
|
+
const audioTrackSuppressed = opts.audioTrack === "none";
|
|
160
|
+
let audioFilePath;
|
|
161
|
+
let cliAudioTrackModule;
|
|
162
|
+
if (!audioTrackSuppressed && opts.audioTrack) {
|
|
163
|
+
// loadAudioTrack rewrites audio_file to an absolute path, validates
|
|
164
|
+
// file existence, so the returned audio track object is self-contained.
|
|
165
|
+
const trackResult = await loadAudioTrack({
|
|
166
|
+
videoFolder,
|
|
167
|
+
trackId: opts.audioTrack,
|
|
168
|
+
});
|
|
169
|
+
audioFilePath = trackResult.audioFilePath;
|
|
170
|
+
cliAudioTrackModule = trackResult.audioTrack;
|
|
171
|
+
if (verbose) {
|
|
172
|
+
console.log(`audio track: ${opts.audioTrack} (${audioFilePath})`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else if (!audioTrackSuppressed && timeline.default_audio_track) {
|
|
176
|
+
// default_audio_track.audio_file is relative to the video folder (the
|
|
177
|
+
// directory containing timeline.ts) per the AudioTrack type contract.
|
|
178
|
+
const defaultAudioPath = resolve(videoFolder, timeline.default_audio_track.audio_file);
|
|
179
|
+
if (!existsSync(defaultAudioPath)) {
|
|
180
|
+
throw new UserError(`Audio file not found: ${defaultAudioPath}`, "Check the audio_file path in your timeline's default_audio_track.");
|
|
181
|
+
}
|
|
182
|
+
audioFilePath = defaultAudioPath;
|
|
183
|
+
if (verbose) {
|
|
184
|
+
console.log(`audio track: default (${audioFilePath})`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Validate the active audio track before proceeding.
|
|
188
|
+
const activeAudioTrack = cliAudioTrackModule ?? (!audioTrackSuppressed ? timeline.default_audio_track : undefined);
|
|
189
|
+
if (activeAudioTrack) {
|
|
190
|
+
// File-existence validation: skip for CLI audio tracks because
|
|
191
|
+
// loadAudioTrack already checked audio_file and threw on missing.
|
|
192
|
+
// Run for default_audio_track where no prior check has been done.
|
|
193
|
+
if (!cliAudioTrackModule) {
|
|
194
|
+
const trackValidation = validateAudioTrack(activeAudioTrack, videoFolder);
|
|
195
|
+
if (!trackValidation.ok) {
|
|
196
|
+
throw new UserError(`Audio track validation failed:\n${trackValidation.errors.join("\n")}`, "Check audio track file paths.");
|
|
197
|
+
}
|
|
198
|
+
for (const w of trackValidation.warnings) {
|
|
199
|
+
console.warn(`Warning: ${w}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const segmentIds = timeline.segments.map((s) => s.id);
|
|
203
|
+
const timingValidation = validateTiming(activeAudioTrack.timing, segmentIds);
|
|
204
|
+
if (!timingValidation.ok) {
|
|
205
|
+
throw new UserError(`Audio track timing validation failed:\n${timingValidation.errors.join("\n")}`, "Check the timing object in your track.ts file.");
|
|
206
|
+
}
|
|
207
|
+
for (const w of timingValidation.warnings) {
|
|
208
|
+
console.warn(`Warning: ${w}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (verbose) {
|
|
212
|
+
console.log(`timeline: ${timelinePath}`);
|
|
213
|
+
console.log(`segments: ${timeline.segments.length}`);
|
|
214
|
+
console.log(`resolution: ${width}x${height} @ ${fps}fps`);
|
|
215
|
+
console.log(`output: ${output}`);
|
|
216
|
+
}
|
|
217
|
+
console.log("Starting render... this may take a while.");
|
|
218
|
+
// 3. Boot Vite dev server with render entry
|
|
219
|
+
const { createServer } = await import("vite");
|
|
220
|
+
const { findPackageRoot, fullReloadPlugin, globalsVirtualModulePlugin, projectVirtualModulePlugin, segmentDiscoveryPlugin, } = await import("./vite_helpers.js");
|
|
221
|
+
const pkgRoot = findPackageRoot();
|
|
222
|
+
const entryDir = resolve(pkgRoot, "src/cli/entry");
|
|
223
|
+
const server = await createServer({
|
|
224
|
+
configFile: false,
|
|
225
|
+
root: entryDir,
|
|
226
|
+
plugins: [
|
|
227
|
+
fullReloadPlugin(),
|
|
228
|
+
segmentDiscoveryPlugin(cwd),
|
|
229
|
+
globalsVirtualModulePlugin({
|
|
230
|
+
timelinePath,
|
|
231
|
+
consumerRoot: cwd,
|
|
232
|
+
renderFps: fps,
|
|
233
|
+
}),
|
|
234
|
+
projectVirtualModulePlugin(projectInfo),
|
|
235
|
+
],
|
|
236
|
+
server: {
|
|
237
|
+
port: 0,
|
|
238
|
+
strictPort: false,
|
|
239
|
+
fs: {
|
|
240
|
+
allow: [cwd, pkgRoot],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
resolve: {
|
|
244
|
+
alias: {
|
|
245
|
+
"@consumer": cwd,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
await server.listen();
|
|
250
|
+
const serverUrl = server.resolvedUrls?.local?.[0];
|
|
251
|
+
if (!serverUrl) {
|
|
252
|
+
await server.close();
|
|
253
|
+
throw new UserError("Vite dev server started but did not report a URL", "This usually means the auto-assigned port failed. Try again or specify a port.");
|
|
254
|
+
}
|
|
255
|
+
const renderUrl = serverUrl.replace(/\/$/, "/render.html");
|
|
256
|
+
if (verbose)
|
|
257
|
+
console.log(`render server: ${renderUrl}`);
|
|
258
|
+
// Track resources for signal cleanup (SIGINT/SIGTERM)
|
|
259
|
+
let ffmpegProcRef = null;
|
|
260
|
+
let browserRef = null;
|
|
261
|
+
const signalCleanup = async () => {
|
|
262
|
+
if (ffmpegProcRef?.stdin) {
|
|
263
|
+
try {
|
|
264
|
+
ffmpegProcRef.stdin.end();
|
|
265
|
+
}
|
|
266
|
+
catch { }
|
|
267
|
+
}
|
|
268
|
+
if (ffmpegProcRef) {
|
|
269
|
+
try {
|
|
270
|
+
ffmpegProcRef.kill();
|
|
271
|
+
}
|
|
272
|
+
catch { }
|
|
273
|
+
}
|
|
274
|
+
if (browserRef) {
|
|
275
|
+
try {
|
|
276
|
+
await browserRef.close();
|
|
277
|
+
}
|
|
278
|
+
catch { }
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
await server.close();
|
|
282
|
+
}
|
|
283
|
+
catch { }
|
|
284
|
+
};
|
|
285
|
+
const onSignal = () => {
|
|
286
|
+
signalCleanup().finally(() => process.exit(1));
|
|
287
|
+
};
|
|
288
|
+
process.on("SIGINT", onSignal);
|
|
289
|
+
process.on("SIGTERM", onSignal);
|
|
290
|
+
try {
|
|
291
|
+
// 4. Launch headless browser (no special CDP flags)
|
|
292
|
+
const browser = await pw.chromium.launch({ headless: true });
|
|
293
|
+
browserRef = browser;
|
|
294
|
+
const context = await browser.newContext({
|
|
295
|
+
viewport: { width, height },
|
|
296
|
+
deviceScaleFactor: 1,
|
|
297
|
+
});
|
|
298
|
+
const page = await context.newPage();
|
|
299
|
+
// Inject time shim BEFORE navigation so it runs before any user code
|
|
300
|
+
await page.addInitScript({ content: TIME_SHIM_SOURCE });
|
|
301
|
+
// Surface page errors
|
|
302
|
+
const pageErrors = [];
|
|
303
|
+
page.on("pageerror", (...args) => {
|
|
304
|
+
const err = args[0] instanceof Error ? args[0] : new Error(String(args[0]));
|
|
305
|
+
pageErrors.push(err);
|
|
306
|
+
if (verbose)
|
|
307
|
+
console.error(`[page error] ${err.message}`);
|
|
308
|
+
});
|
|
309
|
+
try {
|
|
310
|
+
await page.goto(renderUrl, { waitUntil: "domcontentloaded" });
|
|
311
|
+
// Wait for render mode to be ready
|
|
312
|
+
await page.waitForFunction("window.__VW_RENDER_READY__ === true", {
|
|
313
|
+
timeout: 30000,
|
|
314
|
+
});
|
|
315
|
+
// Check for boot errors
|
|
316
|
+
const bootError = (await page.evaluate("window.__VW_RENDER_ERROR__"));
|
|
317
|
+
if (bootError) {
|
|
318
|
+
throw new UserError(`Render boot failed: ${bootError}`);
|
|
319
|
+
}
|
|
320
|
+
// Switch time shim from passthrough (real timers for boot) to
|
|
321
|
+
// driver-controlled mode (virtual timers for deterministic capture).
|
|
322
|
+
await page.evaluate("window.__VW_ENGAGE_VIRTUAL_TIME__()");
|
|
323
|
+
// Retrieve advances from the browser context (segments loaded via Vite,
|
|
324
|
+
// which can handle CSS/JSON/image imports that Node's tsImport cannot)
|
|
325
|
+
const advancesMap = (await page.evaluate("window.__VW_SEGMENT_ADVANCES__"));
|
|
326
|
+
// Validate advances
|
|
327
|
+
const advResult = validateSegmentAdvances(timeline, advancesMap ?? {});
|
|
328
|
+
if (!advResult.ok) {
|
|
329
|
+
throw new UserError(`Advances validation failed:\n${advResult.errors.join("\n")}`, "Check that every segment declares a valid 'advances' array.");
|
|
330
|
+
}
|
|
331
|
+
// Build minimal segment objects from browser-sourced advances
|
|
332
|
+
// for resolveTiming's base layer.
|
|
333
|
+
const browserSegments = timeline.segments.map((entry) => ({
|
|
334
|
+
id: entry.id,
|
|
335
|
+
advances: advancesMap[entry.id] ?? [],
|
|
336
|
+
}));
|
|
337
|
+
// Resolve timing using the four-level precedence. Audio track timing
|
|
338
|
+
// overlays on top of segment advances from the browser.
|
|
339
|
+
const resolved = resolveTiming({
|
|
340
|
+
segments: browserSegments,
|
|
341
|
+
defaultTiming: timeline.default_timing,
|
|
342
|
+
defaultAudioTrack: timeline.default_audio_track,
|
|
343
|
+
cliAudioTrackId: opts.audioTrack,
|
|
344
|
+
cliAudioTrackModule,
|
|
345
|
+
});
|
|
346
|
+
if (verbose && resolved.source !== "segments") {
|
|
347
|
+
console.log(`timing source: ${resolved.source}`);
|
|
348
|
+
}
|
|
349
|
+
// Build segmentAdvances Map from the resolved data
|
|
350
|
+
const segmentAdvances = new Map();
|
|
351
|
+
for (const entry of timeline.segments) {
|
|
352
|
+
const advances = resolved.perSegment[entry.id];
|
|
353
|
+
if (advances) {
|
|
354
|
+
segmentAdvances.set(entry.id, advances);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Build frame schedule and compute total frames
|
|
358
|
+
const schedule = buildFrameSchedule(timeline, segmentAdvances, fps);
|
|
359
|
+
let totalFrames = 0;
|
|
360
|
+
for (const entry of timeline.segments) {
|
|
361
|
+
const advances = segmentAdvances.get(entry.id);
|
|
362
|
+
if (advances && advances.length > 0) {
|
|
363
|
+
totalFrames += Math.round(advances[advances.length - 1] * fps);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (verbose) {
|
|
367
|
+
console.log(`total frames: ${totalFrames} (${(totalFrames / fps).toFixed(1)}s)`);
|
|
368
|
+
console.log("render mode ready, capturing frames...");
|
|
369
|
+
}
|
|
370
|
+
// 5. Start ffmpeg process
|
|
371
|
+
const outputPath = resolve(cwd, output);
|
|
372
|
+
const ffmpegArgs = buildFfmpegArgs({ fps, outputPath, audioFilePath });
|
|
373
|
+
const { process: ffmpegProc, done: ffmpegDone } = spawnFfmpeg(ffmpegPath, ffmpegArgs);
|
|
374
|
+
ffmpegProcRef = ffmpegProc;
|
|
375
|
+
// Attach error handler once outside the loop to avoid MaxListenersExceeded
|
|
376
|
+
const ffmpegErrors = [];
|
|
377
|
+
ffmpegProc.stdin?.on("error", (err) => {
|
|
378
|
+
ffmpegErrors.push(err);
|
|
379
|
+
});
|
|
380
|
+
let frameCount = 0;
|
|
381
|
+
let scheduleIdx = 0;
|
|
382
|
+
const progressIntervalFrames = Math.round(1 * fps); // every 1s of video
|
|
383
|
+
let lastProgressFrame = 0;
|
|
384
|
+
try {
|
|
385
|
+
// 6. Frame-by-frame render loop
|
|
386
|
+
while (frameCount < totalFrames) {
|
|
387
|
+
if (ffmpegErrors.length > 0) {
|
|
388
|
+
throw new UserError(`ffmpeg stdin error: ${ffmpegErrors[0].message}`);
|
|
389
|
+
}
|
|
390
|
+
// Check for page errors
|
|
391
|
+
if (pageErrors.length > 0) {
|
|
392
|
+
const lastErr = pageErrors[pageErrors.length - 1];
|
|
393
|
+
throw new UserError(`Page error during rendering: ${lastErr.message}`, "Check segment play() or mount() for runtime errors.");
|
|
394
|
+
}
|
|
395
|
+
// Check if we need to advance before this frame
|
|
396
|
+
while (scheduleIdx < schedule.length && schedule[scheduleIdx].globalFrame <= frameCount) {
|
|
397
|
+
const entry = schedule[scheduleIdx];
|
|
398
|
+
// Fire renderAdvance (starts transition, does not await completion --
|
|
399
|
+
// WAAPI transitions are driven by the shim's clock advance)
|
|
400
|
+
const hasMore = (await page.evaluate(`window.__VW_RENDER_ADVANCE__(${entry.isLast})`));
|
|
401
|
+
// Check for errors after advance
|
|
402
|
+
const renderError = (await page.evaluate("window.__VW_RENDER_ERROR__"));
|
|
403
|
+
if (renderError) {
|
|
404
|
+
throw new UserError(`Render error: ${renderError}`);
|
|
405
|
+
}
|
|
406
|
+
// Coherence check: segment transitions
|
|
407
|
+
const currentSegment = (await page.evaluate("document.body.dataset.vwSegment"));
|
|
408
|
+
if (!entry.isLast && currentSegment !== entry.segmentId) {
|
|
409
|
+
throw new UserError(`Segment "${entry.segmentId}" transitioned at advance index ${entry.advanceIndex} but advances array has more entries. Remove unused entries from the advances array.`);
|
|
410
|
+
}
|
|
411
|
+
if (entry.isLast && !hasMore && scheduleIdx < schedule.length - 1) {
|
|
412
|
+
// Last advance for this segment but schedule has more segments
|
|
413
|
+
// and renderAdvance returned false -- timeline ended too early
|
|
414
|
+
if (verbose) {
|
|
415
|
+
console.warn(`Warning: renderAdvance returned false at segment "${entry.segmentId}" advance ${entry.advanceIndex}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
scheduleIdx++;
|
|
419
|
+
}
|
|
420
|
+
// Advance virtual clock by one frame's worth of time.
|
|
421
|
+
// This fires pending timers, RAF callbacks, and updates WAAPI animations.
|
|
422
|
+
await page.evaluate(`window.__VW_ADVANCE_CLOCK__(${frameMs})`);
|
|
423
|
+
// Capture frame
|
|
424
|
+
const screenshot = (await page.screenshot({ type: "png" }));
|
|
425
|
+
if (!ffmpegProc.stdin?.writable)
|
|
426
|
+
break;
|
|
427
|
+
await writeWithBackpressure(ffmpegProc.stdin, screenshot);
|
|
428
|
+
frameCount++;
|
|
429
|
+
// Progress output every 1s of rendered video
|
|
430
|
+
if (frameCount - lastProgressFrame >= progressIntervalFrames) {
|
|
431
|
+
const line = formatProgress(frameCount, totalFrames, fps, !!process.stdout.isTTY);
|
|
432
|
+
if (process.stdout.isTTY) {
|
|
433
|
+
process.stdout.write(line);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
console.log(line);
|
|
437
|
+
}
|
|
438
|
+
lastProgressFrame = frameCount;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// End the progress bar line so subsequent output starts fresh
|
|
442
|
+
if (process.stdout.isTTY)
|
|
443
|
+
process.stdout.write("\n");
|
|
444
|
+
if (verbose)
|
|
445
|
+
console.log(` total frames: ${frameCount}`);
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
ffmpegProc.stdin?.end();
|
|
449
|
+
}
|
|
450
|
+
const ffmpegResult = await ffmpegDone;
|
|
451
|
+
if (ffmpegResult.code !== 0) {
|
|
452
|
+
if (verbose) {
|
|
453
|
+
console.error(`ffmpeg stderr:\n${ffmpegResult.stderr}`);
|
|
454
|
+
}
|
|
455
|
+
throw new UserError(`ffmpeg exited with code ${ffmpegResult.code}`, "Check ffmpeg output above for details.");
|
|
456
|
+
}
|
|
457
|
+
const duration = frameCount / fps;
|
|
458
|
+
return { outputPath, frames: frameCount, duration };
|
|
459
|
+
}
|
|
460
|
+
finally {
|
|
461
|
+
await page.close();
|
|
462
|
+
await browser.close();
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
finally {
|
|
466
|
+
process.removeListener("SIGINT", onSignal);
|
|
467
|
+
process.removeListener("SIGTERM", onSignal);
|
|
468
|
+
await server.close();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/cli/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `videowright script` — load a timeline and segments in Node, produce the
|
|
3
|
+
* voiceover script as Markdown.
|
|
4
|
+
*/
|
|
5
|
+
import type { SegmentLoaderMap } from "../timeline/index.js";
|
|
6
|
+
export interface ScriptOptions {
|
|
7
|
+
cwd: string;
|
|
8
|
+
positional?: string;
|
|
9
|
+
write?: boolean;
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ScriptResult {
|
|
13
|
+
markdown: string;
|
|
14
|
+
writtenTo?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build a SegmentLoaderMap by walking the cwd/segments/ directory in Node.
|
|
18
|
+
* Each segments/<id>/index.ts becomes an entry in the map.
|
|
19
|
+
* Directories without an index.ts are warned about.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildNodeSegmentLoaderMap(cwd: string): SegmentLoaderMap;
|
|
22
|
+
/**
|
|
23
|
+
* Run the script flow. Returns the markdown and optional write path.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runScript(opts: ScriptOptions): Promise<ScriptResult>;
|
|
26
|
+
//# sourceMappingURL=script_cmd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script_cmd.d.ts","sourceRoot":"","sources":["../../src/cli/script_cmd.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAK7D,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CA2CvE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAqC1E"}
|