Skip to main content

long-text-tts

将超长文本(最多10万字符)异步转换为语音音频,支持多音库/语速/语调设置,适用于有声内容、新闻播报等场景

Source facts

Repository
qq5855144/GitHubM
Last source activity
May 30, 2026 at 04:51
Detected SKILL.md language
Chinese
Stars
29
Forks
8

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
long-text-tts
description
将超长文本(最多10万字符)异步转换为语音音频,支持多音库/语速/语调设置,适用于有声内容、新闻播报等场景
license
MIT
## 能力概述 百度 AI 长文本语音合成服务,通过两步异步接口将超长文本转换为音频: 1. **创建任务**(`POST /rpc/2.0/tts/v1/create`):提交文本数组,返回 `task_id` 2. **查询任务**(`POST /rpc/2.0/tts/v1/query`):轮询任务状态,成功后返回 `speech_url` 音频链接 | 能力维度 | 说明 | |----------|------| | 文本上限 | 总字数不超过 10 万字符 | | 音频格式 | mp3-16k(默认)、mp3-48k、wav | | 音库选项 | 度小美(0)、度小宇(1)、度逍遥(3)、度丫丫(4) | | 语速/音调/音量 | 均为 0–15,默认均为 5 | | 任务状态 | Running / Success / Failure / Created | | 结果有效期 | 音频下载链接储存 72 小时 | | 平台支持 | Web、MiniProgram | **平台差异关键说明:** | 差异点 | Web | MiniProgram | |--------|-----|-------------| | 音频播放方式 | `<audio>` 标签或 `Audio` 对象,直接使用 Supabase Storage 公开 URL | Taro `InnerAudioContext`,使用 https 链接(须将 http 升级为 https) | | 轮询计数器实现 | `useRef` 防止重复轮询链 | 同 Web,必须用 `useRef` 而非 `useState` | | 缓存策略 | 优先读 Supabase DB 缓存的 `speech_url`,避免重复调用 | 同 Web | --- ## 生成期用法(Agent 直接调用) 完整异步工作流:提交任务 → 轮询直到成功/失败。详见 `references/tts-create-api.md`(提交接口)和 `references/tts-query-api.md`(查询接口)。 ```typescript // 完整异步工作流:submit → poll → result const apiKey = process.env["INTEGRATIONS_API_KEY"]!; // platform_managed,密钥由平台注入 async function createTTSTask( text: string[], options?: { format?: "mp3-16k" | "mp3-48k" | "wav"; voice?: 0 | 1 | 3 | 4; speed?: number; pitch?: number; volume?: number; break?: number; } ): Promise<string> { const body: Record<string, unknown> = { text }; if (options?.format !== undefined) body.format = options.format; if (options?.voice !== undefined) body.voice = options.voice; if (options?.speed !== undefined) body.speed = options.speed; if (options?.pitch !== undefined) body.pitch = options.pitch; if (options?.volume !== undefined) body.volume = options.volume; if (options?.break !== undefined) body.break = options.break; const response = await fetch( "https://app-bo4w33bsdqm9-api-nYWNozBb8X3L-gateway.appmiaoda.com/rpc/2.0/tts/v1/create", { method: "POST", headers: { "Content-Type": "application/json", "X-Gateway-Authorization": `Bearer ${apiKey}`, }, body: JSON.stringify(body), } ); if (!response.ok) throw new Error(`HTTP error: ${response.status}`); const json = await response.json(); if (!json.task_id) throw new Error(`Create failed: ${JSON.stringify(json)}`); return json.task_id as string; } async function queryTTSTask(taskId: string): Promise<{ task_status: "Success" | "Running" | "Failure" | "Created"; speech_url?: string; }> { const response = await fetch( "https://app-bo4w33bsdqm9-api-Q9KWZ2jy8W09-gateway.appmiaoda.com/rpc/2.0/tts/v1/query", { method: "POST", headers: { "Content-Type": "application/json", "X-Gateway-Authorization": `Bearer ${apiKey}`, }, body: JSON.stringify({ task_ids: [taskId] }), } ); if (!response.ok) throw new Error(`HTTP error: ${response.status}`); const json = await response.json(); const taskInfo = json.tasks_info?.[0]; if (!taskInfo) throw new Error("No task info returned"); return { task_status: taskInfo.task_status, speech_url: taskInfo.task_result?.speech_url, }; } async function generateLongTextTTS( text: string[], options?: Parameters<typeof createTTSTask>[1] ): Promise<string> { const taskId = await createTTSTask(text, options); const POLL_INTERVAL_MS = 7000; const TIMEOUT_MS = 10 * 60 * 1000; // 10 分钟 const deadline = Date.now() + TIMEOUT_MS; while (Date.now() < deadline) { await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS)); const result = await queryTTSTask(taskId); if (result.task_status === "Success" && result.speech_url) { return result.speech_url; } if (result.task_status === "Failure") { throw new Error(`TTS task failed: ${taskId}`); } // Running / Created → 继续轮询 } throw new Error(`TTS task ${taskId} timed out after 10 minutes`); } ``` --- ## 生成后用法(应用内通过 Edge Function 调用) 需要两个 Edge Function:`tts-create`(提交任务)和 `tts-query`(查询结果)。 - `tts-create` 的完整代码及前端调用见 `references/tts-create-api.md` - `tts-query` 的完整代码及前端调用(含轮询、缓存、错误处理)见 `references/tts-query-api.md` **前端整体架构(Web & MiniProgram 通用):** 1. 调用 `tts-create` Edge Function 提交任务,获取 `task_id` 2. 将 `task_id` 存入 Supabase DB(状态 `Running`) 3. 使用 `useRef` 计数器轮询 `tts-query`,避免双重轮询链 4. 任务成功后,Edge Function 将 `speech_url` 转存至 Supabase Storage,返回永久 `publicUrl` 5. 将 `publicUrl` 更新至 DB(状态 `Success`),前端展示播放控件 > 下次加载时优先从 DB 读取 `speech_url`;如已有完成记录,直接使用缓存 URL,跳过轮询。 --- ## 注意事项 - **密钥安全**:`INTEGRATIONS_API_KEY` 仅在 Edge Function 服务端使用,严禁暴露到前端 - **计费**:每次调用 `tts-create` 计费,原价 ¥13.5/千次,折扣价 ¥11.25/千次;`tts-query` 不计费 - **错误处理**:务必处理 429(配额超限)和 402(余额不足);Baidu TTS `error_code: 18` 表示 QPS 超限,需退避重试 - **音频链接有效期**:`speech_url` 仅保留 72 小时,应在 Edge Function 侧及时转存至 Supabase Storage - **MiniProgram 特别说明**:原始 `speech_url` 使用 http,微信小程序会阻断非 https 资源,需升级协议或使用 Supabase Storage 的 https URL
View on GitHub