typecho 发布的文章

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

使用 Claude Code 构建这套系统极其高效。你不需要自己从零手写每一行音频对齐与 FFmpeg 命令,而是利用 Claude Code 的 Agent 级终端能力(自动读写代码、运行 CLI、捕获终端报错并自行修 Bug),直接在终端让它帮你搭建并打通整个 Python 自动化管道。项目整体架构系统主要包含三个核心模块:Plaintext[原始视频 + 外语 SRT]

   │
   ▼
  1. 智能翻译模块 (LLM / API) ──► 将 SRT 文本批量翻译为自然中文,保持时间戳不变

    │
    ▼
  2. 配音与对齐引擎 (edge-tts + pydub) ──► 逐句生成中文语音,超长自动加速,补齐静音

    │
    ▼
  3. 视频合成引擎 (FFmpeg) ──► 替换原视频音轨,压制硬字幕,导出 final_output.mp4
    使用 Claude Code 落地搭建步骤1.1. 创建工作区与基础环境:耗时约 2 分钟。在终端中创建一个独立文件夹,准备好原始视频 video.mp4 和外语字幕 sub.srt,然后启动 Claude Code:Bashmkdir video-auto-translator && cd video-auto-translator

    放入你的测试文件 video.mp4 和 sub.srt

启动 Claude Code

claude
2.2. 向 Claude Code 下达构建 Prompt:让 Agent 自动编写完整管道。在 claude 交互终端中,直接复制输入以下提示词:给 Claude Code 的 Prompt:“请在这个目录下帮我从零搭建一个 Python 自动化视频翻译与配音 CLI 工具 auto_dub.py。需求如下:1. 依赖安装:自动检查并用 pip 安装 pysrt、edge-tts、pydub、openai。2. 字幕翻译:读取传入的外语 SRT 字幕,调用 OpenAI API(使用环境变量 OPENAI_API_KEY)将字幕文本批量翻译为自然的中文口语,保留原始时间戳并导出 zh_sub.srt。3. 动态音频对齐:使用 edge-tts(默认音色 zh-CN-YunxiNeural)逐句生成中文配音。计算生成的音频时长:如果大于字幕时间窗,调用 pydub 进行 speedup 动态加速(限速最高 1.4 倍);如果前后有空隙,补齐静音段。4. FFmpeg 视频合成:拼接完整中文音轨,调用 FFmpeg 替换视频原有音频,并将 zh_sub.srt 烧录到视频中,导出为 output.mp4。5. 测试与修复:编写完代码后,请自动执行 python auto_dub.py --video video.mp4 --srt sub.srt --output output.mp4 进行测试。如果遇到 FFmpeg 路径、asyncio 事件循环或 pydub 类型错误,请读取终端报错日志并自行修改代码,直到测试通过。”3.3. 观察 Claude Code 的自主执行与自纠错:全自动无人值守执行。Claude Code 会开始在终端中自主完成以下动作:创建文件:生成 auto_dub.py 代码框架。环境配置:自动运行 pip install 补充依赖。测试运行:调用 python auto_dub.py ... 触发第一版脚本。自动捕获报错并修复:例如如果遇到 edge-tts 在异步循环中的报错或 FFmpeg 的字符转义问题,Claude Code 会实时拦截日志、修正代码并再次重试,直到生成最终视频。核心生成的脚本逻辑 (auto_dub.py 源码参考)以下是 Claude Code 在后台为你生成并调优的核心 Python 源码参考:Pythonimport argparse
import asyncio
import os
import sys
import pysrt
from pydub import AudioSegment
import edge_tts
from openai import OpenAI

def translate_srt_to_chinese(srt_path, output_zh_srt_path):

client = OpenAI(
    api_key=os.environ.get("OPENAI_API_KEY"),
    base_url=os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1")
)
subs = pysrt.open(srt_path, encoding='utf-8')
texts = [f"{i}: {sub.text.replace('\n', ' ')}" for i, sub in enumerate(subs)]

prompt = "请将以下字幕翻译为通顺自然的中文口语,保持原有的编号格式不变:\n" + "\n".join(texts)
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": prompt}]
)

translated_lines = response.choices[0].message.content.strip().split("\n")
zh_map = {}
for line in translated_lines:
    if ":" in line:
        parts = line.split(":", 1)
        try:
            zh_map[int(parts[0].strip())] = parts[1].strip()
        except ValueError:
            continue

for idx, sub in enumerate(subs):
    sub.text = zh_map.get(idx, sub.text)

subs.save(output_zh_srt_path, encoding='utf-8')
print(f"[✓] 中文字幕已生成: {output_zh_srt_path}")
return subs

async def generate_aligned_audio(subs, voice, output_audio_path):

final_audio = AudioSegment.silent(duration=0)
current_time_ms = 0

for idx, sub in enumerate(subs):
    start_ms = sub.start.ordinal
    end_ms = sub.end.ordinal
    max_duration_ms = end_ms - start_ms
    text = sub.text.strip()

    if not text:
        continue

    # 1. 补齐句间空白静音
    if start_ms > current_time_ms:
        silence_gap = start_ms - current_time_ms
        final_audio += AudioSegment.silent(duration=silence_gap)
        current_time_ms = start_ms

    # 2. 生成单句中文 TTS
    temp_file = f"temp_{idx}.mp3"
    await edge_tts.Communicate(text, voice).save(temp_file)
    segment = AudioSegment.from_file(temp_file)

    # 3. 超长时自动变速 (防止重叠下一句)
    if max_duration_ms > 0 and len(segment) > max_duration_ms:
        speed_factor = len(segment) / max_duration_ms
        speed_factor = min(speed_factor, 1.4)  # 最高加速 1.4 倍
        segment = segment.speedup(playback_speed=speed_factor)

    final_audio += segment
    current_time_ms += len(segment)

    if os.path.exists(temp_file):
        os.remove(temp_file)

final_audio.export(output_audio_path, format="mp3")
print(f"[✓] 对齐音频已生成: {output_audio_path}")

def merge_video(video_path, audio_path, srt_path, output_path):

# 使用 FFmpeg 替换原音轨并压制硬字幕
cmd = (
    f'ffmpeg -y -i "{video_path}" -i "{audio_path}" '
    f'-vf "subtitles=\'{srt_path}\'" '
    f'-c:v libx264 -c:a aac -map 0:v:0 -map 1:a:0 "{output_path}"'
)
os.system(cmd)
print(f"[✓] 成品视频已导出: {output_path}")

def main():

parser = argparse.ArgumentParser(description="Auto Translate and Dub Video")
parser.add_argument("--video", required=True, help="原始视频路径")
parser.add_argument("--srt", required=True, help="原始字幕路径")
parser.add_argument("--output", default="output.mp4", help="导出视频路径")
parser.add_argument("--voice", default="zh-CN-YunxiNeural", help="TTS 音色")
args = parser.parse_args()

zh_srt_path = "temp_zh.srt"
temp_audio_path = "temp_dub.mp3"

subs = translate_srt_to_chinese(args.srt, zh_srt_path)
asyncio.run(generate_aligned_audio(subs, args.voice, temp_audio_path))
merge_video(args.video, temp_audio_path, zh_srt_path, args.output)

# 清理中间音频
if os.path.exists(temp_audio_path):
    os.remove(temp_audio_path)

if name == "__main__":

main()

随时日常调用后续当你需要翻译配音新视频时,只需在终端唤醒 Claude Code 或直接运行脚本即可:Bashpython auto_dub.py --video lesson.mp4 --srt lesson.srt --output lesson_chinese.mp4 --voice

将生成的字幕文件(SRT/VTT)转成语音并实现时间轴自动对齐,核心逻辑在于解决“语速不一致”与“句间空白”:生成的语音如果太长会重叠下一句,如果太短则需要补空白静音。目前最主流的实现方式分为零代码可视化方案(适合快速出片)和 Python 自动化代码方案(适合批处理与深度集成)。方案一:剪映 / CapCut(零代码、最快捷)如果你只是想快速生成视频配音,剪映提供了内置的“字幕一键朗读与对齐”能力:导入字幕:打开剪映,将 .srt 字幕文件直接拖入轨道。 批量选定字幕:在时间轴框选所有字幕片段(或按 Ctrl+A)。 开启文本朗读:点击右侧面板的 “文本” -> “文本朗读”,选择喜欢的 AI 音色(如“普通话女声”、“讲解男声”)。 自动对齐:勾选“朗读跟随文本”,剪映会自动为每一句字幕生成对应的音频块,并自动约束音频长度在字幕对应的时间范围内(超长时自动变快语速,超短时留空)。方案二:Python 自动化管线(免费高音色、支持批量)如果你需要将此功能集成到你的自动化剪辑流水线中,可以使用 Microsoft Edge-TTS(免费且音色极自然)配合 pydub / FFmpeg 进行时长匹配与静音填充。核心处理逻辑1.1. 解析 SRT 时间轴:读取 SRT。提取每句字幕的 开始时间、结束时间 以及 文本内容,计算出当前句子的最大可用时长区间(end_time - start_time)。2.2. 插入句间静音:对齐起始时间点。比较当前字幕的 start_time 与上一句结尾时间。若存在空隙,在主音轨中追加对应毫秒数(start_time - current_time)的空白静音段。3.3. 生成 TTS 语音并检测时长:调用 Edge-TTS。将文本传入 TTS 引擎生成临时语音文件,并计算生成的实际音频时长 audio_duration。4.4. 动态变速与追加拼接:消除时间轴溢出。若 audio_duration > max_duration,调用 FFmpeg / pydub 的 speedup 算法,按比例提速音频(变速不变调),确保不压到下一句时间;随后拼接入主音轨。完整 Python 脚本代码Pythonimport asyncio
import os
import pysrt
from pydub import AudioSegment
import edge_tts

async def srt_to_speech(srt_file, output_mp3, voice="zh-CN-XiaoxiaoNeural"):

subs = pysrt.open(srt_file, encoding='utf-8')
final_audio = AudioSegment.silent(duration=0)
current_time_ms = 0

for idx, sub in enumerate(subs):
    start_ms = sub.start.ordinal
    end_ms = sub.end.ordinal
    max_duration_ms = end_ms - start_ms
    text = sub.text.strip().replace('\n', ' ')

    if not text:
        continue

    # 1. 补齐上一句结束到本句开始之间的静音区间
    if start_ms > current_time_ms:
        silence_gap = start_ms - current_time_ms
        final_audio += AudioSegment.silent(duration=silence_gap)
        current_time_ms = start_ms

    # 2. 调用 Edge-TTS 生成单句音频
    temp_file = f"temp_{idx}.mp3"
    communicate = edge_tts.Communicate(text, voice)
    await communicate.save(temp_file)

    segment = AudioSegment.from_file(temp_file)
    seg_duration_ms = len(segment)

    # 3. 若生成的音频超出时间窗,自动进行变速不变调压缩
    if max_duration_ms > 0 and seg_duration_ms > max_duration_ms:
        speed_factor = seg_duration_ms / max_duration_ms
        # 限制最大加速倍率(如最高1.5倍速),避免语速过快失真
        speed_factor = min(speed_factor, 1.5)
        segment = segment.speedup(playback_speed=speed_factor)

    # 4. 追加音频段并更新全局时间轴
    final_audio += segment
    current_time_ms += len(segment)

    # 清理临时文件
    if os.path.exists(temp_file):
        os.remove(temp_file)

# 导出合成好的完整对齐音频
final_audio.export(output_mp3, format="mp3")
print(f"转换成功!生成音频已保存至: {output_mp3}")

依赖安装:pip install edge-tts pysrt pydub

运行示例:

if name == "__main__":

asyncio.run(srt_to_speech("input.srt", "output_synced.mp3"))

方案三:命令行直接转换工具(开箱即用)如果你不想自己写代码,开源社区有现成的 CLI 工具可以直接将 SRT 转为时间轴对齐的语音:edge-srt-to-speech (PyPI 库) Bashpip install edge-srt-to-speech

将 srt 转换为时间轴完全对齐的 mp3 音频

edge-srt-to-speech input.srt output.mp3 --voice zh-CN-YunxiNeural
三种方案如何选择?方案优势适用场景剪映 / CapCut可视化预览、音色丰富、支持画面同步微调个人短视频剪辑、二次人工校验Python 自定义脚本逻辑可控、可定制最大倍速与声音参数融入自建的 Codex / FFmpeg 视频自动化处理流水线edge-srt-to-speech 命令行免写代码,一条命令处理完服务器后台批处理任务、Shell 脚本集成

在 CentOS 7 中,使用 Systemd Timer 代替 Crontab 是更现代、可控性更高的方案。相较于 Cron,Systemd Timer 具备以下核心优势:独立且统一的日志收集:所有输出和报错(stdout/stderr)自动由 journalctl 收集,无需手动做 >> log.txt 日志重定向。精准的环境控制:可以直接在 Service 中定义工作目录(WorkingDirectory)、运行用户(User)和特定环境变量。错过补执行机制(Persistent):如果服务器在原定触发时间处于关机状态,开机后可以自动补执行错过的任务。Systemd 采用双单元机制:需要建立一个 .service 文件定义“做什么”,以及一个 .timer 文件定义“何时触发”。搭建与配置流程1.1. 创建 Service 服务单元 (.service):定义要执行的 Python 脚本。在 /etc/systemd/system/ 目录下创建一个名为 my-python-task.service 的文件:Ini, TOML# /etc/systemd/system/my-python-task.service
[Unit]
Description=My Scheduled Python Script Service
After=network.target

[Service]
Type=oneshot
User=root
WorkingDirectory=/home/user/project
ExecStart=/usr/bin/python3 /home/user/project/myscript.py

如果使用了 Python 虚拟环境,直接将 ExecStart 指向虚拟环境内的解释器:

ExecStart=/home/user/venv/bin/python /home/user/project/myscript.py

[Install]
WantedBy=multi-user.target
2.2. 创建 Timer 定时单元 (.timer):定义触发的时间规则。在同一目录下创建前缀同名的 my-python-task.timer 文件:Ini, TOML# /etc/systemd/system/my-python-task.timer
[Unit]
Description=Run My Python Task Periodically

[Timer]

1. 设定触发时间规则(例如:每天凌晨 02:00 运行)

OnCalendar=--* 02:00:00

2. 若关机错过了执行时间,开机后是否立即补执行一次

Persistent=true

显式绑定要触发的 service(如果文件名同名,此项可省略)

Unit=my-python-task.service

[Install]
WantedBy=timers.target
3.3. 加载并激活 Timer:重新加载 Systemd 配置并启动定时器。文件建立完毕后,需要刷新 Systemd 守护进程并启用该 Timer:Bash# 重新加载 Systemd 配置文件
systemctl daemon-reload

启动 Timer 并设置开机自启(注意是启动 .timer 而非 .service)

systemctl enable --now my-python-task.timer
4.4. 检查运行状态与手动测试:验证定时器与服务是否生效。可以通过以下命令验证 Timer 状态,并手动测试脚本运行是否正常:Bash# 1. 查看当前系统中所有活着的 Timer 列表及其下次触发时间
systemctl list-timers

2. 查看 Timer 自身的激活状态

systemctl status my-python-task.timer

3. 手动触发一次 Service(用于测试脚本本身能否正常正常跑通)

systemctl start my-python-task.service
常用 OnCalendar 时间语法Systemd 提供了非常人性化的时间表达式:需求OnCalendar 写法示例每 5 分钟OnCalendar=:0/5每小时整点OnCalendar=hourly 或 -- :00:00每天凌晨 2 点OnCalendar=daily 或 -- 02:00:00每周一上午 8:30OnCalendar=Mon -- 08:30:00每月 1 号 00:00OnCalendar=monthly 或 -*-01 00:00:00日志查看与故障排查Systemd 自动通过 journald 收集该任务的所有日志(包括 Python 脚本里的 print() 输出与异常抛出):Bash# 查看该 Python 服务的完整历史运行日志
journalctl -u my-python-task.service

实时追踪该服务的最新日志(类似 tail -f)

journalctl -u my-python-task.service -f -n 50

查看最近一次失败日志

journalctl -u my-python-task.service -p err

在 CentOS 7 中,最标准且稳定的方式是使用系统自带的 Cron (crontab) 服务。由于 Cron 运行时的环境变量与普通终端不同,配置时的关键在于:必须全部使用绝对路径,并建议重定向日志输出以便排查错误。操作步骤1.确认 Cron 服务已启动:耗时约 10 秒。在终端检查 crond 服务状态,确保其正常运行并开机自启:Bash# 查看服务状态
systemctl status crond

如果未运行,启动服务并设置开机自启

systemctl start crond
systemctl enable crond
2.获取 Python 和脚本的绝对路径:避免因环境变量导致找不到命令。Cron 默认只在极简的环境变量中运行,直接写 python 或相对路径容易报错。请在终端查询绝对路径:Bash# 查找 Python 绝对路径(例如:/usr/bin/python3 或虚拟环境中的路径)
which python3

进入脚本所在目录获取完整路径

cd /path/to/your_project
pwd
假设你的 Python 路径为 /usr/bin/python3,脚本绝对路径为 /home/user/myscript.py。3.编辑 Cron 任务配置:打开当前用户的 crontab 配置文件。运行以下命令进入编辑界面(默认使用 vi/vim 编译器):Bashcrontab -e
4.添加定时任务规则并保存:配置执行周期与重定向日志。按 i 键进入插入模式,在文件末尾添加你的定时任务规则(强烈建议添加日志输出):Bash# 语法格式:

分 时 日 月 周 /Python绝对路径 /脚本绝对路径 >> /日志绝对路径 2>&1

示例:每天凌晨 2:00 执行 Python 脚本,并将运行日志存入 cron_run.log

0 2 * /usr/bin/python3 /home/user/myscript.py >> /home/user/cron_run.log 2>&1
编辑完成后,按 Esc 退出插入模式,输入 :wq 保存并退出。常用时间配置示例列表Cron 的时间表达式由 5 个占位符组成:分 时 日 月 周。表达式执行频率/5 每 5 分钟执行一次0 每小时的整点执行一次0 2 每天凌晨 2:00 执行30 8 1每周一早上 8:30 执行0 0 1 每月 1 号 midnight (00:00) 执行避坑指南(关键细节)使用了 Python 虚拟环境 (venv / conda)如果你使用了虚拟环境,不需要在 Cron 里写 source activate,直接将路径指向虚拟环境中的 Python 解释器即可:Bash0 2 * /home/user/venv/bin/python /home/user/myscript.py >> /home/user/cron.log 2>&1
脚本内部涉及相对文件路径如果 Python 脚本内部有 open('data.json') 这类读取相对路径的代码,Cron 执行时可能会找不到文件。建议在 Python 脚本开头加上:Pythonimport os
import sys

强制将工作目录切换为当前脚本所在的目录

os.chdir(os.path.dirname(os.path.abspath(__file__)))
如何查看任务是否被触发与排查错误查看系统的 Cron 执行日志:tail -f /var/log/cron查看脚本自己的输出/报错日志:cat /home/user/cron_run.log查看当前用户的所有定时任务:crontab -l