refactor(tree): subtitle 迁入 L3Card/L2Card + 建树管线修正

- L3Card/L2Card 新增 subtitle: str 字段(L1Card 不加)
- L3Node 移除 subtitle 字段(数据迁入 Card)
- assign_subtitles_voronoi 改写 Card.subtitle + L2 聚合
- _collect_card_strings 增加 skip_fields 排除 subtitle
- _node_full_text/_node_anchored_text 保持 字幕:/[sN] 语义
- get_subtitle 读 Card.subtitle(L2/L3)
- verify.py/synthesizer.py: l3.subtitle → l3.card.subtitle
- 迁移脚本 tools/migrate_subtitle_to_card.py(幂等,300 棵树已迁移)
- 9→6 个测试文件适配(3 个无需改动)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 11:57:41 -04:00
parent c72b55508a
commit d3be9b1322
13 changed files with 253 additions and 46 deletions
+14 -2
View File
@@ -13,6 +13,7 @@
from __future__ import annotations
import dataclasses
import re
from dataclasses import dataclass
from typing import TYPE_CHECKING
@@ -254,7 +255,8 @@ def assign_subtitles_voronoi(
entries: 已解析的 SRTEntry 列表。
副作用:
直接修改每个 L3Node.subtitle 字段。
通过 dataclasses.replace 替换 L3Node.card 和 L2Node.card
将字幕写入 card.subtitle 字段。
迁移来源:
TRM3 tools/generate_subtitles.py compute_effective_ranges + assign_subtitles
@@ -299,7 +301,17 @@ def assign_subtitles_voronoi(
right = (ts + next_ts) / 2.0
subtitle_text = extract_subtitle_for_range(entries, (left, right))
l3.subtitle = subtitle_text if subtitle_text else None
l3.card = dataclasses.replace(
l3.card,
subtitle=subtitle_text or "",
)
# L2 字幕聚合:拼接所有 L3 子节点的字幕
l3_subtitles = [l3.card.subtitle for l3 in l2.children if l3.card.subtitle]
l2.card = dataclasses.replace(
l2.card,
subtitle="\n".join(l3_subtitles),
)
logger.debug(
"Voronoi 字幕分配完成: {} 个 L1 节点, {} 条字幕条目",