Export (Final Video)
This is the final step in the workflow, and the only part of the entire product that actually renders pixels.
All previous steps are just annotations: the ledger records "which words to include in the broadcast", subtitles record "what to display on screen", visuals record "which layers to overlay", and the preview combines these three in real-time for viewing without saving to disk. Export burns them into a single file.
text
Required: edit-list.json (mandatory), subtitles.json, visuals.json + modules/
Output: final-video.mp4
Prerequisite tools:
Google Chrome (used to render subtitles and animations as images). FFmpeg/FFprobe from desktop installations are already managed in the Product directory via the App; pure CLI installations require
on the system. A clear error will be thrown if Chrome is missing — do not attempt to bypass this requirement, as Chrome is essential for subtitle and animation layers.
First, read and execute the "Conclusion Level" section in the Business Skill Stage Contract. Export does not enter the editing state machine: it does not modify any project files, perform CAS writes, or advance the stage. The output is a new file, and re-running the command will overwrite it. Therefore, no confirmation prompt is required.
0. Readiness
First, perform the "Readiness Check" from
Check for Updates — verify if skills are up-to-date and if the Runtime is compatible; the
plugin root is also located there (replace
in commands with the actual path). Proceed only if the result is "Ready"; if "New Session Required" or "Stop", follow the corresponding instructions (including the "Prohibit Custom Alternative Interfaces" rule). Business Skills do not include their own environment logic.
If the readiness result is
runtime.kind=desktop-managed
, directly reuse the stable CLI, media tools, and the same
service installed by the desktop App; do not parse the Electron path, install additional FFmpeg/Bun, or start a second Runtime.
Commands
bash
node "<plugin root>/scripts/ensure-running.cjs" --json
node "<plugin root>/scripts/videocut-cli.cjs" export <project> --dry-run --json # Preview the plan without encoding
node "<plugin root>/scripts/videocut-cli.cjs" export <project> --json # Generate final video (default 2x scale, source frame rate)
node "<plugin root>/scripts/videocut-cli.cjs" export <project> --out /path/final-video.mp4 --json
node "<plugin root>/scripts/videocut-cli.cjs" export <project> --scale 1 --json # Use source resolution only
node "<plugin root>/scripts/videocut-cli.cjs" export <project> --keep-work --json # Keep intermediate clips and frame-by-frame PNGs for troubleshooting
Stop immediately if
encounters identity mismatch, port conflict, or unhealthy service; do not use a foreground temporary replacement to continue exporting.
Two Steps — Do Not Skip the First
text
① Run --dry-run first to report the plan: video duration, number of frames, number of subtitle screens, number of visual layers, number of zoom-in segments, output resolution. Read this aloud to the user. If the numbers are incorrect, the upstream steps are wrong — encoding for ten minutes won't fix it.
② Execute the actual export: progress through four stages: assemble → overlay → compose → verify
The
in
must be relayed exactly. They only report one type of issue:
some subtitle screens or visual layers correspond to words that have been cut, so they will not appear in the final video. This is a decision for upstream steps, not something the export process should suppress.
Clarity: Source Video is the Ceiling — Check the Source Before Enlarging
Check the source resolution before exporting (the
field in
provides this):
text
Source width ≥2560 (Retina native screen recording) → Use --scale 1; the output will be native pixels, which is the best scenario.
Source width <1920 (e.g., 960×720) → Pause first: ask the user if there is a high-definition export of the same recording. Screen recording tools often allow re-exporting the same recording at 3x resolution. Replacing the source is more effective than any post-processing (see next section). If no HD source is available → Use --scale 2.
is useful for low-resolution sources because: while enlarging the base video won't make it clearer,
subtitles and animations are re-rendered at the output resolution — these are what the audience actually reads; when the platform compresses the video again, larger files get more bitrate allocation.
Do not adjust for "better clarity". The frame rate should follow the source video; changing it will only misalign animation sampling with the screen recording.
Source Replacement: When the User Provides a High-Definition Version of the Same Recording
This is not part of the export process — export is always read-only. Source replacement is an independent material operation that requires explicit user confirmation; complete it before returning to normal export.
First, verify if they are the same recording (the first two checks are definitive):
bash
ffprobe -v error -show_entries format=duration -of csv=p=0 <two files> # Durations must be identical down to the millisecond
ffmpeg -v error -i <file> -map 0:a:0 -c copy -f md5 - # Audio streams must be bit-for-bit identical
# Extract two or three frames from each and visually compare the content
Bit-for-bit identical audio = identical timeline: The word-by-word script, ledger, subtitles, and visual layers are all tied to word IDs — no changes are needed. This is the realization of "binding to words instead of timestamps" for source replacement.
Four Steps for Source Replacement (tested on 2026-07-29; step ③ was missed at the time, causing the editing preview to throw a "generation failed" error — fingerprint records exist in multiple places; grep for the old fingerprint to find all instances before proceeding):
text
① Replace the files: input/source.mp4 and uploads/source.mp4 are hard-linked — delete both, copy the new file to input/, then create a hard link back to uploads/ (use `ln` on macOS; use `New-Item -ItemType HardLink` on Windows)
② Update the fingerprint: Change the source.sha256 in project.json to that of the new file
③ Check another location: workbench.json also records the source fingerprint (sourceSha256), which is exactly what the editing preview pipeline verifies — missing this will cause the preview to refuse generation (this is correct product behavior: if fingerprints don't match, fail instead of using an old preview)
④ Re-export with --scale 1 — the purpose of source replacement is to get native pixels, so don't enlarge again
Safe practice: Use your search tool to find the first 8 characters of the old fingerprint in all *.json files in the <project directory>; update every occurrence found (cache records like preview-edited/current.json will be recalculated automatically and do not require manual modification).
If the audio is different (re-recorded, edited, or duration differs), this is not source replacement but a new project: the word-by-word script must be re-transcribed, and all annotations are invalid. Do not force the source replacement process.
Acceptance Criteria: Three Items — Missing Any Means Export is Incomplete
text
① Command returns successfully: The product automatically compares the final video's resolution, frame count, and audio track with the plan. If they don't match, throw a readback_mismatch error; the file remains on disk as evidence, and export is not considered complete.
② Extract frames to check pixels: Extract frames from the final video and check visually. Cover at least: one zoom-in segment, one full-screen animation segment, one subtitle-only segment, and one layer boundary.
③ Human audio verification: If no one has listened to the audio, mark it as human listening UNVERIFIED.
② Do not use preview screenshots as a substitute. Preview and final video use different rendering paths; the purpose of acceptance is to verify that they match — using preview screenshots as evidence for the final video is treating the thing to be verified as a given.
To verify zoom-ins, use distinctive landmarks — overall impressions can be misleading (experienced on 2026-07-29): A 1.6x zoom-in on a screen page may still look like a "full page", with almost no difference from the full view in thumbnails — the correct zoom-in frame was once misjudged as "zoom-in missing", leading to half an hour of troubleshooting and two re-exports. The correct verification method is to look for evidence that can only result from cropping: elements cut in half (e.g., a bubble split in the middle), or edge elements that disappear (sidebars, title bars). Compare with the corresponding frame in the source video to confirm.
Use ffmpeg to extract frames:
bash
ffmpeg -v error -ss 8.84 -i final-video.mp4 -frames:v 1 -y frame.png
Troubleshooting
will save three items in the project's
.chengfeng-videocut/export/
directory, which directly isolate which part of the process failed:
text
assembled.mkv Contains only edited footage, no overlays. If this is wrong → The ledger or editing segments are incorrect.
overlay/*.png Contains only overlays with transparent backgrounds. If this is wrong → Subtitle styles or modules are incorrect.
spans/*.mp4 Segmented videos after composition. If this is wrong → Zoom-in or alignment is incorrect.
Troubleshooting Reference:
text
No subtitles/animations in final video → Are overlay PNGs fully transparent? Did the module not implement seek?
Animation stays on first frame → The module did not implement seek, or the GSAP timeline was not paused.
Entire screen is white → The module is missing `:root { color-scheme: dark }`.
Zoom-in frame is misaligned → The module's viewBox and layer's zoom values do not match.
Final video is shorter than planned → A span has insufficient frames; check errors in the compose stage.
Original footage flashes briefly at layer boundaries → Overlay screenshots are outdated (frame marker verification failed). The product uses a frame marker bar at the top of the page to prove which frame each screenshot belongs to; if this recurs, first confirm that overlay PNGs have the marker bar and that compose includes cropping to remove it.
Chrome not found → Install Google Chrome; do not switch to a different rendering path.
Prohibited Actions
- Do not confuse "export succeeded" with "acceptance passed" — a successful command return only means the product's internal checks passed, not that the video is correct.
- Do not use preview screenshots, DOM, or logs as substitutes for extracting frames from the final video.
- Do not report human listening PASS if no one has listened to the audio.
- Do not modify project files (edit subtitles, delete layers, alter the ledger) to make export succeed. Export is read-only and does not write to files.
- Do not perform upstream tasks during export: if subtitles are missing, go create them; if visuals are missing, go make them — do not temporarily patch them in this step.