hexo-tts-reader
v0.2.0
Published
A Hexo plugin that reads your posts aloud. Builds MP3 audio for each post at generate-time using Edge TTS, and injects a floating player into post pages.
Maintainers
Readme
English | 汉语 | Español | Français | Deutsch | 日本語 | 한국어 | Português
浏览器只会加载静态的 <audio> 文件——无需运行时 TTS 服务、无需 API 密钥、
也无需客户端 JavaScript 合成。适合希望提供朗读功能、又不想运维后端或在运行时
为语音 API 付费的博客。
预览
展示真实中文朗读、倍速调节,以及收起播放器后继续播放。点击动图观看有声视频。
也可以在本地打开可交互的文章演示页。

在浏览器中打开 preview.html,即可在浅色与深色模式下试用悬浮播放器
UI——无需 Hexo 站点或 TTS 网络请求。
对真实文章运行 hexo reader:inspect _posts/example.md,可查看最终朗读文本、
音色与分段计划,不调用 TTS。运行 hexo reader:preview _posts/example.md
则会合成最多 300 字的本地试听音频,输出路径位于
.hexo-reader-cache/previews/;此命令需要构建机能访问 Edge TTS。
功能特性
- 构建时合成 — 无需运行时 TTS 服务,无需 API 密钥
- 整篇与分段缓存 — 只改一个段落时可复用其他段落的音频
- 自动注入到文章页面,或使用
{% reader %}标签手动精确放置 - 悬浮播放器,支持播放 / 暂停 / 跳转 / 播放速度(0.75× – 2×)
- 浅色与深色主题,支持多语言文案、键盘和屏幕阅读器
- 智能文本提取 — 跳过代码块、脚本、样式和嵌入式媒体
- 可定制朗读 — 按文章指定音色、语速、朗读稿及术语发音
- 长文安全 — 在句子边界分块输入并拼接 MP3 帧
- 单篇禁用 — 通过 front-matter 按文章关闭
- 构建报告 — 汇总缓存命中、跳过、失败文章和分段复用情况
环境要求
- Node.js >= 18
- Hexo >= 5
- 构建时需能访问 Microsoft Edge 在线 TTS
安装
npm install hexo-tts-reader --save或使用 yarn / pnpm:
yarn add hexo-tts-reader
pnpm add hexo-tts-reader快速开始
安装插件。
在站点的
_config.yml中添加:reader: enable: true运行
hexo clean && hexo generate(或hexo server)。每篇文章页面右下角 会出现悬浮播放器按钮。文案会跟随站点语言,也可通过buttonLabel覆盖播放按钮。
后续构建中,未变化的文章和段落都会复用缓存音频。
配置
完整配置及默认值:
reader:
enable: true
autoInject: true
locale: auto # 跟随站点语言,支持 zh/en/ja/ko/es/fr/de/pt
cacheChunks: true # 复用未修改段落的音频
voice: zh-CN-XiaoxiaoNeural
autoVoice: article # article | true | false
pronunciations: # 原文 -> 朗读时的说法
Hexo: 黑克搜
rate: -8 # -100..100, relative percent
pitch: -2 # -100..100, relative percent
outputFormat: audio-24khz-48kbitrate-mono-mp3
audioDir: audio # public audio output dir
cacheDir: .hexo-reader-cache # local cache dir (gitignore it)
pruneCache: false # 全量渲染后清理无引用音频
position: bottom-right # bottom-right | bottom-left | top-right | top-left
buttonLabel: 朗读本文 # 可选;默认随语言变化
chunkSize: 4000 # split long posts into <= N chars per request
maxTextLength: 100000 # hard upper bound per post
failOnError: false # if true, build fails when TTS fails
timeoutMs: 60000 # per-chunk TTS timeout (ms)
skip: [] # list of substrings to match against post.source选项说明
| 选项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| enable | boolean | true | 总开关。 |
| autoInject | boolean | true | 自动在每篇文章末尾追加播放器。关闭后仅合成带有 {% reader %} 标签的文章。 |
| locale | string | 站点语言 | 播放器文案;auto 跟随 Hexo 的 language,支持 zh/en/ja/ko/es/fr/de/pt。 |
| cacheChunks | boolean | true | 将分段 MP3 缓存在 <cacheDir>/chunks,修改文章时复用未变更段落。 |
| voice | string | zh-CN-XiaoxiaoNeural | 兜底音色;显式设置且未配置 autoVoice 时,全站使用该音色。 |
| autoVoice | article / boolean | article | 默认按文章语言选一个音色;true 按文字片段切换;false 固定使用 voice。 |
| voices | object | 内置映射 | 开启语言路由时,zh、en、ja、ko、ru 对应的音色 ID。 |
| pronunciations | object | {} | 精确匹配的术语发音替换;文章级规则可覆盖全站同名规则。 |
| rate | number | -8 | 相对语速,-100..100。超出范围的值会被截断。 |
| pitch | number | -2 | 相对音调,-100..100。超出范围的值会被截断。 |
| outputFormat | string | audio-24khz-48kbitrate-mono-mp3 | msedge-tts 支持的任意格式。 |
| audioDir | string | audio | 站点根目录下的公开 MP3 输出路径。拒绝路径穿越。 |
| cacheDir | string | .hexo-reader-cache | 本地缓存目录(从站点 base 目录解析)。跨构建持久保存。 |
| pruneCache | boolean | false | 仅在本轮已渲染全部文章时清理无引用的整篇与分段音频;增量构建不会清理。 |
| position | enum | bottom-right | 可选 bottom-right、bottom-left、top-right、top-left。 |
| buttonLabel | string | 随语言变化 | 播放按钮的 aria-label,可显式覆盖。 |
| chunkSize | number | 4000 | 每次 TTS 请求的最大字符数,200..8000。 |
| maxTextLength | number | 100000 | 单篇文章硬上限,100..1000000。更长文本会被截断。 |
| failOnError | boolean | false | 为 true 时,TTS 失败会中止整个 hexo generate。 |
| timeoutMs | number | 60000 | 每个分块的 WebSocket 超时,5000..600000。 |
| skip | string[] | [] | 与 post.source 匹配的子串列表,用于跳过指定文章。 |
按文章禁用
在文章 front-matter 中:
---
title: My private post
reader: false
---手动放置
使用 {% reader %} 标签在文章中任意位置插入播放器。当标签存在时,
该文章会抑制自动注入,因此只会出现一个播放器:
Some intro text.
{% reader %}
The rest of the article.按文章定制朗读
在文章 front-matter 的 reader 对象中可以覆盖音色、语速、音调,并单独撰写
朗读稿。text 只影响音频,网页上显示的文章内容不变:
---
title: 一篇关于 Hexo 的文章
reader:
voice: zh-CN-YunxiNeural
rate: -12
pitch: 0
text: |
欢迎收听这篇文章。今天我们介绍 Hexo。
pronunciations:
Hexo: 黑克搜
---文章级支持 voice、autoVoice、rate、pitch、text 和
pronunciations。只设置 voice 时,该文章固定使用此音色;若同时指定
autoVoice,则按所选路由方式朗读。reader: false 或
reader: { enable: false } 仍可禁用朗读。发音规则区分大小写,精确匹配原文;
较长的词组优先,替换结果不会被再次替换。文章规则覆盖全站同名规则。
最终朗读文本和音色参数共同决定缓存键。
按路径跳过
reader:
skip:
- "draft/"
- "_posts/private/"source 包含上述任一子串的文章将被跳过。
工作原理
flowchart LR
A[Hexo 渲染文章] --> B[提取纯文本]
B --> C{缓存命中?}
C -->|是| E[复制 MP3 到公开目录]
C -->|否| D[通过 WebSocket 调用 Edge TTS]
D --> E
E --> F[注入播放器 markup]- Hexo 渲染文章后(
after_post_render过滤器),插件优先使用自定义朗读稿; 否则从 HTML 中提取纯文本,跳过代码块、脚本、样式和嵌入式媒体。 - 对没有句末标点的段落补停顿,再根据文本、音色设置与合成版本计算 SHA-1 缓存键。
- 若
<cacheDir>/<key>.mp3已存在则复用;否则先从<cacheDir>/chunks复用未变化段落,再通过msedge-tts合成缺失分段,并以原子方式写入 合并结果(*.tmp→ rename)。 - 长文本在句子边界(中英文标点)分块,逐块合成,并作为原始 MP3 帧拼接—— 播放是安全的。
- 生成器在
<audioDir>/<key>.mp3下输出 MP3,并在assets/hexo-reader/下 输出共享的reader.js/reader.css。 - 播放器 markup 会追加到文章末尾或替换
{% reader %};共享 CSS/JS 由 Hexo 在全站注入。
语音
支持 Microsoft Edge 在线 TTS 的任意语音。示例:
| Voice id | Locale | 说明 |
| --- | --- | --- |
| zh-CN-XiaoxiaoNeural | zh-CN | 默认,女声 |
| zh-CN-YunxiNeural | zh-CN | 男声 |
| zh-CN-YunyangNeural | zh-CN | 男声,新闻风格 |
| en-US-AriaNeural | en-US | 女声 |
| en-US-GuyNeural | en-US | 男声 |
| ja-JP-NanamiNeural | ja-JP | 女声 |
| ko-KR-SunHiNeural | ko-KR | 女声 |
完整列表请参阅上游语音目录,或通过 msedge-tts 运行 voices 查询。
如果想比较更柔和的中文朗读,可在站点 _config.yml 里试试女声
zh-CN-XiaoyiNeural 或男声 zh-CN-YunxiNeural:
reader:
voice: zh-CN-XiaoyiNeural
rate: -8
pitch: -2显式设置 voice 后,默认全站固定这个音色。多语言博客可以不设置 voice,
默认 autoVoice: article 会为每篇文章选一个音色,避免遇到英文技术词时频繁换声。
需要逐段切换时可设置 autoVoice: true。音色好听与否比较主观,建议用自己的文章试听。
缓存
缓存位于
<site>/<cacheDir>,跨构建持久保存。开启
cacheChunks时,分段音频保存在<cacheDir>/chunks,旧的整篇缓存仍可命中。键基于内容而非文件名,因此重命名不会触发重新合成,小幅编辑只会重新生成受影响的文章。
增量构建会保留并输出未重新渲染文章所需的缓存音频;即使开启
pruneCache, 也只在全量渲染时清理整篇与分段缓存。修改全站音色或术语发音规则后,请运行
hexo clean && hexo generate,确保未改动的文章也重新渲染。若要强制全部重新合成,删除缓存目录即可。
建议的
.gitignore条目:.hexo-reader-cache/
主题定制
注入的播放器使用以 hexo-reader__ 为前缀的 CSS 类。若要自定义颜色,
可在主题样式表中覆盖,例如:
.hexo-reader {
--hexo-reader-accent: #1f6feb;
}
.hexo-reader.is-expanded .hexo-reader__panel {
border-radius: 12px;
}播放器默认支持 prefers-color-scheme: dark。
故障排查
hexo generate 构建卡住或超时。
构建需要能访问 Edge TTS 网络。若处于防火墙后或离线运行,可设置
failOnError: false(默认)使失败优雅降级,或通过 skip 跳过受影响的文章。
构建后某篇文章没有播放器。
查看 Hexo 日志中的 hexo-reader: TTS failed for "<title>"。若 TTS 失败且
failOnError 为 false,该文章的播放器会被静默跳过。
文章中播放器出现两次。
不要在同一篇文章中同时使用 autoInject: true 与 {% reader %} 标签。
插件在标签存在时已会抑制自动注入——请确保标签已渲染(未被注释),且主题模板
未额外添加副本。
播放器样式错乱(播放/暂停图标重叠)或 PJAX 主题下无法播放。
AnZhiYu、Butterfly 等开启 PJAX 的主题,从首页点进文章时不会执行正文里的
<script defer> / <link>。自 0.1.3 起,插件通过 Hexo injector 将
reader.css / reader.js 注入全站 head / body,并在 pjax:complete 时
调用 hexoReaderBoot()。请升级到最新版后 hexo clean && hexo generate。
音频无法播放 / 404。
确保部署包含 audio/ 与 assets/hexo-reader/ 目录。若使用非默认 audioDir,
请确认 CDN 规则未拦截。
合成时返回 403。
先确认已安装本插件 0.2.0 或更高版本(依赖 msedge-tts 2.1.0),并检查
构建环境的系统时间及网络代理。上游 Edge Read Aloud 接口可能变化,插件无法保证
所有网络环境都能连接。
希望发布站点时从不调用 TTS。
预先用已生成的 MP3 填充 <cacheDir>。插件会按内容哈希复用,无需联网。
说明与限制
- 合成在构建时进行,需要访问 Edge TTS 网络。
- 需要
msedge-tts2.1.0(随本插件安装)。Microsoft 的 Edge Read Aloud 接口会变化;旧客户端可能返回错误页或 403,而不是音频。 - 每篇文章对应一个 MP3;超长文章会分块并拼接。
- 若某篇文章 TTS 失败且
failOnError为false(默认),该文章不会注入播放器,构建继续。 - 缓存键为纯文本 + 合成参数的 SHA-1,仅作内容标识,不用于安全目的。
开发
git clone https://github.com/xichenx/hexo-tts-reader.git
cd hexo-tts-reader
npm install
npm test测试使用 Node 内置测试运行器(node --test)。
相关链接
- npm 包
- GitHub 仓库
- 提交 Issue
- msedge-tts — 底层 TTS 客户端
许可证
MIT © 刘明智(xichen)

