主要涉及 桌面端 ReaderView.tsx 和 FoliateViewer.tsx 中的 TTS 连续朗读逻辑。核心流程如下:
TTS 连续朗读链路
startPageTTS() (ReaderView.tsx:1903) — 获取当前页可见文本段落,调用 ttsPlay() 播放
播放完毕后,handleTTSPageEnd() (ReaderView.tsx:1747) 被触发
handleTTSPageEnd 调用 getTTSSegmentContext() (FoliateViewer.tsx:776) 查找下一页的第一句话
找到后调用 startPageTTSFromCfi() (ReaderView.tsx:1958) 继续朗读
发现的关键问题
问题出在 getTTSSegmentContext (FoliateViewer.tsx:776-841):
这个函数通过 tts.alignCfi(cfi) 在 TTS 引擎的内部句子列表中定位光标,然后用 tts.collectDetails() 获取后续句子。但 alignCfi 内部使用 严格字符串比较 (candidate === cfi) 来匹配 CFI:
// tts.js:473
alignCfi(cfi) {
return this.#detailFromCfi(cfi, { highlight: false });
}
// tts.js:465
#detailFromCfi(cfi, ...) {
const entry = this.#detailList.find((range) => {
const candidate = this.#getCfi(range.cloneRange());
return candidate === cfi; // 严格字符串匹配
});
}
如果 CFI 不匹配,alignCfi 静默失败,光标不会正确定位,后续 collectDetails 可能返回空数组,导致 handleTTSPageEnd 认为没有更多内容而停止。
CFI 可能不匹配的原因是 两套不同的分段逻辑:
getVisibleTTSSegments (FoliateViewer.tsx:481) 使用 blockSelector(p, h1-h6, li 等)和 Intl.Segmenter 对可见区域进行句子分割
TTS 引擎的 getDetailRanges (tts.js:149) 使用 blockTags(额外包含 div, section 等)对整个文档进行句子分割
不同的块标签集合导致分段边界不同,进而生成的 CFI 可能无法匹配。
主要涉及 桌面端 ReaderView.tsx 和 FoliateViewer.tsx 中的 TTS 连续朗读逻辑。核心流程如下:
TTS 连续朗读链路
startPageTTS() (ReaderView.tsx:1903) — 获取当前页可见文本段落,调用 ttsPlay() 播放
播放完毕后,handleTTSPageEnd() (ReaderView.tsx:1747) 被触发
handleTTSPageEnd 调用 getTTSSegmentContext() (FoliateViewer.tsx:776) 查找下一页的第一句话
找到后调用 startPageTTSFromCfi() (ReaderView.tsx:1958) 继续朗读
发现的关键问题
问题出在 getTTSSegmentContext (FoliateViewer.tsx:776-841):
这个函数通过 tts.alignCfi(cfi) 在 TTS 引擎的内部句子列表中定位光标,然后用 tts.collectDetails() 获取后续句子。但 alignCfi 内部使用 严格字符串比较 (candidate === cfi) 来匹配 CFI:
// tts.js:473
alignCfi(cfi) {
return this.#detailFromCfi(cfi, { highlight: false });
}
// tts.js:465
#detailFromCfi(cfi, ...) {
const entry = this.#detailList.find((range) => {
const candidate = this.#getCfi(range.cloneRange());
return candidate === cfi; // 严格字符串匹配
});
}
如果 CFI 不匹配,alignCfi 静默失败,光标不会正确定位,后续 collectDetails 可能返回空数组,导致 handleTTSPageEnd 认为没有更多内容而停止。
CFI 可能不匹配的原因是 两套不同的分段逻辑:
getVisibleTTSSegments (FoliateViewer.tsx:481) 使用 blockSelector(p, h1-h6, li 等)和 Intl.Segmenter 对可见区域进行句子分割
TTS 引擎的 getDetailRanges (tts.js:149) 使用 blockTags(额外包含 div, section 等)对整个文档进行句子分割
不同的块标签集合导致分段边界不同,进而生成的 CFI 可能无法匹配。