Electron Stories 与 Audiobook¶
历史背景: 本页面编写于 Electron 与 Tauri 应用共存的时期。文中提到的 Tauri 辅助工具、页面、测试及回归结果描述的是那段迁移期;Tauri 外壳此后已被移除,共享代码现位于
electron/src/shared/。现有 Tauri 安装用户请参阅迁移指南。
Stories 与 Audiobook 可从侧边栏和命令搜索中访问。两者各自保留独立草稿。选择已保存的默认语音、输入或导入文本,然后渲染。Stories 支持逐行语音覆盖;章节标题和暂停标记使用与 Tauri 相同的 storyToSpans 编译器。两个页面均接受 [pause 500ms] 和 [pause 0.5s];示例、占位符和插入暂停按钮均显示秒格式,且各语言环境占位符中的标记保持原样([pause 0.5s]、[voice:NAME])。Audiobook 脚本中已命名但尚未配音的角色只会收到一条平和的提示(它们将使用主语音朗读),而不是对每个名字发出警告。Audiobook 将原始脚本提交给现有的后端解析器。
纯文本 Audiobook 导入能识别使用 LF、CRLF 或 CR 行结尾的章节标题行。包含现有 Markdown H1 标题的文稿保留其显式结构和原始文本。
两个编辑器共用共享的长文后端、真实章节进度、组装状态,以及显式的停止操作。进度随每个合成片段推进,而非仅在章节边界更新:后端流式传输实时 progress 事件(全书进度占比、剩余时间、实时倍率和平均片段耗时;不存储在作业历史中),跟踪器显示百分比、已用时间、剩余时间和实时倍率(每秒渲染产生的音频秒数)。剩余时间来自最近新合成片段的滚动速率;从缓存提供的章节和行会移动进度条但不会影响速率,因此恢复渲染不会看起来瞬间完成。如果在六个典型片段耗时内(至少两分钟;首个片段前为十分钟,可能包含模型加载)没有新片段落地,跟踪器会提示渲染似乎已停滞,并指向 日志 → 后端。已用时间的时钟在离开并重新打开页面后仍然保留。不支持 progress 事件的旧版后端回退到章节计数估算,该估算忽略缓存章节。停止会中止 HTTP 流,使后端停止调度章节。截断的流不能替换最后成功的输出。中断的服务器清单可显式恢复,而无需重新发送编辑过的文稿。每个页面只列出自己中断的渲染(Stories 在 Stories 页面,书籍在 Audiobook 页面),每个项目一张卡片,显示最新清单;禁用的“恢复”按钮会说明原因(文本转语音未就绪,或已有另一个渲染在运行),“忽略”会在本设备上隐藏该项目的卡片。导航不会停止运行中的渲染;渲染器重启会断开连接,恢复过程使用服务器清单清单。
生成与播放和导出是分离的。Vidstack 提供播放器,使用拖动条而非为长音频解码完整波形;原生媒体提供器报告就绪后,“播放”按钮才可用。现有后端生成 MP3 和 M4B,原生导出对话框保存本地副本。完成的渲染还通过该对话框提供提示单:一个 .txt 文件,每个渲染章节对应一行 HH:MM:SS<TAB>Title。这对于节目笔记、播客服务和忽略内嵌章节的播放器很有用——M4B 内嵌章节,而 MP3 无法做到。时间戳累加与 M4B 元数据使用的整毫秒章节时长相同,因此其整秒、无上限小时值能匹配内嵌起始时间。失败的章节既不添加行也不累加已用时间。
草稿写入复用合并持久化辅助工具,并在生命周期事件时刷新。当前工作草稿使用浏览器存储。已命名的 Stories 和 Audiobook 项目在单独 Electron 数据库中使用共享的原生 IndexedDB 适配器。保存操作等待已提交的事务,并发写入被串行化,打开或删除项目需要确认。项目包含脚本/行、语音、设置和最后的输出引用。存储故障会被呈现。书籍详情包括作者、朗读者、年份、体裁、描述和上传的 JPEG/PNG 封面。响度提供关闭、ACX 和播客预设。Audiobook 发音行到达现有词典引擎;重复单词在渲染前会被标记。这些设置随草稿持久化。完整的角色管理和其它高级控制仍记录在 electron/PARITY.md 中。
electron/tests/longform-smoke.mjs 使用模拟生成和真实音频播放验证主要流程。运行时测试覆盖停止、重复预防、截断响应、显式恢复和共享 Stories 编译。真实已安装模型验证通过一个英文章节:MP3 渲染、缓存 WAV 章节试听,以及 Electron 驱动的 M4B 渲染/播放。多语音、多章节、取消/恢复及其它引擎仍需要真实运行时覆盖。
脚本语音标签现在在角色区域显示已保存的配置文件分配。只有当前脚本中出现的名字才会作为 voice_map 发送;如果标签被重新引入,分配会保留以供复用。分配给已移除配置文件的分配会阻止渲染,直到更改或重置为“默认”。章节、单词和预计运行时长计数复用 Tauri 脚本辅助工具。
Audiobook 预览方案使用后端解析器并暴露逐章节试听。试听发送与完整渲染相同的语音、角色、语言和发音输入,从而预热共享缓存。编辑这些输入会清除过期的试听。预览请求在导航时中止,绝不会替换完整渲染,并暴露自己的 Vidstack 播放。
生产覆盖现在暴露合成步骤、引导、采样温度、后处理、种子和重复变化。当活动引擎声明支持时,情感控制会出现。重置恢复共享的 Tauri 默认值;未触碰的字段从请求中省略。提取的 longformOverrides 辅助工具被两个应用使用,章节试听携带与完整渲染相同的覆盖。无缝衔接控制(行间间隔、段落间间隔、修剪引擎静音)位于同一面板;未触碰或重置的控制显示保留的服务器默认值(零间隔、修剪关闭)。为无缝衔接显式设置间隔并启用修剪;参见 docs/expressive-speech.md。
使用设置为本地已保存 profile 的 VOICESTUDIO_LIVE_PROFILE 运行 electron/tests/longform-live.mjs,以进行明确的真实渲染检查。它会确认当前模型在合成前已经安装。真实运行暴露了共享客户端中缺失的 JSON 请求头,以及数组形状的失败章节结果;回归测试现在覆盖了这两种情况。
Stories 现在包含具有已保存语音分配的命名角色、每行的角色选择、显式的每行语音覆盖、共享/全局语速和每行语速覆盖。语音解析和章节编译复用 Tauri 辅助函数。行可以通过支持键盘访问的按钮上移/下移;章节插入使用相同的标题语法。如果故事中的每个口播片段都已分配,则渲染时无需进行不必要的默认语音选择。被移除的 profile 会阻止渲染,而不是静默更改语音。
全书级语速位于 Stories 设置卡片中,语音列表下方。当某些行自带语速时,卡片会显示数量,并提供一键清除这些覆盖设置,使整本书都跟随滑块。
Stories Auto-cast 接受带标签的对白、剧本文本和带归属的散文。共享的本地解析器会追加行,复用现有角色/语音,并为新说话者分配可用的 profile。规范化后归为同一标识符的不同名称仍保持为独立角色。整个过程不涉及网络或 LLM 调用。
Stories 脚本也可以从已完成的配音构建,这会用该配音的说话者和行替换当前脚本、演员表和待处理导入。该操作位于 Dub 工作区中;参见 docs/electron-dubbing.md。
WebVTT(.vtt)字幕也会在本地导入;在口播行进入 Stories 之前,其提示元数据会被丢弃。
Paste & Split 提供三种预设:Sentences(限制大小,带最大字符数字段)、Paragraphs(在空行处拆分,默认)和 Chapters(每个章节正文占一行)。在所有预设中,# Heading 行始终单独成行。
Stories 在本地导入 TXT、Markdown 和 SRT;EPUB/PDF 仍使用共享的后端导入器。EPUB 导入器会跳过标记的分页以及封面、扉页、献词、目录和版权页等辅助部分。未标记的页面根据书籍自身的结构来判断:只有当阅读起始位置之前有一个简短的、无标题的、未列入目录的页面时才会被跳过,即使 EPUB 2 guide 或 EPUB 3 landmark 声明了该起始位置;带有标题或内容较多的未列入目录的前言会保留;只有在目录所列的文后内容已经结束后,书末同类散页才会被跳过,因此始终保留延续最后一章的未列入目录文件。诸如“Published by”和“Novels by”之类的出版社与书目标题,只在阅读起始位置之前的简短、未列入目录的页面上被跳过;仅凭一个含义模糊的标题(例如“Works”)不足以删除某个章节。未标记的数字段落和分页布局类仍作为叙述内容保留。章节标题优先使用 EPUB 3 目录,其次是 NCX 标签和标题;支持命名空间别名和已声明的编码。格式错误的可选导航会回退到标题,而格式错误的包/容器 XML 会报告导入错误。导入的文本会进入持久化的审阅缓冲区,然后通过 Auto-cast 或 Split into lines 追加到现有作品中。该拆分器与浏览器应用共享,并提供三种预设:Sentences(语句感知拆分器,同样使用 40-2000 字符限制)、Paragraphs(默认;每个空行分隔段落占一行,若文本完全没有空行则每行占一行;支持 LF、CRLF 和孤立 CR 行尾)和 Chapters(每个章节正文占一行,仅在章节标题处拆分——非空 # Title,与渲染器使用的语法相同)。章节标题始终单独成行,以便渲染为章节标记。
Electron 工作区将标题、默认语音、语言和输出格式放在一个固定的设置卡片中。高级演员表、制作和书籍控制使用独立的折叠卡片;保存、打开和删除命名项目位于窗格的 Projects 标签页中,紧挨这些控制项。编辑器保持其完整工作宽度;空的 Story 显示一个“添加第一行”操作,而空的 Audiobook 在脚本字段中直接显示支持的章节和语音标记。
Stories 行试听复用共享的 WAV 汇编器和规范的语音/停顿/语速解析器。试听与完整渲染使用相同的回退逻辑解析内联名称,提供停止按钮,并通过 Vidstack 独立播放。更改合成输入会清除陈旧的播放;导航会中止待处理的试听。浏览器覆盖率测试使用模拟生成来验证实际播放。
可选的 Stories Stems 部分使用共享的 Tauri 汇编器为每个角色渲染一个 WAV。它会报告已完成的角色分组,支持取消,并在完成后提供明确的单独下载。不会触发自动的多文件下载。输入更改会丢弃陈旧的下载链接。浏览器回归测试检查所有角色分组以及下载的 WAV 字节。共享的原生“另存为”桥接会在打包的 Windows 和 Linux 构建中写入渲染器生成的字节;macOS 仍由原生导出流程把关。
EPUB 本地清单和导航链接支持百分号编码的文件名,包括空格和 Unicode。百分号转义只解码一次;字面加号仍保留为文件名的一部分。
EPUB 章节遵守字节顺序标记和 XML/HTML 编码声明,包括旧式拉丁和 CJK 编码。不支持或无效的声明会使用共享的文本导入回退,因此单个错误声明不会导致整本书导入失败。
EPUB 解压限制涵盖必需元数据以及导航和章节内容。没有导航元素的导航文档会回退到章节标题。
长脚本布局¶
长脚本会在页面自身的滚动容器内扩展编辑器,因此永远不会覆盖下方的内容。生成或停止、章节跟踪器和渲染状态共享一个固定在设置窗格上的页脚。当窗格折叠时,同一面板改为固定在编辑器底部。当生成不可用时,面板会给出唯一原因:正在导入、引擎未就绪、没有口播行或没有脚本、某一行没有语音且未设置默认语音,或者某个发音词被列出了两次。
清除脚本¶
Stories 和 Audiobook 提供带确认的 清除脚本 功能。Stories 会移除所有台词、章节标记和待导入文本,同时保留演员表;Audiobook 则会清除其文稿。Web 版 Stories 编辑器还会清除待粘贴文本、停止播放、忽略迟到的预览结果,并在清除、删除或替换台词时释放预览音频。确认框也会列出待导入文本。清除已导入的脚本不会重新创建示例故事。
固定的跟踪器会在设置面板剩余高度内滚动,因此即使在紧凑窗口中,Stop 按钮也始终可及。折叠设置后,这些控件会移入编辑器,并在滚动时保持可见。
SRT 和 WebVTT 文稿导入会保留口语数字,包括倒计时和纯数字提示;提示索引、时间戳、WEBVTT 标头、NOTE/STYLE/REGION 块以及提示标识符会被移除。
已完成的渲染会以作品标题(书名,否则为第一章标题)显示在 Projects 中,并附有一行摘要:声音 · 速度 · 引擎 · 时长。可展开的 制作方式 面板会列出声音、速度、引擎、语言、格式、大小以及任何非默认设置;它仅包含计数和设置,绝不包含脚本文本。在详细信息记录之前生成的渲染会明确注明这一点。
渲染历史包含生效的采样设置(包括性能档默认值)。非有限请求值会被拒绝;较旧的畸形数字详情会被清理,以确保 Projects 和完成事件保持可读。
Clone、Stories 和 Audiobook 中的输出语言选择器会禁用所选引擎声明的有限集合之外的语言。Auto 仍然可用;未知或模型特定的语言集合仍可选择,并由后端校验。参考录音语言和配音翻译目标不受此输出语言控件的约束。切换引擎会保留已选语言,而不会改写已保存的项目。
已完成的渲染库会先按 Stories 和 Audiobooks 进行筛选,再应用行数限制,因此较新的配音任务不会隐藏较旧的已完成图书。
恢复操作会启动一个新任务,同时保留原始检查点,直到所有章节都成功渲染。检查点写入和同步是尽力而为的,因此被中断的恢复任务可能会同时留下原始检查点和新检查点;两者都可复用共享的章节缓存。关闭尚未开始的响应或中断渲染都会使原始计划保持可用,即使替换检查点无法保存也是如此。没有失败章节的渲染会淘汰原始检查点,即使替换检查点无法保存。部分输出会保留原始计划,以便重试其失败章节。
缓存清理会在渲染退出时运行,包括在 Stop 之后,并按最旧优先逐出未使用的章节和片段音频。每次渲染都会在报告进度或加载引擎之前保留现有输入,然后保持整个计划中的章节和片段输入受保护,直到其退出。这包括后续章节以及恢复的有声书尚未到达的可复用片段,还包括正在发布的临时 WAV。一旦计划的输入被识别,无关音频即可被逐出。移动数据目录后,定位较旧条目所需的受保护文件和 JSON 记录会计入缓存大小,因此预算可能会被超出,直到后续渲染能够将其逐出。失败或被取消的 FFmpeg 组装会移除其不完整的输出,并保留恢复检查点。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 om/VoiceStudio