# video_create **Repository Path**: skkai/video_create ## Basic Information - **Project Name**: video_create - **Description**: 视频制作管线 阶段1:TTS旁白 + SRT字幕 阶段2:HTML幻灯片 + Playwright录制 阶段3:FFmpeg混音 阶段4:烧录字幕 阶段5:音量标准化 TTS源头调速 → mutagen实测时长 → 驱动幻灯片 → 录制画面 → 混音烧字幕 → 成品。 - **Primary Language**: Python - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-28 - **Last Updated**: 2026-07-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # video_create > 一条**纯脚本、可复现**的讲解类视频自动生成管线。 > 放一篇按空行分段的中文文稿,跑一条命令,出来一条 1080p · 带字幕 · 音画同步的 MP4。 --- ## 它做什么 ``` script/src.txt (按空行分段的中文文稿) │ ▼ ┌────────────────────────────────────┐ │ 1. gen_slides.py │ 自动改写文案 + 智能布局 + 套主题 │ │ src.txt → in.txt (口语化) + in.html (17 张幻灯片) │ 2. gen_tts.py (edge-tts + │ in.txt → tts/all.mp3 + subtitle.srt + timeline.json │ 音量标准化) │ 每条字幕打上 scene_id(对应 in.txt 的段落索引) │ │ 同 scene 内句间插入静音停顿(SENTENCE_PAUSE_MS) │ 3. Playwright.py (headless 录制) │ 按 scene_id 聚合旁白时长 → 逐段停留 → recording_full.webm │ 4. mix.bat (ffmpeg) │ 画面 + 旁白 → output_mixed.mp4 (H.264 slow + AAC 立体声) │ 5. burn.bat (ffmpeg 烧字幕) │ subtitle.srt → output_burned.mp4 (25 fps 立体声) │ 6. upgrade_to_30fps │ 25→30 fps 帧重复 → output_burned_30fps.mp4 ← 最终高清成品 └────────────────────────────────────┘ ``` **核心原则**:**TTS 源头调速 → mutagen 实测毫秒时长 → 驱动幻灯片 → 录制画面 → 混音烧字幕。** 动画不撑时长,实测为准,所有产物时长严格等于旁白总时长(误差 < 0.1s)。 --- ## 目录结构 ``` video_create/ ├── README.md ← 本文件 ├── README.en.md ← English short version ├── LICENSE ├── agnes/ ← 🤖 AI 生图/视频模块(另一种视频制作方式) │ ├── __init__.py ← 模块入口 │ ├── config.py ← API Key 和配置(从环境变量读取) │ ├── image_client.py← 图像生成客户端(agnes-image-2.1-flash) │ ├── video_client.py← 视频生成客户端(agnes-video-v2.0) │ └── output/ ← 生成的图片和视频 │ ├── images/ │ └── videos/ ├── script/ ← 🔧 管线源码(全部在这里) │ ├── gen_slides.py / gen_slides.bat │ ├── gen_tts.py / gen_tts.bat │ ├── Playwright.py / Playwright.bat │ ├── mix.bat ← ffmpeg 混音(H.264 slow + AAC 立体声) │ ├── burn.bat ← ffmpeg 烧字幕 │ ├── upgrade_to_30fps.bat / .py ← 25→30 fps 后期重插 │ └── video_create.bat ← 一键跑完 6 步 │ ├── mp3/ ← 🔊 TTS 声音选声库(9 个 sample + 对照表) ├── simple/ ← 📦 早期主题的完整成品样本 └── word/ ← 📖 设计文档(背景特效 / 过渡动画方案库) ``` `script/` 里跑完命令后**会生成**这些产物(均为中间或最终产物,不应 commit): ``` script/ ├── src.txt ← 用户放的文稿(必需) ├── in.txt ← gen_slides.py 生成的中间产物 ├── in.html ← gen_slides.py 生成的中间产物 ├── tts/all.mp3 ← gen_tts.py 生成的完整旁白(含 SENTENCE_PAUSE_MS 句间停顿) ├── subtitle.srt ← gen_tts.py 生成的字幕 ├── timeline.json ← gen_tts.py 生成的时间轴(含 scene_id) ├── recording_full.webm ├── output_mixed.mp4 ├── output_burned.mp4 ← 25 fps / 立体声 成品 └── output_burned_30fps.mp4 ← 🔥 30 fps / 立体声 最终高清成品 ``` --- ## 📚 仓库目录速览(脚本之外) 仓库根除了 `script/`,还保留着三个**非源码目录**。它们不参与管线运行, 但对**换声音、换主题、找设计灵感**很有价值,清理时建议保留。 ### 🔊 `mp3/` — TTS 声音选声库 想换发音人?来这里挑。 | 文件 | 用途 | |---|---| | `01语音对照表.txt` | 8 种常用中文 TTS 声音的对照表(性别 / 适用场景 / 风格) | | `女_小北_辽宁.mp3` | zh-CN-liaoning-XiaobeiNeural(辽宁话,幽默) | | `女_小妮_陕西.mp3` | zh-CN-shaanxi-XiaoniNeural(陕西话,明亮) | | `女_晓伊_Lively.mp3` | zh-CN-XiaoyiNeural(活泼) | | `女_晓晓_Warm.mp3` | zh-CN-XiaoxiaoNeural(温暖) | | `男_云剑_Passion.mp3` | zh-CN-YunjianNeural(激情) | | `男_云夏_Cute.mp3` | zh-CN-YunxiaNeural(可爱) | | `男_云希_Sunshine.mp3` | zh-CN-YunxiNeural(阳光、活力)**← 默认使用** | | `男_云扬_Professional.mp3` | zh-CN-YunyangNeural(专业) | **用法**:试听后挑一个喜欢的,把名字填到 `script/gen_tts.py` 顶部 `VOICE` 常量。 ### 📦 `simple/` — 早期主题的完整成品样本 一份**完整跑通过**的视频产物存档(含 `in.txt` + `in.html` + `tts/all.mp3` + `timeline.json` + `subtitle.srt` + `recording_full.webm` + `output_mixed.mp4` + `output_burned.mp4`)。 **作用**: - 看一份"完整的管线产物长什么样"——尤其刚开始接触管线时,直接打开 `output_burned.mp4` 看效果 - 对比"早期主题"(可能是珊瑚橙 / 简单版样式)和当前 `gen_slides.py` 里 `深空蓝` 主题的视觉差异 - 拿来当回归测试基准(每次大改 `gen_slides.py` 后,跑同样输入对比 `output_burned.mp4` 是否仍然可用) ### 📖 `word/` — 设计文档(背景特效 / 过渡动画方案库) 三份参考文档,**写主题或动画时翻一翻**: | 文件 | 用途 | |---|---| | `视频自动化制作管线.docx` (43 KB) | 完整设计文档(Word 格式,含架构图、流程说明) | | `视频自动化制作管线命令.txt` (643 B) | 命令速查表(ffprobe / ffplay / ffmpeg 常用组合) | | `风格.txt` (1.5 KB) | **设计素材库** —— 10 种背景特效 + 12 种过渡动画 + 推荐搭配 | `风格.txt` 里值得高亮的内容: - **背景特效 10 选 1**:浮动光点(当前默认)、流星雨、涟漪扩散、矩阵数字雨、漂浮气泡、网格线流动、烟雾、几何碎片、极光带、噪点颗粒 - **过渡动画 12 选 1**:`fx0`-`fx13`,涵盖 3D 推进、左右平移、垂直翻页、缩放溶解、波浪展开、百叶窗、3D 翻转、弹跳入场等 - **风格推荐搭配**:珊瑚橙→ 流星雨/气泡/极光;青绿科技 → 矩阵/网格/涟漪;暗紫 AI → 烟雾/几何/极光;冰川蓝 → 气泡/涟漪/噪点 **用法**:`gen_slides.py` 顶部 `THEMES` 列表加新主题时,直接照抄 `风格.txt` 里的搭配方案。 --- ## 快速开始 ### 一键跑完全流程 ```cmd cd script :: 1. 把你的文稿放到 script\src.txt(段落用空行分隔,UTF-8) :: 2. 跑: video_create.bat ``` 约 8-10 分钟,产物在: - `script/output_burned.mp4` — 25 fps / 立体声 / H.264 High@4.0 / ~90MB - `script/output_burned_30fps.mp4` — 30 fps 升级版 / 立体声 / ~86MB ### 分步执行 ```cmd cd script 1. python gen_slides.py :: src.txt → in.txt + in.html 2. python gen_tts.py :: in.txt → tts/all.mp3 + subtitle.srt + timeline.json (含 SENTENCE_PAUSE_MS 句间停顿) 3. python Playwright.py :: timeline + in.html → recording_full.webm (5-6 分钟) 4. mix.bat :: webm + all.mp3 → output_mixed.mp4 (H.264 slow + AAC 立体声) 5. burn.bat :: mixed + .srt → output_burned.mp4 (25 fps) 6. upgrade_to_30fps.bat :: output_burned.mp4 → output_burned_30fps.mp4 (1-2 分钟,30 fps) ``` ### 输入文件格式(`script/src.txt`) - **UTF-8 纯文本**,中文 - **每段一段,空行分隔**(一段 ≈ 一个 slide scene,实际可能由 `gen_slides.py` 智能合并短段落) - 段内可多句,全角标点(。,;:) - 例: ``` MS14-064,微软2014年11月修复的高危漏洞,存在于Windows OLE组件中。 攻击者可构造恶意网页,诱导用户使用Internet Explorer访问,从而在目标系统上执行任意代码。 本指南仅供安全研究和授权渗透测试使用... ``` 文案改动 → 编辑 `src.txt`,从第 1 步重跑。`in.txt` / `in.html` 都是自动生成的,**不要手改**。 --- ## 🤖 Agnes AI — 另一种视频制作方式 除了上述脚本管线,本项目还集成了 **Agnes AI SDK**,提供基于 AI 模型的图像和视频生成能力。 ### 功能 - **图像生成**: 使用 `agnes-image-2.1-flash` 模型根据文本描述生成图片 - **视频生成**: 使用 `agnes-video-v2.0` 模型根据文本描述生成短视频 ### 快速开始 ```python from agnes import AgnesImageClient, AgnesVideoClient # 生成图片 img_client = AgnesImageClient() images = img_client.generate_image("A beautiful sunset", size="1024x1024") print(images[0]["url"]) # 生成视频 vid_client = AgnesVideoClient() video_path = vid_client.generate_video_and_download("Ocean waves", duration=5) print(video_path) ``` ### 配置 API Key 从环境变量 `AGNES_API_KEY` 读取,无需修改代码。 ```bash # 设置环境变量 set AGNES_API_KEY=your_api_key ``` ### 输出文件 生成的图片和视频默认保存到 `agnes/output/` 目录。 ### 详细文档 参见 `agnes/README.md` 获取完整 API 文档和使用示例。 --- ## 关键设计(为什么这套管线稳) ### 1. **scene 边界从源头锁定,不靠 HTML 反查** 每条字幕在 `gen_tts.py` 里生成时即打上 `scene_id`(对应 `in.txt` 的段落索引), 写入 `timeline.json`。`Playwright.py` 直接按 `scene_id` 聚合求和得到每个 scene 的旁白时长。 **好处**:彻底规避了"HTML 里 `data-tts` 字段错位 / 跨段合并 / 计数错误"这类隐性 bug。 ### 2. **时长守恒** ``` 总录制时长 = 旁白总时长 旁白总时长 = sum(scene 旁白) 录制组成 = sum(每张停留) + 首屏入场动画 + sum(每段切换动画 + 画面提前量) ``` 每张停留时长 = 旁白时长 - 该张预承担的动画时长,在 `Playwright.py` 里统一扣减。 录完后用 ffmpeg 精确裁剪到旁白时长,消除 goto 加载等系统开销的尾差。 ### 3. **画面提前 LEAD_MS** 幻灯片有入场/切换动画,如果画面跟声音**同步**切换,听起来"声音已经到了,画面还在动"。 所以**画面在声音切换点之前 LEAD_MS 就准备好**(`LEAD_MS = 300`,推荐值)。 ``` 声音切换点(下一段旁白开始) ←── 对齐 ──→ 画面就绪点(动画完成) ↑ 提前 LEAD_MS 触发 ArrowRight ``` 时长守恒自动预留这部分,**改 LEAD_MS 不需要重生成 TTS / timeline**。 ### 4. **TTS 源头调速 + 音量放大** edge-tts 默认偏慢、偏轻。在 `gen_tts.py` 里: - 语音:`zh-CN-YunxiNeural`(可换,顶部 `VOICE` 常量) - 语速:`+20%`(顶部 `RATE` 常量) - 合成后用 ffmpeg `volume=4.0 + alimiter=limit=0.95` 放大并防爆音。 实测 5-6 分钟的讲解稿,TTS 合成 < 1 分钟,音量处理 ~30 秒。 ### 5. **`src.txt` → `in.txt` 的智能改写** `gen_slides.py` 不只是搬运文案,还会做规则级改写:分句、控长、加过渡词, 让文稿更接近口语、读起来不卡。改写后写入 `in.txt` 供 TTS 使用。 ### 6. **句间停顿(避免"一口气讲完"的违和感)** edge-tts 默认合成的每段 MP3 头尾约 100-300ms 静音,但**句间没有停顿**。 `gen_tts.py` 在拼接 `all.mp3` 时,会按 `SENTENCE_PAUSE_MS`(默认 250ms) 在**同 scene 内的相邻字幕之间**插入静音(scene 切换处不加 — 那里已有 600ms 切换动画 + 300ms 画面提前)。 **关键技术点**: - MP3 在 24kHz/48kbps 下每帧 ~24ms,lavfi 会把请求时长上取整到最近的帧边界(250ms → 实际 ~312ms) - `generate_silence_bytes()` 用 mutagen 实测校正,SRT/timeline 记录的就是实测时长 - 跨 scene 处不插停顿,避免和动画切换叠加导致"换页空荡荡" - **改 `SENTENCE_PAUSE_MS` 顶部常量即可**,无需重生成 TTS / timeline 之外的依赖 ### 7. **后期帧率提升(25 → 30 fps)** Playwright 的 `record_video_*` API **故意不暴露 fps 参数**,服务端硬编码 25 fps, 所以 `VIDEO_FPS = 30` 在 Playwright.py 里是无效值(代码里改成 25 以反映现实)。 为拿到 30 fps,新增 `upgrade_to_30fps.py` 用 ffmpeg `fps=30:round=up` 帧重复 把 25fps `output_burned.mp4` 升到 30fps,输出 `output_burned_30fps.mp4`。 **为什么不用 minterpolate 运动补偿**: - 4 分钟素材 minterpolate 实测耗时 **58 分钟**(慢 15 倍实时) - 产生 **111ms 音画偏移**(> 40ms 人耳可察觉阈值) - 帧重复版本仅 **1-2 分钟**,讲解视频几乎察觉不到重复帧 ### 8. **视频编码(H.264 High + 立体声)** `mix.bat` / `burn.bat` 升级到: | 参数 | 值 | 原因 | |---|---|---| | `-preset slow` | (medium → slow) | 同 CRF 文件小 5-10%,质量更高 | | `-profile:v high -level 4.0` | 显式 | 跨平台兼容,1080p@30fps 在 4.0 范围内 | | `-pix_fmt yuv420p` | 显式 | 兼容性最好(覆盖所有播放器) | | `-x264-params keyint=250:min-keyint=25:ref=4:bframes=3:b-adapt=2` | 显式 | 10 秒 1 个 IDR 帧,seek 体验好 | | `-c:a aac -b:a 192k -ac 2` | 立体声 | mono TTS 复制到 L/R,192k 是立体声甜点 | | `-movflags +faststart` | 新增 | moov 原子前置,网络边下边播不卡 | --- ## 调参指南(主要常量一览) | 文件 | 常量 | 默认值 | 含义 | |---|---|---|---| | `gen_tts.py` | `VOICE` | `zh-CN-YunxiNeural` | edge-tts 发音人 | | `gen_tts.py` | `RATE` | `+20%` | TTS 语速 | | `gen_tts.py` | `normalize_loudness(volume, limit)` | 4.0 / 0.95 | 音量放大系数 / 防爆音峰值 | | `gen_tts.py` | `TTS_SOFT_FAIL` 环境变量 | `0`(硬终止) | 失败策略:`0`=任一 TTS 失败立即 `sys.exit(2)`(防偏移);`1`=软跳过,失败段 dur=0 + 占位字幕 | | `gen_tts.py` | `SILENCE_TRIM_DB` / `SILENCE_MIN_DUR` | `-40dB` / `0.2` | 头尾静音裁剪阈值与最短时长(避免字幕时间"灌水"静音) | | **`gen_tts.py`** | **`SENTENCE_PAUSE_MS`** | **250** | **同 scene 内句间停顿(毫秒)**。`0`=关闭。MP3 帧对齐后实际 ~312ms(scene 切换处不加 — 已由动画处理) | | `gen_slides.py` | `THEMES` | 4 个内置主题 | 主题包列表(改这里换风格) | | `gen_slides.py` | `MIN_PARA_LEN` | 45 | 短段落合并阈值(字) | | `Playwright.py` | `VIDEO_WIDTH / HEIGHT` | 1920 × 1080 | 录制分辨率 | | `Playwright.py` | `VIDEO_FPS` | **25** | ⚠️ **Playwright 服务端硬编码 25 fps**(原写 30 是死变量)。真实 30fps 由 `upgrade_to_30fps.bat` 后处理得到 | | `Playwright.py` | `SWITCH_GAP_MS` | 600 | 场景切换动画耗时 | | `Playwright.py` | `LEAD_MS` | 300 | 画面提前量 | | `Playwright.py` | `INTRO_ANIM_MS` | 1200 | 首屏入场动画耗时 | | `Playwright.py` | `BASE_AUDIO_DELAY_MS` | 500 | 混音时的音频延迟基准;实际值叠加 `recording_offset_ms.json` 测得的加载开销 | | `mix.bat` | ffmpeg 参数 | slow / CRF 18 / high@4.0 | 视频编码器:preset slow + H.264 High Profile + AAC 立体声 192k + faststart | | `burn.bat` | 字幕样式 | 微软雅黑 18pt / 白字黑描边 | 烧字幕字体与样式 | | `upgrade_to_30fps.py` | `fps=30:round=up` | 30 fps | 25→30 fps 后期帧重复(比 minterpolate 快 100×) | --- ## 音画对齐(全链路保障) 管线已经从"按时长守恒粗同步"升级到"动态测量 + 硬失败兜底 + 帧率后处理": ### 1. TTS 失败策略 - **默认 hard fail**:任一 TTS 段合成失败(网络抖动/edge-tts 服务异常)→ 立即 `sys.exit(2)`,**不**输出半成品字幕/音频,避免音画永久偏移。 - **soft fail 模式**:`set TTS_SOFT_FAIL=1` 后失败段以 `dur_ms=0` + 占位字幕 `[TTS失败:...]` 跳过,生成的视频在该处静音。 - 旧版硬编码的 `2500ms` 假时长已删除。 ### 2. 头尾静音裁剪 (`trim_silence`) - `gen_tts.py` 在每段 TTS 合成成功后,用 ffmpeg `silencedetect=-40dB / 0.2s` + `silenceremove` 裁掉头尾可听静音。 - 用裁剪后的字节**重新测** mutagen 时长写入 `timeline.json`。 - 旧版"时长守恒"机制隐含的"字幕时间灌水静音"问题被消除。 ### 3. 录制起点动态补偿 - `Playwright.py` 录制收尾时,计算"实际录制时长 - 期望录制时长"得到 `measured_overhead_ms`,写入 `recording_offset_ms.json` 与 `adelay_ms.txt`(单行文本,供 cmd 读取)。 - `mix.bat` 优先读 `adelay_ms.txt`,若不存在则回退到 `BASE_AUDIO_DELAY_MS=500ms`。 - 不再依赖经验值硬编码。 ### 4. 时长守恒校验(未变) 录制过程中 `Playwright.py` 已用 `content_total == narration_total` 做校验,末尾用 `ffmpeg -t` 精确裁剪。 ### 5. 句间停顿(`SENTENCE_PAUSE_MS`,默认 250ms) - 同 scene 内的相邻字幕之间插入静音停顿(scene 切换处不加 — 已有 600ms 动画) - MP3 帧对齐后实际 ~312ms(请求 250ms,lavfi 上取整到最近的 24ms 帧边界) - `generate_silence_bytes()` 用 mutagen 实测校正,避免"代码记 250ms / 实际 312ms"造成 scene 总时长 < 音频总时长的音画偏移 - `0` = 关闭,改 `SENTENCE_PAUSE_MS` 顶部常量即可调 ### 6. 帧率后处理(25 → 30 fps) - Playwright 服务端硬编码 25 fps,无法通过 Chromium flag 绕过 - `upgrade_to_30fps.bat` 调用 `upgrade_to_30fps.py`,用 ffmpeg `fps=30:round=up` 把 `output_burned.mp4` 升到 30 fps - 输出 `output_burned_30fps.mp4`(原 25fps 文件保留) - `-shortest` 让 ffmpeg 自动对齐视频与音频时长(~38ms 微偏移可忽略) --- ## 工具依赖 | 工具 | 用途 | 安装 | |---|---|---| | **Python 3.10+** | 跑脚本 | python.org | | **edge-tts** | TTS 合成 | `pip install edge-tts` | | **mutagen** | MP3 毫秒级时长探测 | `pip install mutagen` | | **Playwright** + Chromium | 录制幻灯片 | `pip install playwright` + `playwright install chromium` | | **ffmpeg / ffprobe** | 混音、烧字幕、裁剪、测时长 | ffmpeg.org | 全部开源免费。 --- ## 二次开发提示 - **换主题**:改 `gen_slides.py` 的 `THEMES` 列表(顶部),不动其他。 - **换片头/片尾**:在 `in.html` 模板里加 scene,或在 `Playwright.py` 录制前后插入额外 `page.goto` + 录屏。 - **多语种**:改 `gen_tts.py` 的 `VOICE` 和 `RATE`;`split_text` 支持英文标点分句。 - **改分辨率**:同时改 `gen_slides.py` 的 HTML 视口尺寸、`Playwright.py` 的 `VIDEO_WIDTH/HEIGHT` 和 `mix.bat` 的 ffmpeg 参数。 - **调试录制**:把 `Playwright.py` 里 `chromium.launch(headless=True)` 改成 `False`,可以实时观察页面切换。 - **改句间停顿**:改 `gen_tts.py` 顶部 `SENTENCE_PAUSE_MS`(默认 250ms;`0`=关闭)。改完后只需重跑 `gen_tts → playwright → mix → burn → upgrade_to_30fps` 五步。 - **改视频编码**:改 `mix.bat` / `burn.bat` 的 ffmpeg 参数(preset / CRF / 码率 / 立体声开关)。三个脚本保持一致编码风格最稳。 - **跳过 30fps 升级**:从 `video_create_tts.bat` 末尾删掉 `call upgrade_to_30fps.bat`,或直接跑 `burn.bat` 后停。 --- ## 法律声明 当前 `script/src.txt` 历史样例是 **MS14-064 漏洞利用讲解**——**仅供安全研究和授权渗透测试使用**。 未经授权对他人系统进行测试属于违法行为。 请在合法授权的环境(如本地隔离虚拟机实验室)中练习。 --- ## 建议的 `.gitignore` `script/` 跑完会生成大量中间/最终产物,**不要 commit**,建议加入: ```gitignore # 中间产物(gen_slides / gen_tts 生成) script/in.txt script/in.html script/timeline.json script/subtitle.srt script/tts/ # 录制/成品 script/recording_full.webm script/recording_offset_ms.json script/adelay_ms.txt script/output_mixed.mp4 script/output_burned.mp4 script/output_burned_30fps.mp4 # Python 缓存 script/__pycache__/ *.pyc ```