Files
simonkoson 1c3963d17c doco: v2说话人分段模式 — ASR说话人分离+大block拆分+三维动画解说识别
- asr_adapter: 新增roleType=1说话人分离参数,新增parse_order_result_with_speaker(),write_asr_result自动输出asr_v2_timed_spk.txt

- fusion_align: 新增speaker-aware alignment v2流程(_annotate_b_lines_with_speakers区间匹配、_detect_speaker_blocks、SYSTEM_PROMPT_SPEAKER_ALIGN大block拆分prompt、_build_broadcast_segments支持block内多段拆分)

- cli: 兼容v1/v2 stats字典

- 新增convert_to_md.py(20期融合A稿docx转md+YAML frontmatter)

- backup_before_spk/: 修改前代码备份
2026-06-24 16:26:05 +08:00

177 lines
5.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
将 20 期融合A稿 (.docx) 转为带 YAML frontmatter 的 .md 文件,
供 TPS 主项目知识库 /api/knowledge/upload 批量导入。
用法:
cd E:\tps-dashboard\doco
python convert_to_md.py
产物落在 doco/deliverables/ 目录下,每期一个 .md 文件。
"""
import glob
import os
import re
import sys
from datetime import datetime
from pathlib import Path
from docx import Document
PROGRAMS_DIR = Path(__file__).parent / "programs"
OUTPUT_DIR = Path(__file__).parent / "deliverables"
# ep001 文件名没有编导和标题信息,需要手工补
EP001_OVERRIDE = {
"title": "现代防空反导大对决",
"author": "", # ← 通哥填
"date": "2026-06-12",
}
def find_fusion_docx(episode_dir: Path) -> Path | None:
"""在 episode 目录里找融合A稿 docx(排除批改稿)。"""
candidates = []
for f in episode_dir.iterdir():
if f.suffix == ".docx" and "融合A稿" in f.name and "批改" not in f.name:
candidates.append(f)
if not candidates:
return None
if len(candidates) == 1:
return candidates[0]
# 优先选带日期前缀的(ep002+ 格式)
for c in candidates:
if re.match(r"\d{8}", c.stem):
return c
return candidates[0]
def parse_metadata_from_filename(docx_path: Path, episode_dir_name: str) -> dict:
"""
从文件名提取元数据。
ep002+ 文件名格式:{YYYYMMDD}{标题}_{编导}_融合A稿.docx
ep001 特殊处理。
"""
if episode_dir_name.startswith("ep001"):
return EP001_OVERRIDE.copy()
stem = docx_path.stem # e.g. "20260127潜艇的仿生之路_穆佩弦_融合A稿"
# 去掉 "_融合A稿" 后缀
stem = re.sub(r"_融合A稿$", "", stem)
# 提取日期 (前8位)
date_str = stem[:8]
try:
dt = datetime.strptime(date_str, "%Y%m%d")
date_iso = dt.strftime("%Y-%m-%d")
except ValueError:
date_iso = ""
rest = stem[8:] # e.g. "潜艇的仿生之路_穆佩弦"
# 最后一个 _ 分隔编导
parts = rest.rsplit("_", 1)
if len(parts) == 2:
title, author = parts
else:
title = rest
author = ""
return {"title": title, "date": date_iso, "author": author}
def docx_to_markdown(docx_path: Path) -> str:
"""将融合A稿 docx 转为 markdown 正文。"""
doc = Document(str(docx_path))
lines = []
for para in doc.paragraphs:
text = para.text.strip()
if not text:
continue
# 大标题(第一段,通常带书名号)
if not lines and (text.startswith("《") or text.startswith("【") is False):
lines.append(f"# {text}")
lines.append("")
continue
# 段头标签:【主持人1】【解说3】【专家2】【导视】隔断:【...】
if re.match(r"^【.+?】$", text) or re.match(r"^隔断\d*[:]【.+?】", text):
lines.append("")
lines.append(f"## {text}")
lines.append("")
continue
# 普通正文段落
lines.append(text)
lines.append("")
return "\n".join(lines).strip() + "\n"
def build_frontmatter(meta: dict, episode_id: str) -> str:
"""生成 YAML frontmatter。"""
fm_lines = ["---"]
fm_lines.append(f"标题: {meta['title']}")
if meta["author"]:
fm_lines.append(f"编导: {meta['author']}")
if meta["date"]:
fm_lines.append(f"播出日期: {meta['date']}")
fm_lines.append("类型: 节目文稿")
fm_lines.append(f"期号: {episode_id}")
fm_lines.append("---")
return "\n".join(fm_lines)
def main():
OUTPUT_DIR.mkdir(exist_ok=True)
# 找所有 ep??? 目录(排除 ep002_004 这种非标准的)
episode_dirs = sorted(
d
for d in PROGRAMS_DIR.iterdir()
if d.is_dir() and re.match(r"^ep\d{3}_\d{8}_", d.name)
)
print(f"找到 {len(episode_dirs)} 个期目录")
success = 0
errors = []
for ep_dir in episode_dirs:
episode_id = ep_dir.name.split("_")[0] # ep001, ep002, ...
docx_path = find_fusion_docx(ep_dir)
if docx_path is None:
errors.append(f"{ep_dir.name}: 未找到融合A稿")
continue
print(f" {episode_id}: {docx_path.name}")
meta = parse_metadata_from_filename(docx_path, ep_dir.name)
frontmatter = build_frontmatter(meta, episode_id)
body = docx_to_markdown(docx_path)
# 输出文件名:ep001_现代防空反导大对决.md
safe_title = meta["title"].replace(":", "").replace("/", "_")
out_name = f"{episode_id}_{safe_title}.md"
out_path = OUTPUT_DIR / out_name
with open(out_path, "w", encoding="utf-8") as f:
f.write(frontmatter + "\n\n" + body)
success += 1
print(f"\n完成:{success}/{len(episode_dirs)} 期转换成功")
if errors:
print("失败:")
for e in errors:
print(f" {e}")
print(f"\n产物目录:{OUTPUT_DIR}")
if __name__ == "__main__":
main()