单人日产 10-14 条、单条 3-6 分钟、风格还不飘。提示词、配音、合成、批处理四段全公开,代码复制就能跑。
文章目录
一、先给结论:三条路线我全跑过
2026 年用 AI 做短视频早就不稀奇了。真正卡住人的地方已经变了——不是"能不能生成",而是"能不能日产 10 条以上、风格还不飘"。
一周时间,三条路线,单人实测均值:
| 方案 | 单条耗时 | 日产量(单人) | 风格一致性 | 成本可控性 | 上手难度 | 推荐指数 |
|---|---|---|---|---|---|---|
| 纯在线工具(可灵 / 即梦等) | 8-15 分钟 | 4-6 条 | 差 | 高 | 低 | ★★★ |
| 纯 ComfyUI 本地 | 10-20 分钟 | 3-5 条 | 高 | 低 | 极高 | ★★★★ |
| Codex + 本地工具链 | 3-6 分钟 | 10-14 条 | 高 | 极低 | 高 | ★★★★★ |
三条都趟过,说几句实话:
- 纯在线工具上手最快,但它是黑盒。同一个提示词,今天出冷调、明天出暖调,做系列号必翻车。
- ComfyUI 一致性最好,但环境能把人劝退——装一天、报错两天是常态。
- Codex + 本地工具链是我最后留下的方案:把胶水代码、批处理脚本交给 Codex 写,人只负责定风格和抽检。
下面就是第三种路线拆开后的全部内容。
二、四步跑通
先定目录结构,后面代码都按这个放:
ai-shorts/
├── prompts/ # 提示词模板
├── scripts/ # narration.txt,每行一条旁白
├── videos/ # 分镜视频(务必英文路径)
├── audios/ # 配音音频
├── output/ # 合成成片
├── tts_batch.py
├── merge_batch.py
└── main.py
2.1 提示词:把「风格锚点」写死
整套流程里最值钱的一步。风格漂移的根因,90% 就出在提示词每次都不一样。
直接复制下面这段:
你是一位有 5 年以上短视频实战经验的导演兼爆款文案写手。
请严格按以下格式输出,不要添加任何额外解释。
主题:程序员深夜改 Bug 的真实日常
目标时长:50 秒
目标受众:25-35 岁程序员与打工人
风格要求:真实、有共鸣、微幽默,杜绝鸡汤和 AI 味
【旁白文案】
(口语化;前 3 秒必须强钩子;总字数 170-190 字;长句短句交替)
【分镜表】
镜头 1(0-8 秒):
- 画面描述:
- 镜头运动:
- 文生视频关键词(必须包含统一风格前缀「电影感,冷色调,真实光影,浅景深,禁止人物变形」):
- 字幕重点:
镜头 2(8-16 秒):
...
(按时间顺序继续到结束)
【BGM 建议】
【整体色调关键词】
关键就一句话:「统一风格前缀」这个字段每次都要一字不改地带上,它是抗漂移的锚点。改主题可以,改锚点不行。
2.2 配音批处理(Edge-TTS)
先装依赖:
pip install edge-tts
# tts_batch.py
import asyncio
from pathlib import Path
import edge_tts
# 旁白推荐云希(偏年轻)或晓晓(偏亲和);
# 云扬是新闻腔,别用在短视频里,一听就"官方"
VOICE = "zh-CN-YunxiNeural"
RATE = "+8%" # 比默认语速稍快,完播率会更好
OUT_DIR = Path("audios")
async def generate_voices(texts: list[str], out_dir: Path = OUT_DIR,
voice: str = VOICE, rate: str = RATE) -> None:
out_dir.mkdir(parents=True, exist_ok=True)
for i, text in enumerate(texts, 1):
output_file = out_dir / f"narration_{i:03d}.mp3"
# 偶发的 403 / 空文件,重试两次基本就过
for attempt in range(3):
try:
communicate = edge_tts.Communicate(text, voice, rate=rate)
await communicate.save(str(output_file))
break
except Exception as exc:
if attempt == 2:
raise
print(f"第 {i} 条失败({exc}),1.5s 后重试")
await asyncio.sleep(1.5)
print(f"已生成:{output_file}")
await asyncio.sleep(0.5) # 串行 + 间隔,避免被限流
if __name__ == "__main__":
texts = [
"凌晨两点,屏幕上的红字还在跳。",
"你以为改完这个 Bug 就能睡了?太天真了。",
]
asyncio.run(generate_voices(texts))
文件名用 narration_001.mp3 这种三位补零的格式,是为了后面排序时不会出现 10 排在 2 前面。
2.3 视频合成:先标准化,再批量合并
这一步 80% 的翻车都不是 ffmpeg 的锅,是素材本身不统一。 先做一次标准化(竖屏 1080×1920 / 25fps / 44100Hz):
ffmpeg -i raw.mp4 \
-r 25 \
-vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -pix_fmt yuv420p -crf 20 \
-c:a aac -ar 44100 -b:a 192k \
videos/std_001.mp4
再批量合并:
# merge_batch.py
import subprocess
from pathlib import Path
def batch_merge(video_dir: str, audio_dir: str, output_dir: str,
dry_run: bool = False) -> None:
video_dir, audio_dir, output_dir = map(Path, (video_dir, audio_dir, output_dir))
output_dir.mkdir(parents=True, exist_ok=True)
videos = sorted(video_dir.glob("*.mp4"))
audios = sorted(audio_dir.glob("*.mp3"))
# 数量不一致时别硬跑,否则音画会整条错位
if len(videos) != len(audios):
raise ValueError(f"数量不匹配:视频 {len(videos)} 个 / 音频 {len(audios)} 个")
for i, (video, audio) in enumerate(zip(videos, audios), 1):
output = output_dir / f"final_{i:03d}.mp4"
cmd = [
"ffmpeg", "-y",
"-i", str(video),
"-i", str(audio),
"-c:v", "copy",
"-c:a", "aac", "-b:a", "192k", "-ar", "44100",
"-shortest",
"-movflags", "+faststart",
str(output),
]
if dry_run:
print(" ".join(cmd))
continue
subprocess.run(cmd, check=True,
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
print(f"合成完成:{output}")
if __name__ == "__main__":
batch_merge("videos", "audios", "output")
-c:v copy 不重编码,秒出;-shortest 让成片以较短的一轨为准,避免尾部黑屏;+faststart 让平台侧边加载边播。
2.4 一条命令串完全流程
# main.py
import argparse
import asyncio
from pathlib import Path
from merge_batch import batch_merge
from tts_batch import generate_voices
ROOT = Path(__file__).parent
def load_scripts(path: Path) -> list[str]:
return [ln.strip() for ln in path.read_text("utf-8").splitlines() if ln.strip()]
def main() -> None:
parser = argparse.ArgumentParser(description="AI 短视频流水线")
parser.add_argument("--scripts", default=str(ROOT / "scripts/narration.txt"),
help="每行一条旁白文案")
parser.add_argument("--skip-tts", action="store_true", help="跳过配音,只做合成")
args = parser.parse_args()
if not args.skip_tts:
asyncio.run(generate_voices(load_scripts(Path(args.scripts))))
batch_merge(ROOT / "videos", ROOT / "audios", ROOT / "output")
if __name__ == "__main__":
main()
之后每天只做两件事:往 narration.txt 里贴文案,把生成好的分镜丢进 videos/,然后 python main.py。
三、高频踩坑与解决方案
1. 风格严重漂移
- 原因:关键词没有强制统一风格前缀,每次描述都是"新的一版"。
- 解决:固定前缀一字不改,并且每批成品人工抽检 3 条——抽检不是为了改,是为了及早发现锚点失效。
2. 口型对不齐 / 音画不同步
- 原因:音频采样率和视频帧率不匹配(44.1k vs 48k、24fps vs 25fps 混着来)。
- 解决:全链路统一 44100Hz + 25fps,在 2.3 的标准化命令里一次做掉。
3. 批量处理时 FFmpeg 随机失败
- 原因:路径含中文、并发过高、某些源的编码不兼容。
- 解决:全英文路径 + 串行执行;
-c:v copy报错的那一条,单独改成重编码试试:
ffmpeg -y -i bad.mp4 -i bad.mp3 \
-c:v libx264 -pix_fmt yuv420p -c:a aac -shortest fixed.mp4
4. 完播率低
- 原因:前 3 秒钩子太弱,节奏拖沓。
- 解决:在提示词里就写死"前 3 秒必须出现冲突或强烈共鸣点"。节奏问题要在文案阶段解决,剪辑阶段只能补救。
5. Edge-TTS 偶发空文件 / 403
- 原因:短时间请求过密被限流。
- 解决:串行生成 + 每条间隔 0.5s + 失败重试(2.2 的代码里已经带上了)。
四、真实效率数据
跑通之后的实际数字:
- 单人稳定日产 10-14 条可直接发布的视频;
- 单条均摊 3-6 分钟,其中人工只占 1 分钟左右;
- 综合效率比纯在线工具提升约 2.5 倍,边际成本接近为零。
一句话总结:这套流程的价值不在"生成",在于把不可控的创意劳动,变成了可控的工业流程。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_52208686/article/details/166130930




