跳转至

已被取代(2026-06-25) 参见 00-roadmap-elevenlabs-parity.md 与规范 01–03。保留以作历史参考。

Stories Editor 与 Audiobook — 成熟度规范

汇编于 2026-06-13。目标为 v0.3.x continuous-to-main 开发线。以下每个工作项均以上述两个功能的当前实现为基准(文件:行号引用见下文)。

0. TL;DR — 核心决策

Stories Editor 和 Audiobook 是同一任务的两个创作前端: 分章节长篇幅 TTS → 带章节标记的音频文件。目前它们构建在迥异的技术栈上,而这种分歧正是大多数成熟度差距的根源:

Stories Editor Audiobook
渲染 客户端 Web Audio 拼接(逐 chunk decodeAudioData,在内存中 concat) 服务端 GPU 池 → ffmpeg .m4b
状态 仅 localStorage(omnivoice.app) 尽力而为的 job_store,退出即丢弃
优势 多角色对白、逐行音色/语速、自动选角 章节标记、SSE 进度、可扩展到长书
上限 无法渲染 10 小时的长书(浏览器内存、无恢复、无响度标准化) 单一文本域,无封面/元数据,无预览/重试/恢复

建议:收敛到一个服务端章节化渲染核心;保留两个不同的创作前端。 Stories Editor 的 “Generate full” 会将其 cast/台词编译为 Audiobook 后端已在使用的同一 Chapter[]/Span[] 模型,并提交同一任务。这消除了重复工作,解除了 Stories 在浏览器端的规模上限,并且让两个功能免费获得恢复、响度标准化、封面和元数据。

Stories 保留快速的客户端单行预览(低延迟、无任务开销)。只有完整导出迁移到服务端。

考虑的替代方案: 将两者完全分离,各自独立成熟。已否决——这会使工作量翻倍(两套元数据系统、两条标准化路径、两套恢复机制),并让 Stories 永远无法渲染整本书长度的输出。如果负责人希望保持分离,则仅适用 §2 + §3 前端部分,并放弃 §1。


1. 共享渲染核心(新)— 后端

新模块 backend/services/longform_render.py,对 audiobook 管线进行泛化(该管线已有正确的形态)。

  • 规范任务模型(已存在于 backend/services/audiobook.py:47-86):Project → Chapter(title, spans) → Span(voice_id, text, pause_ms_after)。将其提升至此模块;Audiobook 与 Stories 都编译为该模型。
  • 复用 synthesize_chapter(audiobook.py:140)、build_chapter_ffmetadata(:186)、build_concat_list(:207)、build_m4b_cmd(:221)。将 build_*_cmd 泛化,使其接受 metadata + cover + format。
  • 新增能力(每项都是一个独立、可测试的纯构建器 + 接入步骤):
  • 全局元数据 → FFMETADATA [global]:title、artist(作者)、album、composer/narrator、date(年份)、genre、comment。
  • 封面图 → ffmpeg -i cover.jpg -map 2 -disposition:v attached_pic(MP4 COVR)。在服务端校验图片(大小/类型)。
  • 响度标准化 → 两遍 ffmpeg loudnorm,使用 ACX 预设(目标 ≈ -19…-21 LUFS 综合响度,≤ -3 dB 真实峰值,噪底 < -60 dB)。开关 + 预设(ACX / Podcast -16 LUFS / Off)。
  • 逐章节检查点 / 恢复:以 hash(span_texts + voice_ids + pauses + engine + voice refs) 为键缓存每个已渲染章节的 WAV。重试时,跳过哈希已有缓存 WAV 的章节 → 在不重新渲染已完成章节的情况下恢复失败/长任务。
  • 并行章节合成:将章节并发提交到现有 _gpu_pool(受池大小限制),而非严格串行。
  • 输出格式:m4b(带章节的 AAC,当前支持)、mp3(通过 ID3 CHAP 帧带章节)、per-chapter files(zip)、stems(每个音色一条音轨 —— Stories 现有的 stems 概念,服务端实现)。
  • 任务持久化 / 库:新增 alembic 表 longform_jobs(id、kind=audiobook|story、title、status、output_path、chapters JSON、metadata JSON、created_at)。完成的书/故事会重新出现在项目/库中,并可再次下载。向后兼容迁移(增量表)。

2. Audiobook 标签页 — 成熟度(前端 + 轻量后端)

锚点:frontend/src/pages/AudiobookTab.jsx、frontend/src/api/audiobook.ts、backend/api/routers/audiobook.py、backend/services/audiobook.py。

P0 - 元数据面板:标题、作者、朗读者、年份、类型、描述、封面图片选择器。接入 §1.1/§1.2。 - 逐章节预览:渲染单个章节(新增 POST /audiobook/preview,复用 synthesize_chapter),让用户在提交整本书之前先试听。 - 章节级重试:章节失败不会终止整个任务;将其标记,让用户只重跑该章节(利用 §1.4 的检查点)。 - 恢复:重新连接/重启中断的任务,跳过已缓存章节。 - 响度标准化开关(ACX 预设默认关闭;需显式选择启用)。

P1 - 导入 → 自动分章:.txt(按 # H1 / ^Chapter \d+ 切分)、EPUB(spine + TOC → 章节;本地解析,无需网络)、拖放文件。 - 发音词典:按项目设置 word→phoneme/重拼覆盖,在合成前应用(与 Stories 共享)。 - ETA + 更丰富的进度(已完成章节数/总数、已用时间、预计剩余时间)。

P2 - SSML-lite([emphasis]、[slow]/[fast]、[spell])映射到引擎 instruct + 分块速率。批处理(排队多本书)。


3. Stories Editor — 成熟度(前端 + 后端)

锚点:frontend/src/components/StoriesEditor.jsx(749 行单体)、frontend/src/store/storiesSlice.ts、frontend/src/utils/storyExport.js、frontend/src/utils/parseScript.js。

P0 - 将 “Generate full” 迁移到共享服务端任务(§1)。新增 storyToSpans() 编译器:cast + 台词 + [voice:]/[pause] 标记 → Chapter[]/Span[]。storyExport.js 缩减为仅预览(保留 StoriesEditor.jsx:271-322 处流畅的客户端单行播放)。 - 逐行重新生成(目前只有完整导出存在)。 - 中止/取消进行中的导出(目前循环没有中止信号)。 - 将情绪/语气字段接通到 instruct(storiesSlice.ts:17 “Phase 3” 桩代码;语气标签会插入文本标记,但从未进入合成环节)。

P1 - 组件拆分:CastPanel.jsx / StoryLine.jsx / LineDrawer.jsx (已在 docs/superpowers/specs/2026-05-30-stories-editor-studio-design.md 中标注)。 - 逐行波形 + 时长缩略图。 - 带标记的分章 M4B 导出(复用 §1;Stories 已在 StoriesEditor.jsx:46 检测 # 章节行)。 - 可选的服务器持久化项目(DB),使大型演员阵容/长脚本突破 localStorage 限制;惰性迁移现有 localStorage 项目。

P2 - EPUB/剧本导入打磨;更丰富的自动选角(性别/声音匹配)。


4. 跨领域关注点

  • 响度标准化、元数据+封面、续播/检查点、并行合成均位于 §1,并被两个功能共同使用——只需构建一次。
  • 发音词典共享服务(用于 Audiobook P1 + Stories)。
  • DB:通过 alembic 新增 longform_jobs(+ 可选的 story_projects),采用增量式、经过测试的升级路径。现有 omnivoice_data/ 和 localStorage 不受影响。
  • i18n:在 audiobook.* 和 stories.* 下新增键;不硬编码 CJK。
  • 测试:扩展 tests/test_audiobook.py —— loudnorm argv(两遍处理形态)、全局元数据 + 封面 ffmeta/argv、storyToSpans() 编译器、续播跳过(缓存章节哈希 → 跳过)、EPUB→章节解析。前端:元数据面板、逐行重新生成、中止。

5. 遵守的约束

  • 跨平台一致性:ffmpeg loudnorm / COVR / aac 在 mac/Win/Linux 上行为一致(ffmpeg 已随附)。响度开关默认关闭 → 不会产生默认行为差异。EPUB 解析在本地进行。
  • 本地优先:无云端、无账户;所有渲染/导入均在设备端完成。
  • 向后兼容数据:alembic 增量表;localStorage 项目惰性迁移,绝不破坏。
  • 版本管理:以 v0.3.x 行上的持续合入主分支补丁形式发布;除非所有者要求,否则不发布 RC,不新增次要版本。

6. 建议的 PR 切分

  1. 共享的 longform_render 核心 + loudnorm + 全局元数据/封面构建器 (仅后端,接在 Audiobook 后面)。先做纯构建器 + 测试。
  2. Audiobook 元数据 + 封面 UI。
  3. 逐章预览 + 重试 + 续播(检查点)。
  4. 文本/EPUB 导入 + 自动分章。
  5. Stories “Generate” → 共享任务;storyToSpans();逐行重新生成;emotion→instruct。
  6. Stories 组件拆分 + 逐行波形;分章 M4B。
  7. DB 任务库(Projects/Library 显示已完成的书/故事)。
  8. (P2)SSML-lite、批处理、发音词典 UI。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 om/VoiceStudio