Spaces:
Sleeping
Sleeping
File size: 4,674 Bytes
db4f540 |
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
"""章节配置加载器"""
from pathlib import Path
from typing import Any
import yaml
from chapterbar.chapter_extractor import BASE_COLOR, Chapter
from chapterbar.chapter_validator import ChapterValidator
class ChapterLoader:
"""章节配置加载器"""
@staticmethod
def load_from_yaml(yaml_path: str) -> tuple[list[Chapter], float]:
"""
从 YAML 文件加载章节配置
Args:
yaml_path: YAML 文件路径
Returns:
(chapters, duration)
Raises:
FileNotFoundError: 文件不存在
ValueError: 配置格式错误或验证失败
"""
# 检查文件是否存在
path = Path(yaml_path)
if not path.exists():
raise FileNotFoundError(f"配置文件不存在: {yaml_path}")
# 读取 YAML
try:
with open(yaml_path, encoding="utf-8") as f:
config = yaml.safe_load(f)
except yaml.YAMLError as e:
raise ValueError(f"YAML 格式错误: {e}") from e
# 验证配置结构
if not isinstance(config, dict):
raise ValueError("配置文件必须是一个字典")
if "duration" not in config:
raise ValueError("配置文件缺少 'duration' 字段")
if "chapters" not in config:
raise ValueError("配置文件缺少 'chapters' 字段")
duration = float(config["duration"])
if duration <= 0:
raise ValueError(f"视频时长必须大于 0,当前值: {duration}")
# 解析章节
chapters = ChapterLoader._parse_chapters(config["chapters"])
# 验证章节
validator = ChapterValidator(chapters, duration)
is_valid, errors, warnings = validator.validate()
if not is_valid:
error_messages = [f" - {err.message}" for err in errors]
raise ValueError("章节配置验证失败:\n" + "\n".join(error_messages))
return chapters, duration, warnings
@staticmethod
def _parse_chapters(chapters_data: list[dict[str, Any]]) -> list[Chapter]:
"""
解析章节数据
Args:
chapters_data: 章节数据列表
Returns:
Chapter 对象列表
"""
if not isinstance(chapters_data, list):
raise ValueError("'chapters' 必须是一个列表")
chapters = []
for i, chapter_data in enumerate(chapters_data):
if not isinstance(chapter_data, dict):
raise ValueError(f"章节 {i + 1} 必须是一个字典")
# 必需字段
if "start" not in chapter_data:
raise ValueError(f"章节 {i + 1} 缺少 'start' 字段")
if "end" not in chapter_data:
raise ValueError(f"章节 {i + 1} 缺少 'end' 字段")
if "title" not in chapter_data:
raise ValueError(f"章节 {i + 1} 缺少 'title' 字段")
# 解析字段
try:
start_time = float(chapter_data["start"])
end_time = float(chapter_data["end"])
except (ValueError, TypeError) as e:
raise ValueError(f"章节 {i + 1} 时间格式错误: {e}") from e
title = str(chapter_data["title"])
# 可选字段:颜色
if "color" in chapter_data:
color_data = chapter_data["color"]
if isinstance(color_data, list) and len(color_data) == 3:
color = tuple(int(c) for c in color_data)
else:
raise ValueError(f"章节 {i + 1} 颜色格式错误,应为 [R, G, B]")
else:
color = BASE_COLOR
chapters.append(Chapter(title=title, start_time=start_time, end_time=end_time, color=color))
return chapters
@staticmethod
def save_to_yaml(chapters: list[Chapter], duration: float, yaml_path: str) -> None:
"""
保存章节配置到 YAML 文件
Args:
chapters: 章节列表
duration: 视频总时长
yaml_path: 输出文件路径
"""
config = {"duration": duration, "chapters": []}
for chapter in chapters:
chapter_data = {
"start": chapter.start_time,
"end": chapter.end_time,
"title": chapter.title,
}
config["chapters"].append(chapter_data)
# 写入文件
with open(yaml_path, "w", encoding="utf-8") as f:
yaml.dump(config, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
|