AI 有声书生成工具
将文字转换为高质量音频,让阅读变成聆听
📖 项目 Wiki:.qoder/repowiki
🎯 项目简介
AI 有声书生成工具是一款面向内容创作者的音频制作工具,帮助自媒体作者、小说爱好者、学习者低成本制作音频内容。
核心功能
- 📝 文本转音频:支持长文本输入,智能分段处理
- 🎵 多种音色:10+ 种优质音色可选
- 🎛️ 参数调节:语速、音调、音量自由控制
- 🎧 在线播放:支持倍速播放、进度控制
- 📚 历史管理:音频记录、收藏功能
- 👑 会员体系:分级权益,灵活选择
🛠️ 技术栈
前端
- uniapp + Vue 3 + TypeScript
- Pinia 状态管理
- 支持 H5 和微信小程序
后端
- Node.js 18 + Koa 2.x
- MongoDB + Mongoose
- FFmpeg 音频处理
- 阿里云 TTS 语音合成
📁 项目结构
audio/
├── client/ # uniapp 前端项目
│ ├── src/
│ │ ├── pages/ # 页面
│ │ ├── store/ # 状态管理
│ │ ├── utils/ # 工具函数
│ │ └── types/ # 类型定义
│ └── package.json
│
├── server/ # Node.js 后端项目
│ ├── src/
│ │ ├── modules/ # 业务模块
│ │ ├── models/ # 数据模型
│ │ ├── middleware/ # 中间件
│ │ └── config/ # 配置
│ └── package.json
│
└── README.md
💰 成本与定价
基于阿里云百炼官方计费(2026-05),单位已对齐官方文档。
API 成本
| 服务 |
模型 |
单价 |
单位 |
| AI 文本生成 |
通义千问 Qwen-Plus |
输入 ¥0.8 / 输出 ¥2.0 |
/百万 token |
| TTS 语音合成 |
CosyVoice v3-flash |
¥1.0 |
/万字符 |
综合成本(1中文 ≈ 1.5 token,150字/分钟)
| 字数 |
AI文本 |
TTS合成 |
API成本 |
售价(×2) |
音频时长 |
| 1千字 |
¥0.003 |
¥0.10 |
¥0.10 |
¥0.21 |
~7分钟 |
| 1万字 |
¥0.03 |
¥1.00 |
¥1.03 |
¥2.06 |
~67分钟 |
| 10万字 |
¥0.30 |
¥10.00 |
¥10.30 |
¥20.61 |
~667分钟 |
| 100万字 |
¥3.00 |
¥100.00 |
¥103.00 |
¥206.00 |
~6,667分钟 |
TTS 占比 ~96%,文本生成几乎可忽略。
定价系数
售价 = API成本 × PRICING_COEFFICIENT(默认 2.0,可调整)
Token 包(按量购买)
| 项目 |
值 |
| 每包 |
100 分钟 |
| 单价 |
¥0.048/分钟(月价的 1.5 倍) |
| 包价 |
¥4.80 |
| 获得额度 |
¥3.20(按月价 ¥0.032/分钟计) |
🚀 快速开始
环境要求
- Node.js >= 18
- MongoDB >= 6.0
- FFmpeg(用于音频处理)
后端启动
cd server
# 安装依赖
npm install
# 复制环境配置
cp .env.example .env
# 启动开发服务
npm run dev
前端启动
cd client
# 安装依赖
npm install
# 启动 H5 开发服务
npm run dev:h5
# 编译微信小程序
npm run build:mp-weixin
⚙️ 配置说明
后端环境变量
# 服务配置
PORT=3000
NODE_ENV=development
# MySQL 数据库
DATABASE_URL="mysql://root:password@localhost:3306/audio-book"
# JWT
JWT_SECRET=your-jwt-secret
JWT_EXPIRES_IN=7d
# 阿里云 TTS
ALIYUN_ACCESS_KEY=your-access-key
ALIYUN_ACCESS_SECRET=your-access-secret
ALIYUN_APP_KEY=your-app-key
📡 API 接口
认证模块
POST /api/auth/send-code - 发送验证码
POST /api/auth/login - 手机号登录
GET /api/auth/user-info - 获取用户信息
TTS 模块
GET /api/tts/voices - 获取音色列表
POST /api/tts/generate - 生成音频
音频模块
GET /api/audio/list - 获取音频列表
GET /api/audio/:id - 获取音频详情
DELETE /api/audio/:id - 删除音频
PUT /api/audio/:id/favorite - 切换收藏
会员模块
GET /api/member/benefits - 获取会员权益
GET /api/member/status - 获取会员状态
POST /api/member/order - 创建订单
👑 会员权益
| 权益 |
免费版 |
月度会员 |
年度会员 |
| 价格 |
¥0 |
¥19.9/月 |
¥199/年 |
| 每日次数 |
3次 |
20次 |
无限 |
| 字数限制 |
5000字 |
50000字 |
无限 |
| 音色选择 |
基础 |
全部 |
全部 |
🔊 TTS 语音合成
设计原则
- 统一分段:所有 TTS Provider 使用 1000 字符/段,优先在标点处断开,确保韵律自然
- 统一异步:所有 Provider 使用异步模式(提交任务 → 轮询 → 下载),避免超时丢失音频
- 降级策略:MiniMax(默认)→ 阿里云 → Mock 模拟
各家 TTS API 限制参考
| 服务商 |
接口类型 |
单次最大长度 |
约等于音频 |
备注 |
| MiniMax |
异步长文本 |
1,000,000 字符 |
~33 小时 |
业界最长 |
| MiniMax |
同步 |
10,000 字符 |
~20 分钟 |
>3,000 推荐异步 |
| 阿里云 CosyVoice |
非流式/单向流式 |
20,000 字符 |
~33 分钟 |
当前使用异步模式 |
| 阿里云 CosyVoice |
WebSocket 流式 |
20,000/累计 200,000 |
~66 分钟 |
流式最佳 |
| 阿里云 |
传统长文本合成 |
80,000 字符 |
~4.4 小时 |
建议 40,000 以内 |
| 火山引擎/豆包 |
异步长文本 |
100,000 字符 |
~3.3 小时 |
音频保存 7 天 |
| 火山引擎/豆包 |
非流式 |
1,024 字节 |
~20 秒 |
建议 <300 字符 |
| 百度 |
长文本异步 |
100,000 字符 |
~3.3 小时 |
一次性合成 |
| 百度 |
短文本 |
5,120 汉字 |
~1.7 分钟 |
~10,240 字节 |
| 讯飞 |
长文本 TTS |
100,000 字符 |
~3.3 小时 |
万字级别快速合成 |
| 讯飞 |
流式在线 |
8,000 字节 |
~1.3 分钟 |
~4,000 汉字 |
| 腾讯云 |
长文本语音合成 |
10,000+ 字符 |
~20 分钟 |
每个 speak 标签 ≤150 字 |
| 腾讯云 |
基础语音合成 |
150 汉字 |
~15 秒 |
中文限制严格 |
| OpenAI |
TTS-1 / TTS-1-HD |
4,096 字符 |
~7 分钟 |
隐藏限制 |
| Google Cloud |
Text-to-Speech |
5,000 字节 |
~3 分钟 |
SSML 也计入 |
| Azure |
实时合成 |
~10 分钟音频 |
~10 分钟 |
按音频时长限制 |
| Azure |
Batch 批量合成 |
无硬性限制 |
>10 分钟 |
异步批量 |
| ElevenLabs |
Flash/Turbo |
5,000 字符 |
~8 分钟 |
付费计划 |
| ElevenLabs |
eleven_v3 |
3,000 字符 |
~5 分钟 |
表现力最强但限制低 |
| Fish Audio |
Fish Speech |
~8,192 tokens |
~5 分钟 |
上下文窗口限制 |
当前分段策略:1000 字符/段,覆盖所有主流接口。不满足 1000 字的接口(火山 300/腾讯 150/百度 60)应走各自异步长文本 API,而非短文本接口。
🔧 运维工具
音频问题扫描与修复
项目提供 server/scripts/fix-audio-issues.ts 脚本,用于检测和修复音频合并异常(如段落丢失、格式不匹配、时长不正确等问题)。
用法
cd server
# 扫描所有音频问题(仅检查,不修复)
npx tsx scripts/fix-audio-issues.ts
# 扫描并自动修复所有问题
npx tsx scripts/fix-audio-issues.ts --fix
# 仅修复指定章节
npx tsx scripts/fix-audio-issues.ts --fix --chapter 11
# 仅修复指定书籍的所有章节
npx tsx scripts/fix-audio-issues.ts --fix --book 5
# 重新合并所有 level=1 章节的音频(下载子节远程音频后合并)
npx tsx scripts/fix-audio-issues.ts --merge-all
# 重新合并指定书籍的 level=1 章节音频
npx tsx scripts/fix-audio-issues.ts --merge-all --book 5
检测能力
| 标记 |
问题类型 |
判断标准 |
说明 |
| 🔴 |
严重异常 |
语速 > 10 字/秒 |
合并丢失大部分段落,音频严重不完整 |
| 🟡 |
轻微异常 |
语速 6~10 字/秒 |
可能丢失部分段落,音频略短 |
| 🟠 |
格式不匹配 |
WAV 内容存为 .mp3 |
浏览器无法正常播放 |
| 🔵 |
时长缺失 |
audioDuration=0 但有 audioUrl |
数据库记录不完整 |
| 🟣 |
合并不完整 |
level=1 章音频 < 子节时长总和 80% |
章级合并丢失子节 |
修复策略
- 有本地分段文件 → 用 FFmpeg 重新合并所有分段,上传新音频
- WAV 存为 .mp3 → 转码为真正 MP3 格式,上传替换
- 合并不完整 → 调用
mergeChapterAudios 重新下载子节音频并合并
- 时长缺失 → 检测实际音频时长,更新数据库
- 远程 URL 且无本地分段 → 提示需重新生成音频(需人工处理)
⚠️ 脚本扫描时如果发现"疑似分段目录"(远程 URL 无法直接关联本地文件),仅作提示,不会自动修复,需人工确认后手动指定 --chapter 参数修复。
📦 部署方式
推荐:SSH 一行部署
零中间环节,最可靠。代码推送到 Git 后执行:
# 仅后端
ssh aliyunos4 "cd /data/ai/audio_codebuddy && git fetch origin master && git reset --hard origin/master && cd server && npx prisma generate && rm -rf dist && npm run build && pm2 reload server"
流程:git push → SSH → git fetch + reset --hard → npm build → pm2 reload
部署前端
# 本地构建
cd my-uniapp-vue3 && npm run build:h5
# 上传到服务器
scp -r dist/build/h5/* aliyunos4:/data/ai/audio/frontend/build/h5/
# 重启前端
ssh aliyunos4 "pm2 reload book-frontend"
部署后验证
# 检查服务状态
ssh aliyunos4 "pm2 status"
# 检查后端健康
ssh aliyunos4 "curl -s http://localhost:3100/health"
# 检查域名可访问
curl -sk -o /dev/null -w "HTTP %{http_code}" https://book.rrbrr.com/
手动步骤(无 SSH 别名时)
# 1. SSH 登录
ssh -p 22622 root@8.159.134.106
# 2. 服务器上执行
cd /data/ai/audio_codebuddy
git fetch origin master
git reset --hard origin/master
cd server
npx prisma generate
rm -rf dist
npm run build
pm2 reload server
🧪 自动化回归测试
项目内置两级自动化测试体系,确保每次上线前所有业务功能可用。
完整测试文档: TESTING.md
测试等级
| 级别 |
命令 |
耗时 |
覆盖范围 |
适用场景 |
| L1 冒烟 |
npm run test:smoke |
~2 min |
健康检查 + 27个 GET 端点 + 8个核心页面加载 |
每次部署后 |
| L2 回归 |
npm run test:regression |
~15 min |
23个业务模块完整 CRUD + 前端 E2E |
正式发版前 |
快速使用
cd server
# 冒烟测试(部署后快速验证)
npm run test:smoke
# 完整回归测试(发版前全面检查)
npm run test:regression
# 运行全部测试
npm run test:all
# 查看 HTML 报告
npm run test:report
# 部署前快速检查
npm run deploy:check
也可以通过项目根目录的 test.bat(Windows):
test smoke # 冒烟测试
test regression # 完整回归
test all # 全部测试
部署集成
# 启动服务并自动运行冒烟测试
bash init.sh --test
覆盖的业务模块
认证、TTS语音合成、音频播放器、收藏、播放历史、搜索、评论、通知、背景音乐、音频编辑、AI书籍生成、专辑管理、播放列表、草稿箱、视频生成、订阅支付、跨平台发布、会员中心、用户反馈、分享、用户偏好、分类浏览、签到 — 共 23 个模块。
测试结构
测试目录位于项目根 tests/,覆盖前后端全项目。
tests/
├── playwright.config.ts # Playwright 两级项目配置
├── global-setup.ts # 全局初始化(健康检查 + 测试用户)
├── global-teardown.ts # 全局清理(自动清理测试数据)
├── helpers/
│ ├── api-client.ts # 统一 API 请求封装
│ ├── test-data.ts # 测试数据工厂(创建/追踪/清理)
│ └── reporter.ts # 自定义报告(AI 可读 Markdown + JSON)
├── smoke/ # L1 冒烟测试
│ ├── health.spec.ts
│ ├── api-readonly.spec.ts
│ └── pages-load.spec.ts
├── regression/ # L2 回归测试(按模块拆分)
│ ├── 01-auth.spec.ts
│ ├── 02-tts.spec.ts
│ ├── ...
│ └── 23-frontend-e2e.spec.ts
└── test-results/ # 测试输出(自动生成)
├── ai-summary.md # 🤖 AI 可读摘要
├── ai-summary.json # 结构化 JSON 结果
└── screenshots/ # 截图
AI 可读输出
测试运行后自动生成 tests/test-results/ai-summary.md,包含:
- 汇总表格:总数 / 通过 / 失败 / 跳过 / 通过率
- 按模块详情:每个用例的结果 + 耗时 + 截图路径
- 失败详情:失败用例的错误信息
AI 编辑器可直接读取该文件判断测试结果。
前端 E2E 验收用例详情
每个用例遵循 Navigate → Locate → Assert → Screenshot 流程,验证具体 UI 元素和视觉效果。
用例文件:tests/regression/23-frontend-e2e.spec.ts
E01 — 首页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/index/index |
页面加载完成(networkidle) |
| 2 |
检查根元素 |
#app 可见 |
| 3 |
定位搜索栏 input |
存在 placeholder="搜索..." 的输入框 |
| 4 |
定位通知按钮 |
页面包含 🔔 元素 |
| 5 |
检查内容区域 |
存在"最近收听"或"欢迎"引导文字 |
| 6 |
截图保存 |
→ test-results/screenshots/e2e-E01-index.png |
E02 — 生成音频页(Tab 页)
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/create/index |
页面加载完成 |
| 2 |
验证页面标题 |
"生成音频" 文字可见 |
| 3 |
验证文本输入区 |
textarea[placeholder*="请输入"] 可见 |
| 4 |
验证音色卡片 |
"选择音色" 文字可见 |
| 5 |
验证参数调节卡片 |
"参数调节" 文字可见 |
| 6 |
验证 AI 生成入口 |
"AI 生成内容" 按钮可见(如存在) |
| 7 |
验证主操作按钮 |
"生成音频" 按钮可见 |
| 8 |
截图保存 |
→ test-results/screenshots/e2e-E02-create.png |
E03 — 书籍生成列表页(Tab 页)
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/book-generator/index |
页面加载完成 |
| 2 |
验证页面标题 |
"书籍生成" 可见 |
| 3 |
验证创建入口 |
"创建新书籍" 按钮可见 |
| 4 |
验证列表或空状态 |
存在"暂无书籍"空状态 或 书籍列表卡片 |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E03-book-list.png |
E04 — 我的(Tab 页)
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/mine/index |
页面加载完成 |
| 2 |
验证根元素 |
#app 可见 |
| 3 |
验证菜单项 |
至少一项存在:"会员中心"、"设置"、"历史记录"、"书籍生成" |
| 4 |
验证退出登录 |
"退出登录" 按钮可见(如已登录) |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E04-mine.png |
E05 — 音频播放器页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/player/index |
页面加载完成 |
| 2 |
验证播放器 UI |
存在"正在播放"标题、或"播放列表"区域、或"暂无音频"空状态 |
| 3 |
截图保存 |
→ test-results/screenshots/e2e-E05-player.png |
E06 — 创建书籍页(一键/简洁模式)
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/book-generator/create |
页面加载完成 |
| 2 |
验证页面标题 |
"创建书籍" 可见 |
| 3 |
验证书名输入框 |
input[placeholder*="书名"] 可见(可选字段) |
| 4 |
验证描述输入框 |
textarea[placeholder*="描述"] 可见(必填字段) |
| 5 |
验证智能推荐 |
"智能推荐" 按钮可见 |
| 6 |
验证高级选项 |
"高级选项" 折叠面板可见 |
| 7 |
验证创建按钮 |
"创建书籍" 主按钮可见 |
| 8 |
验证模式切换 |
"切换到交互模式" 按钮可见 |
| 9 |
截图保存 |
→ test-results/screenshots/e2e-E06-book-create.png |
E07 — 交互式创建页(4步向导)
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/book-generator/interactive |
页面加载完成 |
| 2 |
验证页面标题 |
"交互式创建" 可见 |
| 3 |
验证步骤 Step 1 |
"信息输入" 标签可见 |
| 4 |
验证步骤 Step 2 |
"AI规划" 标签可见 |
| 5 |
验证 Step 1 标题 |
"第一步" 文字可见 |
| 6 |
验证书名输入 |
input[placeholder*="《"] 可见(必填) |
| 7 |
验证 AI 推荐 |
"AI智能推荐" 按钮可见 |
| 8 |
验证下一步按钮 |
"AI分析" 按钮可见 |
| 9 |
验证模式切换 |
"切换到一键模式" 按钮可见 |
| 10 |
截图保存 |
→ test-results/screenshots/e2e-E07-interactive.png |
E08 — 章节详情页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/book-generator/chapter-detail |
页面加载完成 |
| 2 |
验证页面框架 |
#app 可见,页面至少渲染基础框架 |
| 3 |
验证章节信息 |
存在"章节"或"第N章"编号标签 |
| 4 |
验证内容/操作区 |
页面包含内容渲染区或音频/视频操作按钮 |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E08-chapter-detail.png |
E09 — 会员订阅页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/member/index |
页面加载完成 |
| 2 |
验证页面标题 |
"订阅" 文字可见 |
| 3 |
验证 Token 余额 |
"Token" 余额数字可见 |
| 4 |
验证套餐卡片 |
至少一个 "选择此套餐" 按钮可见 |
| 5 |
验证支付方式 |
"支付宝" 或 "微信支付" 标签可见 |
| 6 |
验证订阅按钮 |
"立即订阅" 或 "免费套餐" 按钮可见 |
| 7 |
截图保存 |
→ test-results/screenshots/e2e-E09-member.png |
E10 — 专辑列表页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/albums/index |
页面加载完成 |
| 2 |
验证页面标题 |
"专辑" 文字可见 |
| 3 |
验证内容 |
存在专辑网格列表 或 "暂无专辑" + "创建你的第一个专辑" 空状态 |
| 4 |
截图保存 |
→ test-results/screenshots/e2e-E10-albums.png |
E11 — 搜索页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/search/index |
页面加载完成 |
| 2 |
验证搜索框 |
input[placeholder*="搜索"] 可见 |
| 3 |
验证搜索按钮 |
"搜索" 按钮可见 |
| 4 |
验证推荐区域 |
存在"搜索历史"标签列表、或"热门推荐"区域、或空提示文字 |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E11-search.png |
E12 — 历史记录页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/history/index |
页面加载完成 |
| 2 |
验证时间筛选 |
"全部" 标签可见 |
| 3 |
验证时间标签 |
"今天" 或 "本周" 标签可见 |
| 4 |
验证视图切换 |
"聚合" 或 "列表" 视图切换按钮可见 |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E12-history.png |
E13 — 视频生成页
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/video-generator/index |
页面加载完成 |
| 2 |
验证页面标题 |
"视频生成" 可见 |
| 3 |
验证创建入口 |
"创建新视频" 按钮可见 |
| 4 |
验证引导文案 |
"图片 + 音频 = 精美视频" 可见 |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E13-video-gen.png |
E14 — 首页 JS 运行时错误检查
| 步骤 |
操作 |
预期结果 |
| 1 |
注册 pageerror 监听器 |
收集所有 JS 运行时错误 |
| 2 |
Navigate to /#/pages/index/index |
页面加载完成,等待 5 秒 |
| 3 |
过滤非关键错误 |
忽略 ResizeObserver、Script error、favicon 等 |
| 4 |
断言 |
无 is not a function / Cannot read properties 类严重错误 |
E15 — TabBar 导航一致性
| 步骤 |
操作 |
预期结果 |
| 1 |
Navigate to /#/pages/index/index |
#app 可见 |
| 2 |
Navigate to /#/pages/create/index |
#app 可见 |
| 3 |
Navigate to /#/pages/book-generator/index |
#app 可见 |
| 4 |
Navigate to /#/pages/mine/index |
#app 可见 |
| 5 |
截图保存 |
→ test-results/screenshots/e2e-E15-tabs.png |
📝 开发计划
MVP 阶段(2周)
V1.0 阶段(1个月)
V1.5 阶段(2-3个月)
🎙️ TTS 参考文档
- tts-summary.md — 阿里云百炼 TTS 语音合成完整参考,涵盖三大模型对比、CosyVoice 80+ 音色速查表、指令控制(Instruct)使用指南、声音设计 API、场景推荐等内容。
📄 License
MIT License