Z-TTS API接口文档

接口更新:支持直接返回srt字幕内容,详见文档!2025.12.29

欢迎使用我们的微软TTS API服务。本文档旨在帮助开发者快速理解和集成我们的文本转语音接口。请在开始前仔细阅读,特别是关于认证授权的部分。

准备工作 (Prerequisites)

在调用任何接口之前,您需要准备好您在本站的用户名密码。请注意,请勿使用注册邮箱作为user参数,应使用您的用户名。

核心概念:认证与授权

为了保障接口安全,我们所有的请求都需要进行签名和加密验证。

  1. 时间戳 (Timestamp):所有接口都需要一个13位的毫秒级时间戳,参数名为 ts。

  2. 签名 (Sign):部分接口需要对请求进行签名,签名算法为 md5(username + ts)。

  3. 授权头 (Authorization Header):queryorder 和 mstts 接口需要在HTTP请求头中加入 Authorization 字段。该字段的值是通过 AES 加密生成

    • 模式:ECB

    • 填充:PKCS7

    • 密钥 (Key):一个16位的自定义字符串,在首次获取token时生成或指定。

    • 待加密数据:一个包含user, token, ts的JSON字符串。

API 接口详解

基础URL (Base URL): http://47.96.88.233:2048

1. 获取Token (/gettoken)

此接口用于获取后续接口调用所需的 token 和 key。

接口地址:/gettoken

请求方法:POST

请求参数 (data)

  • user (string): 您的网站用户名。

  • psw (string): 您的网站密码。

  • type (integer): 操作类型。0表示获取token,1表示更新key和token。

  • key (string, optional): 16位自定义密钥。如果留空,系统将为您随机生成一个(建议自动生成,更安全)。

  • ts (string): 13位时间戳。

  • sign (string): 签名,计算方式为 md5(user + ts)。

返回数据示例

{
    "code": 0,
    "maxChar": 20000000,
    "date": "2024-12-31",
    "charNum": 3015,
    "key": "your_16_bit_key",
    "token": "your_generated_token",
    "msg": "success"
}

2. 查询套餐信息 (/queryorder)

查询您当前账户的字符套餐余量和到期时间。

  • 接口地址:/queryorder

  • 请求方法:POST

  • 请求头 (Headers):Authorization: 经过AES加密后的授权字符串。

  • 请求参数 (data):user (string): 您的网站用户名。ts (string): 13位时间戳。sign (string): 签名,计算方式为 md5(user + ts)。

  • 返回数据示例

{
    "code": 0,
    "maxChar": 20000000,
    "date": "2024-12-31",
    "charNum": 3015,
    "msg": "success"
}

3. 微软TTS语音合成 (/mstts)

核心接口,用于将SSML文本合成为语音。

  • 接口地址:/mstts

  • 请求方法:POST

  • 请求头 (Headers):Authorization: 经过AES加密后的授权字符串。

  • 请求参数 (PostData, JSON格式)

    • user (string): 您的网站用户名。

    • srt(boolean):是否返回经 Base64编码srt字幕 内容

    • quality(string):合成音频音质选项,因涉及服务器流量问题暂未开放,默认音频格式质量:audio-24khz-48kbitrate-mono-mp3,此参数留空即可,后续开放等待通知

    • ssml (string): 经过 Base64编码 的 SSML文本。注意:单次请求的原文长度不能超过2000字符。

    • ts (string): 13位时间戳。

    • sign (string): 签名,计算方式为 md5(user + ts)。

  • 返回数据示例

{
    "code": 0,
    "msg": "success",
    "srt": "...",(base64编码的srt字幕内容数据)"
    "base64audio": "UklGRiQ... (base64编码的mp3音频数据)",
    "charNum": 3515,
    "maxChar": 20000000
}

您只需将 base64audio 字段的内容进行Base64解码,即可得到mp3音频文件,将srt 字段内容进行Base64解码,即可得到srt字幕文本内容(请求参数srt为false时,srt内容为空)。

重要注意事项

  1. 启用重定向:所有接口请求都必须启用HTTP重定向(allow_redirects=True)。

  2. 并发限制接口限制最高并发为 3次/秒。超过此限制服务器将触发10分钟限制访问,返回 429 状态码,若一直开启无限制并发,将作封号处理。

  3. SSML文档:关于如何编写SSML以控制语音情感、停顿、多角色发音等高级功能,请参考 微软官方SSML文档教程

API 快速测试工具

为了方便您在编码前快速验证接口的有效性,我们提供了API测试工具。您可以在页面顶部免费下载。

  • 获取token:/gettoken

img

  • 套餐查询: /queryOrder

img

  • 微软TTS语音合成:/mstts

img

Python API 调用完整示例代码

以下是一个完整的Python示例,演示了从获取Token到最终合成语音的全过程。您需要安装 requests 和 pycryptodome 库。

"""
MSTTS API 接口调用示例
支持 gettoken、queryorder、mstts 三个接口
"""

import requests
import hashlib
import time
import base64
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

# =============== 配置区域 ===============
API_BASE_URL = "http://47.96.88.233:2048/"  
USERNAME = ""  # 填写用户名
PASSWORD = ""  # 填写密码
# ========================================


def md5(text: str) -> str:
    """计算MD5哈希"""
    return hashlib.md5(text.encode()).hexdigest()


def aes_encrypt(text: str, key: str) -> str:
    """AES-128-ECB 加密"""
    cipher = AES.new(key.encode('utf-8'), AES.MODE_ECB)
    padded = pad(text.encode('utf-8'), AES.block_size)
    encrypted = cipher.encrypt(padded)
    return base64.b64encode(encrypted).decode('utf-8')


class MSTTSClient:
    def __init__(self, base_url: str, username: str, password: str):
        self.base_url = base_url.rstrip('/')
        self.username = username
        self.password = password
        self.token = None
        self.key = None
    
    def get_token(self, force_new: bool = False) -> dict:
        """
        获取Token
        :param force_new: 是否强制生成新token
        :return: API响应
        """
        ts = str(int(time.time()))
        sign = md5(f"{self.username}{ts}")
        
        data = {
            "user": self.username,
            "psw": self.password,
            "type": "1" if force_new else "0",
            "key": "",  # 留空让服务端生成
            "ts": ts,
            "sign": sign
        }
        
        response = requests.post(f"{self.base_url}/gettoken", json=data,allow_redirects=True)
        response = requests.post(response.url, json=data)
        result = response.json()
        
        if result.get("code") == 0:
            self.token = result.get("token")
            self.key = result.get("key")
            print(f"[gettoken] 成功! 额度: {result.get('charNum')}/{result.get('maxChar')}, 到期: {result.get('date')}")
        else:
            print(f"[gettoken] 失败: {result.get('msg')}")
        
        return result
    
    def _get_authorization(self) -> str:
        """生成Authorization头"""
        if not self.token or not self.key:
            raise ValueError("请先调用 get_token() 获取token")
        
        auth_data = json.dumps({
            "user": self.username,
            "token": self.token
        })
        return aes_encrypt(auth_data, self.key)
    
    def query_order(self) -> dict:
        """
        查询订单/额度信息
        :return: API响应
        """
        if not self.key:
            raise ValueError("请先调用 get_token() 获取token")
        
        ts = str(int(time.time()))
        sign = md5(f"{self.username}{ts}")
        
        headers = {
            "Authorization": self._get_authorization()
        }
        
        data = {
            "user": self.username,
            "ts": ts,
            "sign": sign
        }
        
        response = requests.post(f"{self.base_url}/queryorder", headers=headers,allow_redirects=True)
        response = requests.post(response.url, headers=headers,json=data)
        result = response.json()
        
        if result.get("code") == 0:
            print(f"[queryorder] 成功! 额度: {result.get('charNum')}/{result.get('maxChar')}, 到期: {result.get('date')}")
        else:
            print(f"[queryorder] 失败: {result.get('msg')}")
        
        return result
    
    def synthesize(self, ssml: str, srt: bool = False,quality: str = "audio-24khz-48kbitrate-mono-mp3") -> dict:
        """
        文字转语音合成
        :param text: 要合成的ssml
        :return: API响应,包含base64音频和srt字幕
        """
        if not self.key:
            raise ValueError("请先调用 get_token() 获取token")
        
        # 构建SSML
             
        ts = str(int(time.time()))
        sign = md5(f"{self.username}{ts}")
        
        headers = {
            "Authorization": self._get_authorization()
        }
        
        data = {
            "user": self.username,
            "srt" : srt,
            "quality": quality, #audio-24khz-48kbitrate-mono-mp3 暂未开放,参数留空即可
            "ssml": base64.b64encode(ssml.encode('utf-8')).decode('utf-8'),
            "ts": ts,
            "sign": sign
        }
        
        response = requests.post(f"{self.base_url}/mstts", json=data, headers=headers,allow_redirects=True)
        response = requests.post(response.url, headers=headers,json=data)
        result = response.json()
        
        if result.get("code") == 0:
            print(f"[mstts] 合成成功! 已用: {result.get('charNum')}/{result.get('maxChar')}")
            
            # 保存音频
            audio_data = base64.b64decode(result.get("base64audio", ""))
            if audio_data:
                with open("output.mp3", "wb") as f:
                    f.write(audio_data)
                print(f"[mstts] 音频已保存: output.mp3 ({len(audio_data)} bytes)")
            
            # 保存字幕
            srt_base64 = result.get("srt", "")
            if srt_base64:
                srt_data = base64.b64decode(srt_base64).decode('utf-8')
                with open("output.srt", "w", encoding="utf-8") as f:
                    f.write(srt_data)
                print(f"[mstts] 字幕已保存: output.srt")
        else:
            print(f"[mstts] 合成失败: {result.get('msg')}")
        
        return result


# =============== 使用示例 ===============
if __name__ == "__main__":
    # 检查配置
    if not USERNAME or not PASSWORD:
        print("请先在配置区域填写用户名和密码!")
        exit(1)
    
    # 创建客户端
    client = MSTTSClient(API_BASE_URL, USERNAME, PASSWORD)
    
    # 1. 获取Token
    print("\n=== 步骤1: 获取Token ===")
    token_result = client.get_token()
    
    if token_result.get("code") != 0:
        print("获取Token失败,请检查用户名密码")
        exit(1)
    
    # 2. 查询订单信息
    print("\n=== 步骤2: 查询订单 ===")
    client.query_order()
    
    # 3. 文字转语音
    print("\n=== 步骤3: 文字转语音 ===")

    ssml = '''<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US"><voice name="en-US-AriaNeural"><mstts:express-as style="Cheerful">"That’s remarkable! You’re a genius!"</mstts:express-as>Mom said to her son.</voice><voice name="en-US-JennyNeural">Customize output by <prosody rate="-40.00%"> slowing-down the speed rate.</prosody></voice><voice name="en-US-GuyNeural">Add a break <break time="600ms" /> between words.</voice><voice name="en-GB-SoniaNeural">You can pronounce it <say-as interpret-as="spell">ASAP </say-as>or <sub alias="as soon as possible">ASAP</sub>.</voice><voice name="zh-CN-XiaoxiaoNeural"><s /><mstts:express-as style="chat">可以通过停顿里的高级功能“No break”,解决<phoneme alphabet="sapi" ph="fen 1 ci 2">分词</phoneme>引起的多余停顿问题。</mstts:express-as><s /><mstts:express-as style="chat">也可以合成多角色多情感的有声<prosody contour="(49%, -40%)">书</prosody>,例如:</mstts:express-as></voice><voice name="zh-CN-YunyeNeural">黛玉冷笑道:</voice><voice name="zh-CN-XiaoxiaoNeural"><s /><mstts:express-as style="disgruntled">“我说呢,亏了绊住,不然,早就飞了来了。”</mstts:express-as><s /> </voice><voice name="zh-CN-YunyeNeural">宝玉道:</voice><voice name="zh-CN-YunxiNeural">“只许和你玩,替你解闷。不过偶然到他那里,就说这些闲话。”</voice><voice name="zh-CN-XiaoxiaoNeural"><mstts:express-as style="angry">”好没意思的话!去不去,关我什么事儿?又没叫你替我解闷儿,还许你<mstts:ttsbreak strength="none" />从此<prosody contour="(24%, +49%) (59%, -2%)">不</prosody><prosody rate="-15.00%" contour="(24%, +49%) (59%, -2%)">理</prosody><prosody contour="(24%, +49%) (59%, -2%)">我呢</prosody>!”</mstts:express-as></voice><voice name="zh-CN-YunyeNeural"><s />说着,便赌气回房去了。</voice></speak>'''
           
    client.synthesize(
        ssml=ssml,
        srt=True,
        quality="" #音质选项,暂未开放,参数留空即可
    )
    
    print("\n=== 完成 ===")

 

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。