SuperAPI AI Directory

video-editing

Design & creation

View repository

Updated 10-17 00:38

About this skill

Video Editing Skill — Video Editing Skill (V3)

Adapted for the three major platforms Xiaohongshu / Douyin / WeChat Channels. An end-to-end pipeline from material import → voice-over → story reassembly → platform gating → automatic enrichment → rendering → three-platform export → title copy, tuned for each platform's algorithm, aspect ratio, duration, and review rules—not just an editing tool.

V3 Complete Pipeline (Understand It in One Diagram)

口播音频 + 无声素材
   │
   ├─→ project_bootstrap.py     原始素材目录 → source inventory / project.md
   ├─→ edit_brief_plan.py       用户一句话需求 → 本地脚本 runbook / gates
   ├─→ runtime_preflight.py     workflow profile → FFmpeg/Node 命令、编码器、filters / live gate
   ├─→ interlace_conform.py     交错/telecine 采样 → progressive 工作副本 / 全长 A/B gate
   ├─→ frame_rate_conform.py    手机/录屏 VFR → 全量 PTS 检测 / CFR 工作副本 / live gate
   ├─→ black_edge_trim.py       首尾黑场 + 静音覆盖 → CFR 工作副本 / 原片边界 proof + live gate
   ├─→ program_breaks.py        内部黑场与全音轨静音重叠 → 只读分段候选 / 复核时间点
   ├─→ border_crop.py           画面内持续黑边 → 逐时点 cropdetect 证据 / 保守裁切建议
   ├─→ motion_center.py         局部像素运动中心 → 只读构图复核时间点
   ├─→ loop_fill.py             短片 → 次数/固定时长 hard repeat / 真实接缝 proof + 完整审片 gate
   ├─→ ping_pong_loop.py        短动作 → 端帧去重正放/倒放 / 折返+循环双 proof / live gate
   ├─→ clip_assembly.py         多源片段 → 单次画布/CFR/SAR/音频归一 / 全接缝 proof + live gate
   ├─→ edit_style_profile.py    个人/品牌创意方向、节奏与渲染/文案默认值 → 可移植 profile
   ├─→ production_authorization.py
   │                            确切素材/动作/provider/权利依据 → 显式授权 + live gate
   ├─→ transcribe.py            转写 + 词级时间戳 + 口误标记
   ├─→ transcript_silence_qa.py 原始转写 vs 实测静音 → 幻觉疑点供回听
   ├─→ transcript_lookup.py     原话检索 / 时间窗口读取 → 少量带源时间码的结果
   ├─→ visual_transcript_index.py
   │                            现有关键帧 + 附近原话 → 带源哈希的 JSON/Markdown 复核索引
   ├─→ semantic_transcript_review.py
   │                            全篇前后文审校包 / 最小补丁验证 / 人工 choices gate
   ├─→ transcript_review.py     本地同步媒体 HTML 校稿 / CPS 提示 / review.txt 回写
   ├─→ takes_pack.py            多 take / Scribe transcript → phrase-level 阅读视图
   │                            speaker / audio_event 编辑节拍
   ├─→ script_alignment.py      已审目标稿 → 多 take 原话候选 / choices / render_config
   │                            词/段边界 / 透明分数 / 歧义与缺素材 gate
   ├─→ audio_sync.py            外录音轨自动对齐 / 替换音轨计划
   ├─→ multicam_sync.py         多机位 → 参考时间线 / 时钟漂移证据 / 对齐预览 gate
   ├─→ multicam_switch.py       已同步机位 → 归一化说话者音频 / 自动导播草稿 / 完整审片 gate
   ├─→ scene_boundaries.py      fixed/adaptive 视觉切点 + 逐切点 evidence
   ├─→ visual_dedupe.py         多来源场景 → 感知哈希重复组 / 保留建议 / review gate
   ├─→ video_understanding.py   抽样帧 + 可选 YOLO 检测 / tracks / scene_tags
   ├─→ video_stabilization.py   手持素材 → source-bound 后端计划 / 工作副本 / 全长 A/B gate
   ├─→ chroma_key.py            绿幕/蓝幕 → composite + matte 代表帧 / 人工 review / 完整渲染 gate
   ├─→ highlight_picker.py      长视频精华候选 / brief-query 定向找片段
   ├─→ audio_boundary_snap.py   已选片段 → 词/句末/静音边界校正
   ├─→ shorts_batch.py          精华候选 → per-short render_config / render + QA job sheet
   ├─→ rough_cut.py             ASR 粗剪:去纯口头禅 / 相邻重复句
   ├─→ hook_variants.py         前三秒 hook 批量角度 / 推荐排序 / 风险检查
   ├─→ rewrite_script.py        LLM 重组 5 段式(hook/pain/turn/value/cta)
   ├─→ content_guard.py         80+ 条平台雷区 lint
   ├─→ source_receipts.py       事实 claim → URL/截图 source deck + publish gate
   ├─→ beat_sync.py             BGM beat-grid → 可审计剪辑骨架,或吸附已有切点
   ├─→ speed_ramp.py            impact ranges → source-bound 局部变速计划 / 验证 / apply
   ├─→ freeze_punch.py          impact frame → 定格替换窗口 / punch crop / unchanged audio timeline gate
   ├─→ auto_enrich.py           B-roll / 章节卡 / 贴纸 / 强调点 / BGM 卡点 / imagegen 提示词
   │       └─→ Codex imagegen   gpt-image-2 自动生图(抽象概念配图)
   ├─→ audio_cue_sheet.py       BGM / SFX 音频设计清单 / 生成审批 gate
   ├─→ audio_cue_mix.py         cue sheet + 最终旁白 → 本地/程序化 SFX 单轨 / 1× review gate
   ├─→ storyboard_plan.py       分镜 shot cards / 生成路由 / 连续性锚点
   ├─→ storyboard_animatic.py   每镜静帧 + 可选旁白 → timed MP4 / 1× review / live gate
   ├─→ photo_montage.py         有序照片 → 轻微缩放/叠化/可选配乐 MP4 + 源绑定收据
   ├─→ sequence_handoff.py      相邻镜头 receive/handoff / edit type / 轴线方向 / live gate
   ├─→ reference_story_formula.py
   │                            参考片时间码情绪机制 → 目标分镜 content anchors / live gate
   ├─→ provider_capability.py   provider/surface/model 能力合同 / 核验日期 / live gate
   ├─→ video_prompt_pack.py     Dreamina/Veo/LTX/Wan/Sora 提示词包 / 审批 + capability gate
   ├─→ reference_frame_preflight.py
   │                            首帧/style key 尺寸/方向/透明背景/画幅 gate
   ├─→ generation_task_log.py   异步生成任务台账 / submit_id / 下载 gate
   ├─→ generated_clip_review.py 生成片段 contact sheet / 常识物理 / 连续性 / 重生 gate
   ├─→ generation_chain_handoff.py
   │                            已审上一镜末帧 → 下一镜精确首帧 / 顺序生成 live gate
   ├─→ generated_motion_window.py
   │                            短生成片 0.25s freeze → active intervals / 人工裁切 / live gate
   ├─→ scoped_video_edit_review.py
   │                            原片 vs 局部 AI 编辑结果 / change-only + preserve invariants / A-B gate
   ├─→ generated_sequence_review.py
   │                            已审片段相邻尾帧/首帧/预览 / 跨镜头连续性 gate
   ├─→ final_audio_storyboard.py
   │                            锁定视觉 EDL + 原 storyboard → 最终声音分镜 / voice ledger / live gate
   ├─→ narration_loudness_qa.py 最终独立旁白 + 精确短语范围 → LUFS/spread/dBTP/LRA live gate
   ├─→ caption_speech_qa.py    subtitle_pack + 独立人声轨 → 孤立/错位/越界字幕 live gate
   ├─→ subtitle_glyph_qa.py   subtitle_pack + 显式字体 → 逐字符 cmap 覆盖 / 缺字 live gate
   ├─→ generation_lessons.py    已审片段 → provider/model scoped 提示词经验库
   ├─→ storyboard_assets.py     素材任务清单 / ready 预检 / paid 额度提醒
   │                            可选 media_library.py recommend 排名 B-roll 候选
   ├─→ stock_material_plan.py   远程 stock 搜索规划
   │                            Pexels / Pixabay / Coverr 查询计划 + 素材登记提示
   ├─→ screen_focus.py          录屏点击/热点 → 自动聚焦计划
   ├─→ pip_overlay.py           录屏 + facecam → PIP 小窗计划
   ├─→ color_grade.py           bounded 调色 plan / render_final 单次编码接入
   ├─→ lut_grade.py             外部 .cube LUT 校验 / SHA-256 绑定 / 单次编码接入
   ├─→ jump_cut.py              自适应去停顿 + 20% 删除预算 + 可审计 cut list + 30ms 防爆音 fade
   ├─→ multimodal_dead_air.py   静音 AND 静帧 → source-bound 保守删段 / 单次编码 / live gate
   ├─→ audio_transition.py      显式 J-cut/L-cut → source handle / hash / 1× 试听 gate
   ├─→ edit_revision.py         文本剪辑 artifact → source-bound 审批 / 成组 apply / undo / redo
   ├─→ edit_recipe.py           已审 render_config → typed-slot 可移植配方 / 新素材绑定回放
   ├─→ edit_preflight.py        render_config/enrich_plan/cut list 渲染前预检 gate
   ├─→ platform_safe_area_qa.py 字幕/PIP/CTA/marker → 平台 UI 安全区 gate + SVG guide
   ├─→ subtitle_style_preview.py 真实源帧 → 最终 ASS 预设对比 JPEG / 选择 / live gate
   ├─→ render_final.py          单次编码渲染(可选口播降噪 + enrich_plan/focus_events/pip_overlays + Heavy 字幕 + 响度规范化 + BGM ducking)
   │                            可选 --versioned-output 防覆盖旧成片
   ├─→ subtitle_render_review.py 最终 MP4 + subtitle_pack → 高风险字幕 1× clips / 原尺寸帧 / 完整审片 live gate
   ├─→ render_qa.py             渲染后黑屏/静帧/静音/尺寸质检 + review packet
   ├─→ stream_coverage_qa.py    最终 MP4 → decoded 音视频首尾 PTS / 帧数 / 容器覆盖 live gate
   ├─→ encode_quality_qa.py     同时间线 master vs 重编码件 → SSIM/PSNR / 最差帧 / live gate
   ├─→ audio_channel_qa.py      成片声道活动/起始/平衡/相位/mono fold-down live gate
   ├─→ audio_dropout_qa.py      最终人声/混音短促近静音候选 / 1× WAV 听审 live gate
   ├─→ flash_safety_qa.py       成片亮度/饱和红 flash → 1s/5s 风险窗口 / live gate
   ├─→ temporal_artifact_qa.py  单帧/少数帧 return-to-state spike → 三帧证据 / 人工 audit / live gate
   ├─→ shot_color_qa.py         成片镜头亮度/对比/色度/饱和度/broadcast-range + 切点跳变 gate
   ├─→ retention_rhythm_qa.py   成片 hook 活动 / 长镜头 / 注意力空窗 / 节奏 gate
   ├─→ reference_edit_rhythm.py 参考片 vs 成片 hard-cut 结构 / contact sheets / live gate
   ├─→ speech_continuity_qa.py  成片二次 ASR → 切点复读 / 近重复 take / 句内口吃 gate
   ├─→ lip_sync_review.py       最终 master → 1×/0.25× 口型证据 / 人工 audit / live gate
   ├─→ review_proxy.py          低码率完整审片 MP4 / 可见时间码 / faststart
   ├─→ timeline_view.py         源素材删除段 / 成片输出切点 filmstrip + waveform 复盘图
   ├─→ edit_compare.py          原片连续时钟 vs 最终像素 / 删段置黑 / 映射验证
   ├─→ subtitle_pack.py         SRT/VTT/ASS/JSON 字幕交付包(speed/offset 对齐)
   ├─→ soft_subtitles.py        已审 SRT → MP4 可开关字幕轨 / 无损复制音视频 / live 收据
   ├─→ chapter_mux.py           已审章节 JSON → MP4 可跳转章节 / 无损复制音视频 / live 收据
   ├─→ metadata_scrub.py       分享前清除 MP4 容器标签/章节/额外轨,保留音视频流
   ├─→ hls_vod.py               已审 SDR MP4 → 单码率 HLS 点播目录 / 分片+收据 live verify
   ├─→ subtitle_readability_qa.py
   │                            最终字幕 CPS / 时长 / 行长 / 重叠 / 媒体越界 gate
   ├─→ import_capcut_subtitles.py
   │                            剪映/CapCut 自动字幕 → transcript / gap cut list
   ├─→ srt_edit_plan.py         SRT + keep/drop 编辑指令 → render_config / cut list
   ├─→ project_resume.py        续跑上下文包 / agent handoff / 可选 CLAUDE.md
   ├─→ review_dashboard.py      静态 HTML/JSON 人工复核面板 / gate review queue
   ├─→ export_edl.py            render_config / cut list → EDL + manifest
   ├─→ export_fcpxml.py         render_config / cut list → FCPXML + manifest
   ├─→ export_otio.py           render_config / cut list → OTIO + manifest
   ├─→ framing_preview.py       master → 各平台 cover/contain/blur 真实帧 / 选择 / live gate
   ├─→ multi_export.py          已审画幅策略 → 小红书 3:4 / 抖音 9:16 / 视频号 ≤60s
   ├─→ hdr_sdr.py               PQ/HLG HDR → source-bound Rec.709 SDR / 完整解码 gate
   ├─→ video_enhancement.py     Lanczos 放大 + 可选运动补帧 / 全长 A-B + 带声复核 gate
   ├─→ delivery_encode.py       source-bound 两遍 H.264/AAC / 硬大小上限 / 完整解码 gate
   ├─→ gif_preview.py           视频短摘录 → 调色板 GIF / 源绑定收据 / 完整解码
   ├─→ logo_overlay.py          已审 MP4 + PNG Logo → 四角叠加 / 源绑定收据 / 完整解码
   ├─→ generate_caption.py      标题 + 200-500 字正文 + 3-6 tags + 发布时段
   ├─→ cover_variants.py        2-4 套封面 / feed-size 预览 / 最终选择 gate
   ├─→ approval_receipt.py      已复核交付件 → SHA-256 收据 / stale approval gate
   └─→ publish_package.py       平台视频/封面/字幕/章节/文案上传包 + gate 状态

Complete prompt template for making one short video per day: docs/prompts/15-xhs-daily-tech-video.md (recommended entry point).

For image generation, prioritize using Codex's built-in image_gen tool, namely OpenAI GPT Image 2 (gpt-image-2).

V3 New Scripts Overview (in Call Order)

Script Responsibility Key CLI
project_bootstrap.py Raw material directory → project structure / source inventory / project.md --source raw_dir --project-dir work/day61 `--mode copy
runtime_preflight.py workflow profile → local FFmpeg/Node commands, encoders, filters, environment drift live gate list-profiles / analyze --profile ... --output --strict / verify --report --strict
edit_brief_plan.py Natural language editing requirements → local script runbook / commands / manifest gate --brief --brief-file --source-media --platform --markdown --strict
interlace_conform.py Interlaced/telecine multi-segment detection → bwdif/yadif progressive working copy, full-length A/B manual confirmation and live gate analyze / `plan --mode frame
frame_rate_conform.py Mobile/screen-recording VFR → decoded PTS cadence, precise CFR working copy, frame count/audio-video start and end, and live gate plan <source> --fps 30 --delivery / apply <plan> / verify <plan> --strict
black_edge_trim.py Head/tail blackdetect + silence coverage → source-bound CFR working copy, original footage boundary proof and manual live gate plan --delivery --edge-proof / apply / confirm / verify --strict
program_breaks.py Overlap of internal black frames and silence across all audio tracks → source-bound segment candidate JSON/Markdown; no automatic slicing analyze <video> --output <report.json> / verify <report.json>
loop_fill.py progressive CFR short material → repeat by count/target duration, first real seam 1× proof, complete review and live gate `plan --times
ping_pong_loop.py progressive CFR short action → forward/reverse cycles with end-frame deduplication, turn-around/loop dual proof, cache limit and live gate `plan --start --end --cycles
clip_assembly.py Multi-source video → single canvas/CFR/SAR/timestamp/audio normalization, all-seam 1× proof and live gate plan <clips...> --delivery --boundary-proof / apply / confirm / verify --strict
podcast_audiogram.py Pure audio excerpt + still-frame cover + SRT relative to the excerpt time → vertical MP4 with auxiliary waveform and source-bound receipt render <audio> <cover> <captions.srt> --start --duration --output --receipt / verify <receipt>
gif_preview.py Video short excerpt → silent, palette-optimized GIF and source-bound receipt render <video> --start --duration --output --receipt / verify <receipt>
photo_montage.py 2–100 photos → H.264 MP4 with fixed rhythm, slight zoom, crossfade and optional music; verify source photos, music and final video render <photos...> --output --receipt [--music] / verify <receipt>
logo_overlay.py Reviewed MP4 + PNG Logo → overlay at specified corner/width/opacity, preserve audio track and verify on site render <video> <logo.png> --corner --width-fraction --opacity --margin --output --receipt / verify <receipt>
frame_grid.py Second-level timecode locates the nearest decoded frame; precise frame grid with visible grid numbers or single-frame PNG, with frame number/PTS and source file receipt locate <video> --at-seconds / grid <video> --start-frame --step --count --output --receipt / frame <video> --start-frame --output --receipt / verify <receipt>
border_crop.py Sample top/bottom/left/right black bars within the frame point by point over time; only suggest cropping when consistent across the entire video and three segments analyze <video> --output <report.json> / verify <report.json>
motion_center.py When no detection model is available, mark local motion centers point by point over time; no focus for large-area changes, for manual composition review analyze <video> --output <report.json> / verify <report.json>
edit_style_profile.py Personal/brand creative direction, editing rhythm, rendering/copy defaults → pathless portable profile / digest verification / defaults-only merge template / create --spec / verify --profile --strict / apply --config --receipt
production_authorization.py External uploads, invasive editing, paid generation, voice cloning, real people/IP and publishing → source-bound authorization gate prepare --scope --response-template / audit --request --response --strict / verify --report --strict
transcript_review.py transcript → text or local synchronized media HTML proofreading → reviewed transcript export / html --video --max-cps / apply --review --output
transcript_lookup.py read-only verbatim search or time-window reading, returns bounded JSON, source segment ID/timecode and transcript SHA-256 <transcript.json> search <phrase> --limit / <transcript.json> span --start --end
visual_transcript_index.py pair existing keyframes with nearby transcript segments, generate verifiable JSON/Markdown index build <keyframes.json> <transcript.json> --output --markdown / verify <index.json>
semantic_transcript_review.py transcript → context proofreading package / minimal patch audit / manual choices / reviewed transcript prepare / audit --strict / apply --choices
_internal_text_guard.py intercept internal token from entering the frame internal module, automatically called by render_final
content_guard.py platform landmine lint --script --title --caption --strict
source_receipts.py factual claim → URL/screenshot proof deck, Markdown/HTML and publish gate --claims source_claims.json --html --require-primary-source --strict
takes_pack.py multi take / top-level Scribe words → phrase-level Markdown/JSON, preserve speaker/audio events --transcript take1=... --transcripts-dir --json --break-gap
script_alignment.py reviewed target script → multi take word/segment boundary candidates, manual choices, render_config and gate --target-script --transcript label=... --media label=... --choices --render-config --strict
audio_sync.py scratch audio + external recording track → offset / replace audio track command / gate --reference-media --external-audio --replace-output --apply --strict
multicam_sync.py multi-camera → offset/coverage/audio track selection/pairwise/optional clock drift/alignment preview gate --reference-media --angle --manual-offset --measure-clock-drift --preview-output --apply-preview --strict
multicam_switch.py ready sync plan + speaker-mapped camera audio tracks → source-bound auto director draft, full decode and manual live gate plan --sync-plan --speaker --program-audio --delivery / apply / confirm / verify --strict
rough_cut.py transcript rough cut: remove filler words/repeated sentences --transcript --cut-list / --input --output
scene_boundaries.py FFmpeg fixed/adaptive scene score → scene boundaries + cut evidence --method adaptive --adaptive-threshold --min-scene-score --min-scene-duration
visual_dedupe.py multi-source scene three-point perceptual hash → duplicate groups / keep suggestions / review gate --manifest / <videos...> --hamming-threshold --include-same-source --strict
video_understanding.py sampled frames + optional YOLO object detection + lightweight tracklets --detector yolo --scene-boundaries --external-detections --strict
video_stabilization.py source video → exact FFmpeg backend / source hash / stable working copy / full-length A/B review gate doctor / plan --decision / apply --comparison / confirm / verify --strict
chroma_key.py green screen/blue screen foreground + image/video background → composite/matte preview, four-item manual review, source-bound full render gate prepare --foreground --background --output-video / review / apply / verify --strict
highlight_picker.py long video highlight candidates + brief/query targeted clip finding --transcript --brief/--query --scene-boundaries --render-config
audio_boundary_snap.py selected highlights → word-level, sentence-end and silence boundary correction + blocker --candidates --transcript --media --markdown --strict
shorts_batch.py highlight_candidates → multiple short video render_config / render + QA job sheet --highlights --video --render-config-dir --output-dir --strict
hook_variants.py transcript/clean_script → 8 first-three-seconds hook angles, risk check and recommended ranking --transcript --topic --platform --output --markdown --strict
rewrite_script.py LLM 5-part restructuring + validation --transcript --structure --hook-template --emit-prompt / --llm-output
auto_broll.py B-roll scheduling --transcript --assets --max-single-shot
media_library.py local asset library index + B-roll candidate recommendations + asset source registration init scan search recommend --category broll --json import annotate
stock_material_plan.py topic/script → Pexels/Pixabay/Coverr stock query plan --subject --script --provider --media-library --output --markdown
auto_chapter_cards.py chapter card PNG --script --audio --style --output-dir
auto_stickers.py emotion→sticker --transcript --min-interval
auto_emphasis.py question/number/transition/conclusion → badge + subtle push-in cues --transcript --output --markdown
beat_sync.py BGM → beat edit slots / Markdown review, or snap to existing cut points --bgm --generate-plan --beats-per-cut 4 --output ... --markdown ... / --cuts --window
speed_ramp.py impact ranges → snap/ease/s-curve/hold plan, digest validation and local transactional apply plan <video> --ramp --hold --interpolate-fps / verify --strict / apply --output --receipt
freeze_punch.py impact frame → source-bound freeze-frame emphasis, slight anchor crop, unchanged duration/audio and final video live gate plan <video> --freeze --delivery / apply <plan> / verify --strict
auto_enrich.py orchestrate B-roll / stickers / emphasis points / chapter cards / imagegen cues --transcript --clean-script --bgm --output
imagegen_hint.py detect abstract concepts → generate gpt-image-2 prompts --transcript --clean-script --codex-md
audio_cue_sheet.py transcript → BGM/SFX cue, generation approval and audio gate --transcript --asset-root --require-local-music --require-local-sfx --strict
audio_cue_mix.py audio cue sheet + final narration → local/procedural SFX, 48 kHz stereo single track, full audition live gate plan --cue-sheet --voice --delivery [--synthesize-missing] / apply / confirm / verify --strict
final_audio_storyboard.py locked visual EDL + storyboard → final timeline sound storyboard, voice ledger, omitted-story decisions and live gate prepare --edl --storyboard audit --request --response verify --report --strict
storyboard_plan.py transcript/clean_script → storyboard shot cards + generation routing --transcript --clean-script --output --markdown
reference_story_formula.py reference video + timecoded transcript + target storyboard → emotion/narrative mechanism, per-shot content anchor, copy exclusion strategy and source-bound live gate prepare --reference-video --reference-transcript --target-storyboard --response-template / audit --request --response / verify --report --strict
storyboard_animatic.py storyboard + per-shot still frames + optional narration → source-bound timed MP4, full 1× review and live gate plan --panel SHOT=PATH --delivery / apply / confirm / verify --strict
sequence_handoff.py storyboard → adjacent shot handoff, edit boundaries, 180° axis/direction, audio bridge, edit handles and source-bound live gate prepare --storyboard --response-template audit --request --response verify --report --strict
provider_capability.py exact provider/surface/model → dated mode/aspect ratio/duration/resolution/reference limit capability contract live gate verify --bundle --max-age-days --output --markdown --strict
video_prompt_pack.py storyboard_plan → multi-provider video generation prompt pack + reviewed story formula/boundaries/real last-frame handoff + character/brand/style lock + paid approval/capability gate --storyboard-plan --project-dir --reference-story-formula --sequence-handoff --generation-chain-handoff --capability-profile --approved --strict
reference_frame_preflight.py video_prompt_pack → first-frame/style key existence, decode, dimensions, orientation, aspect ratio, transparent background gate --prompt-pack --require-style-reference --reference shot_id=... --strict
generation_reference_preflight.py prompt pack + image/video/audio reference manifest + provider profile → type/count/duration/mode/@tag role source-bound gate template --prompt-pack / analyze --references --capability-profile --strict / verify --report --strict
generation_task_log.py asynchronous generation task ledger: submit_id/task id, polling, download, local persistence gate add update import-provider-decision report --strict
generated_clip_review.py generated video clip source-bound visual review: contact sheet, scoring, crop range, regeneration suggestions prepare --clip/--asset-manifest audit --request --response verify --report --strict
generation_chain_handoff.py reviewed generated clip + reviewed adjacent boundary → exact last-frame PNG, human confirmation, next shot exact first-frame live gate prepare --clip-review --sequence-handoff --boundary / confirm --decision use_exact_start_frame / verify --strict
generated_motion_window.py short generated clip full-frame freeze → active intervals, human trim/keep/reject, frame-accurate working copy and live gate analyze <clip> confirm --decision apply --output verify --strict
scoped_video_edit_review.py original clip + local AI edit result → same-timestamp A/B evidence, unique change target, item-by-item protection review and live gate prepare --source --edited --change --preserve audit --request --response verify --report --strict
generated_sequence_review.py reviewed generated clips cross-shot continuity review: real last frame/first frame, side-by-side images, silent boundary preview and live gate prepare --clip-review [--storyboard-plan] audit --request --response verify --report --strict
generation_lessons.py extract explicitly approved provider/model/category experience from canonical generated-clip review, and supply it for the next prompt pack selection add --review --clip-id --lesson --approved-by [--supersedes] verify --strict select --provider [--model]
storyboard_assets.py storyboard_plan → material list + ready/paid precheck --storyboard-plan --asset-root --output --strict
screen_focus.py screen recording clicks/hotspots → focused zoom enrich plan --events --event --screen-width --output
pip_overlay.py screen recording + facecam → PIP camera window enrich plan --camera --segment --sync-offset --output
color_grade.py bounded color grading plan + FFmpeg filter + optional existing master reversion --preset --output --markdown --render-output --strict
lut_grade.py local .cube LUT → FFmpeg verified loadable, file SHA-256 bound color grading plan; on-site review at render time plan <look.cube> --output <plan.json> / verify <plan.json>;render_final.py --color-grade <plan.json>
jump_cut.py adaptive silence detection → pause removal plan / deletion budget gate / final cut + cut-point audio fade <input.mp4> --dry-run --cut-list cuts.json --strict / --output jumpcut.mp4 --max-removal-ratio 0.20 --allow-over-budget
multimodal_dead_air.py silence + still frame intersection → source-bound dead zone plan / live verify / single-encode working copy plan --delivery --output --markdown --strict / verify --strict / apply --markdown
audio_transition.py render_config → explicit J-cut/L-cut source handle / hash / single-encode apply / receipt plan --transition AFTER_CLIP,TYPE,DURATION / apply --output / verify --receipt --strict
edit_revision.py Text artifacts such as render_config/enrich_plan → source-bound proposal / independent approval / grouped apply / undo / redo prepare --artifact --depends-on / audit --strict / apply --approval / `status
edit_recipe.py Reviewed render_config → typed-slot pathless recipe / digest verification / replay bound to new assets export --config --name / verify --recipe / replay --bind SLOT=PATH --receipt --strict
edit_preflight.py render_config/enrich_plan/cut list pre-render preflight gate --config render_config.json --enrich-plan enrich_plan.json --output edit_preflight.json --strict
platform_safe_area_qa.py Subtitles, badge, PIP, CTA, chapter cards, marker → platform UI occlusion gate + SVG guide --config --enrich-plan --elements `--platform xhs
subtitle_style_preview.py Real source frame → render_final.py final ASS style comparison JPEG, manual selection, and source/font/style live gate create --video --platform --preview-dir --require-selection / select --report --style / verify --strict
render_final.py Single-pass encoding render + optional personal style defaults / speech denoise / J-cut/L-cut / enrich_plan / narration-driven BGM ducking --config render_config.json --style-profile work/edit_style_profile.json --audio-transition-plan audio_transition_plan.json --speech-denoise light --enrich-plan enrich_plan.json --bgm-ducking --output final.mp4
subtitle_render_review.py Final MP4 + subtitle_pack → high-risk subtitle context clip / midpoint full-size frame, full 1× manual review, and source-bound live gate prepare --video --subtitle-pack --proof-dir / audit --request --response --strict / verify --report --strict
render_qa.py Post-render QA: resolution/audio/black frames/freeze frames/silence + review packet <video.mp4> --platform douyin --json qa.json --review-dir verify/qa
stream_coverage_qa.py final MP4 → full decode, first/last PTS, A/V and container coverage, video frame count source-bound live gate analyze <video> --output --markdown [--expected-duration --expected-video-frames] --strict / verify --report --strict
encode_quality_qa.py Same-timeline reference master vs re-encoded file → full-length SSIM/PSNR, P05, worst-frame timecode, and dual-input live gate analyze <reference> <candidate> --output --markdown --strict / verify --report --strict
video_enhancement.py Existing video → local Lanczos upscale, optional minterpolate frame interpolation, full-length A/B, and audio-enabled manual review live gate `plan <video> --target-short-edge
flash_safety_qa.py final/platform video → large-area brightness/saturated red flash, rolling 1s/5s risk window, and source-bound live gate analyze <video> --output --markdown --strict / verify --report --strict
temporal_artifact_qa.py final/generated video → single-frame/few-frame local mutation screening, before/suspect/after evidence, and manual decision live gate analyze <video> --evidence-dir --response-template --strict / audit --report --response / verify --strict
shot_color_qa.py rendered master → shot brightness/contrast/chroma/saturation/broadcast-range and cut-point jump gate <video.mp4> --scene-boundaries --output shot_color_qa.json --markdown --strict
retention_rhythm_qa.py Finished video hook activity, long shots, attention gaps, evenly spaced/fast cuts, and subtitle pacing risk <video.mp4> --timed-text subtitles.json --output retention_rhythm_qa.json --strict
reference_edit_rhythm.py Reference video vs finished video hard-cut density, shot duration, ending hold, normalized cut points, and contact-sheet source-bound comparison analyze --reference --candidate --evidence-dir [--require-match] / verify --report --strict
speech_continuity_qa.py Finished video secondary transcript → repeated reading / near-duplicate takes / intra-sentence stutter gate <final_transcript.json> --output speech_continuity_qa.json --markdown --strict
lip_sync_review.py Final master → complete phrase 1× with audio / 0.25× silent proof, manual lip-sync audit, and source-bound live gate prepare --video --segment --anchor --proof-dir / audit --request --response --strict / verify --report --strict
review_proxy.py master/platform MP4 → low-bitrate timecoded review video + JSON/Markdown <video.mp4> --output verify/review_proxy.mp4 --dry-run --no-timecode
audio_master_report.py Finished video loudness report: LUFS / true peak / LRA / long silence gate <video.mp4> --output audio_master_report.json --markdown audio_master_report.md --strict
audio_channel_qa.py Final master → channel activity, onset misalignment, L/R balance, phase correlation, and mono fold-down source-bound live gate analyze <media> --output --markdown --strict / verify --report --strict
audio_dropout_qa.py Final vocals/mix → 20 ms short near-silence candidates, 1× WAV evidence, manual listening review, and source-bound live gate analyze <media> --evidence-dir --response-template --strict / audit --response / verify --strict
narration_loudness_qa.py Final standalone narration phrase-by-phrase consistency: target LUFS / segment spread / true peak / LRA source-bound live gate analyze <media> --segments <json> --output --markdown --strict / verify --report --strict
caption_speech_qa.py subtitle_pack cue → independent vocal activity coverage, head/tail silence, internal pauses, and timeline out-of-bounds source-bound live gate analyze <isolated-speech> --subtitle-pack <json> --output --markdown --strict / verify --report --strict
subtitle_glyph_qa.py subtitle_pack + explicit fonts in project → character-by-character Unicode cmap coverage, missing glyph/fallback list, and source-bound live gate analyze --subtitle-pack --font [--fallback-font] --output --strict / verify --report --strict
timeline_view.py Source material deletion segments / finished output cut-point visualization review image <video.mp4> --at 42.5 --output view.png / --rendered-cut-list cuts.json --output-dir verify/
edit_compare.py Original continuous clock vs final pixel two-column video; delete segments to black and verify mapping <source.mp4> <final.mp4> --cut-list --output-speed --output-offset --output
subtitle_pack.py transcript/render_config → SRT/VTT/ASS/JSON subtitle package --transcript work/transcript.json --output-dir output/subtitles / --config render_config.json --speed 1.25 --offset 2.0
soft_subtitles.py Reviewed, aligned with final film timeline SRT → toggleable mov_text MP4 subtitle track; copy original video/audio streams and verify content and timecodes mux final.mp4 final.srt --language zho --output final-soft.mp4 --receipt soft-subtitles.json / verify soft-subtitles.json
chapter_markers.py Reviewed chapters → JSON, FFmetadata, platform timestamps, and browser kind="chapters" WebVTT --chapters draft.json --duration 720 --output-dir output/chapters --strict
chapter_mux.py chapter_markers.py reviewed JSON → MP4 embedded chapters; copy audio/video streams, verify chapter titles/times and stream hashes mux final.mp4 chapters.json --output final-chapters.mp4 --receipt chapter-mux.json / verify chapter-mux.json
metadata_scrub.py Share-version MP4 clears container and stream tags, chapters, extra tracks; copy and verify audio/video streams scrub final.mp4 --output share.mp4 --receipt metadata-scrub.json / verify metadata-scrub.json
hls_vod.py Reviewed H.264/AAC SDR MP4 → single-bitrate VOD index.m3u8 + TS segments; verify independent keyframes, frame count, complete decode, and file hashes package final.mp4 --output-dir output/hls --receipt verify/hls_vod.json / verify verify/hls_vod.json
subtitle_readability_qa.py output-aligned subtitles → CPS, duration, line length, overlap, and media out-of-bounds gate <subtitle_pack.json> --media final.mp4 --output subtitle_readability_qa.json --strict
import_capcut_subtitles.py Jianying/CapCut auto subtitles or SRT → transcript + gap cut list --draft <draft_dir> / --srt captions.srt --transcript work/capcut_transcript.json --cut-list work/capcut_gap_cut.json
srt_edit_plan.py SRT + manual/agent keep/drop instructions → edit plan / render_config / cut list --srt captions.srt --guide edit_guide.md --source-media origin/talking.mp4 --render-config work/render_config.json --strict
project_resume.py Local artifacts → resume context pack / agent handoff --project-dir work/day58 --markdown work/day58/project_resume.md --agent-note work/day58/CLAUDE.md
review_dashboard.py Local artifacts → static HTML/JSON review queue + gate snapshot --project-dir work/day58 --html work/day58/review_dashboard.html --strict
export_edl.py NLE handoff: export EDL + manifest --config render_config.json --output edit.edl / --cut-list rough_cut.json --output rough.edl
export_fcpxml.py NLE handoff: export FCPXML + manifest --config render_config.json --output edit.fcpxml / --cut-list rough_cut.json --output rough.fcpxml
export_otio.py NLE handoff: export OpenTimelineIO .otio + manifest --config render_config.json --output edit.otio / --cut-list rough_cut.json --output rough.otio
framing_preview.py master → real-frame previews for each platform cover/contain/blur, manual selection, and source/filter/preview live gate create --video --platforms --require-selection / select --platform --strategy / verify --strict
multi_export.py Three-platform export, can consume reviewed framing strategy <input.mp4> --platforms xhs douyin wxch --framing-preview work/framing_preview.json
hdr_sdr.py PQ/HLG master → source-bound Hable tone-map / BT.709 limited tags / complete decode gate plan --delivery / apply / verify --strict
delivery_encode.py master → target-size H.264/AAC MP4 / source hash / two-pass bitrate / complete decode gate plan --delivery --max-size-mib / apply / verify --strict
generate_caption.py Title/body/tag --script --profile --output
cover_variants.py Multiple cover A/B options, personal style defaults, feed-size previews, title coordination, and final selection <video> --title --style-profile --caption --platform --render --select cover-c --strict
approval_receipt.py Reviewed video/cover/copy/subtitles/QA → SHA-256 receipt and expired-approval gate create --artifact ... --approved-by / verify --strict
publish_package.py Publish upload package: platform video, cover, subtitles, chapters, copy, and gate status --project-dir --platforms --video xhs=... --strict
profiles/__init__.py Audience tier loading load_profile("tech_pro")

New render_final.py flags in V3

Flag Default Description
--profile tech_pro Off Load pacing/subtitle/BGM defaults from scripts/profiles/tech_pro.yaml
--style-profile work/edit_style_profile.json Off Validate personal/brand editing style profile, only fill controlled fields missing from config or null
--primary-speed 1.25 1.0 Main output speed.--speed can still add extra variants
--no-loudnorm Not passed = loudness normalization enabled Disable dynaudnorm + acompressor + loudnorm
`--speech-denoise light medium strong`
--no-content-guard Not passed = lint enabled Disable platform rule checks (not recommended)
--subtitle-style karaoke normal Word-by-word karaoke subtitles
--enrich-plan work/enrich_plan.json Off Can be passed multiple times; automatically incorporates B-roll / chapter cards / stickers / generated images / focus_events / pip_overlays
--color-grade work/color_grade.json Off Incorporate color_grade.py output or preset, placed before subtitles/HUD
--bgm-ducking Off Use the final narration track to trigger FFmpeg sidechain, dynamically ducking the BGM;--no-bgm-ducking can override config
--versioned-output Off Output to the next <name>_V<N>.mp4, avoiding overwriting the previous final cut

V3 Day58 production lessons (already encoded into default behavior)

Lesson How V3 solves it
Top leaks 1.25x such internal tokens _internal_text_guard automatically rejects, rule in scripts/_internal_text_guard.py
Subtitle Hiragino W3 too thin find_chinese_font() default order: Source Han Sans Heavy > Smiley Sans > STHeiti Medium > PingFang Semibold
Middle section unclear after speed-up render_final default dynaudnorm=f=250:g=15 + acompressor=threshold=-18dB:ratio=3 + loudnorm=I=-16:TP=-1.5:LRA=11
Want 1.25× as main output but --speed still keeps 1.0× Added --primary-speed first-class citizen
Whisper miswords in subtitles (ChatGPTT etc.) rewrite_script.py go through cleaned script first, original Whisper words only for timestamps
Platform violation words only known after discovery (throttling) content_guard.py automatic lint before render

Old version (V2) reference material

Below is the workflow documentation from the V2 era. Still valid, but for daily use it is recommended to first see docs/prompts/15.

Prerequisites

Before performing any operation, first run the environment detection:

python3 scripts/utils.py

This will automatically detect the platform (macOS/Linux/WSL/Windows), GPU type, available encoders, Whisper engine, and provide a diagnostic report.

Prerequisites

Before performing any operation, first run the environment detection:

python3 scripts/utils.py

This will automatically detect the platform (macOS/Linux/WSL/Windows), GPU type, available encoders, Whisper engine, and provide a diagnostic report.

If dependencies are missing, prompt the user to install:

  • ffmpeg: brew install ffmpeg(macOS) or apt install ffmpeg(Linux/WSL) or download the Windows version
  • whisper:
    • Apple Silicon (M1/M2/M3/M4): pip install mlx-whisper(recommended, Metal acceleration fastest)
    • NVIDIA / CPU: pip install faster-whisper(recommended, 4x faster) or pip install openai-whisper
  • China users accelerated installation (Apple Silicon):pip install mlx-whisper -i https://pypi.tuna.tsinghua.edu.cn/simple Other platforms:pip install faster-whisper -i https://pypi.tuna.tsinghua.edu.cn/simple

If the project root has .venv virtual environment, activate it before running Python scripts:

source .venv/bin/activate  # macOS/Linux/WSL
# Windows: .venv\Scripts\activate

Platform notes

  • macOS (Apple Silicon): automatically uses VideoToolbox hardware encoding acceleration; Whisper engine automatically selects mlx-whisper(if installed), recommends large-v3-turbo model (via mlx-community/whisper-large-v3-turbo)
  • Subtitle fonts (short video): by default prioritizes Heavy / Medium weight Chinese fonts: user library's Source Han Sans SC Heavy / Smiley Sans > system STHeiti Medium > PingFang SC Semibold. If neither exists, automatically downloads Heavy weight from adobe-fonts/source-han-sans and caches to ~/.cache/video-editing/fonts/. Never default to thin fonts like Hiragino W3
  • macOS (Intel): Use VideoToolbox encoding, Whisper uses CPU mode
  • Linux: Automatically detects NVIDIA GPU (NVENC), Intel QSV, AMD AMF
  • WSL: Supported, automatically detects Windows font path (/mnt/c/Windows/Fonts/)
  • Windows: Recommended to run in a WSL2 environment; supports QSV/AMF hardware encoding
  • No discrete GPU (integrated graphics): Intel iGPU uses QSV encoding, AMD iGPU uses AMF encoding; Whisper recommends the medium model (instead of large)
  • Chinese users: Automatically detects the China region, uses the Tsinghua pip mirror and HuggingFace mirror to download models, and can also be force-enabled via the --mirror parameter

Linux GPU Configuration Guide (NVIDIA / Intel Arc)

When using GPU-accelerated Whisper speech recognition on Linux, different graphics cards require different configuration approaches. Running python3 scripts/utils.py will automatically detect the GPU model and provide recommendations, but if you encounter problems, please refer to the following approaches.

Approach A: NVIDIA 40 Series GPUs (RTX 4060 / 4070 / 4080 / 4090)

The 40 series (Ada Lovelace architecture, Compute Capability 8.9) has the most mature support for faster-whisper and works out of the box.

Installation steps:

# 1. 安装 NVIDIA 驱动(535+)和 CUDA Toolkit 12.4+
sudo apt install nvidia-driver-535 nvidia-cuda-toolkit
# 或从 NVIDIA 官网安装最新驱动:https://www.nvidia.com/drivers

# 2. 验证 CUDA
nvidia-smi  # 应显示驱动版本和 CUDA 版本

# 3. 安装 faster-whisper(自动安装匹配的 CTranslate2)
pip install faster-whisper>=1.1.0

Configuration notes:

  • CUDA Toolkit: 12.4+ (12.6 recommended)
  • CTranslate2: >= 4.5.0 (installed automatically with faster-whisper)
  • Compute precision: float16(default), int8_float16, int8 are both usable
  • Whisper model: recommended large-v3(VRAM >= 6GB)
  • No special configuration needed,python3 scripts/transcribe.py will automatically detect and use CUDA

Approach B: NVIDIA 50 Series GPUs (RTX 5060 / 5060 Ti / 5070 / 5080 / 5090)

The 50 series (Blackwell architecture, Compute Capability 12.0, sm_120) requires extra attention to the CUDA version and compute precision settings.

Known issues: CTranslate2 reports the error cuBLAS failed with status CUBLAS_STATUS_NOT_SUPPORTEDwhen using INT8 precision on the Blackwell architecture, because Blackwell's INT8 Tensor Cores require matrix dimensions to be aligned to a multiple of 16. CTranslate2 >= 4.7.1 has fixed this issue, but to be safe, this tool automatically uses float16 precision when a 50 series GPU is detected.

Installation steps:

# 1. 安装 NVIDIA 驱动(565+,必须支持 Blackwell)
#    从 NVIDIA 官网下载最新驱动:https://www.nvidia.com/drivers
#    或使用包管理器安装 565 以上版本
sudo apt install nvidia-driver-565

# 2. 安装 CUDA Toolkit 12.8+(Blackwell 最低要求)
#    推荐从 NVIDIA 官网安装:https://developer.nvidia.com/cuda-downloads
#    选择 Linux > x86_64 > Ubuntu > deb (network)

# 3. 验证 CUDA
nvidia-smi  # 应显示 CUDA 12.8+

# 4. 安装 faster-whisper 和最新 CTranslate2
pip install faster-whisper>=1.1.0
pip install --upgrade ctranslate2>=4.7.1  # 确保包含 Blackwell 修复

# 5. 如果仍然报错,强制使用 float16 精度(本工具已自动处理)
#    手动测试:
python3 -c "
from faster_whisper import WhisperModel
model = WhisperModel('tiny', device='cuda', compute_type='float16')
print('CUDA float16 OK')
"

Configuration notes:

  • CUDA Toolkit: >= 12.8 (13.0+ recommended, latest is 13.2)
  • NVIDIA driver: >= 565
  • CTranslate2: >= 4.7.1 (includes the INT8 padding fix)
  • Compute precision: float16recommended (most stable);int8_float16 may be usable on CTranslate2 >= 4.7.1
  • If int8 still reports an error, the tool automatically downgrades to float16
  • Whisper model: recommended large-v3(VRAM >= 6GB)
  • utils.py will automatically detect 50 series GPUs (by querying nvidia-smi for "RTX 50" in the GPU name) and select a safe float16 precision

Troubleshooting: If the CUBLAS_STATUS_NOT_SUPPORTED error occurs:

  1. Confirm the CTranslate2 version is >= 4.7.1:python3 -c "import ctranslate2; print(ctranslate2.__version__)"
  2. Confirm the CUDA version is >= 12.8:nvidia-smi or nvcc --version
  3. Try manually specifying --compute-type float16(if using transcribe.py directly)
  4. Confirm the driver version is >= 565:nvidia-smi Check Driver Version

Option C: Intel Arc Dedicated Graphics (A770 / A750 / B580)

Intel Arc graphics do not support CUDA, so faster-whisper (which depends on CTranslate2/CUDA) cannot be GPU-accelerated directly on Intel Arc. An alternative solution is needed.

Recommended solution: OpenVINO + Whisper (easiest to use)

# 1. 安装 OpenVINO
pip install openvino openvino-genai

# 2. 使用 OpenVINO GenAI 的 WhisperPipeline
python3 -c "
import openvino_genai as ov_genai
pipe = ov_genai.WhisperPipeline('OpenVINO/whisper-large-v3-fp16-ov', device='GPU')
result = pipe.generate('audio.wav', language='<|zh|>')
print(result.texts[0])
"

# 3. 或使用 Hugging Face 预转换模型
pip install optimum[openvino]
# 从 HuggingFace 下载 OpenVINO 格式 Whisper 模型
# https://huggingface.co/OpenVINO/whisper-medium-int8-ov

Alternative solution: whisper.cpp + SYCL (better performance, more complex configuration)

# 1. 安装 Intel oneAPI Base Toolkit
#    https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit-download.html
wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \
  | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] \
  https://apt.repos.intel.com/oneapi all main" \
  | sudo tee /etc/apt/sources.list.d/oneAPI.list
sudo apt update && sudo apt install intel-oneapi-base-toolkit

# 2. 编译 whisper.cpp(启用 SYCL 后端)
source /opt/intel/oneapi/setvars.sh
git clone https://github.com/ggml-org/whisper.cpp.git
cd whisper.cpp
cmake -B build -DWHISPER_SYCL=ON
cmake --build build --config Release

# 3. 下载 Whisper 模型并运行
./build/bin/whisper-cli -m models/ggml-large-v3.bin -f audio.wav -l zh

Configuration Notes:

  • Intel Arc does not support CUDA; faster-whisper on Intel Arc can only use CPU mode
  • The OpenVINO solution is the simplest, supporting Arc A770/A750/B580 and integrated graphics
  • whisper.cpp + SYCL offers better performance (on A770, close to mid-range NVIDIA graphics), but requires a oneAPI environment
  • SYCL support for B580 (Battlemage architecture) is still being optimized; A770 is currently more stable
  • Recommended Whisper model: medium(for the 12GB VRAM A770) or small(for the 8GB VRAM A750)
  • If the user has Intel Arc graphics, the script will automatically detect this and run faster-whisper in CPU mode (as a fallback)

GPU Configuration Quick Reference

Graphics Series Architecture CUDA Toolkit Driver Version CTranslate2 Compute Precision Whisper Engine
RTX 40xx Ada Lovelace (sm_89) >= 12.4 >= 535 >= 4.5.0 float16 / int8 both supported faster-whisper
RTX 50xx Blackwell (sm_120) >= 12.8 >= 565 >= 4.7.1 float16 (recommended) faster-whisper
Intel Arc Xe HPG / Battlemage N/A i915 N/A N/A OpenVINO or whisper.cpp+SYCL
Intel iGPU Integrated Graphics N/A i915 N/A int8 (CPU) faster-whisper (CPU mode)
No Dedicated GPU CPU N/A N/A Any version int8 (CPU) faster-whisper (CPU mode)

Workflow

Phase 0a: Project Bootstrap (Project Startup / Raw Material Import)

When the user provides a raw material folder, download directory, or a set of unorganized materials, first use project_bootstrap.py to establish a stable project structure, rather than editing directly on the external raw paths:

python3 scripts/project_bootstrap.py \
  --source ~/Downloads/raw-shoot \
  --project-dir work/day61 \
  --title "Day61 launch edit" \
  --output work/day61/work/source_inventory.json \
  --markdown work/day61/work/source_inventory.md \
  --project-note work/day61/project.md \
  --strict

Output:

  • origin/raw|broll|audio|bgm|images|assets|sidecars/: working copy inside the project.
  • work/source_inventory.json:project_bootstrap.v1, recording the external source_path, the in-project project_path, category, action, and next actions.
  • work/source_inventory.md: a material list for human review.
  • project.md: cross-session project memory.
  • next_steps.md: list of next commands.

Default --mode copy, files with the same name automatically get a suffix, no overwriting. For large materials on the same disk, --mode hardlinkcan be used; on failure it falls back to copy and writes a warning. This script does not transcode, render, upload, call an LLM, or submit any generation tasks. To use material import as an analysis gate, run:

python3 scripts/pipeline_manifest.py \
  --project-dir work/day61 \
  --target-stage analysis \
  --require source_inventory \
  --strict

Phase 0aa: Runtime Preflight (Verify Local Capabilities Per Task)

Before opening media or starting rendering, run runtime_preflight.py. For transcription, probe, or stream extraction only, use media_io; for tasks that will encode the final picture, use core_edit, then append as needed hls_vod / soft_subtitles / gif_preview / logo_overlay / podcast_audiogram / ping_pong_loop / multicam_switch / video_enhancement / storyboard_animatic / audio_cue_mix / generation_chain_handoff / captions / qa / edge_black_trim / hdr_sdr / stabilization / interlace / remotion:

python3 scripts/runtime_preflight.py analyze \
  --profile core_edit \
  --profile captions \
  --profile qa \
  --output work/runtime_preflight.json \
  --markdown work/runtime_preflight.md \
  --strict
python3 scripts/runtime_preflight.py verify \
  --report work/runtime_preflight.json --strict
python3 scripts/pipeline_manifest.py . \
  --require runtime_preflight --strict

Must treat missing and unknown: The former indicates that the parseable manifest explicitly contains no components, while the latter indicates that the version command or FFmpeg listing failed, timed out, or could not be parsed; both stop the current workflow. After fixing the environment according to the Markdown action, rerun analyze, and do not manually edit the JSON. The report is bound to the selected profile, the relevant command versions, the FFmpeg component states, and report_id;pipeline_manifest.py on-site re-verification; after the environment or report drifts, old results become invalid immediately.

This step only performs introspection and does not run real encoding, GPU, font, or provider jobs. After it passes, you still need to execute the project input preflight, real media probe, full decode, and final audiovisual QA. See Runtime Preflight for details.

Phase 0ab: Interlace Conform (conditional gate for legacy broadcast / DV / DVD)

When the material shows combing,TFF/BFF、1080i/576i/480i , or telecine clues, first run interlace_conform.py analyze. It runs FFmpeg ideton the head, middle, and tail, classifying the material as progressive, true TFF/BFF interlaced, telecine candidate, or mixed/uncertain. Telecine must stop and go through IVTC; progressive stays as is; uncertain material may use a classification override only with frame-by-frame evidence.

After confirming true interlacing, use plan --reviewed-by --note --delivery work/<name>-progressive.mp4 --comparison verify/<name>-interlace-compare.mp4 to bind the decision. By default, prefer bwdif; yadif is a fallback with a warning;frame preserves the frame rate,field preserves the motion of each field and precisely doubles the rate. Apply is promoted only after passing the progressive field flag, live idet, dimensions/frame rate/duration/audio-video start and end, and full decode; then watch the full 1× A/B, and pass all of combing, motion, line detail, field order, and audio sync before confirming. See Interlace Conform for details.

Phase 0ac: Frame-rate Conform (optional for phone / screen-recording VFR)

When the source material is known to be VFR,r_frame_rate and avg_frame_rate are suspicious, or cumulative audio-video drift appears after multiple cuts, first create a project-local CFR working copy:

python3 scripts/frame_rate_conform.py plan origin/phone.mp4 \
  --fps 30 \
  --delivery work/phone-cfr.mp4 \
  --project-dir . \
  --output work/frame_rate_conform_plan.json \
  --markdown work/frame_rate_conform_plan.md
python3 scripts/frame_rate_conform.py apply work/frame_rate_conform_plan.json
python3 scripts/frame_rate_conform.py verify work/frame_rate_conform_plan.json --strict

The script reads the PTS intervals of all decoded video frames and records variable/non-monotonic intervals, the exact target rational frame rate, source/output SHA-256, the media contract, and the full decode receipt. Apply is atomically promoted only when the output frame intervals are constant,frame_count ≈ duration × fps, audio-video start and end are within tolerance, and display dimensions/orientation are correct; afterward, all transcription, cutting, subtitles, sync, and rendering use work/phone-cfr.mp4. 30/60 fps must be chosen based on real motion and the platform; frame-rate increases only duplicate frames, while frame-rate decreases lose motion samples. HDR/BT.2020/>8-bit, obvious existing audio-video offset, or independent device clock drift are outside the scope of this tool's automatic repair. See Frame-rate Conform for details.

Phase 0ad: Edge-black Trim (optional for leading / trailing black)

When black appears at the start or end of capture-card, camera pre-record, or transcoded files, use black_edge_trim.py plan to run FFmpeg blackdetect; by default it also uses silencedetect to verify that the exact removal range is at least 95% silent, considering only intervals touching the timeline head or tail; black in the middle remains unchanged.

python3 scripts/black_edge_trim.py plan origin/capture.mp4 \
  --delivery work/capture-edge-trimmed.mp4 \
  --edge-proof verify/capture-edge-proof.mp4 \
  --audio-policy silent_only \
  --project-dir . \
  --output work/black_edge_trim_plan.json \
  --markdown work/black_edge_trim_plan.md
python3 scripts/black_edge_trim.py apply work/black_edge_trim_plan.json

The script keeps the default 0.08-second visual padding, applies the same source-time trim to audio and video, and outputs H.264/yuv420p CFR with optional 48 kHz AAC; atomic promotion occurs only after the media contract and full decode pass. The edge proof comes from the original footage and includes the proposed removal region and immediately adjacent content. After watching the full 1× proof and delivery and confirming the first and last frames, content coverage, and audio continuity, run confirm, then execute verify --strict. When it is confirmed that the sound in the black frames should also be removed, you can explicitly use --audio-policy allow_audible; this override retains a warning. See Edge-black Trim.

When long programs or recorded masters need internal segment boundaries found, run program_breaks.py analyze <video> --output work/program_breaks.json; it only marks internal blackdetect and each audio track silencedetect overlapping intervals where both hold, and by default takes the overlap midpoint as the review time. Use verify <report.json> to rescan the source and verify the JSON/Markdown. Candidates are not approved cut points; watch and listen at normal speed at each spot before deciding segmentation, especially checking fades, subtitles on black backgrounds, and passages with sound. Material with no audio track cannot prove silence and will be explicitly rejected.

Phase 0ae: Loop Fill (fill a fixed duration with short background / ambient clips)

When short videos need to be repeated to a specified number of times or to fill a fixed slot, use loop_fill.py plan to bind a progressive CFR source, destination, deliverables, and the first real seam proof. It preserves or explicitly discards source audio, and audio and video advance together on repeat; it does not automatically add crossfade, nor does it package arbitrary start and end points as a seamless loop.

python3 scripts/loop_fill.py plan origin/ambient.mp4 \
  --duration 00:30 \
  --audio-mode drop \
  --delivery work/ambient-30s.mp4 \
  --seam-proof verify/ambient-loop-seam.mp4 \
  --project-dir . \
  --output work/loop_fill_plan.json \
  --markdown work/loop_fill_plan.md
python3 scripts/loop_fill.py apply work/loop_fill_plan.json

After watching the seam proof and the complete delivery at normal speed, and confirming the picture seam, motion continuity, repeated flicker frames, audio seam, and slot coverage, run confirm, then use verify --strict or pipeline_manifest.py --require loop_fill_plan --strict to approve. For VFR, first run frame_rate_conform.py; for HDR/BT.2020/>8-bit, first determine the hdr_sdr.py color path. See Loop Fill.

Phase 0af: Ping-pong Loop (forward-reverse loop of short motion)

When the start and end of a gesture, product rotation, or generated video motion do not close, and a back-and-forth loop is needed, use ping_pong_loop.py. First limit the motion range to a few seconds; FFmpeg reverse caches all selected frames, and the planner records a conservative memory estimate and stops early when it exceeds --max-working-set-mib .

python3 scripts/ping_pong_loop.py plan origin/gesture.mp4 \
  --start 1.2 --end 2.05 --cycles 4 \
  --delivery work/gesture-ping-pong.mp4 \
  --turnaround-proof verify/gesture-turnaround.mp4 \
  --loop-seam-proof verify/gesture-loop-seam.mp4 \
  --project-dir . \
  --output work/ping_pong_loop_plan.json \
  --markdown work/ping_pong_loop_plan.md
python3 scripts/ping_pong_loop.py apply work/ping_pong_loop_plan.json

For the period, use 0…N-1,N-2…1, actively excluding the turnaround point and the duplicate end frame at the period end. Source audio is always discarded; add BGM/SFX only after visual approval. After watching the turnaround proof, loop-seam proof, and complete delivery at normal speed, and confirming the turnaround motion, loop motion, repeated still frames, composition, and creative intent all pass , run confirm, then use verify --strict or pipeline_manifest.py --require ping_pong_loop_plan --strict to approve. See Ping-pong Loop.

Phase 0ag: Clip Assembly (safe concatenation of multi-source video)

When the opening, main program, generated clip, and ending come from different canvases, orientations, frame rates, SAR, timestamps, sample rates, or channel counts, use clip_assembly.py instead of concat copy. Pass each unique source in final playback order; for reuse of the same clip, use loop_fill.py。

python3 scripts/clip_assembly.py plan \
  origin/intro.mp4 origin/main.mov origin/outro.mp4 \
  --width 1080 --height 1920 --fps 30 \
  --fit contain --audio-mode fill-silence \
  --delivery output/assembled.mp4 \
  --boundary-proof verify/clip-assembly-boundaries.mp4 \
  --project-dir . \
  --output work/clip_assembly_plan.json \
  --markdown work/clip_assembly_plan.md
python3 scripts/clip_assembly.py apply work/clip_assembly_plan.json

The script unifies PTS, canvas, rotation, CFR, SAR 1:1,yuv420p , and 48 kHz stereo within one FFmpeg filter graph, then hard-cut outputs H.264/AAC. When at least one segment has audio, segments without audio tracks use equal-length silence to maintain A/V coverage; when sound is completely unnecessary, use --audio-mode drop. Existing audio tracks whose start or end does not match the video, or HDR/BT.2020/>8-bit, will stop at the plan stage.

apply only promotes atomically after total duration, dimensions, frame rate, decoded cadence, SAR, audio/video start and end, and full decode all pass, and generates a normal-speed proof covering all seams from the exact delivery file. After fully playing the delivery and proof, make observable conclusions on clip order, visual seams, frame continuity, audio seams, and complete coverage, then confirm and verify --strict. This tool does not invent transitions or semantic continuity; when J-cut/L-cut is needed, use a separate audio transition plan. See Clip Assembly.

Phase 0b: Production Authorization (authorize by exact scope)

When the workflow involves handing material to an external service, changing the original narrative order/trim scope, consuming generation quota, cloning a real person's voice, using a real person/minor/public figure/brand/IP, or publishing directly, before the action occurs first write production_authorization_scope.v1. scope must name the in-project material, the specific action, purpose, exact provider/surface, possible cost/quota, and applicable rights subject; for the full schema see Production Authorization.

python3 scripts/production_authorization.py prepare \
  --project-dir . \
  --scope work/production_authorization_scope.json \
  --output work/production_authorization_request.json \
  --markdown work/production_authorization_request.md \
  --response-template work/production_authorization_response.json \
  --strict

# 逐项填写 approve/reject、note、rights basis 与 evidence_note 后:
python3 scripts/production_authorization.py audit \
  --project-dir . \
  --request work/production_authorization_request.json \
  --response work/production_authorization_response.json \
  --output work/production_authorization.json \
  --markdown work/production_authorization.md \
  --strict

python3 scripts/production_authorization.py verify \
  --project-dir . \
  --report work/production_authorization.json \
  --strict

prepare binds the scope and each source asset's relative path, size, and SHA-256;audit requires a complete item-by-item decision on actions and rights items;verify re-reads the scope, material, request, and response on site and recomputes the report. Any change to a provider, purpose, cost statement, rights subject, material bytes, or decision invalidates the old report. This artifact only records a local, self-reported review scope; it does not perform upload/editing/generation/publishing, nor does it substitute for identity authentication, digital signatures, real authorization documents, or legal advice.

Phase 0: Media Library Setup (material library initialization)

On first use, help the user set up the material directory structure:

python3 scripts/media_library.py init [project_dir]

This creates the following directory structure:

media/
├── raw/      — 原始素材(摄像机/手机直出的视频)
├── broll/    — B-roll 素材(城市街景、产品特写等)
├── bgm/      — 背景音乐(MP3/WAV/M4A)
├── assets/   — 叠加素材(水印 PNG、Logo 等)
└── output/   — 输出目录

Ask about material sources:

  1. Ask the user where the video files are located (local path, external device, or cloud)
  2. Suggest copying/moving the original material to the media/raw/ directory
  3. Ask whether there is auxiliary material such as B-roll, BGM
  4. If the user's videos are scattered across multiple directories, suggest first gathering them into media/raw/

Scan and build the index:

python3 scripts/media_library.py scan [project_dir]

The indexing system automatically:

  • Scans all video/audio/image files
  • Extracts metadata such as duration, resolution, frame rate
  • Associates existing transcript files
  • Small projects (< 200 files) use a JSON index (media_index.json)
  • Large projects automatically upgrade to a SQLite index (media_index.db)
  • Manual upgrade:python3 scripts/media_library.py upgrade

Check material library status:

python3 scripts/media_library.py status

Search material:

python3 scripts/media_library.py search "关键词"

Recommend B-roll candidates:

python3 scripts/media_library.py recommend "AI workflow dashboard" \
  --project-dir . \
  --category broll \
  --target-duration 3 \
  --target-aspect 9:16 \
  --json

Recommendation results include score、reasons、absolute_path, for a human/agent to confirm before writing to render_config or enrich_plan. By default, index files that no longer exist are filtered out; to clean up a stale index add --include-missing.

Plan stock queries when local material is insufficient:

python3 scripts/stock_material_plan.py \
  --subject "AI workflow automation" \
  --script work/transcript.json \
  --provider pexels \
  --provider pixabay \
  --provider coverr \
  --media-library . \
  --output work/stock_material_plan.json \
  --markdown work/stock_material_plan.md

stock_material_plan.py only generates stock_material_plan.v1 and a Markdown review, without going online, downloading, or consuming quota. It draws on MoneyPrinterTurbo's video_terms / Pexels / Pixabay / Coverr / video_count material planning method, while maintaining this skill's artifact-first style.

Register downloaded or client-provided materials after confirming authorization:

python3 scripts/media_library.py import /path/to/downloaded.mp4 \
  --project-dir . \
  --category broll \
  --copy \
  --provider pexels \
  --source-url "https://www.pexels.com/video/demo-123/" \
  --creator "Demo Creator" \
  --license "Pexels License" \
  --tag "workflow,dashboard"

python3 scripts/media_library.py annotate media/broll/downloaded.mp4 \
  --project-dir . \
  --source-url "https://example.com/source" \
  --license "owned" \
  --tag "client-approved"

After registration, provider、source_url、creator、license will enter media_index.json/db, and subsequently asset_provenance.py will perform the publish gate check.

Phase 0.25: Creator-owned Edit Style Profile (personal/brand editing style, optional)

When the user wants multiple projects to maintain the same creative direction, editing rhythm, subtitle/cover/color grading/BGM habits, and title spelling, first generate and manually edit the spec, then create a portable profile:

python3 scripts/edit_style_profile.py template \
  --output work/edit_style_profile_spec.json

# 编辑 spec 中的真实偏好和 approval basis 后:
python3 scripts/edit_style_profile.py create \
  --spec work/edit_style_profile_spec.json \
  --output work/edit_style_profile.json \
  --markdown work/edit_style_profile.md \
  --strict

python3 scripts/edit_style_profile.py verify \
  --profile work/edit_style_profile.json \
  --strict

When rendering, add render_final.py --style-profile work/edit_style_profile.json; when generating title copy and covers, add the same parameter to generate_caption.py and cover_variants.py respectively. The Profile only fills controlled fields that are missing or null in the config; the project config and explicit CLI (including cover --style) always take precedence. It describes "how editing is usually done", while edit_recipe.py stores "how this timeline is reproduced"; the two do not replace each other. The Profile's digest can only detect file drift; it is not a signature, identity authentication, or proof of rights. For complete usage, see docs/prompts/98-edit-style-profile.md.

Phase 0.5: Source Receipts (fact-source proof deck, optional but recommended)

If the video contains news, data, product facts, health/financial/legal judgments, source page screenshots, or "official statements", first turn claims and evidence into source receipts before proceeding to storyboarding/publishing:

python3 scripts/source_receipts.py \
  --claims work/source_claims.json \
  --project-dir . \
  --output work/source_receipts.json \
  --markdown work/source_receipts.md \
  --html work/source_receipts.html \
  --require-primary-source \
  --strict

source_receipts.py only verifies the provided URLs and local screenshot/evidence files; it does not fetch from the internet, take screenshots, or upload.source_claims.json in screenshot / source_file are resolved relative to the directory containing the claims JSON. High-risk claims such as news, data, finance, health, and law must have source_url; when a visual proof card is needed, add --require-screenshot. The publish gate can enforce the check with pipeline_manifest.py --require source_receipts --strict , andsummary.blocking > 0 will block.

Phase 1: Audio Extraction

For each input video file, use extract_audio.py to extract audio:

python3 scripts/extract_audio.py "<video_path>"

Output: <video_name>_audio.wav files in the same directory as the video.

Phase 2: Speech Recognition

Use transcribe.py to perform speech recognition on the audio and generate sentence-by-sentence text with timestamps:

python3 scripts/transcribe.py "<audio_path>" --model auto --language zh --detect-fillers
  • --model auto: automatically select the best model based on hardware (NVIDIA GPU → large-v3, Apple Silicon → large-v3-turbo, integrated graphics → medium, CPU only → small)
  • You can also specify manually:tiny, base, small, medium, large-v3, large-v3-turbo
  • --engine auto: automatically detect faster-whisper (recommended) or openai-whisper
  • --mirror: Chinese users can use a mirror source to download models
  • --language:zh(Chinese),en(English),ja(Japanese), etc.; you can also omit it to let whisper detect automatically
  • --silence-threshold 1.0: silence detection threshold (seconds), default 1.0. Set to 0 to disable
  • --word-timestamps: enable word-level timestamps (required for karaoke subtitles)
  • --detect-fillers: detect filler words (Chinese: 嗯/呃/那个/就是说; English: um/uh/like/you know), and mark pure filler segments as recommended skips

Output: <video_name>_transcript.json files in the same directory as the audio, in the following format:

{
  "segments": [
    {"id": 1, "start": 0.0, "end": 2.5, "text": "大家好"},
    {"id": 2, "start": 2.5, "end": 5.1, "text": "今天我们来聊一个话题"}
  ],
  "silences": [
    {"start": 15.2, "end": 18.5, "duration": 3.3, "before_segment": 5, "after_segment": 6}
  ],
  "filler_words": [
    {"segment_id": 3, "text": "嗯那个", "fillers_found": ["嗯", "那个"], "is_filler_only": true},
    {"segment_id": 7, "text": "就是说我觉得这个方案", "fillers_found": ["就是说"], "is_filler_only": false}
  ]
}

Silence detection: transcribe.py automatically analyzes the gaps between adjacent speech segments. Gaps exceeding the threshold (default 1 second) are marked as silence and output to the silences field. These silences are usually the speaker's pauses, stumbles, or slips of the tongue, and these regions should be avoided when selecting clips to build render_config.json.

For isolated vocals or voiceover tracks without continuous music, run the original transcription silence verification again:

python3 scripts/transcript_silence_qa.py analyze origin/talking_audio.wav \
  origin/talking_transcript.json --output verify/transcript_silence_qa.json
python3 scripts/transcript_silence_qa.py verify verify/transcript_silence_qa.json

The report records the actual silence seconds, proportion, and source file hash segment by segment; by default -40 dBFS, minimum silence 0.15s, when the silence proportion reaches 0.8 it is marked as review. Replay by the marked timecode, then correct it in the transcription review. It does not automatically delete words or change timecodes; quiet vocals may be falsely marked, and music or ambient sound may mask hallucinations.verify will re-measure and reject reports or input drift.

When you need to find a specific original utterance in a long transcript, use python3 scripts/transcript_lookup.py work/transcript.json search "产品发布时间" --limit 10; to see the text of a certain section of the source video, use python3 scripts/transcript_lookup.py work/transcript.json span --start 90 --end 120. The output is bounded JSON, containing the source segment ID, source timecode, and transcript SHA-256; search ignores case, spaces, and punctuation, and can span adjacent segments separated by no more than 2 seconds. It only locates the original utterance and does not infer semantically similar content; cut points and wording still need to be checked against the original media and proofread.

Phase 2a: Video Keyframe Extraction

For talking-head videos (especially footage shot while walking outdoors with ambient sound), audio transcription alone cannot reveal the visual content of the video. Use extract_keyframes.py to extract video keyframes and generate a contact sheet so you can fully understand the video content:

When you have scene detection, subtitle, or review timecodes, first run frame_grid.py locate origin/clip.mp4 --at-seconds 20.4, and read the real PTS of nearest.decoded_frame and the two adjacent frames. Then run --start-frame near that frame. The frame_grid.py grid origin/clip.mp4 --start-frame 240 --step 2 --count 16 --output verify/grid.png --receipt verify/grid.jsonwith CELL 1 and other markers in the PNG correspond to the cell in the JSON; after finding the target, run frame_grid.py frame origin/clip.mp4 --start-frame 250 --output verify/frame.png --receipt verify/frame.json to export the exact frame. Before handoff, use verify <receipt> to verify the source video, PNG, and mapping receipts. Both locating and frame extraction decode sequentially from the start, so queries deep into long videos take time; the grid holds at most 36 cells, and the contact sheet cannot replace normal-speed playback.

python3 scripts/extract_keyframes.py "<video_path>"

Parameter description:

  • --max-frames 16: maximum number of keyframes (default 16)
  • --threshold 0.4: scene change detection sensitivity (0.0-1.0, lower extracts more keyframes, default 0.4)
  • --cols 4: number of columns in the contact sheet grid (default 4)
  • --thumb-width 320: thumbnail width (default 320px)
  • --output-dir: keyframe output directory (default <video_name>_keyframes/)
  • --no-storyboard: extract keyframes only, do not compose a contact sheet

Output:

  • <video_name>_keyframes/ — each keyframe PNG image (named with timestamps)
  • <video_name>_storyboard.png — the composed contact sheet (grid layout + timestamp annotations)
  • <video_name>_keyframes.json — keyframe metadata (timestamp, frame number, file path)

Why keyframe extraction is needed:

  • Talking-head videos are often shot while walking, and scene changes in the frame (street → park → coffee shop) are important narrative cues
  • A timeline diagram lets the AI see both the audio content (transcript) and the visual content (keyframes) at the same time, enabling better shot-selection judgments
  • It can surface information that pure audio analysis cannot capture: body language, facial expression changes, environment switches, product displays, etc.

Typical workflow:

# 1. 提取音频并转录
python3 scripts/extract_audio.py video.mp4
python3 scripts/transcribe.py video_audio.wav --model auto --language zh --detect-fillers

# 2. 提取关键帧生成时序图
python3 scripts/extract_keyframes.py video.mp4

# 3. 把每帧与前后 3 秒内的原话对齐,查看索引并核验来源
python3 scripts/visual_transcript_index.py build video_keyframes.json video_transcript.json \
  --output work/visual_transcript_index.json --markdown work/visual_transcript_index.md
python3 scripts/visual_transcript_index.py verify work/visual_transcript_index.json

Each frame in the index shows at most 8 verbatim quotes, with truncation marked;--radius can be set to 0.1–30 seconds,--max-segments can be set to 1–12. The frames are sampled frames, and the verbatim quotes are from nearby time windows; you cannot conclude from them that something appeared in the frame, nor can they replace full playback of the original video. The index must be rebuilt after changes to the source video, keyframe metadata, images, transcription, or Markdown.

Key points for comprehensive AI Agent analysis:

  • Compare the keyframe timeline diagram with the transcript, annotating for each time period "what was said + what the frame shows"
  • Find high-quality segments where the visuals best match the speech (e.g., when saying "this shop", the frame is pointed right at the shop)
  • Identify segments with blurry visuals, occlusion, or poor lighting, and recommend skipping them
  • For outdoor shooting, watch for segments with severe camera shake and lower their priority during shot selection

Phase 2a: Adaptive Scene Boundaries

Before splitting long videos into clips, sampled understanding, or final-cut pacing analysis, preferentially generate adaptive scene boundaries for moving shots:

python3 scripts/scene_boundaries.py video.mp4 \
  --method adaptive \
  --adaptive-threshold 3.0 \
  --min-scene-score 0.15 \
  --min-scene-duration 1.0 \
  --output work/scene_boundaries.json \
  --markdown work/scene_boundaries.md

adaptive compares each frame's FFmpeg scene score with the mean of its preceding and following neighborhood, which reduces dense false cuts caused by continuous panning, motion, or flicker;boundary_evidence[] keep the score, adaptive ratio, and neighborhood mean; you must first review the Markdown before handing it to highlight_picker.py --scene-boundaries. When you need to reproduce the old workflow or work with fixed-camera footage, use --method fixed --threshold 0.35.

Phase 2b: Visual Dedupe (cross-footage duplicate shot review)

Before multi-camera, multi-take, re-transcoded, or B-roll candidates enter the timeline, use visual_dedupe.py to perform three-point perceptual hash review on scenes from multiple sources:

python3 scripts/visual_dedupe.py \
  --manifest work/visual_dedupe_sources.json \
  --output work/visual_dedupe.json \
  --markdown work/visual_dedupe.md \
  --strict

The manifest's sources[] provides for each source id、video, optional scene_boundaries , and quality_score; relative paths are based on the manifest directory. By default, the script compares only different sources, requires at least two of the 10%/50%/90% sampling points to match, and uses quality_score, resolution, and file size for keep recommendations. It only outputs a review artifact and never deletes or moves source footage. When duplicate groups are found, first manually review the source range, then exclude confirmed duplicate candidates from the downstream edit plan.

Phase 2c: Video Understanding (sampled frames + optional YOLO)

When people, phones, computer screens, products, vehicles, or other dynamic objects in the footage could affect cropping, privacy masking, or B-roll selection, use video_understanding.py to generate a structured visual understanding artifact. No detector installation is required by default; to run YOLO, first install the optional dependency ultralytics.

To inspect motion positions when no detection model is available, run python3 scripts/motion_center.py analyze video.mp4 --output work/motion_center.json, then use verify work/motion_center.json to re-measure on site. The report compares downscaled adjacent grayscale frames at the default 2 fps;local_motion gives a 0–1 normalized center,static and global_change do not report a center. Pixel motion may come from the background or camera movement and must not be passed directly as a person-detection result to smart_reframe.py --detections; first review the corresponding time point, then decide the composition.

# 无 detector:只抽样帧并生成 review shell
python3 scripts/video_understanding.py video.mp4 \
  --output work/video_understanding.json \
  --markdown work/video_understanding.md

# 可选 YOLO:检测对象并生成 detections/tracks/scene_tags
pip install ultralytics
python3 scripts/video_understanding.py video.mp4 \
  --scene-boundaries work/scene_boundaries.json \
  --detector yolo \
  --model yolo11n.pt \
  --output work/video_understanding.json \
  --markdown work/video_understanding.md \
  --strict

Output:

  • video_understanding.json — video_understanding.v1, containing frames[]、detections[]、tracks[]、scene_tags[]、warnings[]
  • video_understanding.md — a manual review table listing sampled frames, detection counts, tracks, labels, and warnings

Typical downstream:

python3 scripts/smart_reframe.py video.mp4 \
  --detections work/video_understanding.json \
  --platform douyin \
  --output work/reframe_douyin.json \
  --markdown work/reframe_douyin.md

python3 scripts/privacy_redact.py \
  --video video.mp4 \
  --detections work/video_understanding.json \
  --output work/privacy_redaction.json \
  --markdown work/privacy_redaction.md

Note: The built-in tracklets are lightweight associations made for talking-head editing and are not equivalent to frame-by-frame multi-object tracking. For highly dynamic footage such as sports, traffic, or multi-person occlusion, you can use Ultralytics model.track(..., tracker="bytetrack.yaml"), BoT-SORT, or Norfair to generate denser detection/track results, then convert them into the same detections[] / tracks[] JSON.

Phase 2.5: Transcript Review

After transcription is complete, you must review every entry of the text in transcript.json one by one and fix the following two types of issues:

1. Speech recognition errors (ASR errors): Common Whisper recognition error types:

  • Proper nouns/product names: e.g. "opencloud" → "OpenClaw", "cloudcode" → "Claude Code", "cloud ops" → "Claude Opus"
  • Homophone errors: e.g. "小红树" → "小红书", "检映" → "剪映", "断耕" → "断更", "懒得讲" → "懒得剪"
  • English spelling: e.g. "scale" → "skill", "箱子" → "视频"
  • Tail hallucinations: Whisper sometimes generates meaningless repeated text at the end of quiet segments; these should be deleted directly

2. Speaker errors:

  • Repetition/stuttering: the speaker repeats the same sentence or gets stuck and restarts; mark these as skippable
  • Garbled segments: segments recognized as meaningless text due to unclear speech (e.g. consecutive single-character fragments); mark these as skippable

When there are many technical terms, personal names, homophones, or mixed Chinese-English speech, first run semantic_transcript_review.py prepareto let the current Agent/model fill in a provider-neutral response; then run audit. Do not treat model confidence as approval: copy the choices template bound to source_sha256 + review_id from the audit Markdown, and only after reviewing each item with approve / reject run apply。audit . It derives full coverage from the source transcript and rejects whole-sentence rewrites, non-minimal character patches, number/punctuation changes, out-of-bounds/overlapping patches, and stale transcript hashes. After a successful apply, hand transcript_semantic_reviewed.json to the synchronized media HTML below to continue listening and reviewing; see docs/prompts/79-semantic-transcript-review.md for details.

python3 scripts/semantic_transcript_review.py prepare \
  --transcript work/transcript.json \
  --output work/semantic_review_request.json \
  --markdown work/semantic_review_request.md

python3 scripts/semantic_transcript_review.py audit \
  --transcript work/transcript.json \
  --response work/semantic_review_response.json \
  --output work/transcript_semantic_review.json \
  --markdown work/transcript_semantic_review.md \
  --strict

python3 scripts/semantic_transcript_review.py apply \
  --transcript work/transcript.json \
  --audit work/transcript_semantic_review.json \
  --choices work/semantic_review_choices.json \
  --output work/transcript_semantic_reviewed.json

Review workflow:

  1. Use transcript_review.py html to generate a local page with no external dependencies; in a pure terminal environment, use export to generate text.
  2. On the page, click the timecodes to proofread against the media; during playback, the current segment is highlighted automatically. Inline editing supports local autosave, find and replace, and CPS highlighting in yellow.
  3. Explicitly save transcript_review.txt; do not let the page or agent overwrite the original transcript directly.
  4. Use apply to generate transcript_reviewed.json; subsequent cleanup, rough cut, storyboard, and subtitles all use the reviewed file uniformly.
  5. For slips of the tongue/garbled segments, mark them as suggested skips when displaying the segment list (Phase 3).
python3 scripts/transcript_review.py html \
  --transcript work/transcript.json \
  --video origin/talking.mp4 \
  --corrections work/corrections.json \
  --output work/transcript_review.html \
  --max-cps 20

python3 scripts/transcript_review.py apply \
  --transcript work/transcript.json \
  --review work/transcript_review.txt \
  --output work/transcript_reviewed.json

The HTML and media are opened only locally; nothing is uploaded and no LLM is called. When the browser cannot write files directly, it downloads transcript_review.txt; put it back into work/ and then apply. CPS is a pre-rendering hint; you still need to run subtitle_readability_qa.py --strict.

Note: This step must be completed before Phase 5 (rendering), because the subtitle text comes from transcript.json. Only after correction and re-rendering can the subtitle text in the final video be guaranteed correct.

Phase 2.5a: Target Script Alignment (assemble original speech per the confirmed draft, optional)

If the client, editor, or user has already confirmed the final cut draft, and the same sentence was recorded in multiple takes or is scattered across different source materials, first organize the target draft as "one complete spoken unit per line", then match it against the reviewed transcript:

python3 scripts/script_alignment.py \
  --target-script work/target_script.md \
  --transcript take-a=work/take-a_transcript_reviewed.json \
  --transcript take-b=work/take-b_transcript_reviewed.json \
  --media take-a=origin/take-a.mp4 \
  --media take-b=origin/take-b.mp4 \
  --output work/script_alignment.json \
  --markdown work/script_alignment.md \
  --render-config work/render_config.json \
  --clean-script work/clean_script.md \
  --strict

The script only performs local lexical matching; it does not call an LLM, does not modify source files, and does not judge expression/shot/performance quality. It outputs each sentence's stable candidate id, source time, original speech, and sequence/coverage/ngram/length evidence; when word timestamps exist, it tightens to word boundaries first, and when only segment timestamps exist, it conservatively keeps the whole segment. Low scores, the top two being too close, no candidates, missing source material, or duplicate occupation of source time will write summary.blocking and cause --strict to return 2.

When multiple takes of the same text produce ambiguous_match , you must watch/listen to the candidates in the Markdown, write the confirmed result as {"choices":{"target-001":"<candidate-id>"}}, and then rerun with --choices work/script_alignment_choices.json . Manual choices can only resolve lexical low scores/multiple interpretations; they cannot bypass missing files or time overlaps.summary.blocking=0 before entering edit_preflight.py / render_final.py; for detailed usage see docs/prompts/78-script-alignment.md.

Phase 2.6: ASR Rough Cut (rough cut of filler words/repeated sentences, optional)

If the transcript contains many pure filler words, stumbles with retakes, or adjacent repeated sentences, first use rough_cut.py to generate an auditable rough-cut plan:

python3 scripts/rough_cut.py --transcript work/transcript.json --cut-list work/rough_cut.json

After confirming the plan, you can directly render the rough-cut version:

python3 scripts/rough_cut.py \
  --transcript work/transcript.json \
  --input origin/talking.mp4 \
  --output output/talking.roughcut.mp4 \
  --cut-list work/rough_cut.json

rough_cut.py will output decisions / removed_segments / keep_segments / speedup_ratio. It does not call an LLM and does not submit any paid tasks; it only uses transcribe.py --detect-fillers 's filler metadata and adjacent text similarity for a conservative rough cut. Before rendering, use timeline_view.py --cut-list work/rough_cut.json to view the deleted segments in the source material; after rendering, use --rendered-cut-list work/rough_cut.json to view the actual splice points in the final cut.

Phase 2.6b: Multimodal Dead-Air (conservative dead-zone removal via silence + still frame, optional)

If pure audio jump_cut.py may mistakenly delete pauses that still contain facial expressions, gestures, product displays, or screen operations, switch to an AND gate of audio silence and still video frames:

python3 scripts/multimodal_dead_air.py plan origin/talking.mp4 \
  --delivery work/talking-dead-air-tight.mp4 \
  --output work/multimodal_dead_air_plan.json \
  --markdown work/multimodal_dead_air_plan.md \
  --strict
python3 scripts/multimodal_dead_air.py verify work/multimodal_dead_air_plan.json --strict
DEAD_AIR_CUT_COUNT="$(python3 -c 'import json; print(len(json.load(open("work/multimodal_dead_air_plan.json"))["removed_segments"]))')"
python3 scripts/timeline_view.py origin/talking.mp4 \
  --cut-list work/multimodal_dead_air_plan.json \
  --output-dir verify/dead-air-cuts \
  --limit "$DEAD_AIR_CUT_COUNT"
python3 scripts/multimodal_dead_air.py apply work/multimodal_dead_air_plan.json \
  --markdown work/multimodal_dead_air_plan.md

By default, only segments where the still frame covers the silence by at least 60% are selected, and only the intersection of the two is deleted; 80ms padding, 30ms audio fade, and a 20% deletion budget continue to apply. The plan is bound to the source SHA-256 and media contract; apply uses a temporary MP4 and atomically promotes it only after H.264/AAC,yuv420p, size, frame rate, sample rate, channels, duration, and full decoding all pass.timeline_view.py by default only looks at 20 cut points, so you must pass this plan's actual deleted segment count to --limitas above; if the count is 0, stop and keep the original video. It does not understand performance, semantics, or intentional pauses; all source cut points must still be reviewed first, and the output must still be fully reviewed at 1× with sound. For details see docs/prompts/88-multimodal-dead-air.md.

Phase 3: User Interaction (user interaction)

Show the clip list to the user, in the following format:

视频片段列表:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  #   | 时间区间          | 内容
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  1   | 00:00.0 - 00:02.5 | 大家好
  2   | 00:02.5 - 00:05.1 | 今天我们来聊一个话题
  3   | 00:05.1 - 00:08.3 | 这个话题非常有意思
  ...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

请选择要合成的片段(示例):
  - 连续范围:1-10
  - 多个片段:1,3,5,7
  - 混合选择:1-4,6,8-10

If there are multiple video files, display the clip list for each video separately, allowing the user to select across videos.

AI Smart Clip Selection Suggestions

When displaying the clip list, the AI agent should provide a recommendation rating (1-5 stars) for each clip based on the following dimensions:

Attractiveness Rating Dimensions:

  1. Hook Strength (first 3 seconds): Whether there is an engaging opening (question, counterintuitive viewpoint, emotional trigger)
  2. Information Density: The amount of effective information conveyed per second (avoid repetition and filler)
  3. Emotional Variation: Whether there are emotional ups and downs (humor→serious→surprise)
  4. Completeness: Whether the clip forms a complete narrative unit (has a beginning, development, and ending)

Auto-Skip Suggestions:

  • Clips in the transcript that are is_filler_only: true (pure filler words)
  • Adjacent clips with silent gaps > 2 seconds (restarting after a stumble)
  • Clips whose transcribed text highly duplicates the previous clip (restating after a slip of the tongue)

Auto-Split Long Videos into Short Clips (when video > 3 minutes): The AI agent should analyze the transcript to identify topic transition points (semantic breaks, transition words such as "next," "additionally"), group the clips by topic into independent short videos (each 30-90 seconds), and calculate an overall attractiveness rating for each group:

推荐短视频拆分方案:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  方案 | 片段范围    | 时长  | 主题         | 推荐指数
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  A    | #1-#8       | 45s   | 痛点引入      | ★★★★★
  B    | #9-#18      | 62s   | 核心方法      | ★★★★☆
  C    | #19-#25     | 38s   | 实操演示      | ★★★☆☆
  D    | #1-#25      | 2m25s | 完整版       | ★★★★☆
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

After waiting for the user to reply with their selection, proceed to Phase 4.

After selection is complete and before batch rendering, it is recommended to correct the cut points using word-level timestamps to avoid candidate ranges falling in the middle of a word or at the end of a half sentence:

python3 scripts/audio_boundary_snap.py \
  --candidates work/highlight_candidates.json \
  --transcript work/transcript.json \
  --media origin/long-talk.mp4 \
  --output work/audio_boundary_plan.json \
  --markdown work/audio_boundary_plan.md \
  --strict

Output audio_boundary_plan.v1 's selected[] can be passed directly to shorts_batch.py --highlights. The script only reads local candidates, transcript, and optional media; it does not render, upload, or call a provider.--strict will return 2 when word-level timestamps are missing, time ranges are invalid, source media is missing, or the safe boundary exceeds the platform duration.

Phase 4: Render Config

Based on the user's selection, generate the render_config.json configuration file.

Important Note: The render configuration is a project-level file

The render_config.json format shown in the SKILL is only a template/reference; do not directly modify any files in the Skill repository. Each video project should create an independent render configuration under the user working directory:

# 在用户的视频项目工作目录下创建 script/ 文件夹
mkdir -p script/

# 在 script/ 下为每个视频项目创建独立的渲染配置文件
# 例如:script/render_config.json

Directory Structure Example:

~/Videos/my-project/          ← 用户工作目录
├── script/                   ← 渲染配置目录(每个项目独立)
│   └── render_config.json    ← 本项目的渲染配置
├── media/
│   ├── raw/                  ← 原始素材
│   ├── broll/
│   └── output/               ← 最终输出
├── *_audio.wav               ← 提取的音频
└── *_transcript.json         ← 转录文件

Render Configuration File Format (script/render_config.json):

{
  "clips": [
    {"video": "path/to/video1.MOV", "segment_id": 4, "transcript": "path/to/transcript1.json"},
    {"video": "path/to/video1.MOV", "segment_id": 5, "transcript": "path/to/transcript1.json"},
    {"video": "path/to/video2.MOV", "segment_id": 1, "transcript": "path/to/transcript2.json",
     "broll": "path/to/cityscape.mp4", "broll_start": 5.0}
  ],
  "title": "封面标题文字",
  "subtitle": "副标题/情感钩子(可选)",
  "cover_style": "news",
  "cover_duration": 2.0,
  "cover_image": "path/to/custom_cover.png",
  "cover_use_frame": false,
  "video_overlay": "path/to/overlay.png",
  "rec_blink": {
    "dot_image": "path/to/dot.png",
    "x": 55, "y": 66,
    "period": 1.0
  },
  "end_cards": [
    {"text": "感谢观看\n更多内容敬请期待", "duration": 3.5}
  ],
  "bgm": "path/to/background_music.mp3",
  "bgm_volume": 0.15,
  "bgm_fade_out": 3.0,
  "bgm_ducking": true,
  "bgm_ducking_threshold": 0.03,
  "bgm_ducking_ratio": 8,
  "bgm_ducking_attack_ms": 20,
  "bgm_ducking_release_ms": 500,
  "subtitle_style": "karaoke",
  "subtitle_highlight_color": "#FFFF00",
  "chapters": [
    {"title": "章节名", "start": 0.0, "end": 30.0}
  ],
  "text_badges": [
    {"text": "关键结论", "start": 12.0, "end": 13.5}
  ],
  "broll_overlays": [
    {"video": "path/to/cityscape.mp4", "start": 8.0, "end": 10.0, "source_start": 2.0}
  ],
  "image_overlays": [
    {"image": "path/to/generated.png", "start": 18.0, "end": 21.0, "fit": "cover"}
  ],
  "pip_overlays": [
    {"video": "path/to/facecam.mp4", "start": 0.0, "end": 42.0, "position": "bottom_right", "sync_offset": 0.18}
  ]
}

Using the Render Configuration:

# 渲染时指定 script/ 下的配置文件
python3 scripts/render_final.py --config script/render_config.json --output media/output/final.mp4

B-roll Replacement (broll field):

  • Adding "broll": "path/to/video.mp4" to a clip can replace that clip's visuals while keeping the original audio
  • broll_start specifies from which point in the B-roll video to start the extraction (default 0.0)
  • B-roll videos are automatically scaled/cropped to match the main video resolution
  • Applicable scenarios: screen-recorded talking-head footage paired with city street scenes, audio narration paired with visuals, etc.

Auto-Enrich Integration (recommended):

  • First run auto_enrich.py --output work/enrich_plan.json
  • When rendering, add --enrich-plan work/enrich_plan.json
  • broll[].suggested_asset to convert into timed B-roll video overlay;chapter_cards[]、stickers[] and emphasis_cues[] will be converted into ASS badge;emphasis_cues[] can also generate a slight center push-in without a red frame;chapter_cards[].png / imagegen[].image_path / imagegen[].generated_path will be converted into a timed image overlay
  • pip_overlays[] will be converted into a timed facecam / camera small window; camera audio is ignored by default, and the main audio still comes from the render config
  • imagegen cues that do not actually generate files are only output as hints and will not block rendering
  • The merged visible text will still go through _internal_text_guard and content_guard.py

Beat Edit Plan beat-editing skeleton (optional for music videos / beat-synced montages):

  • Run beat_sync.py --bgm origin/bgm.mp3 --generate-plan --duration 30 --beats-per-cut 4 --min-segment 0.75 --max-segment 3 --output work/beat_edit_plan.json --markdown work/beat_edit_plan.md
  • Output beat_edit_plan.v1:cut_times[]、program-time segments[], per-cut-point boundary_evidence[], detection method, and summary
  • By default, propose one cut point every 4 beats; the shortest/longest shot guards will first try to select a nearby beat, and only insert when there is truly no suitable beat duration_guard
  • librosa When unavailable or read fails, generate only a fixed BPM grid and mark the status as review; you must listen to the music to verify, and must not treat fallback as real beats
  • The plan only defines time slots; it does not select media, render, or modify source files; after confirmation, map the shots into render_config / EDL / OTIO
  • Existing cut times still use the original command beat_sync.py --bgm origin/bgm.mp3 --cuts work/cut_times.json --window 0.2

Speed Ramp local slow motion / velocity edit (optional for action, product reveals, games, montages):

  • First find the impact frame frame by frame, then run speed_ramp.py plan origin/action.mp4 --ramp 4.6,5.0,1,0.25,s_curve --hold 5.0,5.8,0.25 --ramp 5.8,6.2,0.25,1,ease --interpolate-fps 120 --output work/speed_ramp_plan.json --markdown work/speed_ramp_plan.md
  • verify will recompute the plan id, source file SHA-256, source/output coverage, and per-segment duration / speed; if the source clip or pieces drift, block immediately
  • apply renders using a temporary file in the same directory, and only replaces the target after success; by default it does not overwrite, does not follow output symlinks, and cannot overwrite the source
  • --interpolate-fps is FFmpeg motion interpolation, which may produce limb / edge artifacts; extremely slow audio must be auditioned, and if necessary add during the plan stage --mute-audio
  • The final result must be played back completely at 1× with sound; after speed changes, regenerate subtitles / timecoded artifacts, run render QA, and redo final approval. See docs/prompts/83-speed-ramp.md

Freeze-Punch keyframe freeze emphasis (optional for expression peaks, action landing points, product reveals):

  • First determine the impact frame by frame, then run freeze_punch.py plan origin/reaction.mp4 --freeze 4.8,0.8,1.08,0.5,0.42 --delivery work/reaction-freeze-punched.mp4 --output work/freeze_punch_plan.json --markdown work/freeze_punch_plan.md
  • It replaces the short picture window after the impact frame with the impact frame; audio and total duration remain unchanged, so subtitles, chapters, or cue timecodes will not drift because of this operation
  • A pending plan will block; after reading the Markdown, run apply, then run verify --strict. apply only atomically promotes the output and writes back the hash after H.264/AAC, yuv420p, size/fps/duration/audio track contract, and full-length decoding pass
  • You must check the freeze entry/exit, visible speaker frozen mouth, motion jumps, subject cropping, and upscale softness at 1× with audio. Drift in source clip/plan/final render will invalidate the live gate; old QA, approval receipt, and publish package must be redone. See docs/prompts/101-freeze-punch.md

J-cut / L-cut audio lead-in / carry-over transitions (optional for interviews, narrative, scene changes):

  • Run only at boundaries explicitly selected by a human:audio_transition.py plan work/render_config.json --transition 1,j_cut,0.4 --transition 3,l_cut,0.5 --output work/audio_transition_plan.json --markdown work/audio_transition_plan.md
  • J-cut uses the real source audio handle before the next clip's in point; L-cut uses the handle after the previous clip's out point and skips an equal-length opening audio segment of the next clip to restore sync. Insufficient handle blocks directly; do not fake it with silence
  • The plan binds render config, transcript, source/B-roll SHA-256, and canonical plan id;verify will recompile from live inputs; manually editing the digest cannot hide drift
  • Use audio_transition.py apply ... --output output/master.mp4 --receipt work/audio_transition_apply.json, or add to render_final.py Add --audio-transition-plan; subtitles, overlays, BGM, and misaligned main audio are still completed in a single FFmpeg encode
  • You must listen at 1× with headphones and on a phone to each changed boundary one by one, and confirm there is no dropped word, repetition, double voice, click, pumping, or ambient noise floor jump. Machines can only verify timing/hash, not whether overlapping dialogue is appropriate. See docs/prompts/86-audio-transition.md

Audio Cue Mix sound effect cue single-track mixing (audio_cue_sheet.v1 run after review):

  • Use the final standalone narration to execute audio_cue_mix.py plan --project-dir . --cue-sheet work/audio_cue_sheet.json --voice work/final_narration.wav --delivery work/audio_cue_mix.wav --output work/audio_cue_mix.json --markdown work/audio_cue_mix.md; local sound effects must be located within the project and bound to SHA-256/media contract
  • When transition_whoosh / emphasis_ping / success_chime / warning_tick is missing, you may explicitly add --synthesize-missing to use the deterministic FFmpeg recipe; unknown categories, missing explicit assets, cue out of range, or illegal gain remain blocking
  • apply uniformly 48 kHz stereo, trim/pad, short fade, millisecond-level adelay and limited gain;amix normalize=0 maintains narration level, limiter only does peak protection, and the output is atomically promoted only after complete decoding passes
  • After listening through the complete 1×, run confirm, and confirm item by item speech_intelligibility / cue_timing / sfx_level / creative_fit / clicks_or_clipping; then verify --strict. Drift in the binding of cue sheet, narration, sound effects, output, or review will block again
  • This output does not include BGM; music continues through render_final.py --bgm-ducking. After mixing in music, listen through completely again and run the final audio QA. See docs/prompts/123-audio-cue-mix.md

Video Stabilization source-bound handheld stabilization (only for unwanted shake):

  • First run video_stabilization.py doctor。plan --backend auto preferring two-pass vidstab; when this filter is missing, the single-pass deshake will be explicitly written into the plan with a degradation warning retained, and the backend will not be silently switched during apply
  • After viewing the original footage, use plan origin/handheld.mp4 --decision stabilize --reviewed-by editor --output work/video_stabilization_plan.json --markdown work/video_stabilization_plan.md to record the source SHA-256, profile, and human decision; intentional handheld / pan should instead use --decision keep
  • apply ... --output work/handheld-stabilized.mp4 --comparison verify/handheld-stabilization-compare.mp4 to write only a new working copy and full-length left/right A/B; then you must watch the complete comparison at 1×, and then run confirm --reviewed-by ... --note ...
  • pipeline_manifest.py will check in real time the hashes and review status of the source footage, stabilized version, and comparison. Stabilization cannot fix rolling shutter, motion blur, or being out of focus; use the working copy afterward and do not overwrite origin/. See docs/prompts/84-video-stabilization.md

Video Enhancement local upscaling / frame interpolation delivery (run when an existing finished video needs a higher size or frame rate):

  • First run runtime_preflight.py analyze --profile video_enhancement ... --strict, then use video_enhancement.py plan output/master.mp4 --target-short-edge 1080 --fps 60 --enhanced output/master-enhanced.mp4 --comparison verify/video-enhancement-ab.mp4 --output work/video_enhancement_plan.json --markdown work/video_enhancement_plan.md
  • --target-short-edge to adapt to both landscape and portrait at the same time, or alternatively use --scale 2; when only interpolating frames, omit the size parameters. The script only accepts upscaling and targets not lower than the source cadence; source hash, display size, target settings, filter, output, and review all enter the canonical plan
  • apply uses Lanczos and optional FFmpeg motion-compensated minterpolate, outputs H.264/AAC; the output is atomically promoted only after size, frame rate, duration, audio track presence, and complete decoding of both MP4s pass
  • Watch the full-length source-left/enhanced-right A/B at normal speed, then separately watch the complete enhanced version with sound;confirm must item by item give detail / edges / motion_cadence / audio_sync of pass|fail. Any fail continues to block.
  • Lanczos is only deterministic resize and cannot recover details that do not exist in the source footage; frame interpolation may produce ghosting/warp at cuts, occlusions, hands, faces, and high-speed motion. When ML VSR or generative restoration is needed, use separately authorized tools and re-run real output review. See docs/prompts/126-video-enhancement.md

Chroma Key green screen / blue screen keying and background replacement (only for explicit solid-color backdrops):

  • First run chroma_key.py prepare --project-dir . --foreground origin/presenter-green.mp4 --background origin/studio.png --output-video output/presenter-studio.mp4 --preview-dir verify/chroma_key --report work/chroma_key.json --markdown work/chroma_key.md
  • prepare to generate a composite and a black-and-white matte at each of the default 15%/50%/85% time points; check hair/hand/clothing edges, subject gaps, green/blue spill, and background perspective/light color; if necessary change only one --similarity / --blend / --despill and preview again
  • After all four items are confirmed, run review --edge-quality pass --subject-integrity pass --spill-control pass --background-fit pass --reviewer ... --note ..., then run apply --strict and verify --strict
  • Image backgrounds stay; video backgrounds loop from the first frame and their audio track is ignored; final audio uses only the foreground. Any drift in source/background/preview/filter/output invalidates the live gate
  • This is not backdrop-free AI matting or frame-by-frame roto; complex transparent objects, reflections, backdrop wrinkles, or subjects of the same color should be re-shot or handed to a professional keyer. For full instructions see docs/prompts/99-chroma-key.md

PIP Overlay screen-recording camera inset (optional for screen-recording tutorials):

  • Run pip_overlay.py --camera origin/facecam.mp4 --segment "0,18,bottom_right" --segment "18,42,top_right" --sync-offset 0.18 --output work/pip_overlay_plan.json --markdown work/pip_overlay_plan.md
  • Append --enrich-plan work/pip_overlay_plan.jsonwhen rendering; can be passed repeatedly together with screen_focus_plan.json、enrich_plan.json - Outputs pip_overlay_plan.v1, including pip_overlays[], position of each segment, source_start, sync offset, size ratio, opacity, and fade in/out
  • render_final.py will compress the camera inset timeline in sync with --primary-speed / --speed , avoiding PIP and main-picture misalignment in speed-changed output

Audio Sync external audio automatic alignment (recommended for in-camera recording + external microphone):

  • Run audio_sync.py --reference-media origin/camera.mp4 --external-audio origin/lav.wav --output work/audio_sync_plan.json --markdown work/audio_sync_plan.md --replace-output output/camera_lav_synced.mp4 --strict
  • Outputs audio_sync_plan.v1, including alignment.offset_seconds、confidence、replace_audio.command, a Markdown review table, and summary.blocking
  • A positive offset means delaying the external audio track; a negative offset means trimming the beginning of the external audio track
  • After confirming the plan, add --apply to execute the FFmpeg audio track replacement; the script copies the original video stream and encodes the synchronized external audio track as AAC
  • If the automatic estimate has low confidence, you can use a manual offset such as --offset 0.18 to skip estimation;pipeline_manifest.py will block audio sync artifacts with low confidence or missing files

Multicam Sync reversible multicam synchronization plan (recommended when two or more devices record the same event):

  • Run multicam_sync.py --reference-media origin/cam-a.mp4 --angle origin/cam-b.mp4 --angle origin/cam-c.mp4 --measure-clock-drift --output work/multicam_sync_plan.json --markdown work/multicam_sync_plan.md --preview-output output/verify/multicam_sync_preview.mp4 --apply-preview --strict
  • Outputs multicam_sync_plan.v1: offset/confidence for each track, automatically loudest audio track, reference/source coverage, common overlap, pairwise consistency, optional drift probes/linear fit, and preview command
  • Original footage is not modified or re-encoded; only --apply-preview generates a short grid preview. A positive offset means that camera is t=0 located later on the reference timeline
  • Cameras with multiple audio tracks can use --audio-stream "origin/cam-b.mp4=2" to specify the active track; for cameras without audio or when manually setting offsets, use --manual-offset "origin/cam-c.mp4=1.24"
  • Drift measurement is off by default; when enabled, it takes 5 windows of 20 seconds by default, and fits only when there are at least 4 consensus inliers for offset(R)=intercept+slope*R, reporting explicitly signed ppm, measurement resolution, cumulative drift, residual, and unapplied atempo/setpts advisory factors
  • Drift evidence only covers the selected reference/source audio track; it cannot be used to automatically assert that the video PTS or other audio tracks share the same clock
  • Drift exceeding the threshold or unreliable fitting enters the review gate; the script does not auto-correct, and audio and video must use the same affine mapping. When not enabled, runs longer than 30 minutes must still be reviewed at head/middle/tail
  • For detailed usage and boundaries, see docs/prompts/76-multicam-sync.md

Audio-guided Multicam Switch generates a directing draft based on speaker audio (optional after synchronization passes):

  • Run multicam_switch.py plan --sync-plan work/multicam_sync_plan.json --speaker angle_00_cam-a=主持人 --speaker angle_01_cam-b=嘉宾 --program-audio angle_00_cam-a --delivery output/multicam_switch_draft.mp4 --output work/multicam_switch_plan.json --markdown work/multicam_switch_plan.md --strict
  • Each audio channel is normalized separately by P10 noise floor/P95 peak; the camera is switched only when both the activity score and the lead margin meet the threshold, ambiguous windows keep the previous camera, and flashes shorter than --min-shot fold back to the adjacent shot
  • Highly correlated audio is blocked by default as shared mix/strong crosstalk;--allow-correlated-audio only retains an explicit override warning and cannot prove that speaker detection is reliable. Do not map wide shots, program masters, or cameras without independent speaker audio tracks to speaker candidates
  • --program-audio specifies continuous program audio; the microphone is not switched when the camera view changes. After apply unifies canvas/FPS/SAR, it renders with hard cuts and only promotes the output after completing the media contract and full decode
  • After a complete 1× playback with sound, confirm must record item by item speaker_selection / cut_timing / sync / audio_continuity; drift in source, sync plan, settings, output, or review will invalidate the manifest gate
  • For detailed usage and boundaries, see docs/prompts/127-multicam-switch.md

Storyboard Plan storyboarding and generation routing (recommended before generating assets):

  • Run storyboard_plan.py --transcript work/transcript.json --clean-script work/clean_script.md --output work/storyboard_plan.json --markdown work/storyboard_plan.md
  • Output the timecode, narration, first/motion/last-frame descriptions,codex_imagegen / dreamina_video / remotion_hyperframes / media_library_broll routing, fallback, and continuity anchors for each shot
  • Before using a generative video provider, first run provider_capability.py verify --bundle work/provider_capabilities.json --max-age-days 30 --strict, and verify the exact UI/API surface, model, mode, aspect ratio, duration, resolution, reference limit, and evidence date
  • Optionally run video_prompt_pack.py --storyboard-plan work/storyboard_plan.json --asset-root work --style-reference work/imagegen/style-key.png --capability-profile work/provider_capabilities.json --require-capability-profile --resolution 720p --output work/video_prompt_pack.json --markdown work/video_prompt_pack.md --strictto convert the storyboard into Dreamina Seedance, Veo, LTX, Wan, Sora prompt packs, a shared style lock, approval, and capability gate
  • Before submitting image-to-video, run reference_frame_preflight.py --prompt-pack work/video_prompt_pack.json --output work/reference_frame_preflight.json --markdown work/reference_frame_preflight.md --require-style-reference --strictto block missing/corrupted/direction-conflicting/severe aspect-ratio-conflicting reference frames
  • Then run storyboard_assets.py --storyboard-plan work/storyboard_plan.json --asset-root work --media-library . --output work/storyboard_assets.json --markdown work/storyboard_assets.md --strict, and before rendering confirm the asset ready
  • dreamina_video only indicates suitability for video generation and does not automatically submit tasks; confirmation is required before submitting to Dreamina because it may consume credits
  • For image generation, prefer the built-in Codex image_gen tool, i.e., OpenAI GPT Image 2 (gpt-image-2).

Reference Story Formula reference film story formula transfer (run when the user requests borrowing the emotional mechanism of a reference film):

  • Put the reference video and its timecoded transcript into the project, then run reference_story_formula.py prepare --project-dir . --reference-video origin/reference.mp4 --reference-transcript origin/reference_transcript.json --target-storyboard work/storyboard_plan.json --output work/reference_story_formula_request.json --markdown work/reference_story_formula_request.md --response-template work/reference_story_formula_response.json --strict
  • Watch the reference film in full and fill in the mechanism / viewer_state_before / viewer_state_after / trigger / camera_function / transferable_rule / do_not_copyfor each beat; each reference transcript segment must belong to exactly one ordered beat
  • Fill in beat_id / content_anchor / viewer_shift / surface_change / visual_actionfor each shot of the target storyboard, and the order must not go backward; also explicitly exclude the reference film's pixels, audio, wording, branding, and specific plot
  • Run audit --request ... --response ... --output work/reference_story_formula.json --markdown work/reference_story_formula.md --strict, then execute verify --report work/reference_story_formula.json --strict. Drift in inputs, response, or derived reports fails closed; long verbatim overlap enters a visible warning
  • Add to video_prompt_pack.py Add --reference-story-formula work/reference_story_formula.json. The script will live-verify the byte binding with the current storyboard and inject the corresponding structure, target content anchors, and anti-copy contract into each prompt. It does not prove copyright, originality, retention effectiveness, or generation quality. See docs/prompts/125-reference-story-formula.md

Storyboard Animatic shot duration preview (recommended before paid generation or final editing):

  • Prepare one reviewed still frame for each storyboard shot, then run storyboard_animatic.py plan --project-dir . --storyboard work/storyboard_plan.json --panel shot_001=work/storyboard/shot_001.png --panel shot_002=work/storyboard/shot_002.png --delivery verify/storyboard_animatic.mp4 --output work/storyboard_animatic.json --markdown work/storyboard_animatic.md
  • --panel SHOT_ID=PATH must exactly cover all shots; optionally add --audio work/narration.wav. The tool binds the SHA-256 and media metadata of the storyboard, panels, and audio, and generates a CFR H.264 MP4 with shot IDs/timecodes according to shot starts
  • After executing apply , play back the complete 1× and check shot_order / timing_rhythm / panel_legibility / visual_continuity / audio_sync, then run confirm; when there is no audio, audio_sync=not_applicable
  • verify --strict and pipeline_manifest.py --require storyboard_animatic --strict will re-read all inputs on the spot, fully decode the deliverable, and verify the review/output binding. Animatic only verifies still-frame order and pacing; subsequent generated clips and the final cut still require their own reviews. See docs/prompts/121-storyboard-animatic.md

Sequence Handoff pre-generation shot handoff and edit boundaries (recommended when there are two or more generated shots):

  • Run sequence_handoff.py prepare --project-dir . --storyboard work/storyboard_plan.json --output work/sequence_handoff_request.json --markdown work/sequence_handoff_request.md --response-template work/sequence_handoff_response.json --strict
  • Review carrier_type / offer_from / receive_in / edit_type / match_requirementfor each adjacent shot pair, while declaring the 180° axis, screen motion direction, audio bridge, head/tail edit handles, risks, fallback, and specific review notes; automatic suggestions only read the storyboard text and cannot replace human spatial/action judgment
  • Generate the report with audit --request work/sequence_handoff_request.json --response work/sequence_handoff_response.json --output work/sequence_handoff.json --markdown work/sequence_handoff.md --strict , then recompute on the spot with verify --report work/sequence_handoff.json --strict ;revise, missing fields, illegal axis/direction flips, source storyboard or decision drift all fail closed
  • Add --project-dir . --sequence-handoff work/sequence_handoff.jsonto the final prompt pack; the tool will first live-verify, then write the first-shot handoff-out, last-shot receive-in, and bidirectional contracts for intermediate shots into the corresponding provider prompts
  • Text contracts do not prove final pixel continuity; after generation, still execute generated_clip_review.py and generated_sequence_review.py, and review all boundaries at normal speed after assembly. See docs/prompts/120-sequence-handoff.md

Video Prompt Pack + Reference Frame Preflight video generation prompts and reference frame gating (recommended before generating video):

  • First establish provider_capabilities.jsonaccording to docs/prompts/96-provider-capability.md, then run provider_capability.py verify --bundle work/provider_capabilities.json --max-age-days 30 --strict; the profile binds the exact provider/surface/model and source, and by default blocks if older than 30 days
  • Run video_prompt_pack.py --project-dir . --storyboard-plan work/storyboard_plan.json --sequence-handoff work/sequence_handoff.json --asset-root work --character "same host" --brand-anchor "palette=charcoal,white,signal yellow" --style-reference work/imagegen/style-key.png --capability-profile work/provider_capabilities.json --require-capability-profile --resolution 720p --output work/video_prompt_pack.json --markdown work/video_prompt_pack.md --strict
  • Output global.character_sheet_prompt、global.style_reference、global.sequence_handoff、items[].prompt、items[].sequence_handoff、items[].negative_prompt、items[].surface/model/resolution、items[].capability_profile、items[].capability_issues、items[].approval_status and summary.blocking
  • --style-reference will bind the same style key to all shots and add the same STYLE LOCK to each provider prompt
  • After the reference images are written to disk, run reference_frame_preflight.py --prompt-pack work/video_prompt_pack.json --output work/reference_frame_preflight.json --markdown work/reference_frame_preflight.md --require-style-reference --strict
  • reference_frame_preflight.v1 to check whether the first frame/style key exists, is decodable, matches direction/aspect ratio, has too low resolution, and has a transparent background; gating items feed into pipeline_manifest.py
  • --provider veo|ltx|wan|sora|dreamina_seedance can rewrite the same storyboard into a specified model prompt;--animate-stills will convert the codex_imagegen reference image route into an image-to-video prompt
  • --strict will return 2 if the generated-video provider has not yet been explicitly approved with --approved, or if the capability profile is missing, expired, or outside its verified limits; the script does not submit provider tasks or consume credits
  • For image generation, prefer using Codex's built-in image_gen tool, i.e. OpenAI GPT Image 2 (gpt-image-2).

Generation Reference Preflight image / video / audio generation reference gate (must run when using multimodal reference):

  • The provider profile's reference_media must record whether frame/reference are mutually exclusive, whether audio-only is supported, the total file count, and the extension, byte, single-item duration and total duration limits for images/video/audio; unverified limits must not be filled in by guessing
  • First use generation_reference_preflight.py template --project-dir . --prompt-pack work/video_prompt_pack.json --output work/generation_references.json to build the manifest; for each actually submitted asset fill in kind / path / role / exclude, and the order is the @ImageN / @VideoN / @AudioN order for each type
  • Run generation_reference_preflight.py analyze --project-dir . --prompt-pack work/video_prompt_pack.json --references work/generation_references.json --capability-profile work/provider_capabilities.json --output work/generation_reference_preflight.json --markdown work/generation_reference_preflight.md --strict
  • The report fully decodes local references, checks type, extension, size, single/total duration, count, audio-only, frame/reference mode mutual exclusion and capability profile binding, and outputs shots[].provider_prompt with explicit roles and exclusions
  • When submitting, use the ordered asset list and provider_promptfrom the ready report; afterward re-check on site with verify --report ... --strict or pipeline_manifest.py --require generation_reference_preflight --strict . See docs/prompts/122-generation-reference-preflight.md

Generation Task Log asynchronous generation task ledger (recommended after submitting a generation task):

  • Generate a pending-approval ledger from the provider decision:generation_task_log.py import-provider-decision --provider-decision work/provider_decision.json --log work/generation_tasks.json --markdown work/generation_tasks.md --strict
  • After submitting to Dreamina/即梦, you must save submit_id:generation_task_log.py add --log work/generation_tasks.json --provider dreamina --task-id <submit_id> --shot-id shot_002 --expected-path work/generated_video/shot_002.mp4 --status submitted
  • After download completes, update the local file:generation_task_log.py update --log work/generation_tasks.json --provider dreamina --task-id <submit_id> --status downloaded --asset-path work/generated_video/shot_002.mp4 --markdown work/generation_tasks.md
  • Output generation_task_log.v1, including poll_command、download_command、readiness and summary.blocking
  • completed but without local asset_path will still block;pipeline_manifest.py automatically identifies generation_tasks.json and lists tasks not zeroed out as a gate

Generated Clip Review generated video clip review (must run after generated results are downloaded and before assembly):

  • First refresh storyboard_assets.json, then run generated_clip_review.py prepare --project-dir . --asset-manifest work/storyboard_assets.json --contact-sheet-dir verify/generated_clips --output work/generated_clip_review_request.json --markdown work/generated_clip_review_request.md --response-template work/generated_clip_review_response.json
  • The reviewer must fully perform four passes: 1× with sound, 0.25×, watching the picture muted, and listening to the sound only; a contact sheet is only sampled navigation and cannot replace playing the full clip
  • For each clip, the response records six 1–5 scores for identity/wardrobe, action/end state, motion/anatomy/physics, camera, frame integrity, look consistency, plus hard-fail code,keep_ranges / remove_ranges, whether to regenerate and the specific prompt_fix
  • pass requires a weighted score ≥80 with no deleted segments;pass_with_edits requires ≥65, with keep/remove precisely covering the entire film; common-sense/physics, identity, props, text/watermark, continuity or audio-visual hard fails must be fail regardless of how high the total score is
  • After filling in the response, run generated_clip_review.py audit --request work/generated_clip_review_request.json --response work/generated_clip_review_response.json --output work/generated_clip_review.json --markdown work/generated_clip_review.md --strict; any clip/contact sheet drift, missed review, illegal interval or need for regeneration will block
  • Before assembly/publishing, run again generated_clip_review.py verify --report work/generated_clip_review.json --strict, or use pipeline_manifest.py --require generated_clip_review --strict. The reviewer label is not identity authentication or a digital signature

Generation Chain Handoff reviewed generated clip end-frame handoff (run when continuously generating the same scene):

  • Use this only when sequence_handoff.v1 has approved that adjacent boundary and the previous shot's generated_clip_review.v1 is pass or pass_with_edits ; do not force a handoff for scene changes, time jumps, deliberate jump cuts, or ordinary edit matches
  • Run generation_chain_handoff.py prepare --project-dir . --clip-review work/generated_clip_review.json --sequence-handoff work/sequence_handoff.json --boundary boundary_001 --frame-output work/generation_chain/boundary_001_tail.png --output work/generation_chain_handoff.plan.json --markdown work/generation_chain_handoff.plan.md; the script enumerates decoded frames, takes the exact last frame from the full clip or the last approved keep range, and requires the source frame/PNG RGB24 pixel digests to match
  • After viewing the previous shot and the PNG, execute confirm --decision use_exact_start_frame --reviewed-by ... --same-scene-confirmed --tail-frame-accepted --original-anchors-preserved --notes ... --output work/generation_chain_handoff.json --strict; when the tail frame is unsuitable, use reject and return to the storyboard, generation, or ordinary edit boundary
  • Before submitting the next shot, run verify --plan work/generation_chain_handoff.json --strict, then to video_prompt_pack.py add --generation-chain-handoff work/generation_chain_handoff.json. Change the target shot to image_to_video, and the reviewed tail frame becomes the exact first frame; the prompt simultaneously retains the original character/product/style anchors and is limited to one primary action and one camera behavior
  • Drift in upstream reports, clips, approved ranges, frame index/PTS, PNG bytes/pixels, human decisions, or canonical artifact IDs will block; each newly generated clip still requires per-clip review, and the cross-shot review is run only after all are complete. See docs/prompts/124-generation-chain-handoff.md

Generated Motion Window Valid Motion Window for Generated Video (run after per-clip visual review and before cross-shot assembly):

  • The contact sheet may miss frozen openings or brief motion lasting 0.25–1 second; for each generated clip that passes visual review, run generated_motion_window.py analyze work/generated_video/shot_001.mp4 --project-dir . --output work/generated_motion_window/shot_001.json --markdown work/generated_motion_window/shot_001.md
  • The script uses local FFmpeg freezedetect to save freezes[] and its complement active_intervals[], bound to the source SHA-256, media contract, parameters, and canonical plan id; a fresh analyze is hard-blocked, and detector suggestions cannot be treated as approval
  • After playing the full source clip at 1×, run confirm ... --decision trim|keep|reject --reviewed-by ... --note ...。trim uses the suggested range by default, or an explicit --start/--end; both boundaries must fall within the active interval
  • trim continues to block before apply;apply ... --output work/generated_motion_window/shot_001-active.mp4 only writes a new H.264/AAC,yuv420p working copy, verifies duration/size/fps/audio track and fully decodes it before atomic promotion. Drift in the original clip, plan, detection evidence, or output fails closed
  • The new working copy must be fully replayed again, and generated-clip review, sequence/final QA, and downstream approvals must be rerun;pipeline_manifest.py --require generated_motion_window --strict live-verifies each work/generated_motion_window/*.json. FFmpeg freeze only measures full-frame similarity and cannot judge action meaning, performance, identity, physics, or product quality. See docs/prompts/102-generated-motion-window.md

Scoped Video Edit Review Scoped Review of Local AI Video Edits (must run after edit modes such as outfit change/background replacement/packaging replacement/local VFX):

  • Declare exactly one change per provider call; run scoped_video_edit_review.py prepare --project-dir . --source origin/source.mp4 --edited work/scoped_video_edit.mp4 --change-category wardrobe --change "change only the jacket from blue to red" --preserve subject_identity --preserve performance_motion --preserve camera_motion --preserve framing_composition --preserve source_audio --evidence-dir verify/scoped_video_edit --output work/scoped_video_edit_review_request.json --markdown work/scoped_video_edit_review_request.md --response-template work/scoped_video_edit_review_response.json
  • prepare to bind the original clip/edited result SHA-256 and media contract, generate side-by-side JPEGs at matching timestamps at 15%/50%/85% of the range, and a full side-by-side H.264 preview; when both input audio tracks exist, the preview keeps source/edited as separate audio tracks
  • The reviewer must separately watch the original clip and the edited result in full at 1× with sound, then watch the full A/B scope preview; the target change and each --preserve may only be pass|fail|not_observable, and must not guess pass when unclear
  • audit --strict is ready only when the target is genuinely complete, all protected items pass, all three playback passes confirm completeness, and duration/size/frame rate have not drifted; on failure, a specific repair_action must be written
  • Before assembly/publishing, run verify --report work/scoped_video_edit_review.json --strict, or pipeline_manifest.py --require scoped_video_edit_review --strict. The script does not call a provider, does not upload assets, and does not consume credits; the suggested prompt is a provider-neutral contract and cannot replace capability verification of the current surface. See docs/prompts/100-scoped-video-edit-review.md

Generated Sequence Review cross-shot continuity review (must run after two or more generated clips have each passed review and before assembly):

  • Run generated_sequence_review.py prepare --project-dir . --clip-review work/generated_clip_review.json --storyboard-plan work/storyboard_plan.json --evidence-dir verify/generated_sequence --output work/generated_sequence_review_request.json --markdown work/generated_sequence_review_request.md --response-template work/generated_sequence_review_response.json
  • Following storyboard order, for each adjacent boundary the script extracts the previous shot's tail frame, the next shot's head frame, a side-by-side image, and a silent 1× preview of "tail + head" within the approved range; if the upstream is pass_with_edits, use only the approved first/last keep range
  • After fully viewing the preview and comparison, fill in match|intentional_change|mismatch|not_applicablefor identity/wardrobe, prop state, spatial orientation, action end state, camera framing, lighting/palette. At least two items must be genuinely evaluated;mismatch must fail, giving the failure code and specific repair_action
  • Run generated_sequence_review.py audit --request work/generated_sequence_review_request.json --response work/generated_sequence_review_response.json --output work/generated_sequence_review.json --markdown work/generated_sequence_review.md --strict, and before assembly/publishing run verify --report work/generated_sequence_review.json --strictagain; any drift in clips, upstream reviews, storyboard, or any evidence bytes will block
  • pipeline_manifest.py --require generated_clip_review --require generated_sequence_review --strict can set both the per-clip and cross-shot layers as gates. The preview is silent and cannot replace the full audio-visual review of the final master; reviewer labels and SHA-256 are not identity authentication or signatures

Generation Lessons generated-video lessons loop (optional; only distill lessons that are generalizable and explicitly approved by a human):

  • From completed generated_clip_review.json select one clip and run generation_lessons.py add --library work/generation_lessons.json --review work/generated_clip_review.json --clip-id shot_002 --category hand_contact --lesson "For hand-to-prop contact, isolate one interaction and keep the hand visible through release." --approved-by "<reviewer-label>" --model seedance-2.0
  • add will recompute the canonical review in real time, allowing the expected blocker "the clip genuinely needs regeneration" but rejecting source/contact-sheet drift, missed reviews, invalid responses, and tampering with the stored summary/report-id; the entry is bound to the report/request/clip/contact-sheet SHA-256.approved_by is only an audit label, not identity authentication or a signature
  • Before reuse, run generation_lessons.py verify --library work/generation_lessons.json --strict; to preview available entries by provider/model/category use generation_lessons.py select --library work/generation_lessons.json --provider dreamina_seedance --model seedance-2.0 --limit 3 --output work/selected_generation_lessons.json --markdown work/selected_generation_lessons.md
  • When new evidence conflicts with an old rule, do not delete the old entry; create a new lesson and add --supersedes <old-lesson-id>. The old evidence remains in the library, but as long as the new entry also matches the current provider/model/category,select excludes the superseded rule
  • Explicitly add video_prompt_pack.py ... --lesson-library work/generation_lessons.json --lesson-model seedance-2.0 --lesson-limit 3to the next generation prompt package. Provider-specific rules take priority; when --lesson-model is not supplied, model-specific lessons will not be misapplied; the script only appends human-approved lesson, will not automatically treat clip-specific prompt_fix as a general rule, and will not automatically regenerate or consume credits
  • For image generation, prefer Codex's built-in image_gen tool, i.e. OpenAI GPT Image 2 (gpt-image-2).
  • For details see docs/prompts/89-generated-clip-review.md

Storyboard Assets asset inventory and preflight (recommended after storyboarding and before rendering):

  • Run storyboard_assets.py --storyboard-plan work/storyboard_plan.json --asset-root work --media-library . --output work/storyboard_assets.json --markdown work/storyboard_assets.md
  • Output each shot's asset status:ready / candidate_found / needs_generation / needs_approval / needs_render / search_needed
  • When an asset index is supplied via --media-library,media_library_broll shots receive candidate_paths + candidate_scores, ranked transparently by tag, filename, metadata, duration, and aspect ratio
  • With --strict , any asset not ready returns exit code 2, suitable for placement right before the final render
  • needs_approval Represents tasks such as Dreamina/即梦 that may consume credits and must be confirmed before submission; for image generation, prefer Codex's built-in image_gen tool, i.e., OpenAI GPT Image 2 (gpt-image-2).

Custom cover (cover_image):

  • Provide a custom cover PNG path, which takes precedence over the automatically generated cover
  • The dimensions should match the video resolution (e.g., 1080x1920)

Persistent overlay (video_overlay):

  • Provide a transparent PNG path to persistently overlay on the entire video after the cover
  • Applicable scenarios: brand watermarks, series identifiers (e.g., DAY labels, data metrics), etc.
  • The PNG must contain an Alpha channel (RGBA); transparent areas will not obscure the video

Blinking dot (rec_blink):

  • Overlay a periodically blinking small dot PNG on the video (e.g., a camcorder REC indicator)
  • dot_image: dot PNG path (12-16px recommended, RGBA format)
  • x, y: pixel coordinates of the dot within the video frame
  • period: blink period (seconds), default 1.0 (0.5s on + 0.5s off)

End cards (end_cards):

  • Append black-screen text cards at the end of the video, each card with a 300ms fade in/out
  • text: card text content, use \n for line breaks
  • duration: display duration per card (seconds), 3.0-4.0 recommended
  • Text is centered, at 1.4 times the body subtitle font size

Spoken-word steady noise floor cleanup (speech_denoise):

  • Default off; enable "light" / "medium" / "strong" only when air conditioning, fans, electrical hum, or slight room noise floor are relatively steady
  • CLI available: --speech-denoise light; when enabled in config, use --no-speech-denoise to temporarily disable
  • Three fixed levels: 80 Hz, 2-pole high-pass, FFT reduction 6/9/12 dB;strong will not exceed 12 dB
  • Fixed order: highpass → afftdn → atempo → dynaudnorm → acompressor → loudnorm → cover delay → BGM ducking/mix
  • Tracks that have already undergone VAD/noise gate, Adobe Podcast, Descript, RX, or strong in-camera noise reduction usually stay off; digital silence, noise bursts, multiple mics, transient taps/coughs, and reverb must be handled separately
  • First render 10–20 seconds of off/light/medium A/B, listen with headphones at normal speed for consonants, trailing sounds, and pauses; after rendering continue running audio_master_report.py --strict

Background music (bgm):

  • Provide a background music file path (MP3/M4A/WAV, or other FFmpeg-supported formats)
  • bgm_volume: BGM volume (0.0-1.0), default 0.15 (vocals primary, BGM secondary)
  • bgm_fade_out: ending fade-out duration (seconds), default 3.0
  • bgm_ducking: true or CLI --bgm-ducking: use the final narration track as a sidechain to automatically lower BGM when narration is present; music naturally recovers during the cover, pauses, and end credits when there is no narration
  • Default ducking parameters are threshold 0.03、ratio 8、attack 20ms、release 500ms; adjust only when audition or audio_master_report.py evidence indicates it is needed
  • The ducking branch uses amix normalize=0 to preserve vocal loudness and adds alimiter=0.95 at the end of the mix to prevent peak overflow; old configurations still default to off, maintaining compatibility with existing outputs
  • BGM automatically loops until the video ends; no need to pre-trim the length
  • Recommended free commercial-use music sources: Pixabay Music, Mixkit, YouTube Audio Library
  • Track selection advice: for spoken-word/tutorials use soft instrumental music (no vocals), not too strong a rhythm, to avoid competing with the voice

Subtitle style presets (subtitle_style):

Style Effect Applicable scenarios
normal White text with black outline (default) Suitable for all scenarios
karaoke Word-by-word highlight Music/rhythmic content
bold_pop Thick outline, high contrast MrBeast/Hormozi style
pop_in Each subtitle scales in on entry, then springs back Faster-paced short videos; subtitles shorter than 0.3 seconds stay static
neon Neon cyan-purple Tech/trendy content
minimal Minimalist, no outline Artistic/quiet content
yellow_pop Yellow text with black outline High visibility, outdoor/noisy footage

Karaoke subtitles / word-by-word highlight (subtitle_style: "karaoke"):

  • Set "subtitle_style": "karaoke" in config to enable word-by-word highlighted subtitles
  • You need to first run speech recognition with the --word-timestamps parameter to obtain word-level timestamps
  • subtitle_highlight_color: highlight color for the current word, default "#FFFF00"(yellow)
  • subtitle_base_color: base color for words not yet spoken, default "#FFFFFF"(white)
  • subtitle_base_alpha: base color opacity hex, default "80"(semi-transparent)
  • If the transcript has no word-level timestamps, it automatically falls back to even distribution by character (slightly worse effect)
  • You can also override config via the CLI parameter --subtitle-style karaoke - Typical workflow:
    # 1. 转录时开启逐词时间戳
    python3 scripts/transcribe.py audio.wav --model auto --language zh --word-timestamps
    # 2. 渲染时选择 karaoke 字幕风格
    python3 scripts/render_final.py --config render_config.json --output final.mp4 --subtitle-style karaoke
    

Per-subtitle pop-in subtitles (subtitle_style: "pop_in"): use render_final.py --subtitle-style pop_in, or set "subtitle_style": "pop_in"in the render config. Each subtitle independently performs an 80% → 108% → 100% scale based on its duration at the final video speed; subtitles shorter than 0.3 seconds are displayed directly at full size. A still frame fromsubtitle_style_preview.py --styles pop_in can only check the final glyph shape, position, and contrast; the entrance animation and safe area must be checked at normal speed in the final MP4.

Audio source replacement (M4A/standalone audio):

  • If you have a separately recorded audio file (M4A, etc.), you can first convert it with ffmpeg to an MP4 with a black-screen video track:
    ffmpeg -f lavfi -i "color=c=black:s=1080x1920:r=30:d=60" -i audio.m4a -c:v libx264 -c:a aac -shortest audio.mp4
    
  • Then in the clip, use "video": "audio.mp4" to provide the audio source and "broll" to provide the visuals
  • This lets you combine re-recorded voiceover with any visuals

Cover title:

  1. If the user provides a title, use it directly.
  2. If the user has no special requirements, from the audience's perspective summarize an appealing title (6-15 characters).
  3. subtitle is optional, used to add an emotional hook or key information.
  4. Mobile-first: On mobile list pages, the cover is just a thumbnail; the title must first ensure readability, then consider visual details.

Cover text layout rules:

  • Design the title for at most about 8 Chinese characters per line; wrap automatically when exceeded, and do not cram a single line too full.
  • Estimate English/numbers at about half a Chinese character width; for example, AI、GPT-5 and similar should not blow out the entire line width.
  • The subtitle font size defaults to about 50% of the title, serving only as supplementary information and not competing with the main title for visual focus.
  • If the title exceeds two lines, prioritize shortening the copy; do not keep shrinking the font size to force it in.
  • For tutorial/tool covers, keep the main title at 4-8 characters and the subtitle at 6-12 characters as much as possible.

Cover style (cover_style) — choose the most suitable style based on the video content:

Style Applicable scenarios Visual effect
bold Tutorials, popular science, technology Black background + large white bold text, concise and impactful
news Hot topics, opinions, controversy Dark gradient background + white headline + yellow subheadline, strong visual impact
frame Vlogs, live footage, scenes Video first frame as background + dark overlay + white text with stroke
gradient Life, emotions, art Purple-pink gradient background + glowing white text, gentle and elegant
minimal Reflection, culture, depth Pure black background + thin white text, minimalist and restrained
white Tutorials, products, branded content Pure white background + dark text, stronger modern feel
techcard Screen recording, software tutorials, AI tool demos Large headline on the left + visual card on the right, balancing information density and readability

The AI agent should automatically choose based on the video topic and content tone:

  • Tech/tools → bold or news
  • Controversy/opinions → news(white headline + yellow subheadline is the most eye-catching)
  • Vlogs/live footage → frame
  • Emotions/life → gradient
  • Depth/culture → minimal
  • Pure desktop screen recording / software tutorials → techcard preferred; if the visuals are too cluttered, fall back to bold / white

Background frame selection rules:

  • Do not mechanically use the first frame. For screen-recording tutorials, prefer a frame with higher information density and a more complete interface as the background or card image.
  • If the background visuals would interfere with headline legibility, prefer pure-background styles like bold / white / minimal .
  • When previewing a cover on its own, use scripts/generate_cover_image.py --frame-timestamp 00:10:00 to specify the frame timestamp.

Multi-cover A/B plans and final selection:

python3 scripts/cover_variants.py output/final_xhs.mp4 \
  --title "20分钟出片" \
  --subtitle "AI剪辑完整流程" \
  --caption output/caption.json \
  --platform xhs \
  --frame-timestamp 12.5 \
  --output-dir output/covers \
  --render \
  --output work/cover_variants.json \
  --markdown work/cover_variants.md

By default, produce 3 plans and *_preview.png thumbnails. First compare using feed-size previews, then rerun and add --select cover-c --require-selection --strict to record the final cover. The title handles topic/keywords; the cover handles outcome/emotion/contrast/evidence; when you need to enforce non-duplication, add --require-distinct-cover-text。pipeline_manifest.py --require cover_variants to make the selection a publish gate. If AI-generated or edited cover base images are needed, prefer Codex's built-in image_gen tool for image generation, i.e., OpenAI GPT Image 2 (gpt-image-2).

Cover duration (cover_duration):

  • Default 2.0 seconds, freezing the first frame and overlaying the cover
  • Can also be overridden via the --cover-duration command-line argument

Chapter segmentation:

  • Segment chapters according to the video content logic; no more than 4 chapters is recommended
  • Chapter names should be short (2-4 characters), e.g., pain point, cause, solution, tool
  • Chapter timestamps need to be precisely calculated based on the cumulative duration of the selected clips

Phase 4.8: Edit Revision (reversible revision of the editing artifact, optional)

When the user requests changes to render_config.json、enrich_plan.json, captions, or subtitle sidecars, and at the same time needs a full review, dependency drift prevention, and undo/redo, first prepare a proposal:

python3 scripts/edit_revision.py prepare \
  --project-dir . \
  --artifact work/render_config.json \
  --artifact work/enrich_plan.json \
  --depends-on work/transcript_reviewed.json \
  --title "收紧开头并调整 B-roll" \
  --reason "已完成时间码审片,采用第二版开头。" \
  --output work/edit_revision_proposal.json

Only modify the proposal's artifacts[].proposed_content, then run audit. A valid audit's pending_approval returning 2 under --strict is an expected manual gate; you must separately save an approval JSON bound to the same review_id、decision: approve and a non-empty approved_by_label , and only then willapply write multiple files as a single revision as a group and create work/edit_revision_history.json. Runtime write errors will attempt to restore the old bytes, but cross-file OS-level atomic commit is not guaranteed; after a process anomaly, first run status --strict. status checks artifacts, applied dependencies, and content-addressed before/after blobs in real time; it refuses undo/redo when there are external manual edits, dependency drift, symlinks, or blob corruption. Raw media, code, final output videos, and verify/ are outside the management scope. For the detailed workflow, see docs/prompts/80-edit-revision.md.

Phase 4.85: Portable Edit Recipe (portable editing recipe, optional)

When the user wants to save an already-reviewed render_config.json as a template, or replicate the same timeline/subtitle/BGM/overlay structure with a different batch of materials, first export a path-free recipe:

python3 scripts/edit_recipe.py export \
  --config work/render_config.json \
  --name fast-tech-explainer \
  --description "快节奏科技口播" \
  --output work/recipes/fast-tech-explainer_edit_recipe.json \
  --markdown work/recipes/fast-tech-explainer_edit_recipe.md

Source preflight must pass before export; local file references become typed slots, the same path occupies only one slot, and the recipe does not store the original paths. Open the Markdown slot table in the new project and pass --bind SLOT=PATH once for each slot, then run replay, while writing --receipt and --markdown; the script verifies the portable digest, slot set, file types, and existence, records the new binding hash, and runs preflight again on the output config. It does not overwrite by default; add --forceonly when the target is definitively correct. The recipe hash is not a signature or manual approval; after playback you must still render and watch the complete final video. For the detailed workflow, see docs/prompts/82-edit-recipe.md.

Phase 4.9: Platform Safe Area QA (platform UI safe area gate)

Before rendering or multi-platform export, check separately for each target platform whether subtitles, emphasis badges, PIP portraits, CTAs, chapter cards, and click markers enter the top status bar, the bottom caption area, or the right-side interaction button bar:

python3 scripts/platform_safe_area_qa.py \
  --config work/render_config.json \
  --enrich-plan work/enrich_plan.json \
  --platform xhs \
  --output verify/xhs_platform_safe_area_qa.json \
  --markdown verify/xhs_platform_safe_area_qa.md \
  --guide verify/xhs_platform_safe_area_guide.svg \
  --strict

For the Douyin and WeChat Channels derived versions, use --platform douyin / --platform wxchrespectively. The script estimates bboxes according to render_final.py 's default ASS, PIP, and focus-marker geometry rules; CTAs/Logos outside the renderer can provide pixel or normalized bboxes via --elements custom_elements.json . When platform app UIs change, use --safe-left / --safe-top / --safe-right / --safe-bottom to override the measured margins.

This is a local, auditable layout gate: it does not upload, call an LLM, or do OCR, and it does not infer the subject/text positions inside generated images or covers. Existing full-image chapter cards are marked uncheckable, and the SVG guide or rendered frame must be opened for manual review.summary.blocking > 0 will make --strict return 2; to make it a required production-line item, use pipeline_manifest.py --require platform_safe_area_qa --strict.

Phase 4.95: Subtitle Style Preview (real-footage subtitle style preview)

Before final encoding, first render the real ASS preset onto early/middle/late representative frames of the source video:

python3 scripts/subtitle_style_preview.py create \
  --project-dir . \
  --video origin/talking.mp4 \
  --platform xhs \
  --text "这句字幕要覆盖中英文和数字 2026" \
  --preview-dir verify/subtitle_styles \
  --output work/subtitle_style_preview.json \
  --markdown work/subtitle_style_preview.md \
  --require-selection

python3 scripts/subtitle_style_preview.py select \
  --report work/subtitle_style_preview.json \
  --style normal

python3 scripts/subtitle_style_preview.py verify \
  --report work/subtitle_style_preview.json \
  --strict

By default, compare normal / minimal / bold_pop, and use render_final.py 's same ASS builder, font, font size, and center crop to the target aspect ratio. Check both phone size and full size; after confirmation, pass the chosen value to render_final.py --subtitle-style. Changes to the source video, font, style definition, or JPEG bytes invalidate the old selection.karaoke The preview only proves the visual style; the actual word-by-word pacing still depends on word-level timestamps; in the end you still need to run safe-area, readability QA, and 1× final-cut review. See docs/prompts/94-subtitle-style-preview.md for details.

Phase 5: Single-Pass Render

Use render_final.py to generate the final video from the original video with a single encode:

# 如没有在 Phase 0.25 生成 profile,删去 --style-profile。
python3 scripts/render_final.py --config render_config.json \
  --style-profile work/edit_style_profile.json \
  --enrich-plan work/enrich_plan.json \
  --output final.mp4 --speed 1.25 1.5

Core principle:

  • Single video (the most common scenario): use select/aselect + between() expressions to filter all retained segments in one pass; FFmpeg decodes the full source video but only encodes the selected frames, combined with -crf 18 -preset medium to encode only once
  • Multi-video mashup: use trim/atrim trimming + concat concatenation (automatic fallback)
  • The cover uses tpad to freeze the first frame + adelay to add silence; subtitle times are automatically offset, all completed in a single encode
  • The chapter timeline is not burned into the video; after rendering it is output as text for the user to manually paste into platforms such as Xiaohongshu

Parameter description:

  • --config: path to the render config JSON
  • --style-profile: optional; after validation, only fills in missing personal/brand style defaults in the config
  • --enrich-plan: optional; reads the JSON output by auto_enrich.py and automatically connects B-roll / chapter cards / stickers / emphasis points / already-generated image cues back into the render
  • --output: output file path
  • --speed 1.25 1.5: also outputs speed-changed versions (each speed-changed version is also encoded directly from the original video, not recompressed from an already-encoded video)
  • --cover-duration 2.0: cover freeze duration (seconds), overrides the config's cover_duration
  • --font-path: custom font file
  • --font-size: subtitle font size (default 120, i.e. portrait width 1080 ÷ 9 = 120, each Chinese character occupies 1/9 of the screen width, automatically scaled based on actual resolution)
  • --no-subtitles、--no-cover: skip the corresponding feature

Output:

  • final.mp4(original speed)
  • final_1_25x.mp4(1.25× speed)
  • final_1_5x.mp4(1.5× speed)
  • After rendering, the terminal outputs the chapter timeline text, which can be copied directly to Xiaohongshu

Multi-platform format export:

python3 scripts/render_final.py --config render_config.json --output final.mp4 \
  --formats vertical square horizontal

Also outputs:

  • final_vertical.mp4(9:16 Douyin/Xiaohongshu/TikTok)
  • final_square.mp4(1:1 Instagram)
  • final_horizontal.mp4(16:9 YouTube/Bilibili)

The cropping strategy is center-crop, keeping the main subject of the frame unchanged.

Automatic features:

  • Subtitles automatically detect language, automatically wrap lines, and are positioned with portrait optimization
  • The cover automatically overlays title text (with stroke and shadow), freezing the first frame for 1-2 seconds
  • Subtitle times for speed-changed versions are automatically scaled
  • B-roll is automatically scaled and cropped to match the main video resolution
  • Timed B-roll / image overlays from --enrich-plan are automatically scaled and cropped to match the main video resolution
  • The ending card automatically concatenates black frames + silence + fade-in/fade-out subtitles
  • Persistent overlays and blinking dots are automatically enabled after the cover
  • Video/audio durations are automatically aligned (-shortest), avoiding duration mismatch issues during platform uploads

Phase 5b: Export to JianYing / CapCut

If the user wants to continue editing in JianYing (adding effects, color grading, fine-tuning transitions, etc.), the editing plan can be exported directly as a JianYing project file, without ffmpeg rendering:

python3 scripts/export_capcut.py --config render_config.json --output ./my_draft

Export contents:

  • Video track: All selected segments arranged in order on the main video track, including the opening cover (frozen first frame or custom cover image)
  • Subtitle track: The transcript of each segment as an independent subtitle paragraph, positioned below the picture
  • Text track: Cover title, subheading, and ending card text (separate from subtitles for easy individual editing)
  • Audio track: Background music (BGM), with volume set automatically
  • Transitions: Fade-in/fade-out transitions automatically added between segments (300ms)
  • B-roll: Supports B-roll picture replacement (using the broll path as the video source)

Parameter description:

  • --config: Path to the render config JSON (shares the same render_config.json with render_final.py)
  • --output: Output folder path (draft_content.json and draft_meta_info.json will be created)
  • --name: Project name (defaults to the title in the config)

Usage:

  1. Copy the exported folder to the Jianying draft directory:
    • macOS: ~/Movies/JianyingPro/User Data/Projects/com.lveditor.draft/
    • Windows: %APPDATA%/JianyingPro/User Data/Projects/com.lveditor.draft/
  2. Open Jianying, and you will see the project in the "Drafts" list
  3. In Jianying, you can continue to add effects, adjust transitions, color grade, export, etc.

Notes:

  • The exported project file references video/audio assets using absolute paths; make sure the asset files are not moved
  • Supports JianyingPro; Jianying 6+ versions can open the generated drafts
  • This feature and Phase 5 (ffmpeg rendering) are mutually exclusive: if the user only needs the final video, use render_final.py; if further editing in Jianying is needed, use export_capcut.py

If subtitles have already been generated in Jianying/CapCut with Auto Captions and manually proofread, they can be reverse-imported as this project's transcript:

python3 scripts/import_capcut_subtitles.py \
  --draft ~/Movies/JianyingPro/User\ Data/Projects/com.lveditor.draft/day58 \
  --transcript work/capcut_transcript.json \
  --cut-list work/capcut_gap_cut.json \
  --markdown work/capcut_subtitles.md \
  --source-media origin/talking.mp4

You can also import an SRT exported from Jianying:

python3 scripts/import_capcut_subtitles.py \
  --srt exports/capcut_auto_captions.srt \
  --transcript work/capcut_transcript.json \
  --srt-output output/subtitles/capcut_clean.srt

--cut-list Generate keep_segmentsfrom subtitle gaps, suitable for first reviewing with timeline_view.py --cut-list , then handing off to export_edl.py or a subsequent rough-cut workflow. By default, only subtitle material is read; if the draft stores auto captions as a regular text track, add --include-overlays.

If you already have an SRT and subtitle-number editing notes written by a human/agent, use srt_edit_plan.py to directly generate a reviewable edit plan:

work/edit_guide.md Example:

title: 发布会高光
platform: xhs
keep 3-5: 先用产品发布和用户反应
drop 1-2: 铺垫太慢
keep 8: 补一句核心结论
python3 scripts/srt_edit_plan.py \
  --srt exports/capcut_auto_captions.srt \
  --guide work/edit_guide.md \
  --source-media origin/talking.mp4 \
  --output work/srt_edit_plan.json \
  --render-config work/render_config.json \
  --cut-list work/srt_edit_cut.json \
  --markdown work/srt_edit_plan.md \
  --strict

keep/include/use/select lines generate the final output order in order of appearance;drop/skip/exclude/remove lines are written to review.--cut-list The output is sorted by original source time, suitable for timeline_view.py --cut-list to inspect cut points; for the actual reordered output see --render-config. To force every subtitle number to be reviewed, add --require-all-reviewed --strict.

Phase 5c: NLE Handoff EDL / FCPXML / OTIO (hand off to Premiere / FCP / Resolve)

If the user does not need a Jianying draft, but instead wants to hand the automatic edit plan to professional editing software for color grading, mixing, fine editing, or collaborative review, you can export a single-track EDL, single-spine FCPXML, or OpenTimelineIO .otio:

python3 scripts/export_edl.py --config render_config.json --output work/edit.edl --fps 30
python3 scripts/export_edl.py --cut-list work/rough_cut.json --output work/rough_cut.edl --fps 30
python3 scripts/export_fcpxml.py --config render_config.json --output work/edit.fcpxml --fps 30 --width 1080 --height 1920
python3 scripts/export_fcpxml.py --cut-list work/rough_cut.json --output work/rough_cut.fcpxml --fps 30
python3 scripts/export_otio.py --config render_config.json --output work/edit.otio --fps 30
python3 scripts/export_otio.py --cut-list work/rough_cut.json --output work/rough_cut.otio --fps 30

export_edl.py / export_fcpxml.py / export_otio.py will all also write a <output>.json manifest, preserving absolute source paths, exact seconds, record/source timecode, and the event list.export_otio.py By default writes two tracks, V1 + A1; use --no-audio-track to hand off video only; complex subtitles, overlays, chapter cards, and B-roll are still handled via render_final.py / export_capcut.py as the source of truth; NLE handoff is only responsible for the lightweight selection timeline.

Phase 5d: Final Audio Storyboard (rebuild sound after the visual EDL is locked)

For multi-segment generated video or continuous short films, lock the visual EDL first, then rebuild narration, dialogue, ambience, Foley, and music against the final record timeline; do not apply the pre-edit full-film audio plan back onto shortened footage as-is. Each EDL event's label should correspond to storyboard_plan.json shot id:

python3 scripts/export_edl.py \
  --config work/render_config.json \
  --output work/locked_visual.edl \
  --manifest work/locked_visual.edl.json \
  --fps 30

python3 scripts/final_audio_storyboard.py prepare \
  --project-dir . \
  --edl work/locked_visual.edl.json \
  --storyboard work/storyboard_plan.json \
  --output work/final_audio_storyboard_request.json \
  --markdown work/final_audio_storyboard_request.md \
  --response-template work/final_audio_storyboard_response.json \
  --strict

# 填完 response 后:
python3 scripts/final_audio_storyboard.py audit \
  --project-dir . \
  --request work/final_audio_storyboard_request.json \
  --response work/final_audio_storyboard_response.json \
  --output work/final_audio_storyboard.json \
  --markdown work/final_audio_storyboard.md \
  --strict
python3 scripts/final_audio_storyboard.py verify \
  --project-dir . \
  --report work/final_audio_storyboard.json \
  --strict

Each final section must explicitly specify single_track / sectioned_tracks / stems, shared tone, voice, sound, music, stems, and the rationale for retaining native sound; each completely removed story beat must choose remove / rewrite_into_adjacent / offscreen_bridge. Duplicate voiced lines, missed review, time mapping, or input/response/report drift fail closed. The script does not generate audio, does not submit to a provider, and does not consume credits; the approved JSON must still be rewritten into a timed cue sheet according to the actual audio tool, and separate confirmation is required before paid generation. See docs/prompts/95-final-audio-storyboard.md for details.

If the final narration is assembled from multiple TTS runs, segmented recordings, or sentence-by-sentence post-processing, measure phrase by phrase on the final standalone narration track before mixing in BGM/SFX:

python3 scripts/narration_loudness_qa.py analyze work/final_narration.wav \
  --segments work/final_narration_segments.json \
  --project-dir . \
  --output verify/narration_loudness_qa.json \
  --markdown verify/narration_loudness_qa.md \
  --strict
python3 scripts/narration_loudness_qa.py verify \
  --report verify/narration_loudness_qa.json \
  --project-dir . \
  --strict

The default target for each segment is -18 LUFS ±2 LU, and the spread for non-exception segments is ≤1 LU、true peak ≤-2 dBTP、LRA ≤5 LU. Creative exceptions must be written per segment as reason + reviewer, and must not bypass the peak ceiling; keep at least two non-exception phrases to judge the spread. The report is bound to the narration, segment manifest, media contract, algorithm, and on-site re-measurement. It does not judge timbre, pronunciation, breathing, seams, or performance, nor does it replace the post-mix full-film audio_master_report.py check of -16 LUFS . See docs/prompts/105-narration-loudness-qa.md for details.

Phase 6: Post-render Validation

After rendering is complete, run the validation process on the final video:

6a. Machine QA (run first):

python3 scripts/render_qa.py final.mp4 \
  --platform douyin \
  --json final_qa.json \
  --review-dir verify/final_qa \
  --review-clips

render_qa.py checks container metadata, platform dimensions, video/audio streams, black frames, long stills, and long silences. For Xiaohongshu derivative files use --platform xhs, for Douyin/WeChat Channels use --platform douyin or --platform wxch。--review-dir writes render_qa_review.json / .md,--review-clips extracts short MP4s for suspicious intervals; if you only want a quick metadata check, you can add --no-filters.

6a00. Final audio/video track coverage (must run for the master and every important platform version):

python3 scripts/stream_coverage_qa.py analyze final.mp4 \
  --project-dir . \
  --output verify/stream_coverage_qa.json \
  --markdown verify/stream_coverage_qa.md \
  --strict
python3 scripts/stream_coverage_qa.py verify \
  --report verify/stream_coverage_qa.json \
  --project-dir . --strict

This gate fully decodes the final cut and, for the first video/audio streams, computes the decoded frame count, first-frame PTS, last-frame end time, and timeline digest respectively; by default it blocks audio/video head-tail offset or streams not covering the container timeline. When the render plan has an exact duration/frame count, append --expected-duration、--expected-video-frames. AAC priming/discard does not use nb_frames as a hard gate; the video frame count is still checked. After the report passes, play the exact deliverable file fully at 1×. See docs/prompts/114-stream-coverage-qa.md for details.

6a0. Same-timeline re-encode quality (must run for compressed / transcoded derivatives):

python3 scripts/encode_quality_qa.py analyze output/master.mp4 output/final_delivery.mp4 \
  --project-dir . \
  --output verify/encode_quality_qa.json \
  --markdown verify/encode_quality_qa.md \
  --strict
python3 scripts/encode_quality_qa.py verify \
  --report verify/encode_quality_qa.json --project-dir . --strict

Use only when reference/candidate keep the same timeline and the same framing; an fps difference exceeding 0.01, a duration difference of more than 1 frame, or different display aspect ratio will be rejected outright. By default mean SSIM ≥0.95、P05 SSIM ≥0.88, finite-frame mean PSNR ≥35 dB; Lanczos normalization is applied, with a WARN, only when the candidate resolution differs but the aspect ratio matches. Review by Markdown worst-frame timecode A/B and play through completely at 1×; metrics must not be treated as manual picture-quality approval. It does not test audio, and it does not apply to crop/grade/HDR/retime/interpolation; this implementation does not call libvmaf, and does not claim VMAF.pipeline_manifest.py --require encode_quality_qa --strict can be set as a release gate. See docs/prompts/107-encode-quality-qa.md.

6a1. Audio channel integrity (mandatory for the final master and important platform versions):

python3 scripts/audio_channel_qa.py analyze final.mp4 \
  --project-dir . \
  --output verify/audio_channel_qa.json \
  --markdown verify/audio_channel_qa.md \
  --strict

python3 scripts/audio_channel_qa.py verify \
  --report verify/audio_channel_qa.json \
  --project-dir . \
  --strict

audio_channel_qa.py uses local FFmpeg fixed windows to measure left/right channel activity, onset offset, energy balance, phase correlation, and mono fold-down energy loss. Mono passes directly through the channel-specific check; stereo is measured live; multichannel requires a clearly defined downmix first. It does not modify audio, nor does it replace a full 1× stereo/mono listening test, the loudness/peak check ofaudio_master_report.py , or a sync review. See docs/prompts/106-audio-channel-qa.md.

6a2. Short audio dropouts (final isolated voice preferred):

python3 scripts/audio_dropout_qa.py analyze work/final_narration.wav \
  --project-dir . \
  --evidence-dir verify/audio_dropout_clips \
  --output verify/audio_dropout_qa_scan.json \
  --markdown verify/audio_dropout_qa_scan.md \
  --response-template verify/audio_dropout_qa_response.json \
  --strict

By default, use a 20 ms window to locate 40–400 ms short near-silences between active audio. When candidates appear, play the input and all WAVs through completely at 1×, fill in dropout | intentional_pause | uncertain, then run audit and verify. Confirmed dropouts and uncertainties continue to block; intentional pauses are kept as warnings. Prefer the final isolated dialogue/narration stem, as BGM/SFX may mask dropouts. It does not recognize speech, does not repair audio, and does not replace audio_master_report.py. See docs/prompts/113-audio-dropout-qa.md.

Caption / Speech QA (caption and isolated voice activity alignment)

python3 scripts/caption_speech_qa.py analyze work/final_narration.wav \
  --subtitle-pack output/subtitles/final.json \
  --project-dir . \
  --output verify/caption_speech_qa.json \
  --markdown verify/caption_speech_qa.md \
  --strict
python3 scripts/caption_speech_qa.py verify \
  --report verify/caption_speech_qa.json \
  --project-dir . --strict

Only pass the final isolated dialogue/narration track, or a speech-dominant track explicitly free of BGM/SFX, to this script. It uses local FFmpeg silencedetect to invert silent intervals into audio-activity intervals, then checks one by one whether a subtitle_pack.v1 cue has no activity at all, too low an effective activity ratio, excessive leading/trailing silence, excessive internal pauses, or extends beyond the audio track timeline. The report binds the voice track, subtitle JSON, media contract, parameters, full measurements, and canonical id;pipeline_manifest.py --require caption_speech_qa --strict can be set as a release gate. Amplitude activity is not speech recognition / forced alignment; BGM/SFX can cause false passes, and ready cannot replace a full 1× caption review over the final mix. See docs/prompts/109-caption-speech-qa.md.

Subtitle Glyph QA (recommended for rare characters / emoji / multilingual subtitles)

python3 scripts/subtitle_glyph_qa.py analyze \
  --subtitle-pack output/subtitles/final.json \
  --font 'fonts/NotoSansSC[wght].ttf' \
  --project-dir . \
  --output verify/subtitle_glyph_qa.json \
  --markdown verify/subtitle_glyph_qa.md \
  --strict
python3 scripts/subtitle_glyph_qa.py verify \
  --report verify/subtitle_glyph_qa.json \
  --project-dir . --strict

The script reads the Unicode cmapof TTF/OTF/TTC/OTC directly, covers all visible characters in subtitle_pack.v1 , and binds the subtitles, primary font, explicit fallbacks, coverage assignments, and canonical report id together. Implicit system font substitution does not count as evidence; missing glyphs block, explicit fallbacks warn by default, and--require-primary can prohibit fallbacks. It does not prove shaping, color emoji, ASS layout, or visual legibility; the final step still requires watching the subtitles at full 1× and extracting full-resolution frames. See docs/prompts/110-subtitle-glyph-qa.md.

Subtitle Render Review (mandatory for final cuts with burned-in subtitles)

python3 scripts/subtitle_render_review.py prepare \
  --project-dir . \
  --video output/final.mp4 \
  --subtitle-pack output/subtitles/final.json \
  --proof-dir verify/subtitle_render \
  --output work/subtitle_render_review_request.json \
  --markdown work/subtitle_render_review_request.md \
  --response-template work/subtitle_render_review_response.json

# 完整 1× 看完 final,并逐条填写 response 后:
python3 scripts/subtitle_render_review.py audit \
  --request work/subtitle_render_review_request.json \
  --response work/subtitle_render_review_response.json \
  --output work/subtitle_render_review.json \
  --markdown work/subtitle_render_review.md --strict
python3 scripts/subtitle_render_review.py verify \
  --report work/subtitle_render_review.json --project-dir . --strict

The script selects first, last, longest, highest CPS, shortest, middle, and evenly distributed cues from the exact final, exports 1× context clips with audio and full-size JPEGs at subtitle midpoints. Passing requires full playback of the entire video, and every sample must be visible / matches / readable / clear / repair_action=none; missing, stale copy, unreadable, cropped, occluded, or unobservable all fail closed. The report binds the final, subtitle pack, proof bytes, media contract, sampling settings, manual response, and canonical id; it must be redone after any re-render or evidence drift. It does not do OCR or automated readability scoring, and the default sampling does not replace a full item-by-item review. See docs/prompts/111-subtitle-render-review.md.

6a2. Flicker / photosensitive-risk heuristic precheck (required for final master and important platform versions):

python3 scripts/flash_safety_qa.py analyze final.mp4 \
  --project-dir . \
  --output verify/flash_safety_qa.json \
  --markdown verify/flash_safety_qa.md \
  --strict

python3 scripts/flash_safety_qa.py verify \
  --report verify/flash_safety_qa.json \
  --project-dir . \
  --strict

flash_safety_qa.py uses local FFmpeg low-resolution frame-by-frame sampling to separately record luminance and saturated-red opposite transitions covering at least 25% of the frame, and pairs them into flashes; more than 3 flashes in a rolling 1-second window, or at least 10 flashes in a rolling 5-second window, blocks. On a hit, play at normal speed by Markdown time range, prioritize removing repeated white/red flashes, lowering contrast/red saturation, reducing flash area, or reducing frequency, then re-render from the timeline. The report binds the source SHA-256, media contract, parameters, algorithm contract, and on-site recomputation evidence;pipeline_manifest.py --require flash_safety_qa --strict can be set as a release gate. It is only a heuristic screen; it does not detect harmful spatial patterns, and it is not medical advice, legal compliance evidence, or WCAG/Harding/broadcast certification; high-risk or regulated deliveries must escalate to an accredited professional photosensitivity analyzer. See docs/prompts/104-flash-safety-qa.md.

6a3. Single-frame / few-frame transient artifacts (recommended for final master and generated assets):

python3 scripts/temporal_artifact_qa.py analyze final.mp4 \
  --project-dir . \
  --evidence-dir verify/temporal_artifact_frames \
  --output verify/temporal_artifact_qa.json \
  --markdown verify/temporal_artifact_qa.md \
  --response-template work/temporal_artifact_response.json \
  --strict

The script looks for local spikes where 1–3 sampled frames briefly deviate and then return to an approximately original picture; an ordinary hard cut should not satisfy the two-sided abrupt-change-and-recovery-similarity contract. After a candidate hit, full 1× playback is required, then describe each image before / suspect / after, fill in intentional_edit|artifact|uncertain and run audit. The latter two decisions remain blocking; drift in source, algorithm, analysis, JPEG, response, or derived state invalidates the live gate. It does not recognize faces, identity, physics, or semantics, and it may miss sustained drift and small-area problems. See docs/prompts/108-temporal-artifact-qa.md.

6b. Shot color / exposure QA (recommended for multicam, B-roll, generated assets, or after grading):

python3 scripts/shot_color_qa.py final.mp4 \
  --output verify/shot_color_qa.json \
  --markdown verify/shot_color_qa.md \
  --strict

shot_color_qa.py uses FFmpeg on the final encoded file signalstats to aggregate luminance, contrast, U/V, saturation, and broadcast-range metrics by shot, and compares luminance/chroma differences between adjacent shots. Visual jumps default to WARN only and must be checked against the master using Markdown timecodes and timeline_view.py ; persistent out-of-range values in non-full-range output or a shot with no sample defaults to blocking. --scene-boundaries work/scene_boundaries.json can reuse already-reviewed scenes, or the script can detect them automatically. It is not calibrated scopes, HDR proof, white balance/skin tone judgment, or aesthetic scoring.pipeline_manifest.py --require shot_color_qa --strict can make the report a release requirement.

6c. Retention pacing risk audit (recommended for short-video master / platform export):

python3 scripts/subtitle_pack.py \
  --config render_config.json \
  --output-dir output/subtitles \
  --basename final_master \
  --speed 1.25 \
  --offset 2.0

python3 scripts/retention_rhythm_qa.py final.mp4 \
  --timed-text output/subtitles/final_master.json \
  --output verify/retention_rhythm_qa.json \
  --markdown verify/retention_rhythm_qa.md \
  --strict

retention_rhythm_qa.py Reuse the FFmpeg scene score on the rendered final cut, and you can merge output-aligned subtitle JSON to check the first three seconds of scene/subtitle activity, long visual holds, scene + subtitle attention gaps, mechanical evenly spaced rhythm, overly dense fast cuts, and subtitles that do not refresh for a long time. When there is no timed text, an inactive hook only produces a warning, to avoid misjudging continuous camera movement or slight motion effects as a hard failure; high-confidence severe long holds / attention gaps are written to summary.blocking. This is an observable rhythm risk, not a platform retention rate or viral-hit prediction. After a hit, first look at the Markdown time range and the master, then go back to the source timeline to re-render.pipeline_manifest.py --require retention_rhythm_qa --strict You can make the report a required item for publishing.

6c.1. Reference video editing rhythm quantification (run when the user explicitly provides a reference video):

python3 scripts/reference_edit_rhythm.py analyze \
  --project-dir . \
  --reference origin/reference-ad.mp4 \
  --candidate final.mp4 \
  --evidence-dir verify/reference_edit_rhythm \
  --output work/reference_edit_rhythm.json \
  --markdown work/reference_edit_rhythm.md \
  --strict

python3 scripts/reference_edit_rhythm.py verify \
  --report work/reference_edit_rhythm.json \
  --strict

reference_edit_rhythm.py Run the same hard scene detection on the reference video and the candidate video, bind the two videos and the two contact sheets with SHA-256/media contracts, and compare cuts/minute, median shot, final-hold ratio, normalized cut positions, and opening/middle/closing cut share. By default, deviations only produce a WARN; add the following only when structural matching is an explicit acceptance condition: --require-match. Watch both videos in full, borrow only the structure, and do not copy the reference video's visuals, audio, branding, or story. The scene score will miss some dissolves and in-shot motion, and a contact sheet cannot replace 1× playback. See docs/prompts/92-reference-edit-rhythm.md.

6c.2. Final cut lip-sync review (digital human / generated speech / lip-sync close-up):

python3 scripts/lip_sync_review.py prepare \
  --project-dir . \
  --video final.mp4 \
  --segment hook=2.40:6.10 \
  --anchor hook="把品牌卖点说清楚" \
  --proof-dir verify/lip_sync \
  --output work/lip_sync_review_request.json \
  --markdown work/lip_sync_review_request.md \
  --response-template work/lip_sync_review_response.json

# 看完 1× 带声和 0.25× 静音 proofs、填写 response 后:
python3 scripts/lip_sync_review.py audit \
  --request work/lip_sync_review_request.json \
  --response work/lip_sync_review_response.json \
  --output work/lip_sync_review.json \
  --markdown work/lip_sync_review.md \
  --strict
python3 scripts/lip_sync_review.py verify --report work/lip_sync_review.json --strict

Phrases must come from the final master and be fully visible, 1–10 seconds long, preferably containing p/b/m-type closed-lip anchors.pass Require all of the following to pass: plosive closed lips, vowel timing, mouth movement while speaking, visible speaker, and audio quality;not_observable fail closed. The script only binds manually reviewed master/proof bytes and does not perform face or automatic phoneme recognition. Any cut, speed change, audio replacement, re-encode, or evidence drift requires re- prepare → audit. See docs/prompts/93-lip-sync-review.md.

6d. Full review proxy (run when you need to share the whole video or give precise timecode feedback):

python3 scripts/review_proxy.py final.mp4 \
  --output verify/final_review_proxy.mp4 \
  --manifest verify/final_review_proxy.json \
  --markdown verify/final_review_proxy.md

review_proxy.py does not modify the master; it outputs a lightweight MP4 up to 720p, 24fps, H.264/AAC,+faststart , and burns in at the top left REVIEW PROXY and elapsed timecode. Review comments should cite the visible timecode, and final QA and publishing still use the master / platform export.--dry-run writes only reproducible commands and artifacts,--no-timecode can turn off the timecode.

6e. Visual retrospective (run when QA has WARN/FAIL or you spot-check cut points):

python3 scripts/timeline_view.py final.mp4 --at 42.5 --radius 1.5 --output verify/42_5s.png
python3 scripts/timeline_view.py origin/talking.mp4 --cut-list work/jumpcut.json --output-dir verify/cuts --limit 12
python3 scripts/timeline_view.py output/rough_cut.mp4 --rendered-cut-list work/rough_cut.json --output-speed 1.25 --output-offset 1.0 --output-dir verify/rendered_cuts --json verify/rendered_cuts.json

timeline_view.py generates a filmstrip + waveform PNG; the upper half shows visual continuity, and the lower half shows speech/silence boundaries.--cut-list view the source material timeline,--rendered-cut-list then map the accumulated duration by keep_segments to the actual splice points in the final cut; when there is global speed change or an opening cover, pass --output-speed / --output-offsetsynchronously. The JSON retains output cut points and the preceding and following source ranges; videos without audio automatically output only the filmstrip.

6f. Source-time comparison between original footage and final cut (run during structural editing review):

python3 scripts/edit_compare.py \
  origin/talking.mp4 final.mp4 \
  --cut-list work/rough_cut.json \
  --output-speed 1.25 \
  --output-offset 2.0 \
  --output verify/source_vs_final.mp4 \
  --report verify/edit_compare.json \
  --markdown verify/edit_compare.md

The left column plays the original clip continuously; the right column projects the final delivered pixels back onto the same source clock, with deleted ranges shown as black. The script checks duration, dual-column dimensions, source-clock audio track, black frames for deleted segments, and pixels of representative retained segments. V1 only supports single source, ascending time, non-overlapping keep_segments plus global speed/offset; reordering, multiple sources, or nonlinear speed changes must go back to the NLE/OTIO timeline for review. See docs/prompts/74-edit-compare.md for details.

6g. Subtitle sidecar delivery (run when the platform needs SRT/VTT):

python3 scripts/subtitle_pack.py \
  --config render_config.json \
  --output-dir output/subtitles \
  --basename final_master \
  --speed 1.25 \
  --offset 2.0 \
  --formats srt vtt ass json

subtitle_pack.py can generate SRT/VTT/ASS/JSON from transcript.json or render_config.json .--config concatenates the subtitle timeline in final clips order;--speed aligns to render_final.py --primary-speed;--offset aligns to the opening cover seconds. The JSON manifest retains each cue's source segment and proofreading warnings such as over_max_chars .

When the player needs toggleable subtitles, run soft_subtitles.py mux final.mp4 output/subtitles/final_master.srt --language zho --output output/final-soft.mp4 --receipt verify/soft_subtitles.jsonwith the reviewed SRT aligned to the final MP4, then run soft_subtitles.py verify verify/soft_subtitles.json. The script only accepts sources with a single video, at most one audio, and no other tracks; it copies the audio/video streams, adds one mov_text subtitle track, and checks the SRT timing, extracted subtitle content, source/output stream hashes, full decode, and file drift. Afterready_for_human_review , still watch the result at 1× with sound in the target player and confirm subtitle toggling, Chinese display, and audio/video sync. Frames that already have burned-in subtitles will not be removed by this command; before delivery, confirm whether double subtitles will appear.

When a long video needs in-player jumpable chapters, first use chapter_markers.py to generate and manually verify chapters.json, ensuring the last chapter's end time matches the final MP4; then run chapter_mux.py mux output/master.mp4 output/chapters/chapters.json --output output/master-chapters.mp4 --receipt verify/chapter_mux.json and chapter_mux.py verify verify/chapter_mux.json. The script accepts a single-video MP4 with at most one audio and no chapters, copies the audio/video streams, and verifies each chapter's title/time, stream hashes, and full decode. Finally, still check chapter jumping in the target player; if the upload platform does not read MP4-embedded chapters, deliver chapters-youtube.txtseparately.

A web player can add the chapters.vtt exported in the same run as <track kind="chapters" src="chapters.vtt" srclang="zh"> to the <video>consistent with the chapter timeline; VTT uses non-overlapping chapters, millisecond timecodes, and plain-text titles. Whether the browser presents a chapter menu depends on the player implementation; actually try jumping during delivery; after the opening or re-editing changes the timeline, re-export according to the final video duration.

Before publicly sharing an MP4, you can run metadata_scrub.py scrub output/master.mp4 --output output/share.mp4 --receipt verify/metadata_scrub.json, then run metadata_scrub.py verify verify/metadata_scrub.json. It keeps only the first video and optionally the first audio, removing non-structural container/stream tags, chapters, and extra tracks such as subtitles/data; it verifies the SHA-256 of the audio/video encoded streams, duration, full decode, and output tags. If the shared version needs chapters or soft subtitles, explicitly remux and re-verify after cleaning. This tool only checks container/stream tags visible to FFprobe; it does not scan frames, audio content, or information possibly carried inside encoded streams; you still need to review the shared version at normal speed.

After generating the JSON, run the read-only release gate on the final subtitles:

python3 scripts/subtitle_readability_qa.py \
  output/subtitles/final_master.json \
  --media final.mp4 \
  --output verify/subtitle_readability_qa.json \
  --markdown verify/subtitle_readability_qa.md \
  --strict

subtitle_readability_qa.py checks the output-timeline for invalid times, out-of-order entries, overlaps, extremely short flashes, CPS, duration, line count/line length, and can use FFprobe to verify that cues do not exceed the end of the final video. Ordinary CPS/layout risks are only WARN; you must watch the normal-speed master; deterministic timing incidents and extreme reading speeds are written to summary.blocking. It does not perform OCR and does not replace manual review of fonts, strokes, position, or occlusion.pipeline_manifest.py --require subtitle_readability_qa --strict You can make the report a publish requirement.

6g.1 Platform framing preview (run when master differs from target ratios or the picture contains protected information):

python3 scripts/framing_preview.py create \
  --project-dir . --video output/master.mp4 \
  --platforms xhs douyin wxch \
  --preview-dir verify/framing \
  --output work/framing_preview.json \
  --markdown work/framing_preview.md \
  --require-selection

python3 scripts/framing_preview.py select \
  --report work/framing_preview.json --platform xhs --strategy contain
python3 scripts/multi_export.py output/master.mp4 \
  --output-dir output --platforms xhs douyin wxch \
  --framing-preview work/framing_preview.json

Inspect the actual early/mid/late JPEGs per platform.cover Faces, hands, multi-person relationships, readable UI, logos, product edges, or document boundaries must not be cropped out; when at risk, choose contain or blur. Source/preview bytes, display orientation, platform canvas, filter digest, or selection drift will all block before encoding. Sample images do not replace full 1× platform export review; see docs/prompts/103-framing-preview.md.

6h. Publish upload package (run before final upload):

python3 scripts/publish_package.py \
  --project-dir work/day58 \
  --platforms xhs douyin wxch \
  --output work/day58/publish_package.json \
  --markdown work/day58/publish_package.md \
  --strict

publish_package.py does not upload or call platform APIs; it only combines the platform MP4s from multi_export.py , the titles/bodies/tags fromgenerate_caption.py , covers, SRT/VTT, chapter text, and the pipeline_manifest gate status into publish_package.v1. If cover_variants.json exists and the selected_cover file is valid, the reviewed cover is used first; an explicit --cover can override. If a platform video is missing, a caption is empty, or the pipeline manifest is already blocked,--strict returns 2. When handing off to an external publish connector, use this JSON as the handoff; for manual upload, see the Markdown checklist.

6i. Final approval receipt (run after client/manual confirmation):

python3 scripts/approval_receipt.py create \
  --project-dir work/day58 \
  --artifact output/day58_xhs.mp4 \
  --artifact output/day58_douyin.mp4 \
  --artifact output/day58_wxch.mp4 \
  --artifact output/cover.png \
  --artifact output/day58_caption.json \
  --artifact verify/render_qa.json \
  --approved-by "reviewer label" \
  --output verify/approval_receipt.json \
  --markdown verify/approval_receipt.md

python3 scripts/publish_package.py \
  --project-dir work/day58 \
  --require-approval-receipt \
  --strict

approval_receipt.py binds only explicitly listed stable deliverables; do not put the pipeline manifest, publish package, or dashboard—which are rewritten every time—into the receipt.verify --strict and pipeline_manifest.py --require approval_receipt --strict re-read the current files and compare SHA-256; any re-render, cover change, copy edit, subtitle deletion, or path becoming a symlink makes the old approval expire.approved_by is only a local label, not authentication or a digital signature. For details, see docs/prompts/77-approval-receipt.md.

6j. Resume context package (run when wrapping up across sessions/automation):

python3 scripts/project_resume.py \
  --project-dir work/day58 \
  --target-stage publish_ready \
  --output work/day58/project_resume.json \
  --markdown work/day58/project_resume.md \
  --agent-note work/day58/CLAUDE.md \
  --strict

project_resume.py reuses pipeline_manifest.py 's local gate; it does not render, upload, or submit generation tasks. It outputs project_resume.v1, containing phase、recommended_first_action、next_actions[], recent artifacts, a key gate snapshot, and a one-sentence suggested_promptthat can be handed directly to the next agent. When a long workflow has been context-compressed, automation ends, or you need to hand off to another agent, prioritize the Markdown/agent note as the handoff entry point.

6k. Manual review panel (run before final confirmation/handoff):

python3 scripts/review_dashboard.py \
  --project-dir work/day58 \
  --target-stage publish_ready \
  --output work/day58/review_dashboard.json \
  --html work/day58/review_dashboard.html \
  --strict

review_dashboard.py reuses pipeline_manifest.py 's local gate and outputs review_dashboard.v1 plus an HTML file that can be opened directly in a browser. It puts blocking/missing/warning gates into review_items[], and also lists next_actions[], the latest artifacts, and the full gate snapshot. Suitable for the user's final confirmation, and also for leaving to the next agent when automation ends.

6l. Final cut repeated-speech / stutter gate:

python3 scripts/extract_audio.py output/final.mp4
python3 scripts/transcribe.py output/final_audio.wav --model auto --language zh --word-timestamps
python3 scripts/speech_continuity_qa.py output/final_transcript.json \
  --output verify/speech_continuity_qa.json \
  --markdown verify/speech_continuity_qa.md \
  --strict

You must re-transcribe the rendered master; do not reuse the source material transcript.speech_continuity_qa.py It checks for exact repeated speech across the end/start boundary of adjacent segments, near-duplicate takes in adjacent segments, and immediate stuttering within a sentence; different speakers are not checked against each other by default. After a hit, first audition the master by timecode, then adjust the source render_config / cut list, re-render and re-run.pipeline_manifest.py --require speech_continuity_qa --strict This report can be made a release requirement.

6m. PQ/HLG HDR → Rec.709 SDR delivery (run when the HDR master targets ordinary social media/web):

python3 scripts/hdr_sdr.py plan output/final_hdr.mp4 \
  --delivery output/final_sdr.mp4 \
  --output work/hdr_sdr_plan.json \
  --markdown work/hdr_sdr_plan.md
python3 scripts/hdr_sdr.py apply work/hdr_sdr_plan.json
python3 scripts/hdr_sdr.py verify work/hdr_sdr_plan.json

hdr_sdr.py Only accepts metadata that explicitly declares smpte2084 PQ or arib-std-b67 HLG, and requires BT.2020 primaries/matrix; unknown/contradictory color tags fail closed. Correct conversion must have FFmpeg zscale + tonemap, and bare tonemap or a degraded fallback that only reduces to yuv420p is not allowed. apply uses linear-light float + Hable to produce H.264/AAC,yuv420p, BT.709 limited-range MP4, explicitly verifying the four color tags, duration/size/fps/audio track, hash and full-length decode before atomically promoting. Dolby Vision/HDR10+ dynamic metadata is not retained; skin tones, highlights, shadows, gradients and saturated colors must be fully reviewed on a trusted SDR display, then QA and approval re-run on the SDR file. See docs/prompts/87-hdr-sdr.md.

6n. Target-size delivery encode (run when there is an explicit MB cap):

python3 scripts/delivery_encode.py plan output/final.mp4 \
  --delivery output/final_delivery.mp4 \
  --max-size-mib 18 \
  --output work/delivery_encode_plan.json \
  --markdown work/delivery_encode_plan.md
python3 scripts/delivery_encode.py apply work/delivery_encode_plan.json \
  --markdown work/delivery_encode_plan.md
python3 scripts/delivery_encode.py verify work/delivery_encode_plan.json

delivery_encode.py Binds the master SHA-256, uses two-pass libx264 to calculate the H.264/AAC bitrate within a 6% container margin; output exceeding the hard cap, container/stream/size/fps/duration/audio mismatch, or full FFmpeg decode failure all block. apply first writes only a temporary MP4 in the same directory, and only atomically promotes it after technical verification passes, without overwriting the source file. Full decode is not picture-quality approval; if composition/timeline are unchanged, immediately run encode_quality_qa.py, then watch the delivered version at normal speed, run render_qa.py and put the final SHA-256 into approval_receipt.py. See docs/prompts/85-delivery-encode.md and docs/prompts/107-encode-quality-qa.md.

6o. Final subtitle text verification:

  1. Read the text of all transcript segments used in the final video
  2. Following the segment order of the final video, check each one for the following issues:
    • Residual speech-recognition errors: homophones and proper-noun errors that Phase 2.5 may have missed
    • Uncleaned slips of the tongue: whether the speaker's slips (e.g., saying the opposite, repeating) remain in the final video
    • Contextual coherence: whether there is semantic discontinuity or a logical jump between adjacent segments after cross-video splicing
    • Subtitle consistency: whether the same word/name is spelled consistently across segments
  3. If issues are found, list them and show them to the user:
    字幕校验结果:
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      #  | 问题类型     | 原文 → 建议修正
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      3  | 识别错误     | "检映" → "剪映"
      7  | 口误        | "先说了结果" → 建议删除此片段
     12  | 名称不一致   | "opencloud" → 统一为 "OpenClaw"
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    
  4. After the user confirms the corrections, modify the corresponding transcript.json, then re-execute Phase 5 rendering

Important Notes

Video quality and encoding guidelines (most important)

  1. Single-encode principle: From the original video to the final output, only one encode is allowed. Multiple re-encodes (e.g., encode for splitting, then encode for burning subtitles, then encode for adding a cover) are strictly prohibited; each re-encode accumulates quality loss. Use render_final.py the select/aselect + between() approach (single video) or the trim/atrim + concat approach (multiple videos), completing all operations of trimming, concatenation, subtitles and cover in one ffmpeg command.
  2. Speed-changed versions are also encoded directly from the original video:--speed 1.25 1.5 the speed-changed version in filter_complex integrates setpts + atempo, directly from the original video in one step, do not re-compress from an already-encoded 1x video.
  3. Encoding parameters: use a fixed bitrate (e.g. -b:v 12M) rather than a quality parameter (e.g. -q:v). A fixed bitrate allows precise control of file size and quality, avoiding -q:v behaving inconsistently across different encoders. Set it with reference to the original video bitrate (typically 8-15 Mbps).
  4. Old workflow scripts are for preview only:split_video.py、burn_subtitles.py、merge_clips.py、generate_cover.py、add_chapter_bar.py can still be used on its own, but the final output must use render_final.py. The old scripts are suitable for quickly previewing the effect of a single clip.

Rotation detection

  1. Rotation detection: for portrait videos recorded on devices such as iPhone, the encoded dimensions may be 1920x1080 + rotation=-90 metadata. All scripts use utils.get_video_info() to uniformly detect rotation and automatically swap width and height, ensuring the correct display dimensions are obtained. Rotation must be checked first when detecting video information.

Chapter timeline

  1. Number of chapters: no more than 4 chapters is recommended, with chapter names of 2-4 characters.
  2. Do not burn the timeline into the video: after rendering is complete, output the chapter timeline (including the cover offset) as text for the user to manually copy into the video description on platforms such as Xiaohongshu.

Other

  1. Multi-video processing: if the user provides multiple videos, run Phase 1-2.5 independently for each video, then in Phase 3 display the clip lists for all videos together, supporting mixed clip selection across videos.
  2. Recognition model selection: for Chinese videos, it is recommended to use the large model; thebase/small model has a lower Chinese recognition rate. Thelarge model requires about 2.9GB of download space.
  3. Working directory: all intermediate files (audio, transcription) are saved in the directory where the video file is located, for ease of management. After rendering is complete, temporary files (ASS subtitle files, filter_complex scripts) should be cleaned up.
  4. Error handling: if a step fails, report the specific error message to the user and suggest possible solutions.
  5. Subtitle font: LXGW WenKai Medium (Xiawu WenKai Medium) is used by default, downloaded and cached automatically. ffmpeg needs to be compiled with libass and libfreetype. On macOS it can be obtained via brew install ffmpeg .
  6. Portrait adaptation: subtitle position and font size have been optimized for 9:16 portrait videos (such as Xiaohongshu, Douyin). The default font size is 120 (= screen width 1080 ÷ 9), with each Chinese character occupying about 1/9 of the portrait width. Landscape videos are also supported, and the font size automatically scales to the actual resolution.

Remotion Voiceover Workflow (generating video from voice)

When the user has only voice (or an audio file) but no visuals, use Remotion to generate video visuals that match the voice.

For detailed Remotion API references, template styles, and component structures, see REMOTION_VOICEOVER.md.

Applicable scenarios

  • Audio-only voiceover — you have a recording/TTS voice and need to generate matching visuals
  • Audio + static images — you have voice and image assets and need to combine them into a dynamic video
  • Podcast visualization — turn podcast/conversation audio into a video with subtitles and visual effects
  • Narrated video — a combination of voiceover + text animation + background images

Remotion Workflow

  1. Audio preparation — use extract_audio.py to extract audio, or directly use the audio file provided by the user
  2. Speech recognition — use transcribe.py to generate transcript.json (sentence-by-sentence timestamps)
  3. Timeline generation — the AI Agent analyzes the transcript content semantically and generates timeline.json:
  • Group multiple segments semantically into scenes
  • Select a type for each scene (title/content/kinetic/quote, etc.)
  • Choose background visuals, text animations, and transition effects
  • Configure subtitle styles (TikTok word-by-word highlight / bottom subtitles / full-screen text)
  1. Asset preparation — Collect/generate the image assets required for the scenes
  2. Remotion rendering — Use npx remotion render to render the final video according to timeline.json
  3. Post-processing (optional) — Use render_final.py to merge with other video clips

Video template style selection

Style Use cases Visual effects
tiktok Short-form talking-head videos, knowledge sharing Gradient background + keyword cards + TikTok-style word-by-word subtitles + progress bar
tutorial Tutorials, popular science, product introductions Split image/text layout + Ken Burns images + bullet points fading in line by line + bottom subtitles
kinetic Emotional, motivational, copywriting Full-screen large text popping in line by line + elastic motion effects
podcast Podcasts, interviews, conversations Avatar + audio waveform visualization + quote text
news News broadcasts, industry updates Top banner + Lower Third info bar + scrolling subtitles
slideshow Product introductions, travel, photo albums Multi-image Ken Burns + transition effects + caption text

Remotion environment requirements

# 需要 Node.js 18+ 和 npm
node --version  # >= 18.0.0
npm --version

# 安装 Remotion standup 项目依赖
cd remotion-standup
npm install

Standup Comedy / Audio-only Video Generation (Standup Comedy Workflow)

When the user has a standup comedy audio track (or any voice-only content) but no visuals, you can generate a video with text motion effects:

Step 1: Speech recognition

python3 scripts/transcribe.py audio.wav --model auto --language zh

Step 2: Generate timeline

# 默认风格
python3 scripts/generate_standup_timeline.py transcript.json \
    --audio audio/standup.wav \
    --output remotion-standup/public/timeline.json

# 活力风格(更多夸张动效)
python3 scripts/generate_standup_timeline.py transcript.json \
    --audio audio/standup.wav \
    --style energetic \
    --output remotion-standup/public/timeline.json

# 选择特定片段 + 自定义字体
python3 scripts/generate_standup_timeline.py transcript.json \
    --audio audio/standup.wav \
    --segments 1-10,12,15-20 \
    --font "LXGW WenKai, sans-serif" \
    --output remotion-standup/public/timeline.json

Step 3: Preview and render

cd remotion-standup

# 把音频文件复制到 public 目录
cp /path/to/audio.wav public/audio/standup.wav

# 开发预览
npx remotion studio

# 渲染最终视频
npx remotion render StandupVideo out/standup.mp4 --codec=h264 --crf=18

The script automatically:

  • Detects punchlines/short sentences (exclamation marks, keywords, contrast between short sentences followed by long sentences) → uses exaggerated effects such as slam/shake/bounce
  • Enlarges font size for short sentences (emphasis 1.3-1.6x), shrinks long sentences to avoid overflow
  • Uses radial gradients + eye-catching color schemes (red/orange/gold/green) for punchlines
  • Uses dark gradient backgrounds + different animation loops for regular sentences
  • Automatically inserts line breaks in the middle of long Chinese sentences

12 text animation effects:

Animation Effect Suitable scenarios
fadeIn Fade in Calm narration
springIn Elastic entrance Normal conversation
scaleUp Scale up from small Emphasis
scaleDown Scale down from large Slam-in feel
typewriter Typewriter Quotation/conversation
bounce Bounce Lively/funny
shake Shake Punchlines/roasting
slam Rapid slam-in Heavy punchlines
wave Character-by-character wave Opening/ending
glitch Glitch flicker Surprise/twist
rotateIn Rotating entrance Topic switch
splitReveal Expand from center Reveal/unveiling

3 style presets:

Style Description
default Balanced mix of all animations, suitable for most content
calm Uses only gentle animations (fadeIn/springIn/typewriter), suitable for storytelling/in-depth content
energetic Leans toward exaggerated animations (slam/shake/bounce/glitch), suitable for standup comedy/funny content

timeline.json configuration format

{
  "fps": 30,
  "width": 1080,
  "height": 1920,
  "audioSrc": "public/audio/voiceover.wav",
  "style": "tiktok",
  "font": {
    "family": "Noto Sans SC",
    "titleWeight": "700",
    "bodyWeight": "400",
    "titleSize": 72,
    "bodySize": 48
  },
  "colors": {
    "background": "#1a1a2e",
    "text": "#ffffff",
    "highlight": "#FFD700",
    "accent": "#e94560"
  },
  "scenes": [
    {
      "id": 1,
      "startMs": 0,
      "endMs": 5000,
      "type": "title",
      "title": "今天聊一个话题",
      "background": { "type": "gradient", "colors": ["#667eea", "#764ba2"] },
      "transition": { "type": "fade", "durationMs": 500 }
    }
  ],
  "captions": {
    "enabled": true,
    "style": "tiktok",
    "fontSize": 56,
    "highlightColor": "#FFD700"
  }
}

Font Catalog

The project includes a built-in catalog of downloadable free fonts, covering fonts commonly used in Chinese and English video production.

Font Management

# 查看环境报告(包含已缓存字体列表)
python3 scripts/utils.py

# 在 Python 中使用字体 API
python3 -c "
from scripts.utils import list_available_fonts, download_font

# 列出所有可用字体
for f in list_available_fonts():
    status = '✓' if f['cached'] else '○'
    print(f\"{status} {f['id']:25s} {f['name']:25s} {f['use_case']:8s} {f['description']}\")

# 下载指定字体
download_font('lxgw-wenkai')
download_font('inter')
"

Chinese Fonts

Font ID Name Style Use License
noto-sans-sc Noto Sans SC Versatile sans-serif All scenarios SIL OFL
noto-serif-sc Noto Serif SC Formal serif Cultural/classical/in-depth SIL OFL
lxgw-wenkai LXGW WenKai Handwritten kai Literary/cultural/emotional (~24MB) SIL OFL
lxgw-wenkai-lite LXGW WenKai Lite Handwritten kai Same as above, smaller size (~13MB) SIL OFL
lxgw-wenkai-bold LXGW WenKai Medium Handwritten kai bold Titles SIL OFL
zcool-kuaile ZCOOL KuaiLe Round and cute Relaxed/entertainment titles SIL OFL
zcool-qingke-huangyou ZCOOL QingKe HuangYou Handwritten trendy Fashion/young titles SIL OFL
zcool-xiaowei ZCOOL XiaoWei Elegant and neat Body text/subtitles SIL OFL

English Fonts

Font ID Name Style Use License
inter Inter Modern sans-serif All scenarios SIL OFL
montserrat Montserrat Geometric sans-serif Titles SIL OFL
poppins Poppins Rounded geometric Titles SIL OFL
roboto Roboto Neutral modern All scenarios Apache 2.0
oswald Oswald Condensed sans-serif News/headline titles SIL OFL
playfair-display Playfair Display Elegant serif Literary/high-end titles SIL OFL

Font Selection Recommendations

Default font: LXGW WenKai Medium — handwritten kai style, combining aesthetics and readability, the best choice for talking-head/Vlog videos. If not cached, it will be downloaded automatically.

Default font size: 120 (portrait 1080px width ÷ 9 = 120), i.e. each Chinese character occupies about 1/9 of the screen width, clear and easy to read on a phone's portrait screen.

Video Type Recommended Chinese Font Recommended English Font
Talking-head/Vlog (default) LXGW WenKai Medium Inter / Roboto
Tech/tutorial Noto Sans SC Inter / Roboto
News/information Noto Sans SC Oswald / Montserrat
Cultural/in-depth LXGW WenKai / Noto Serif SC Playfair Display
Entertainment/relaxed ZCOOL KuaiLe Poppins
Fashion/trendy ZCOOL QingKe HuangYou Montserrat
Formal/business Noto Sans SC Inter

Using Fonts in FFmpeg

# 下载字体后,通过 --font-path 参数指定
python3 scripts/render_final.py --config render_config.json --output final.mp4 \
  --font-path fonts/LXGWWenKai-Regular.ttf

Using Fonts in Remotion

// 方式 1: @remotion/google-fonts(英文字体推荐)
import { loadFont } from "@remotion/google-fonts/Inter";
const { fontFamily } = loadFont();

// 方式 2: @remotion/fonts(本地/CJK 字体推荐)
import { loadFont } from "@remotion/fonts";
loadFont({
  family: "LXGW WenKai",
  url: staticFile("fonts/LXGWWenKai-Regular.ttf"),
  format: "truetype",
});

// 方式 3: @remotion/google-fonts 加载 CJK
import { loadFont } from "@remotion/google-fonts/NotoSansSC";
const { fontFamily } = loadFont("normal", {
  weights: ["400", "700"],
  subsets: ["chinese-simplified"],  // 重要:指定子集减小体积
});

Notes

  • CJK fonts are large (10-20MB); when using @remotion/google-fonts , be sure to specify subsets: ["chinese-simplified"] to reduce size
  • Font cache: downloaded fonts are saved in the project's fonts/ directory,.gitignore already excludes this directory
  • Chinese users: font downloads automatically use the jsDelivr CDN for acceleration
  • Commercial safety: all fonts in the catalog are under the SIL OFL or Apache 2.0 license and can be used commercially for free

FAQ / Troubleshooting

When you encounter an error, first run environment diagnostics:

python3 scripts/utils.py

Q1: No such filter: 'drawtext' or No such filter: 'ass'

Cause: When ffmpeg was compiled, it did not include libfreetype(required by drawtext) or libass(required for subtitles).

Diagnosis:

ffmpeg -hide_banner -filters 2>/dev/null | grep -E "drawtext|ass|subtitles"

If there is no output, the corresponding filter is missing.

Solution:

  • macOS: The standard brew install ffmpeg may not include these libraries. Use a third-party tap to install the full version:
    brew tap homebrew-ffmpeg/ffmpeg
    brew install homebrew-ffmpeg/ffmpeg/ffmpeg --with-fdk-aac
    
    This tap enables by default --enable-libfreetype --enable-libass --enable-libfontconfig。
  • Linux/WSL:apt install ffmpeg usually provides a build with these libraries. If they are missing, install the development dependencies and compile from source:
    sudo apt install libfreetype6-dev libfontconfig1-dev libass-dev
    
  • Scope of impact: When drawtext is missing, subtitle burning and cover text will fail or automatically fall back.

Q2: Undefined constant or missing '(' in 'iw*0.5-tw/2'

Cause: ffmpeg drawtext's x expression uses tw(text width), but in some ffmpeg versions tw is not available in the context of the x parameter.

Solution: The script has fixed this issue (using the pixel value {pixel_x}-text_w/2 instead of iw*{frac}-tw/2). If you modified the script and encounter this error, use text_w instead of tw, and make sure the x expression does not contain iw* dynamic calculations.

Q3: Invalid alpha value specifier '%{eif:...}' (drawtext fontcolor)

Cause: Trying to use the fontcolor parameter with an embedded %{eif} expression to achieve a transparency gradient, but ffmpeg does not support using this syntax in color values.

Solution: Use drawtext's alpha parameter (independent of fontcolor), instead of trying to embed an expression in fontcolor=white@'%{eif:...}' . Correct usage:

drawtext=text='hello':fontcolor=white:alpha='if(lt(t,1),t,1)'

Incorrect usage (will cause an error):

drawtext=text='hello':fontcolor=white@'%{eif:if(lt(t,1),t,1):d:2}'

Q4: ffmpeg hardware encoder failure (h264_videotoolbox / h264_nvenc / h264_qsv error)

Cause: The detected hardware encoder does not support the current video parameters (such as special resolutions or color spaces), or the driver version is incompatible.

Diagnosis:

ffmpeg -encoders 2>/dev/null | grep -E "nvenc|videotoolbox|qsv|amf"

Solution: In scripts/utils.py , temporarily modify get_ffmpeg_encoder() so that it directly returns ("libx264", ["-preset", "fast", "-crf", "18"]).

Q5: Chinese subtitles display as boxes (tofu blocks)

Cause: There is no available Chinese font file on the system.

Diagnosis:

python3 -c "from scripts.utils import find_chinese_font; print(find_chinese_font())"

If it returns (None, ...), no Chinese font was found.

Solution:

  • macOS: The system includes PingFang SC by default, so this issue generally does not occur.
  • Linux/WSL: Install a Chinese font package:
    sudo apt install fonts-noto-cjk
    
  • WSL alternative: The script will automatically try /mnt/c/Windows/Fonts/msyh.ttc(Microsoft YaHei), provided that the font is installed on Windows.
  • Specify manually: Use the --font-path /path/to/your/font.ttf parameter.
  • Automatic download: On first run, the script will try to download Noto Sans SC from Google Fonts (users in China use the jsDelivr CDN) and cache it in the fonts/ directory.

Q6: Whisper model download failure / timeout

Cause: Network issues, especially for users in China who cannot access HuggingFace.

Solution:

  • Use the --mirror parameter:python3 scripts/transcribe.py audio.wav --mirror --model auto
  • Or manually set environment variables:
    export HF_ENDPOINT=https://hf-mirror.com
    
  • When using faster-whisper, the model is downloaded from HuggingFace; set HF_ENDPOINT it will automatically use the mirror.
  • When using openai-whisper, models are downloaded from GitHub, and Chinese users may need a proxy. It is recommended to switch to faster-whisper.

Q7: pip install faster-whisper Installation failed / timed out

Solution: Chinese users should use the Tsinghua mirror:

pip install faster-whisper -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn

Q8: ffmpeg not found or version too old in WSL environment

Diagnosis:

which ffmpeg && ffmpeg -version | head -1

Solution:

sudo apt update && sudo apt install ffmpeg

If the system repository's ffmpeg version is too old (< 4.0), use the PPA:

sudo add-apt-repository ppa:savoury1/ffmpeg4
sudo apt update && sudo apt install ffmpeg

Q9: Poor video quality / blurry

Cause: The video has been re-encoded multiple times, and each encoding causes quality loss.

Solution: Must use render_final.py single encoding. Check whether split_video.py + burn_subtitles.py + merge_clips.py + generate_cover.py + add_chapter_bar.py old process (causes 4-5 re-encodes) is used in the workflow. Switch to render_final.py --config one-step solution.

Known Issues & Solutions

The following issues were encountered and their solutions verified during actual video production (Day 7-8).

K1: Mixed frame rate source footage using concat copy causes video freeze

Symptom: After using ffmpeg -f concat -c copy to concatenate multiple B-roll clips, the video freezes around the 1-minute mark.

Root cause: DJI footage is 30/1 fps, while some DJI and all iPhone MOV files are 30000/1001 (29.97fps).-c copy Concatenation does not re-encode, and different frame rates cause discontinuous timestamps, causing the player to hang at the switch point.

Solution: Use source-bound clip assembly to unify the canvas, CFR, SAR, timestamps, pixel format, and audio layout of each segment within the same output encoding:

python3 scripts/clip_assembly.py plan \
  origin/clip-a.MOV origin/clip-b.mp4 \
  --width 1080 --height 1920 --fps 30 \
  --delivery output/assembled.mp4 \
  --boundary-proof verify/clip-assembly-boundaries.mp4 \
  --project-dir . \
  --output work/clip_assembly_plan.json
python3 scripts/clip_assembly.py apply work/clip_assembly_plan.json

Fully play the deliverable and all seam proofs, and after confirmation run confirm / verify --strict. It is forbidden to use -c copy concat for mixed frame rates, canvases, SAR, time base, or audio layouts.

Detection method:clip_assembly.py plan reads the complete decoded best_effort_timestamp_time, media contract, and source hash of each source clip; only looking at r_frame_rate or the FFmpeg exit code cannot prove that the concatenation result is continuous.


K2: Audio truncated in complex filter_complex

Symptom: Audio suddenly disappears midway through the video (e.g., at 1:55), while the video continues playing normally.

Root cause: In a single filter_complex , the workflow simultaneously uses amix(mixing anullsrc for silence padding) + atempo(speed change) + video processing;amix 's duration=longest is unreliable when combined with a synthesized null audio source, causing the audio stream to end prematurely.

Solution: Split audio processing into separate steps; do not perform audio padding + speed change + video processing simultaneously in one filter_complex:

  1. adelay Add silent cover segment
  2. atempo=1.25 Speed change processing
  3. apad=whole_dur=X Pad to target duration
  4. Export as WAV, then use -c:v copy -c:a aac to merge with the video

Principle: Audio padding + speed change + video processing should not be placed in the same filter_complex.


K3: render_final.py select filter OOM with a large number of clips

Symptom: "Cannot allocate memory" error when using render_final.py to render 100+ clips (e.g., voiceover with B-roll scenes).

Historical root cause: Early _clips_in_temporal_order() Only checks whether all clips come from the same video and are in increasing time order; does not check whether there is a broll field. When all clips reference the same speech video (paired with different B-roll footage), it still takes the select filter path, generating a giant select filter containing 100+ between() expressions, causing OOM.

Solution:

  • Clip-level broll already forces the trim/concat path and no longer uses the select filter.
  • For auto_enrich.py the B-roll cues produced, prefer using render_final.py --enrich-plan work/enrich_plan.json, which connects as a timed video overlay and does not require splitting the spoken audio into a large number of small segments.
  • If 100+ clip segments are still constructed manually, it is recommended to use a manual ffmpeg pipeline:
    1. Unify the frame rate of all B-roll segments and then concat them
    2. Process the audio separately (extract, trim, change speed, pad)
    3. Use -c:v copy -c:a aac to merge video and audio
  • If render_final.py's many-clip mode must be used, ensure the number of segments is within a reasonable range (recommended < 50 segments)

K4: English words truncated in Chinese subtitles

Symptom: When subtitles wrap, English words (such as "OpenClaw", "Claude Code") are split down the middle.

Root cause:wrap_subtitle_text() Chinese mode calculates line width by character count, treating English letters as single characters of the same width as CJK characters. When wrapping at a middle position, it is unaware of ASCII word boundaries and truncates directly.

Solution: The line-wrapping function needs to:

  1. Use display width calculation: CJK characters = 1 unit, ASCII characters ≈ 0.5 units
  2. When finding the wrap point, detect ASCII word boundaries (consecutive letters/digits count as one word), and do not break in the middle of an English word or number
  3. Prefer wrapping at punctuation, spaces, and CJK/ASCII boundaries

K5: Cover/end card does not appear

Symptom: end_cards is configured in the render config, but the end card is not visible in the final video.

Root cause: The total duration after B-roll concatenation far exceeds what is needed (e.g., 215 seconds are actually needed but 431 seconds of B-roll were concatenated). After concatenating cover + B-roll + end card, when using the -t parameter to limit the total duration, the end card is cut off beyond the duration limit.

Solution: Before concatenating the cover and end card, the B-roll must first be precisely trimmed to match the audio duration:

# 1. 获取音频时长
audio_dur=$(ffprobe -v 0 -show_entries format=duration -of csv=p=0 audio.wav)
# 2. 裁切 B-roll 到音频时长
ffmpeg -i broll_concat.mp4 -t $audio_dur -c copy broll_trimmed.mp4
# 3. 再拼接封面 + 裁切后的 B-roll + 结尾卡片

Principle: Before concatenation, ensure the duration of each part is precisely known; do not rely on -t to truncate afterward.

Vertical Audiogram for Audio-Only Podcasts

When there is only audio and a cover and a short video needs to be posted, select a short segment with complete semantics, and prepare an SRT aligned with that excerpt starting from 0 seconds. If there is native video, prioritize cutting the real speaking footage. When a new cover is needed, prioritize Codex's built-in image_gen(GPT Image 2); if a cover already exists, use it directly.

Run runtime_preflight.py analyze --profile podcast_audiogram --output work/runtime_preflight.json --strict, then run podcast_audiogram.py render origin/episode.wav origin/cover.png work/excerpt.srt --start 32 --duration 28 --output output/audiogram.mp4 --receipt verify/podcast_audiogram.json. The script binds the audio, cover, subtitles, settings, and final output, producing 720×1280 H.264/AAC with an auxiliary waveform and burned-in subtitles; a single clip is at most 600 seconds.podcast_audiogram.py verify verify/podcast_audiogram.json rechecks the hash, media specs, and full decoding.ready_for_human_review only means the technical checks passed; before delivery you must still watch it once at 1× with sound, verifying subtitles against speech, cover rights and recognizability, waveform position, and end trimming. After modifying any input, re-render and re-review.

Short Animated Preview GIF

When a short action from an already-reviewed video needs to be shown in a chat or document, run runtime_preflight.py analyze --profile gif_preview --output work/runtime_preflight.json --strict, then run gif_preview.py render output/master.mp4 --start 5 --duration 3 --width 480 --fps 12 --output output/preview.gif --receipt verify/gif_preview.json. Excerpts are limited to 10 seconds, widths of 64–640 even pixels, 5–20 fps, and 20 MiB; FFmpeg generates a full-film palette in the same task and exports a silent GIF.gif_preview.py verify verify/gif_preview.json Re-check the source film/GIF hashes, frame count, dimensions, duration, and complete decoding. Before delivery, watch the full loop at the actual display size to confirm motion start/end, subtitle readability, and color banding; GIFs contain no sound, and the officially released video still uses the reviewed MP4.

When the user provides ordered photos to create a memories, travel, or product gallery short film, first run runtime_preflight.py analyze --profile photo_montage --output work/runtime_preflight.json --strict, then run photo_montage.py render assets/01.jpg assets/02.jpg assets/03.jpg --music assets/music.wav --each 1 --transition 0.3 --zoom 1.08 --output output/montage.mp4 --receipt verify/photo_montage.json, and finally run photo_montage.py verify verify/photo_montage.json. The order of the passed paths is the playback order; the default is a normal crossfade, and add --effect fadewhitewhen a flash-white effect is explicitly required. The script scales photos proportionally to fill the canvas and center-crops them, defaulting to 1080×1920/30 fps; photos with details, text, or people near the edges should first be checked one by one for composition. The receipt verifies photos, music, output hash, exact frame count, duration, and complete decoding;ready_for_human_review indicates that the full film still needs to be watched and listened to at normal speed, and the flash-white effect also requires running flash_safety_qa.py for re-checking. If the material is generated by a built-in image generation tool, handle the final bitmap according to the image and video workflow first, then use it for this script.

Overlay a brand logo on a reviewed final cut

When a fixed PNG logo needs to be added to a reviewed MP4, run runtime_preflight.py analyze --profile logo_overlay --output work/runtime_preflight.json --strict, then run logo_overlay.py render output/master.mp4 media/assets/logo.png --corner bottom-right --width-fraction 0.15 --opacity 0.7 --margin 24 --output output/master-logo.mp4 --receipt verify/logo_overlay.json. The width occupies 5%–40% of the frame, and one of the four corners can be selected; margin is in output pixels, and the logo must fall entirely within the frame. The script re-encodes H.264/yuv420p video, copies the optional single audio track, verifies frame count, duration, canvas, and complete decoding, and binds the original film, logo, and output with SHA-256.logo_overlay.py verify verify/logo_overlay.json on-site re-check; after changing any file, re-render and re-review. Before delivery, watch the full film at 1× with sound to check logo position, opacity, subtitle/platform UI occlusion, and audio-video sync.

Package a reviewed final cut as HLS video on demand

When a website needs static HLS VOD files, first confirm that the final MP4 has completed sound-on review, audio and visual quality QA. Run runtime_preflight.py analyze --profile hls_vod --output work/runtime_preflight.json --strict, then run hls_vod.py package output/master.mp4 --output-dir output/hls --receipt verify/hls_vod.json --segment-seconds 6. Input is limited to a single video, at most a single AAC audio track, H.264/yuv420p, SDR, CFR MP4; for HDR, VFR, rotation metadata, and extra subtitle/chapter streams, first create a suitable SDR/CFR master. The output directory and receipt must be new paths, and the script does not overwrite existing files.

The script forces keyframes at the segment interval and outputs index.m3u8 and TS,hls_vod.py verify verify/hls_vod.json on-site verifies that the first video packet of each segment is a keyframe, the source/output video frame counts, playlist duration, SHA-256 of all files, and decoding of the entire playlist. It only generates single-bitrate VOD; adaptive bitrate and uploading are not implemented. When deploying, upload the entire directory together, configure .m3u8 and .ts MIME types, and fully test seeking, start/end, subtitles, and sync with sound in the target player; transcode quality needs manual A/B comparison with the reviewed MP4.