feat(12-01): implement SRT parser module

- Add Segment dataclass for subtitle segments
- Add parse_srt() function to parse SRT content string
- Add parse_srt_file() function to parse from file path
- Support standard SRT format with timestamps
- Handle special characters and multi-line subtitles

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 23:32:57 +08:00
parent 7023a57644
commit c259e0ac2f
2 changed files with 186 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
"""SRT Parser Module
解析 SRT 字幕文件格式,将字幕转换为结构化的 Segment 对象。
SRT 格式示例:
1
00:00:00,000 --> 00:00:05,000
第一句字幕内容
2
00:00:05,000 --> 00:00:10,000
第二句字幕内容
"""
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
from typing import List
@dataclass
class Segment:
"""字幕分段数据结构
Attributes:
id: 字幕序号
start_time: 开始时间(秒)
end_time: 结束时间(秒)
text: 字幕文本
"""
id: int
start_time: float
end_time: float
text: str
# SRT 块正则表达式
# 格式: 序号\n时间码\n文本(可能多行)\n\n
SRT_BLOCK_PATTERN = re.compile(
r'(\d+)\s*\n'
r'(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})\s*\n'
r'(.*?)(?=\n\n|\Z)',
re.DOTALL
)
def parse_srt_time(time_str: str) -> float:
"""解析 SRT 时间格式 (HH:MM:SS,mmm) 为秒数
Args:
time_str: SRT 时间字符串,如 "00:05:30,500"
Returns:
秒数,如 330.5
Example:
>>> parse_srt_time("00:05:30,500")
330.5
>>> parse_srt_time("01:02:03,004")
3723.004
"""
# 分割时间和毫秒
main_part, millis = time_str.rsplit(',', 1)
hours, minutes, seconds = main_part.split(':')
total_seconds = (
int(hours) * 3600 +
int(minutes) * 60 +
int(seconds) +
int(millis) / 1000
)
return total_seconds
def parse_srt(content: str) -> List[Segment]:
"""解析 SRT 格式字符串
将 SRT 格式的字幕内容解析为 Segment 对象列表。
Args:
content: SRT 格式的字符串内容
Returns:
Segment 对象列表
Raises:
ValueError: 内容为空或格式无效
Example:
>>> content = "1\\n00:00:00,000 --> 00:00:05,000\\n第一句\\n\\n"
>>> segments = parse_srt(content)
>>> len(segments)
1
>>> segments[0].text
'第一句'
"""
# 检查空内容
if not content or not content.strip():
raise ValueError("Empty SRT content")
segments = []
# 查找所有 SRT 块
matches = SRT_BLOCK_PATTERN.findall(content)
if not matches:
raise ValueError("Invalid SRT format: no valid blocks found")
for match in matches:
try:
segment_id = int(match[0])
start_time = parse_srt_time(match[1])
end_time = parse_srt_time(match[2])
# 清理文本:移除前后空白,合并多行为单行
text = ' '.join(match[3].strip().split())
segment = Segment(
id=segment_id,
start_time=start_time,
end_time=end_time,
text=text
)
segments.append(segment)
except (ValueError, IndexError) as e:
# 跳过解析失败的块
continue
if not segments:
raise ValueError("Invalid SRT format: no valid segments parsed")
return segments
def parse_srt_file(file_path: Path) -> List[Segment]:
"""从文件解析 SRT 格式
读取文件内容并解析为 Segment 对象列表。
Args:
file_path: SRT 文件路径
Returns:
Segment 对象列表
Raises:
FileNotFoundError: 文件不存在
ValueError: 内容格式无效
Example:
>>> from pathlib import Path
>>> segments = parse_srt_file(Path("subtitles.srt"))
>>> len(segments)
10
"""
if not file_path.exists():
raise FileNotFoundError(f"SRT file not found: {file_path}")
content = file_path.read_text(encoding='utf-8')
return parse_srt(content)