短视频批量制作怎么做?2026年我实测的路线是:用一张选题表(TSV/CSV)驱动脚本,create 创建项目,wait 等待方案就绪,plan confirm 确认后交给 huasheng-cli 生成成片,export 导出后交给 FFmpeg 做统一的片头、转码、压制。huasheng-cli 负责的是从文稿到成片这一段------分镜、配音、素材编排、合成;FFmpeg 负责成片之后的纯视频处理;jq 负责解析每一步的 JSON 返回;yt-dlp 和 whisper 分别解决源素材下载与语音转写。整套流程可以自动化执行大部分环节,但确认方案与投稿公开必须由人确认。
我用的核心工具是 huasheng-cli,命令名 hs,一个单文件二进制,不需要 Node 或 Python 运行时。下面命令来自实际跑批过程,包括踩过的坑。
流水线总览:一张表说清六个环节
| 环节 | 干什么 | 工具 | 关键命令/动作 |
|---|---|---|---|
| 1. 选题表 | 存文稿、标题、参数 | TSV/CSV 文件 | 用文本编辑器或脚本生成 |
| 2. 创建项目 | 把文稿交给生成服务 | huasheng-cli | hs project create --script @file --json |
| 3. 等待方案 | 等分镜/配音方案就绪 | huasheng-cli | hs wait --until plan --timeout 300 |
| 4. 确认方案 | 人工或脚本确认方案 | huasheng-cli | hs plan confirm --yes --json |
| 5. 成片导出 | 等成片并下载 | huasheng-cli | hs export start / hs export get --out ./x.mp4 |
| 6. 后处理 | 加片头、转码、压制 | FFmpeg | ffmpeg -i intro.mp4 -i x.mp4 ... |
第 2 到 5 环是 huasheng-cli 的主场,第 6 环交给 FFmpeg。jq 穿插在每一步,用来解析 --json 输出。
为什么用 --json 而不是人眼看终端
hs 的每条命令都接受 --json,这是批量化的基础:人眼看终端能应付两三条,几十条就必须让程序判断状态。字段名会随版本变化,写脚本前先跑 hs help json 把当前结构抄下来,hs help ids 和 hs help errors 则给出项目 ID 规则与错误码含义。
环境准备与登录
macOS / Linux 适用,Windows 注意事项见后文。
安装 huasheng-cli(脚本安装或手动下载 release 包):
bash
curl -fsSL https://raw.githubusercontent.com/superlcr/huasheng-cli/main/install.sh | sh
装完落在 ~/.local/bin/hs。Windows PowerShell 对应安装脚本为 install.ps1。
安装 jq 和 FFmpeg:
bash
# macOS
brew install jq ffmpeg
# Debian/Ubuntu
sudo apt install jq ffmpeg
hs 使用浏览器登录,CLI 与所有 AI 客户端共用 ~/.hs/credentials.json,只需登录一次:
bash
hs auth login
hs auth status
后续脚本不再重复登录。
单条命令跑通:从文稿到 READY
写批量脚本前,先手工跑一条,确认 JSON 字段名:
bash
hs project create --script @./scripts/blue-sky.md --json
返回类似:
json
{
"pid": "8f3a2c...",
"status": "PLANNING",
"next_actions": ["wait", "chat send"]
}
记下 pid,执行:
bash
hs use 8f3a2c...
hs wait --until plan --timeout 300 --json
hs plan show --json
hs plan confirm --yes --json
hs wait --until done --timeout 900 --json
hs export start --json
hs export status --task <task_id> --json
hs export get --out ./output/test.mp4 --timeout 300 --json
这三条导出命令分工不同,别混用:export start 启动渲染并返回任务 id;export status 用那个 id 查进度;export get 只负责把成片下载到 --out 指定的路径,它不需要任务 id。我一开始给 export get 传了任务 id,参数不认。
单条跑通后,把字段结构记下来,再写批量循环。
状态机与退出码
huasheng-cli 状态机:create → PLANNING →(PAUSED / PLAN_READY)→ PRODUCING → READY,另有 FAILED。
hs wait 的退出码是脚本判断成败的关键:
| 退出码 | 含义 | 处理方式 |
|---|---|---|
| 0 | 成功等到目标状态 | 继续下一步 |
| 非零 | 超时、失败或参数错误 | 记录日志,进入重试队列 |
--timeout 是等待的最长时间(秒),超时返回非零。hs wait --json 会输出最终状态和 next_actions,失败时可解析下一步动作。
bash 批量版:读表、创建、等待、确认、导出
适合几十条以内的小批量:
bash
#!/usr/bin/env bash
set -euo pipefail
INPUT="topics.tsv"
OUTPUT_DIR="output"
mkdir -p "$OUTPUT_DIR"
step() { # 跑一条 hs 命令,失败就打日志并让本条选题跳过
if ! "$@" --json; then
echo " 失败:$*" >&2
return 1
fi
}
tail -n +2 "$INPUT" | while IFS=$'\t' read -r key title script_file _rest; do
echo "==> $key: $title"
pid=$(hs project create --script "@${script_file}" --json | jq -r '.pid // empty')
[[ -z "$pid" ]] && { echo " 创建失败" >&2; continue; }
echo " PID: $pid"
hs use "$pid"
step hs wait --until plan --timeout 300 || continue
step hs plan confirm --yes || continue
step hs wait --until done --timeout 900 || continue
step hs export start || continue
out="${OUTPUT_DIR}/${key}_${title}.mp4"
step hs export get --out "$out" --timeout 300 || continue
echo " 完成:$out"
done
三个细节:tail -n +2 跳过表头;step 把六处重复的错误分支收成一处;确认方案不可撤销,所以批量前一定先手工跑一两条验证文稿写法。
jq 解析要点
统一用 jq 提取字段,别用 grep。三个习惯:读数组用 jq -r '.next_actions[]';判断某个动作是否可用用 jq -e '.next_actions | index("chat answer")' 看退出码;取字段一律带默认值,写成 jq -r '.reason // .error // empty',缺字段时得到空串而不是字面的 null。
Python 批量版:并发与失败重试
bash 版是串行的,几十条以上就嫌慢,而且重试逻辑写起来啰嗦。Python 版只需换掉三个地方:用 subprocess 包一层 hs、用线程池并发、把重试收进装饰器。
要注意一件事:hs use <pid> 是写在 ~/.hs 里的全局当前项目,并发时会互相覆盖。并发版必须每条命令都显式带 --pid,不能靠 hs use。 这是我第一版跑串了两条选题才发现的。
python
#!/usr/bin/env python3
import csv, json, subprocess, time
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
OUT = Path("output"); OUT.mkdir(exist_ok=True)
RETRY = 2
def hs(*args, timeout=300):
"""调一条 hs 命令,返回解析好的 JSON;非零退出码抛异常。"""
p = subprocess.run(["hs", *args, "--json"],
capture_output=True, text=True, timeout=timeout)
if p.returncode != 0:
raise RuntimeError(f"hs {' '.join(args)}: {p.stderr.strip()}")
return json.loads(p.stdout)
def retry(fn, *args, tries=RETRY, **kw):
for i in range(1, tries + 1):
try:
return fn(*args, **kw)
except RuntimeError:
if i == tries:
raise
time.sleep(20 * i) # 退避,别贴着重试
def one(row):
key, title, script_file = row[0], row[1], row[2]
pid = hs("project", "create", "--script", f"@{script_file}")["pid"]
# 并发下不用 hs use,每条命令显式带 --pid
retry(hs, "wait", "--pid", pid, "--until", "plan", "--timeout", "300", timeout=330)
hs("plan", "confirm", "--yes", "--pid", pid)
retry(hs, "wait", "--pid", pid, "--until", "done", "--timeout", "900", timeout=930)
hs("export", "start", "--pid", pid)
hs("export", "get", "--pid", pid, "--out", str(OUT / f"{key}_{title}.mp4"),
"--timeout", "300", timeout=330)
return key, pid
with open("topics.tsv", newline="", encoding="utf-8") as f:
rows = list(csv.reader(f, delimiter="\t"))[1:]
with ThreadPoolExecutor(max_workers=3) as pool:
futs = {pool.submit(one, r): r for r in rows}
for fu in as_completed(futs):
row = futs[fu]
try:
key, pid = fu.result()
print(f"完成 {key} pid={pid}")
except Exception as e:
print(f"失败 {row[0]}: {e}") # 记下来单独续跑,别整批重来
并发数别开太大,成片是服务端排队的,本地开十个线程也不会更快。断点续跑靠 --pid:哪条失败了,把它的 pid 记下来,之后 hs wait --pid <pid> --until done 再 hs export get --pid <pid> 就能接着取,不必重新 create。
FFmpeg 批量后处理:片头、转码、压制
huasheng-cli 导出 mp4 后,统一加片头、压制参数、分辨率交给 FFmpeg。
批量添加片头:
bash
mkdir -p final
for f in output/*.mp4; do
ffmpeg -y -i intro.mp4 -i "$f" \
-filter_complex "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" \
-c:v libx264 -crf 23 -preset fast -c:a aac \
"final/$(basename "$f")"
done
统一分辨率把 -filter_complex 换成 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2",加水印换成 overlay,循环骨架不变,这里不再展开。
分工边界与周边开源工具
huasheng-cli 解决从文稿到成片的创作过程:分镜、画面编排、配音、合成。FFmpeg 解决对已有视频文件的处理:拼接、转码、分辨率调整、加水印、抽帧。两者阶段不同,不要用 FFmpeg 生成视频内容,也不要用 huasheng-cli 做转码压制。
周边工具:
- yt-dlp :下载公开视频素材,之后可用
hs material add --url <公网地址>登记素材。注意material add是按公网 URL 登记你自己的素材,不是全网搜索下载。 - whisper :把音频或视频文稿转成文本,再交给 huasheng-cli。
hs clip srt也可导 SRT 字幕,两者互补。 - jq :解析 JSON,所有
--json输出都靠它。
诚实局限
huasheng-cli 做不到什么,实测边界如下:
- 文稿与素材会上传到服务端用于生成视频。虽然有凭据隔离,但你需要知道数据去向。它没有独立遥测,也没有后台自动更新。
- 确认方案不可撤销。
hs plan confirm --yes执行后开始成片,不能反悔。hs publish --submit会让内容公开,必须亲自确认。批量脚本里确认方案不要加自动逻辑。 - 它不做全网素材检索。
material add --url只是登记你自己提供的公网 URL,画面由服务端在分镜环节配置。 - 主要是中文创作场景。外语文稿的配音和分镜效果没有大量测试,不保证。
- 仓库很新,star 数少(两位数),且没有 LICENSE 文件。二次分发前要先问作者。
- Windows 包未签名,首次运行会触发 SmartScreen 提示。客户端找不到
hs时要填绝对路径(Windows 上对应where hs)。
这几条里,第 2 条对批量场景影响最大:脚本一旦自动确认方案,错的文稿也会照做下去。
踩坑记录
- 路径问题 :
hs use <pid>会把状态存到~/.hs下,cron 或定时任务里要确认HOME可写。hs export get --out建议用绝对路径。 - 等待超时 :成片时间不固定,
hs wait --until done --timeout 900有时会超时,但项目其实还在PRODUCING。不要立即重新 create,先hs use <pid>再hs wait --until done --timeout 300续等。
总结
2026 年,我用 huasheng-cli 的 --json 输出与退出码,配合 jq 解析,把一张选题表批量跑成视频文件。流水线:选题表 → project create → wait until plan → plan confirm --yes → wait until done → export get,之后用 FFmpeg 做片头、转码、压制。断点续跑靠 hs use <pid> 和 --pid,失败诊断靠 next_actions 与 FAILED 的 reason 字段。这套流程需要分步操作,但每一步都结构化、可重试,适合有命令行基础的内容生产者把重复劳动交给脚本。