自动视频生成器 | 自动化视频生成器 v2
快速开始(3步)
第1步:上传 Skill
下载 auto-video-generator.zip
打开 Trae → 设置 → 规则和技能 → + 创建
拖拽上传 zip 包 → 选择"全局" → 确认
第2步:首次使用自动引导
首次说"生成演示视频"时,AI 会自动检测并提示:
检测到需要安装以下软件:
- Playwright(浏览器自动化)
- FFmpeg(视频处理)
- Edge TTS(自然语音合成)
点击"一键安装"或手动安装:
[一键安装] [查看教程]
第3步:开始使用
安装完成后,直接说:
"生成演示视频"
"帮我制作产品教程"
"录制原型演示"
"用我的声音生成视频"
前置依赖说明
本 Skill 需要以下软件支持:
软件 用途 自动安装 手动安装
Node.js 运行环境 ❌ 需预先安装 下载
Playwright 浏览器录屏/逐帧截图 ✅ 自动 pip install playwright
FFmpeg 视频合并/音频同步 ✅ 自动 winget install FFmpeg
edge-tts Edge神经语音(推荐) ✅ 自动 pip install edge-tts
Windows SAPI 备用语音引擎 ✅ 已内置 无需安装
一键安装脚本(按系统选择)
Windows 用户 双击运行:安装依赖.bat
macOS / Linux 用户 chmod +x install-mac.sh && ./install-mac.sh
# 或 chmod +x install-linux.sh && ./install-linux.sh
手动安装(如果自动脚本失败)
Windows: pip install playwright edge-tts npx playwright install chromium winget install --id=Gyan.FFmpeg -e
macOS: pip install playwright edge-tts npx playwright install chromium brew install ffmpeg
语音方案(核心改进)
方案对比
方案 自然度 成本 特点 推荐场景
Edge TTS 神经语音 ⭐⭐⭐⭐⭐ 免费 微软Edge同款,抖音常用 默认首选
Windows SAPI ⭐⭐ 免费 内置,机械感强 备用方案
Fish Audio ⭐⭐⭐⭐⭐ 免费/付费 支持声音克隆 想用自己的声音
CosyVoice ⭐⭐⭐⭐⭐ 开源免费 阿里开源,情感表达强 本地部署
Edge TTS 语音选择指南
中文女声(推荐):
语音ID 名称 风格
zh-CN-XiaoxiaoNeural 晓晓 年轻女性,活泼自然(默认)
zh-CN-XiaoyiNeural 晓伊 温柔知性
zh-CN-XiaomengNeural 晓梦 新闻播报风格
中文男声:
语音ID 名称 风格
zh-CN-YunxiNeural 云希 年轻男性,自然亲切
zh-CN-YunjianNeural 云健 成熟稳重
使用方式:
import edge_tts
communicate = edge_tts.Communicate('你的文案', 'zh-CN-XiaoxiaoNeural', rate='-5%')
await communicate.save('output.mp3')
声音克隆(使用自己的声音)
Fish Audio 流程:
注册 fish.audio 获取 API Key
上传一段你的录音(10秒以上)
获取声音模型 ID
在脚本中指定你的声音:
communicate = edge_tts.Communicate(text, voice='your-voice-model-id')
GPT-SoVITS(本地开源):
克隆仓库:git clone https://github.com/RVC-Boss/GPT-SoVITS
录制参考音频(3~10秒)
微调模型后导出为 ONNX
通过 API 调用生成语音
音画同步技术方案(核心机制)
问题背景
传统视频生成常见问题:
视频画面不随讲解变化(一直显示同一页)
讲趋势图时画面已经滚到五峰图
语音和视频时长不匹配
解决方案:场景化录制 + 时长匹配
┌─────────────────────────────────────────────┐
│ 视频生成流程 (v2) │
│ │
│ Step 1: 定义场景列表 │
│ ├── 场景1: {name, text, action} │
│ ├── 场景2: {name, text, action} │
│ └── ... │
│ ↓ │
│ Step 2: 按场景生成语音 + 获取每段时长 │
│ ├── 场景1语音: 27.2s │
│ ├── 场景2语音: 14.8s │
│ └── ... │
│ ↓ │
│ Step 3: 按时长逐帧录制视频 │
│ ├── 执行场景1 action → 截图27.2s×FPS帧 │
│ ├── 执行场景2 action → 截图14.8s×FPS帧 │
│ └── ... │
│ ↓ │
│ Step 4: FFmpeg合并 ( -shortest ) │
│ 输出: 音画完美同步的视频 │
└─────────────────────────────────────────────┘
核心代码模式
SCENES = [
{ "name": "开场-KPI卡片", "text": "这段语音只描述当前画面可见的内容...", "action": "scroll_top", # 页面操作指令 },
{ "name": "下钻到车队", "text": "点击第一客运公司进行下钻...", "action": "drill_fleet", # Vue交互操作 },
]
async def generate_scene_audios():
scene_durations = []
for idx, scene in enumerate(SCENES):
# Edge TTS 生成语音
communicate = edge_tts.Communicate(scene['text'], VOICE)
await communicate.save(f'narration_{idx}.mp3')
# ffprobe 获取精确时长
duration = get_audio_duration(f'narration_{idx}.mp3')
scene_durations.append(duration)
async def capture_frames(scene_durations):
for idx, scene in enumerate(SCENES):
dur = scene_durations[idx]
# 先执行页面操作(滚动/点击/下钻等)
await run_action(page, scene["action"])
# 再按语音时长截图(确保画面与语音匹配)
await save_frames(dur)
关键原则
每段语音只描述当前画面可见内容 —— 不做跨场景描述
先执行操作,再开始截图 —— 确保画面已更新
使用 -shortest 参数合并 —— 以较短者为基准裁剪
Vue 组件交互录制
直接调用 Vue 实例方法
对于 Vue 单页应用,通过 JavaScript 直接调用实例方法触发交互:
// 下钻到车队
await page.evaluate(() => {
const el = document.querySelector('.route-efficiency-v2-container');
const vm = el.__vue__;
vm.drillDownToFleet(vm.companyData[0]);
})
// 切换Tab
await page.evaluate(() => {
document.querySelector('.route-efficiency-v2-container').__vue__
.routeAnalysisActiveTab = 'threeLow';
})
// 展开面板
await page.evaluate(() => {
document.querySelector('.route-efficiency-v2-container').__vue__
.matrixExpanded = true;
})
内部滚动容器处理
很多现代 Web 应用使用内部滚动容器而非 window.scrollTo:
# 错误:window.scrollTo 不起作用
await page.scroll_to(1000)
# 正确:控制内部滚动容器的 scrollTop
async def scroll_wrapper(page, target_y):
await page.evaluate(f'''() => {{
const wrapper = document.querySelector('.content-scroll-wrapper');
if (wrapper) wrapper.scrollTop = {target_y};
}}''')
# 或者滚动到特定元素
async def scroll_to_element(page, selector, offset=-60):
await page.evaluate(f'''() => {{
const el = document.querySelector('{selector}');
const wrapper = document.querySelector('.content-scroll-wrapper');
if (el && wrapper) wrapper.scrollTop = el.offsetTop + {offset};
}}''')