文档音频工作流 (Docs TTS)
文档分类下的文章(如 docs/LookAround/)支持 TTS 朗读音频播放。与博客音频不同,文档音频播放器自动注入到页面布局中,无需在每篇文章的 MDX 文件里手动引入组件。
当前已接入音频的文档分类:
| 分类 | 目录 | 文章数 | 中文音频 | 英文音频 |
|---|---|---|---|---|
| 东张西望 (LookAround) | docs/LookAround/ | 8 | 已生成 | 已生成 |
架构概览
文档音频系统复用了博客音频播放器,但使用独立的文档音频清单和独立 OSS 路径。这样博客和文档可以共享播放体验,又不会互相污染 manifest。
组件关系
BlogAudioPlayer (核心播放逻辑)
├── BlogPostPage 自动注入:<BlogAudioPlayer slug={audioSlug} />
│ ├── slug 由 getBlogAudioSlug(metadata) 从 permalink 推导
│ └── 读取 blogAudioManifest.json(默认清单)
│
└── DocsAudioPlayer (薄包装)
└── <DocsAudioPlayer slug="xxx" />
└── 传入 docsAudioManifest.json + keyPrefix="docs/"
关键文件
| 文件 | 作用 |
|---|---|
src/components/BlogAudioPlayer/index.js | 核心播放器组件,支持 manifest 和 keyPrefix props |
src/components/DocsAudioPlayer/index.js | 文档音频薄包装,传入文档清单和前缀 |
src/theme/BlogPostPage/index.js | 博客页面布局 swizzle,自动注入博客播放器 |
src/utils/blogAudio.js | 从 permalink 推导博客音频 slug |
src/utils/lookAroundDocs.js | LookAround 文档检测工具函数 |
src/theme/DocItem/Layout/index.js | 文档布局,自动注入文档播放器 |
src/data/blogAudioManifest.json | 博客音频清单(静态 import) |
src/data/docsAudioManifest.json | 文档音频清单(静态 import) |
static/audio/blog/manifest.json | 博客音频清单(备用访问路径) |
static/audio/docs/manifest.json | 文档音频清单(备用访问路径) |
清单文件结构
docsAudioManifest.json 的 key 格式:
- 中文:
docs/{slug},如docs/omega-horizontal-vertical-analysis - 英文:
en/docs/{slug},如en/docs/omega-horizontal-vertical-analysis
每个条目包含:
{
"docs/omega-horizontal-vertical-analysis": {
"urls": [
"https://oss.nevergpdzy.com/Audio/docs/omega-horizontal-vertical-analysis_001.mp3",
"https://oss.nevergpdzy.com/Audio/docs/omega-horizontal-vertical-analysis_002.mp3"
],
"voice": "茉莉",
"generatedAt": "2026-05-02T06:10:34.705519+00:00"
}
}
OSS 路径
| 内容 | OSS 路径 |
|---|---|
| 中文文档音频 | Audio/docs/{slug}.mp3 或 Audio/docs/{slug}_001.mp3 等 |
| 英文文档音频 | Audio/docs/en/en_{slug}.mp3 或 Audio/docs/en/en_{slug}_001.mp3 等 |
| 文档音频清单 | Audio/docs/manifest.json |
文档音频 manifest 中的公开 URL 必须使用 https://oss.nevergpdzy.com/,不要把已退役 picture 域名重新写回 src/data/docsAudioManifest.json 或 static/audio/docs/manifest.json。
与博客音频(Audio/blog/)完全独立,互不干扰。
生成策略
文档文章通常比博客长,而且包含表格、参考资料和图片资源段落。生成器的 docs 模式做了这些处理:
- 提取纯文本,去除 frontmatter、代码块、JSX、图片、原始 URL、来源/参考资料/图片资源等不适合朗读的段落。
- 在处理普通 Markdown 之前先转换表格,把每行转换成适合朗读的 短句,而不是直接朗读竖线和分隔符。
- 按段落边界分块,默认
--chunk-char-limit 2400,必要时可以对单篇文章降低到1200之类的更稳值。 - 并发只发生在文章级别,
--article-jobs会同时处理多篇文章;同一篇文章内部的 chunk 始终串行生成,保证音频顺序和文字顺序严格一致。 - 每个 chunk 生成后用
ffprobe检查时长,明显过短或截断的音频会删除并重试。 - 只要有文章失败,本次 publish manifest 不会上传,避免发布部分缺段的清单。
中文语音使用「茉莉」,提示词强调缓慢、温柔舒缓、自然停顿。英文语音使用 Chloe,保持自然流畅朗读提示词,不额外强制舒缓节奏。不要再用 atempo 或其他 MP3 后处理去改变语速;语速应由 TTS 提示词和分块稳定性控制。
生成音频
全量生成中文 LookAround 音频
cd ../tts-blog-generator
python generate.py --type docs --lang zh --force --article-jobs 2
默认扫描 ../Dev-Knowledge-Base/docs/LookAround。
全量生成英文 LookAround 音频
cd ../tts-blog-generator
python generate.py --type docs --lang en \
--blog-dir "../Dev-Knowledge-Base/i18n/en/docusaurus-plugin-content-docs/current/LookAround" \
--force --article-jobs 2
英文音频使用 Chloe 语音,文件名带 en_ 前缀,存储在 OSS 的 Audio/docs/en/ 目录下。
只重生成单篇文章
cd ../tts-blog-generator
# 中文
python generate.py --type docs --lang zh \
--include mercedes-benz-g-class-history-category-industry-position \
--force --article-jobs 1
# 英文
python generate.py --type docs --lang en \
--blog-dir "../Dev-Knowledge-Base/i18n/en/docusaurus-plugin-content-docs/current/LookAround" \
--include mercedes-benz-g-class-history-category-industry-position \
--force --article-jobs 1
如果某篇中文长文出现短音频、乱码感朗读或 2 分钟左右突然读错内容,先检查抽取文本,再降低分块上限重新生成:
python generate.py --type docs --lang zh \
--include mercedes-benz-g-class-history-category-industry-position \
--force --article-jobs 1 --chunk-char-limit 1200
这次 G-Class 中文音频就是按 1200 字左右重新分成 15 段生成,替代了原来的 7 段长 chunk。
常用参数
| 参数 | 说明 |
|---|---|
--type docs | 生成文档音频(默认扫描 docs/LookAround/) |
--lang zh / --lang en | 指定语言 |
--blog-dir <path> | 自定义扫描目录,英文文档必须显式传入英文镜像目录 |
--include <slug> | 只处理指定 slug 或文件名,适合单篇修复 |
--force | 强制重新生成,并移除目标文章的旧 manifest 条目 |
--article-jobs <n> | 文章级并发数;同一篇文章内 chunk 仍然串行 |
--chunk-char-limit <n> | 单个 TTS chunk 的最大字符数 |
--dry-run | 预览分块和文件名映射,不调用 API |
--skip-upload | 跳过 OSS 上传 |
文本检查
重新生成有问题的文章前,必须检查要送给 TTS 的文本。推荐用生成器的抽取函数导出检查文件:
cd ../tts-blog-generator
python - <<'PY'
from pathlib import Path
import generate
slug = "mercedes-benz-g-class-history-category-industry-position"
path = Path("../Dev-Knowledge-Base/docs/LookAround") / f"{slug}.md"
text = generate.extract_text(path)
chunks = generate.chunk_text(text, limit=1200)
Path("output").mkdir(exist_ok=True)
Path("output/check_zh_gclass.txt").write_text(
"\n\n".join(
f"=== chunk {i:03d} / {len(chunks)} ({len(chunk)} chars) ===\n{chunk}"
for i, chunk in enumerate(chunks, 1)
),
encoding="utf-8",
)
print(len(text), [len(chunk) for chunk in chunks])
PY
检查重点:
- 没有
�、鈥、盲、鏂等乱码特征。 - 没有原始 URL、Markdown 图片、表格分隔符、参考资料附录。
- chunk 编号、文件名和正文顺序一致。
- 表格被转换成可读句子,而不是竖线和分隔线。
注意:Windows PowerShell 直接 Get-Content 中文文件时可能显示乱码,这是控制台编码问题。以 Python encoding="utf-8" 读出的文本为准。