Spaces:
Paused
Paused
| <html lang="zh"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ttsfm</title> | |
| <link rel="stylesheet" href="styles.css"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <div class="content-wrapper"> | |
| <!-- Header Section --> | |
| <header class="main-header"> | |
| <div class="header-top"> | |
| <h1>ttsfm</h1> | |
| <a href="https://github.com/dbccccccc/ttsfm" target="_blank" class="github-link"> | |
| <i class="fab fa-github"></i> | |
| <span>GitHub</span> | |
| </a> | |
| </div> | |
| <p class="subtitle">支持多种语音的文本转语音 API</p> | |
| <div class="header-bottom"> | |
| <div class="version-badge"> | |
| <span>版本: <strong id="version">1.3.0</strong></span> | |
| </div> | |
| <div class="language-selector"> | |
| <a href="index.html" class="lang-btn">English</a> | |
| <button class="lang-btn active" data-lang="zh">中文</button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Disclaimer Section --> | |
| <section class="content-section disclaimer-notice"> | |
| <div class="disclaimer-container"> | |
| <div class="disclaimer-icon"> | |
| <i class="fas fa-info-circle"></i> | |
| </div> | |
| <div class="disclaimer-content"> | |
| <h2>免责声明</h2> | |
| <p>此项目仅用于学习测试,请使用<a href="https://platform.openai.com/docs/guides/audio" target="_blank">https://platform.openai.com/docs/guides/audio</a> OpenAI的官方服务进行生产环境使用。</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Status Section --> | |
| <section class="content-section status-section"> | |
| <h2>服务状态</h2> | |
| <div class="status-container"> | |
| <div class="status-card"> | |
| <div class="status-header"> | |
| <h3>队列状态</h3> | |
| <div class="status-indicator" id="status-indicator"></div> | |
| </div> | |
| <div class="queue-stats"> | |
| <div class="stat-item"> | |
| <span class="stat-label">活动请求:</span> | |
| <span class="stat-value" id="queue-size">0</span> | |
| </div> | |
| <div class="stat-item"> | |
| <span class="stat-label">最大容量:</span> | |
| <span class="stat-value" id="max-queue-size">-</span> | |
| </div> | |
| </div> | |
| <div class="queue-progress-container"> | |
| <div class="queue-progress-bar" id="queue-progress-bar"></div> | |
| </div> | |
| <div class="queue-load-text" id="queue-load-text">无负载</div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Playground Section --> | |
| <section class="content-section playground-section"> | |
| <h2>立即体验</h2> | |
| <div class="playground-container"> | |
| <div class="playground-form"> | |
| <div class="form-group"> | |
| <label for="playground-text">要转换的文本</label> | |
| <textarea id="playground-text" rows="4" placeholder="输入要转换为语音的文本..."></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="playground-instructions">指令</label> | |
| <textarea id="playground-instructions" rows="2" placeholder="例如:用欢快和兴奋的语气说话"></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="playground-voice">语音</label> | |
| <select id="playground-voice"> | |
| <option value="alloy">Alloy</option> | |
| <option value="ash">Ash</option> | |
| <option value="ballad">Ballad</option> | |
| <option value="coral">Coral</option> | |
| <option value="echo">Echo</option> | |
| <option value="fable">Fable</option> | |
| <option value="onyx">Onyx</option> | |
| <option value="nova">Nova</option> | |
| <option value="sage">Sage</option> | |
| <option value="shimmer">Shimmer</option> | |
| <option value="verse">Verse</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="playground-format">响应格式</label> | |
| <select id="playground-format"> | |
| <option value="mp3">MP3</option> | |
| <option value="opus">Opus</option> | |
| <option value="aac">AAC</option> | |
| <option value="flac">FLAC</option> | |
| <option value="wav">WAV</option> | |
| <option value="pcm">PCM</option> | |
| </select> | |
| </div> | |
| <button id="playground-submit" class="playground-button"> | |
| <i class="fas fa-play"></i> 生成语音 | |
| </button> | |
| </div> | |
| <div class="playground-output"> | |
| <div class="audio-section"> | |
| <h3>语音预览</h3> | |
| <div id="preview-audio" class="audio-player"></div> | |
| </div> | |
| <div class="audio-section"> | |
| <h3>生成结果</h3> | |
| <div id="playground-status" class="playground-status"></div> | |
| <div id="playground-audio" class="audio-player"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Main Content --> | |
| <main class="main-content"> | |
| <!-- Quick Start --> | |
| <section class="content-section"> | |
| <h2>快速开始</h2> | |
| <p>选择您喜欢的编程语言开始使用 API:</p> | |
| <!-- Python Example --> | |
| <div class="code-block"> | |
| <div class="code-header"> | |
| <span class="code-language">Python</span> | |
| <button class="copy-button" onclick="copyCode(this)"> | |
| <i class="fas fa-copy"></i> | |
| </button> | |
| </div> | |
| <pre><code class="language-python"> | |
| import requests | |
| import os | |
| def generate_speech(text, voice="alloy", format="mp3", instructions=None): | |
| url = "https://ttsapi.site/v1/audio/speech" | |
| headers = { | |
| "Content-Type": "application/json" | |
| } | |
| data = { | |
| "input": text, | |
| "voice": voice, | |
| "response_format": format | |
| } | |
| # 如果提供了指令,则添加到请求中 | |
| if instructions: | |
| data["instructions"] = instructions | |
| response = requests.post(url, json=data, headers=headers) | |
| if response.status_code == 200: | |
| # 根据格式获取适当的文件扩展名 | |
| ext = format.lower() | |
| filename = f"output.{ext}" | |
| # 保存音频文件 | |
| with open(filename, "wb") as f: | |
| f.write(response.content) | |
| print(f"音频已保存为 {filename}") | |
| return filename | |
| else: | |
| error = response.json() | |
| print(f"错误: {response.status_code}, {error}") | |
| return None | |
| # 使用示例 | |
| text = "你好,这是一个测试。" | |
| voice = "alloy" | |
| format = "mp3" # 支持的格式:mp3、opus、aac、flac、wav、pcm | |
| instructions = "请用欢快和兴奋的语气说话" | |
| # 使用默认格式(MP3)生成语音 | |
| generate_speech(text, voice, instructions=instructions) | |
| # 使用 WAV 格式生成语音 | |
| generate_speech(text, voice, format="wav", instructions=instructions)</code></pre> | |
| </div> | |
| <!-- JavaScript Example --> | |
| <div class="code-block"> | |
| <div class="code-header"> | |
| <span class="code-language">JavaScript</span> | |
| <button class="copy-button" onclick="copyCode(this)"> | |
| <i class="fas fa-copy"></i> | |
| </button> | |
| </div> | |
| <pre><code class="language-javascript">async function generateSpeech(text, voice = 'alloy', format = 'mp3', instructions = null) { | |
| const response = await fetch('https://ttsapi.site/v1/audio/speech', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| input: text, | |
| voice: voice, | |
| response_format: format, | |
| ...(instructions && { instructions }) | |
| }) | |
| }); | |
| if (response.ok) { | |
| const blob = await response.blob(); | |
| // 创建音频元素用于播放 | |
| const audio = new Audio(URL.createObjectURL(blob)); | |
| audio.play(); | |
| // 可选:下载文件 | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `output.${format}`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| document.body.removeChild(a); | |
| return blob; | |
| } else { | |
| const error = await response.json(); | |
| console.error('错误:', error); | |
| throw error; | |
| } | |
| } | |
| // 使用示例 | |
| const text = '你好,这是一个测试。'; | |
| const voice = 'alloy'; | |
| const format = 'mp3'; // 支持的格式:mp3、opus、aac、flac、wav、pcm | |
| const instructions = '请用欢快和兴奋的语气说话'; | |
| // 使用默认格式(MP3)生成语音 | |
| generateSpeech(text, voice, undefined, instructions); | |
| // 使用 WAV 格式生成语音 | |
| generateSpeech(text, voice, 'wav', instructions);</code></pre> | |
| </div> | |
| </section> | |
| <!-- Available Voices --> | |
| <section class="content-section"> | |
| <h2>可用语音</h2> | |
| <div class="voice-list"> | |
| <span class="voice-name">alloy</span> | |
| <span class="voice-name">ash</span> | |
| <span class="voice-name">ballad</span> | |
| <span class="voice-name">coral</span> | |
| <span class="voice-name">echo</span> | |
| <span class="voice-name">fable</span> | |
| <span class="voice-name">onyx</span> | |
| <span class="voice-name">nova</span> | |
| <span class="voice-name">sage</span> | |
| <span class="voice-name">shimmer</span> | |
| <span class="voice-name">verse</span> | |
| </div> | |
| </section> | |
| <!-- API Reference --> | |
| <section class="content-section"> | |
| <h2>API 参考</h2> | |
| <div class="api-endpoint"> | |
| <h3>生成语音(OpenAI 兼容)</h3> | |
| <pre><code class="language-http">POST /v1/audio/speech</code></pre> | |
| <div class="request-body"> | |
| <h4>请求参数</h4> | |
| <table class="params-table"> | |
| <tr> | |
| <th>参数</th> | |
| <th>类型</th> | |
| <th>必需</th> | |
| <th>描述</th> | |
| </tr> | |
| <tr> | |
| <td>input</td> | |
| <td>string</td> | |
| <td>是</td> | |
| <td>要转换为语音的文本</td> | |
| </tr> | |
| <tr> | |
| <td>voice</td> | |
| <td>string</td> | |
| <td>是</td> | |
| <td>要使用的语音(见可用语音)</td> | |
| </tr> | |
| <tr> | |
| <td class="partial-param">instructions</td> | |
| <td>string</td> | |
| <td>否</td> | |
| <td><em>发送到后端服务时映射为 "prompt" 参数。可用于指导语音情感或风格。</em></td> | |
| </tr> | |
| <tr> | |
| <td class="partial-param">response_format</td> | |
| <td>string</td> | |
| <td>否</td> | |
| <td>音频响应的格式。支持的格式:mp3、opus、aac、flac、wav、pcm。默认为 mp3。</td> | |
| </tr> | |
| <tr> | |
| <td class="compat-param">model</td> | |
| <td>string</td> | |
| <td>否</td> | |
| <td><em>仅用于 OpenAI 兼容性 - 完全忽略。</em></td> | |
| </tr> | |
| <tr> | |
| <td class="compat-param">speed</td> | |
| <td>number</td> | |
| <td>否</td> | |
| <td><em>仅用于 OpenAI 兼容性 - 完全忽略。</em></td> | |
| </tr> | |
| </table> | |
| <div class="compatibility-notice"> | |
| <p><strong>注意:</strong> <span class="compat-inline">灰色</span> 的参数完全被服务忽略或可能导致误导行为。只有 <code>input</code>、<code>voice</code> <code>response_format</code> 和 <code>instructions</code> 会影响实际的 TTS 输出。</p> | |
| </div> | |
| <!-- Instructions Parameter Details --> | |
| <div class="parameter-details"> | |
| <h4>指令参数的工作原理</h4> | |
| <p><code>instructions</code> 参数在发送到后端服务时映射为 <code>prompt</code> 参数。它可用于指导语音情感、语气或风格。以下是一些有效的指令示例:</p> | |
| <ul class="examples-list"> | |
| <li><strong>情感指导:</strong> "请用欢快和兴奋的语气说话"</li> | |
| <li><strong>角色模仿:</strong> "请模仿一位睿智的老巫师说话"</li> | |
| <li><strong>上下文提示:</strong> "这是读给孩子的,请用温柔的语气"</li> | |
| <li><strong>朗读风格:</strong> "请以新闻广播的风格朗读"</li> | |
| </ul> | |
| <div class="tip-box"> | |
| <p><strong>提示:</strong> 请保持指令清晰简洁。过于复杂的指令可能无法被正确解释。</p> | |
| </div> | |
| </div> | |
| <h4>响应格式</h4> | |
| <p>API 以请求的格式返回音频,具有以下标头:</p> | |
| <ul> | |
| <li><code>Content-Type</code>:根据请求的格式(例如,MP3 为 "audio/mpeg")</li> | |
| <li><code>Access-Control-Allow-Origin</code>:*(启用 CORS)</li> | |
| </ul> | |
| <h4>错误响应</h4> | |
| <table class="error-table"> | |
| <tr> | |
| <th>状态码</th> | |
| <th>描述</th> | |
| </tr> | |
| <tr> | |
| <td>400</td> | |
| <td>缺少必需参数(input 或 voice)</td> | |
| </tr> | |
| <tr> | |
| <td>429</td> | |
| <td>速率限制超出或队列已满。速率限制时包含 Retry-After 标头。</td> | |
| </tr> | |
| <tr> | |
| <td>500</td> | |
| <td>内部服务器错误</td> | |
| </tr> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Queue System --> | |
| <div class="api-endpoint"> | |
| <h3>队列系统</h3> | |
| <p>API 使用队列系统高效处理多个请求:</p> | |
| <ul> | |
| <li>最大队列大小:可通过 <code>MAX_QUEUE_SIZE</code> 环境变量配置(默认:100 个请求)</li> | |
| <li>请求按 FIFO(先进先出)顺序处理</li> | |
| <li>速率限制:可通过 <code>RATE_LIMIT_REQUESTS</code> 和 <code>RATE_LIMIT_WINDOW</code> 环境变量配置(默认:每个 IP 地址 60 秒内最多 30 个请求)</li> | |
| <li>可以通过 <code>/api/queue-size</code> 端点监控队列状态</li> | |
| <li>Web 界面每 2 秒更新一次队列状态</li> | |
| <li>根据使用率显示队列负载状态(低/中/高)</li> | |
| </ul> | |
| <h4>队列状态端点</h4> | |
| <pre><code class="language-http">GET /api/queue-size</code></pre> | |
| <p>返回包含队列信息的 JSON:</p> | |
| <pre><code class="language-json">{ | |
| "queue_size": 0, // 当前队列中的请求数 | |
| "max_queue_size": 100 // 队列最大容量 | |
| }</code></pre> | |
| <div class="response-codes"> | |
| <h4>响应状态码</h4> | |
| <ul> | |
| <li><code>200</code> - 成功</li> | |
| <li><code>429</code> - 队列已满或超出速率限制</li> | |
| <li><code>500</code> - 服务器错误</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| </div> | |
| <script src="script.js"></script> | |
| </body> | |
| </html> |