#!/usr/bin/env python3 """ Script to SRT with Timing · 剧本到 SRT 时间轴生成器 ==========================​===================== 用剧本台词和配音时长生成正式 SRT,别手写测试时间轴。 功能: 1. 读取剧本台词(JSON/MD/TXT) 2. 读取配音文件(获取每条台词的实际时长) 3. 自动计算时间轴(考虑角色对话间隔) 4. 生成标准 SRT 字幕文件 5. 支持多角色、旁白、音效标注 依赖: pip install pysrt 输入格式: # 剧本 JSON 格式(推荐) [ {"id": 1, "character": "苏白", "text": "付费才能修仙?", "audio_file": "./audio/subai_001.wav"}, {"id": 2, "character": "旁白", "text": "天道宗门口", "audio_file": "./audio/narrator_001.wav"} ] # 剧本 MD 格式(兼容) ## E1-SHOT01 **苏白**:付费才能修仙?(audio: subai_001.wav) **旁白**:天道宗门口(audio: narrator_001.wav) 输出 SRT 格式: 1 00:00:01,000 --> 00:00:03,500 付费才能修仙? 2 00:00:03,800 --> 00:00:05,200 天道宗门口 用法: # 从剧本 JSON + 配音文件生成 SRT python script-to-srt-with-timing.py --script script.json --output subtitles.srt # 指定配音目录(自动匹配 audio_file) python script-to-srt-with-timing.py --script script.json --audio-dir ./audio/ --output subtitles.srt # 从 MD 剧本生成 python script-to-srt-with-timing.py --script script.md --output subtitles.srt # 自定义对话间隔(秒) python script-to-srt-with-timing.py --script script.json --gap 0.3 --output subtitles.srt 路径: video-ai-system/engines/subtitle-pipeline/srt-tools/script-to-srt-with-timing.py """ import argparse import json import os import sys import re from pathlib import Path try: import pysrt except ImportError: print("[ERROR] 缺少依赖:pysrt") print("请先安装:pip install pysrt") sys.exit(1) def parse_script_json(script_path: str) -> list: """ 解析剧本 JSON 文件 :param script_path: 剧本 JSON 文件路径 :return: 台词列表 [{"id", "character", "text", "audio_file", "start", "end"}] """ with open(script_path, "r", encoding="utf-8") as f: script = json.load(f) print(f"[INFO] 读取剧本 JSON:{len(script)} 条台词") return script def parse_script_md(script_path: str) -> list: """ 解析剧本 MD 文件(兼容格式) :param script_path: 剧本 MD 文件路径 :return: 台词列表 """ with open(script_path, "r", encoding="utf-8") as f: lines = f.readlines() script = [] current_id = 0 for line in lines: # 匹配:**角色**:台词(audio: file.wav) match = re.match(r"\*\*(.+?)\*\*[::]\s*(.+?)\s*\(audio:\s*(.+?)\)", line.strip()) if match: character = match.group(1).strip() text = match.group(2).strip() audio_file = match.group(3).strip() script.append({ "id": current_id + 1, "character": character, "text": text, "audio_file": audio_file }) current_id += 1 print(f"[INFO] 读取剧本 MD:{len(script)} 条台词") return script def get_audio_duration(audio_path: str) -> float: """ 获取音频文件时长(秒) :param audio_path: 音频文件路径 :return: 时长(秒) """ try: import ffmpeg probe = ffmpeg.probe(audio_path) duration = float(probe['format']['duration']) return duration except ImportError: # 如果没有 ffmpeg-python,使用 ffprobe 命令行 try: import subprocess cmd = [ "ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", audio_path ] result = subprocess.run(cmd, capture_output=True, text=True, check=True) probe = json.loads(result.stdout) duration = float(probe['format']['duration']) return duration except Exception as e: print(f"[WARN] 无法获取音频时长:{audio_path} ({e})") # 估算:中文普通话约 4-6 字/秒 return 2.0 # 默认 2 秒 except Exception as e: print(f"[WARN] 无法获取音频时长:{audio_path} ({e})") return 2.0 def calculate_timings(script: list, audio_dir: str = None, gap: float = 0.2) -> list: """ 计算时间轴 :param script: 台词列表 :param audio_dir: 配音文件目录(如果 audio_file 是相对路径) :param gap: 对话间隔(秒) :return: 带时间轴的台词列表 """ current_time = 0.0 for item in script: # 获取配音文件时长 audio_file = item.get("audio_file") if audio_file and os.path.isfile(audio_file): duration = get_audio_duration(audio_file) elif audio_file and audio_dir: audio_path = os.path.join(audio_dir, audio_file) if os.path.isfile(audio_path): duration = get_audio_duration(audio_path) else: # 估算时长 text = item.get("text", "") duration = len(text) / 5.0 # 假设 5 字/秒 else: # 没有配音文件,估算时长 text = item.get("text", "") duration = len(text) / 5.0 # 假设 5 字/秒 # 设置开始和结束时间 item["start"] = current_time item["end"] = current_time + duration # 更新当前时间(加上间隔) current_time = item["end"] + gap print(f"[INFO] 时间轴计算完成:总时长 {current_time:.2f} 秒") return script def generate_srt(script: list, output_path: str): """ 生成 SRT 字幕文件 :param script: 带时间轴的台词列表 :param output_path: 输出 SRT 文件路径 """ # 创建 pysrt SubRipFile subs = pysrt.SubRipFile() for item in script: # 创建字幕项 sub = pysrt.SubRipItem() sub.index = item["id"] sub.text = item["text"] # 设置时间 start_seconds = item["start"] end_seconds = item["end"] sub.start.hours = int(start_seconds // 3600) sub.start.minutes = int((start_seconds % 3600) // 60) sub.start.seconds = int(start_seconds % 60) sub.start.milliseconds = int((start_seconds % 1) * 1000) sub.end.hours = int(end_seconds // 3600) sub.end.minutes = int((end_seconds % 3600) // 60) sub.end.seconds = int(end_seconds % 60) sub.end.milliseconds = int((end_seconds % 1) * 1000) subs.append(sub) # 保存 SRT 文件 os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) subs.save(output_path, encoding="utf-8") print(f"[OK] SRT 字幕文件已生成:{output_path}") print(f"[INFO] 共 {len(script)} 条字幕") def main(): parser = argparse.ArgumentParser( description="Script to SRT with Timing · 剧本到 SRT 时间轴生成器", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 示例: python script-to-srt-with-timing.py --script script.json --output subtitles.srt python script-to-srt-with-timing.py --script script.md --audio-dir ./audio/ --output subtitles.srt python script-to-srt-with-timing.py --script script.json --gap 0.3 --output subtitles.srt """ ) parser.add_argument("--script", required=True, help="剧本文件路径(JSON 或 MD 格式)") parser.add_argument("--audio-dir", help="配音文件目录(如果 audio_file 是相对路径)") parser.add_argument("--output", required=True, help="输出 SRT 文件路径") parser.add_argument("--gap", type=float, default=0.2, help="对话间隔(秒,默认 0.2)") args = parser.parse_args() # 检查剧本文件是否存在 if not os.path.isfile(args.script): print(f"[ERROR] 剧本文件不存在:{args.script}") sys.exit(1) # 解析剧本 if args.script.lower().endswith(".json"): script = parse_script_json(args.script) elif args.script.lower().endswith(".md"): script = parse_script_md(args.script) else: print(f"[ERROR] 不支持的剧本格式:{args.script}") print("支持格式:.json, .md") sys.exit(1) if not script: print("[ERROR] 剧本为空,请检查格式") sys.exit(1) # 计算时间轴 script = calculate_timings(script, args.audio_dir, args.gap) # 生成 SRT 文件 generate_srt(script, args.output) print("\n[OK] 处理完成") if __name__ == "__main__": main()