当你的 YouTube 频道每月需要产出 8-12 集 40 到 80 分钟的长篇视频时,每一集的压制、字幕合成和上传前检查都是重复劳动。作为一个信奉”能用脚本解决的事情绝不动手”的效率主义者,我建立了一套基于 Shell 和 FFmpeg 的全自动视频后处理管线。
管线概览
整个管线包含四个阶段:
- 输入检查:验证所有素材文件(视频、字幕、封面)是否存在且格式正确
- 预处理:SRT 字幕清洗、时间轴修复、简繁转换
- 多码率压制:同时生成 1080p 和 720p 两个版本
- 归档与通知:移至成品目录、更新数据库、发送完成通知
阶段一:输入检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #!/bin/bash
EPISODE_DIR="$1" REQUIRED_FILES=("raw.mp4" "subtitle.srt" "cover.png")
for file in "${REQUIRED_FILES[@]}"; do if [ ! -f "$EPISODE_DIR/$file" ]; then echo "❌ 缺少文件: $file" exit 1 fi done
DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$EPISODE_DIR/raw.mp4") echo "✅ 素材完整,视频时长: ${DURATION}秒"
|
阶段二:字幕预处理
原始字幕文件常有格式问题:时间轴重叠、空行、编码错误。用一个 Python 脚本做预处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import re, sys
def clean_srt(filepath): with open(filepath, "r", encoding="utf-8") as f: content = f.read() content = re.sub(r"\n{3,}", "\n\n", content) content = re.sub(r"(\d{2}:\d{2}:\d{2}),(\d{3})", r"\1.\2", content) with open(filepath, "w", encoding="utf-8") as f: f.write(content) print(f"✅ 字幕清洗完成: {filepath}")
if __name__ == "__main__": clean_srt(sys.argv[1])
|
阶段三:多码率并行压制
利用 GNU Parallel 同时压制 1080p 和 720p 两个版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #!/bin/bash
INPUT="$1/raw.mp4" SUBTITLE="$1/subtitle_clean.srt" OUTPUT_DIR="$1/output" mkdir -p "$OUTPUT_DIR"
render() { local res="$1" local height="$2" local bitrate="$3" ffmpeg -hwaccel cuda -hwaccel_output_format cuda \ -i "$INPUT" \ -vf "scale_npp=-2:$height,subtitles=$SUBTITLE:force_style='FontSize=$((height/36))'" \ -c:v h264_nvenc -preset p4 -cq 23 -b:v "$bitrate" \ -c:a aac -b:a 128k \ -movflags +faststart \ "$OUTPUT_DIR/${res}.mp4" }
export -f render export INPUT SUBTITLE OUTPUT_DIR
parallel --line-buffer render ::: "1080p 1080 6M" "720p 720 3M"
echo "✅ 压制完成"
|
GNU Parallel 让两个版本的压制同时进行,充分利用多核 CPU + GPU,总耗时接近单版本时间而非两倍。
阶段四:归档与通知
1 2 3 4 5 6 7 8 9 10
| #!/bin/bash
EPISODE_DIR="$1" EPISODE_NAME=$(basename "$EPISODE_DIR")
mv "$EPISODE_DIR/output" "/mnt/media/final/$EPISODE_NAME"
echo "✅ 归档完成: $EPISODE_NAME"
|
如需通知,可以接入 Telegram Bot 或企业微信 Webhook。
统一调度入口
1 2 3 4 5 6 7 8 9 10 11 12
| #!/bin/bash
EPISODE_DIR="$1" set -e
./preflight.sh "$EPISODE_DIR" python3 subtitle_cleaner.py "$EPISODE_DIR/subtitle.srt" ./parallel-render.sh "$EPISODE_DIR" ./archive-and-notify.sh "$EPISODE_DIR"
echo "🎉 管线执行完毕!"
|
使用时只需:bash pipeline.sh ./episodes/ep42-chapter-5
管线监控与异常处理
生产环境中的批量管线不能是”一锤子买卖”——你必须知道它是什么时候失败的、为什么失败。建议在每个阶段出口添加状态码检查和告警。
在 pipeline.sh 中加入错误捕获和日志记录,每条管线执行都留下完整轨迹。当某一集处理失败时,不是整条管线崩溃,而是将该集标记为”失败”并继续处理下一集——这样不会因为一集的问题阻塞整个生产排期。
字幕质量的最后一道防线
自动生成的英文字幕虽然有 PowerShell 脚本做断句优化,但在正式压制之前,建议跑一遍拼写检查。Linux 上的 aspell 或 Python 的 pyspellchecker 都能做批量检查。我通常会在字幕清洗脚本末尾加入拼写验证环节,自动标记可疑单词供人工复核——这比纯手动逐行校对效率高出 10 倍以上。
素材管理的数据库化
随着集数越来越多,分散在文件夹里的视频素材会变成灾难。建议用一个轻量级 SQLite 数据库来管理所有素材的元数据:文件名、时长、状态、关联字幕文件、最后处理时间。这样当你需要”找到所有包含某个历史人物的视频”或”重新压制最近 30 天的失败集数”时,一条 SQL 查询就能搞定,而不是在文件管理器里大海捞针。
总结
这套管线从手工每集 2 小时的工作量,压缩到了 15 分钟全自动(主要是 GPU 压制时间)。投入前期写脚本的一个下午,换来的是后续无数个”不用操心”的日日夜夜。这就是自动化真正的价值——不是替代创造力,而是让创造力不再被琐碎的重复劳动所消耗。