之前朗读能力只是占位:“StoryPage.audio_url”虽然指向 “/api/v1/assets/audio/{story_id}/{页码}.mp3”,但磁盘上没有 MP3,“assets.py” 只返回 404 占位 JSON;阅读页点击插图 “audio.play()” 也会静默失败。用户看得到「AI 朗读中」文案,却听不到声音。

本篇接入 DashScope CosyVoice,补齐创作流水线 narrating 阶段、按需合成、阅读页音色切换与翻页自动联播。

一、后端 TTS 合成:CosyVoice + 按音色落盘

1.1 改动方向

“TTSProvide” 仅有配置检查,没有调用 SDK 的逻辑。

音频路径约定是“data/audio/{story_id}/{页码}.mp3”,但从未写入文件。

“.env”里“TTS_VOICE_NAME=default-child-friendly”不是 CosyVoice 合法音色 ID。

1.2 改法:“story_tts.py”统一合成入口

新增 “backend/app/services/story_tts.py”,使用 “dashscope.audio.tts_v2.SpeechSynthesizer”非流式 “call()”,按页写入:

data/audio/{story_id}/{voice_id}/{页码}.mp3

核心逻辑:

def audio_page_path(*, story_id, voice_id, page_index):
    return data_root() / "audio" / story_id / voice_id / f"{page_index}.mp3"

def synthesize_page_mp3(*, text, out_path, voice, api_key, speech_rate=0.95):
    synthesizer = SpeechSynthesizer(
        model=voice.model,
        voice=voice.id,
        format=AudioFormat.MP3_22050HZ_MONO_256KBPS,
        speech_rate=speech_rate,
    )
    audio = synthesizer.call(text.strip())
    out_path.write_bytes(audio)

 1.3 Key 与默认音色

“TTSProvider”支持 Key 回退,与出图共用 DashScope 账号即可:

TTS_API_KEY → DASHSCOPE_API_KEY → IMAGE_API_KEY

默认音色改为“longxiaochun_v2”(活泼童声);占位值 “default-child-friendly”自动映射到该 ID。

env新增

TTS_API_KEY=(留空复用 IMAGE_API_KEY)

TTS_MODEL_NAME=cosyvoice-v3-flash

TTS_VOICE_NAME=longxiaochun_v2

二、音色目录与按需合成:切换偏好不必重跑全书

2.1 可选音色(本地 Key 实测可用)

“tts_voices.py”维护白名单目录,阅读页下拉与 API 校验共用:

音色 ID

展示名

模型

适用场景

longxiaochun_v2

龙小春 · 活泼童声

cosyvoice-v2

儿童绘本默认

longwan_v2

龙婉 · 温柔女声

cosyvoice-v2

睡前故事

longanyang

龙安阳 · 沉稳男声

cosyvoice-v3-flash

讲故事男声

longxiaochun

龙小春 · 经典童声

cosyvoice-v1

经典童声

不同音色使用不同 CosyVoice 模型版本;目录里为每个 ID 绑定了对应“model”,避免 voice/model 不匹配导致合成失败。

2.2 API

接口

作用

GET /api/v1/tts/voices

返回音色列表、“default_voice”、“tts_configured”

GET /api/v1/assets/audio/{story_id}/{page}.mp3?voice={id}

读缓存;不存在则读 DB 取 “page.text”现场合成再返回

2.3 安全与路径

“voice” 参数必须在 “VOICE_CATALOG” 白名单内,否则 400。

目录名只允许 “a-z A-Z 0-9”,防止路径注入

三、阅读页:音色偏好 + 翻页联播

3.1新版“reader.html”交互

控件行为
顶栏“音色”下拉从“GET /tts/voices”加载;偏好存“localStorage”(“storybook.ttsVoice”)

听故事按钮

开关听书模式;偏好存"storybook.listenMode"

暂停

关闭听书模式并停止当前音频

点击插图

等同开启听故事并播放当前页

3.2 翻页自动联播

听故事模式开启时:

1. "renderPage()"结束自动 "playCurrentPage({ autoplay: true })"。

2. 当前页"audio.onended" → "currentPage += 1" → 渲染下一页 → 继续播放。

3. 最后一页播完提示「故事朗读完毕」。

4. 手动上一页 / 下一页会先 "stopAudio()",再渲染;若仍在听故事模式则对新页自动开播。

切换音色时:若正在朗读或听故事模式已开,立即用新音色重播当前页。

四、删书清理与磁盘布局

“story_assets.purge_story_image_assets”扩展为同时删除:

data/audio/{story_id}/ 

仍保留“page_attempts”审计与 RAG 语料。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐