起因

刷短视频的时候经常能看到那种”二次配音”的搞笑视频——拿一段影视剧片段,配上完全不相干的沙雕文案,声音还挺像原角色的。这种视频制作门槛其实不低:要找素材、去字幕、克隆声音、合成语音、加字幕、剪辑……全手工做一条少说半小时。

我就想:这整个流程能不能全自动化?

上传原始视频,系统自动提取声纹;写好文案,一键合成成品视频,甚至自动发到视频号。整套东西全部跑在我的 MacBook Pro M2 上,不花一分钱 GPU 费用。

技术选型:在 M2 上跑 AI 的正确姿势

M2 的 16GB 统一内存意味着 CPU 和 GPU 共享内存,这是跑本地 AI 模型的天然优势。关键是选对框架——不是 PyTorch,而是 Apple 的 MLX

最终的技术栈:

环节 方案 为什么选它
语音转文字 mlx-whisper + whisper-large-v3-turbo MLX 原生优化,M2 上转录速度是实时的 10 倍以上
声音克隆 mlx-audio + Qwen3-TTS (8bit) 目前开源方案中音质最好的,支持 zero-shot 声音克隆
人声分离 demucs-mlx 从有背景音乐的视频中提取纯净人声,大幅提升克隆质量
字幕去除 video-subtitle-remover (STTN) AI 画面修复,把字幕区域”抹掉”
音视频处理 FFmpeg 没什么好说的,音视频处理的瑞士军刀
后端 FastAPI + MySQL 异步任务支持好,ORM 省事
前端 React + TypeScript + Tailwind 快速出活
自动发布 Playwright 操控微信视频号后台的 Shadow DOM
视频元数据 DeepSeek API 根据文案自动生成标题、描述、话题标签

全部本地推理,除了 DeepSeek 的元数据生成需要网络,其他环节零 API 费用。

开发过程中踩的坑

坑 1:声纹被背景音乐污染

第一版做出来,声音克隆效果很差——合成出来的声音像机器人。排查后发现,原视频不仅有人声还有背景音乐,直接拿混合音频作为参考,声纹就被污染了。

解决方案:在创建角色时加了一步 demucs 人声分离。上传视频 → 提取音频 → demucs 分离出纯人声 → 用纯人声作为参考音频。效果立竿见影。

image

坑 2:字幕没去掉

video-subtitle-remover 的默认模式(全画面检测)效果很差,基本就是原封不动。翻了半天文档发现要用 sttn-det 模式,并且需要精确指定字幕区域坐标。

解决方案:前端做了一个视频播放器 + 可视化拖拽框,用户播放视频找到字幕位置后,拖拽圈定字幕区域,系统按这个区域做 AI 修复。

坑 3:FFmpeg 中文字幕变方块

烧录字幕时,所有中文变成了小方块。

解决方案:FFmpeg 的 ASS 字幕滤镜需要显式指定中文字体。macOS 自带 “Heiti SC”(黑体),加上 FontName=Heiti SC 就好了。另外还有一个隐蔽的坑——macOS 路径里的冒号(:)需要转义成 \:,否则 FFmpeg 的 subtitle filter 会解析失败。

image

坑 4:视频时长匹配

文案合成出来的音频可能 30 秒,但角色的视频素材有 3 分钟的、有 10 秒的。需要一套智能匹配算法:

  • 如果有足够长的单段视频 → 裁剪
  • 如果单段不够 → 多段拼接
  • 如果所有素材加起来还不够 → 循环

实现起来不难,但边界情况很多,比如避免在同一个视频的同一位置反复裁剪。

image

坑 5:微信视频号后台是 Shadow DOM

视频号创作者后台用了 wujie-app 微前端框架,表单元素都在 Shadow DOM 里。Playwright 的标准选择器根本找不到。

解决方案:通过 evaluate() 注入 JS,先拿到 Shadow Root,再在里面查询元素。上传文件更麻烦——Shadow DOM 里的 <input type="file"> 无法直接操作,最后用了一个 hack:把 input 克隆到主 DOM 中,设置文件后再把 FileList 同步回原始 input。

坑 6:数据库迁移

没用 Alembic 这种专业迁移工具(因为项目初期,表结构变动太频繁)。每次加新字段,CREATE_ALL 不会给已有表加列,导致 500 错误。

解决方案:在 init_db() 里写了一个自动迁移函数,启动时用 inspect 检查每个表的现有列,对比 ORM 模型定义,自动执行 ALTER TABLE ADD COLUMN。简单粗暴但好用。

image

最终效果

完整的合成流程大约 40-60 秒(取决于文案长度),包含 9 个步骤:

  1. Qwen3-TTS 声音克隆合成语音
  2. 混入背景音乐(音量自动降低)
  3. 根据音频时长智能匹配视频片段
  4. 添加固定水印”二次配音 仅供娱乐”
  5. 烧录字幕到视频画面内
  6. 缩放到 1080x1920 竖屏 + 黑边填充
  7. 添加飘动水印
  8. 合并音频
  9. DeepSeek 生成短标题、描述、话题标签

一条命令自动发布到微信视频号:

cd publisher && .venv/bin/python main.py auto --collection "搞笑"

项目架构

React 前端 (Vite :3000)
    │
    │ API Proxy
    ▼
FastAPI 后端 (:8000)
    ├── 角色管理 (创建/上传视频/去字幕/BGM)
    ├── 文案库 (CRUD)
    ├── 合成中心 (异步任务)
    └── 工具箱 (视频裁剪/音频裁剪)
    │
    ├── mlx-whisper (语音转文字)
    ├── mlx-audio/Qwen3-TTS (声音克隆)
    ├── demucs-mlx (人声分离)
    ├── video-subtitle-remover (字幕去除)
    ├── FFmpeg (音视频处理)
    └── DeepSeek API (元数据生成)
    │
    ▼
MySQL (角色/文案/任务元数据)
本地文件系统 (视频/音频/字幕)

一些感悟

Apple Silicon 跑 AI 真的能打。MLX 框架让 M2 16GB 能同时跑 Whisper + TTS + Demucs,不用买 GPU 云服务器,电费都省了。

AI 编码助手改变了开发节奏。这个项目从 0 到完整可用,核心开发时间大约一周。很多重复性的代码(FFmpeg 命令封装、CRUD 接口、前端表单)都是 AI 辅助完成的,我主要在做架构决策和调试。

声音克隆的质量取决于参考音频的质量。最重要的不是模型多强,而是参考音频有多干净。加了 demucs 人声分离后,同一个模型的效果提升了一个档次。

短视频领域的工程化空间很大。从素材管理、内容生成、到自动发布,每个环节都有优化空间。这套系统目前是单用户本地运行的,但稍作改造就能变成一个 SaaS 产品。