字幕服务 API 协议说明
1. 概述
本文档面向第三方集成,描述字幕服务相关的 HTTP 接口,开发者可据此自行实现服务端。服务覆盖两类能力:
| 能力 | 用途 | 接口 |
|---|---|---|
| 字幕翻译 | 将字幕翻译为另一种语言 | GET /api/languages · POST /api/translate |
| 语音合成 TTS | 字幕 → 严格时间对齐的配音音频 | POST /api/tts/conversions · GET /api/tts/conversions/{id} · GET /api/tts/conversions/{id}/audio · DELETE /api/tts/conversions/{id} |
上游引擎可自由选型:翻译可用任意大模型(如阿里云 DashScope 的
qwen-mt-flash、DeepSeek 等);TTS 可用任意语音合成引擎(如cosyvoice-v3-flash、Edge-TTS、Azure 等),是否支持声音复刻由所选引擎决定。本文档只约定接口协议,不限定实现。
翻译为同步阻塞接口;TTS 为异步任务(提交后轮询查询)。
2. 通用约定
2.1 Base URL
- 直连:
http(s)://<host>:<port> - 反向代理:可能带路径前缀,如
https://<host>/subtitle-api
下文示例以 http://localhost:3000 为准,请按实际部署替换。端口由服务端 .env 的 PORT / HTTPS_PORT 决定。
2.2 编码与格式
- 请求/响应均为 UTF-8。
- 含文件上传的接口(翻译、TTS 提交)使用
multipart/form-data。 - 查询类接口使用
application/json。 - 翻译的 JSON 响应统一结构:成功为
{ "success": true, "data": ... },失败为{ "success": false, "error": "..." }。 - TTS 接口使用独立的响应格式(见 §6 各接口说明)。
2.3 鉴权
当服务端启用鉴权([auth] enabled = true)时,除 GET /health 外的所有 /api/* 路由均受保护。满足以下任一条件即放行:
| 方式 | 说明 |
|---|---|
| IP 白名单 | 客户端真实 IP 在服务端 [auth] allowed_ips 列表中 |
| 请求头令牌 | 请求头携带非空 X-ZWPlayer(任意非空值即可) |
# 客户端 IP 不在白名单时,请求需附加:
-H "X-ZWPlayer: <你的令牌>"
未通过鉴权时返回 401:
{ "success": false, "error": "未授权:IP 不在白名单且 API Key 无效" }
3. 字幕翻译 —— 支持的目标语言
共 13 种目标语言,每种语言有一个稳定的 code(ISO 639-1),作为提交给 /api/translate 的契约值。显示名随 UI 语言变化,但 code 恒定不变。
| code | 中文显示名 | 原生名称 |
|---|---|---|
zh |
中文 | 中文 |
en |
英语 | English |
ja |
日语 | 日本語 |
ko |
韩语 | 한국어 |
fr |
法语 | Français |
de |
德语 | Deutsch |
es |
西班牙语 | Español |
ru |
俄语 | Русский |
pt |
葡萄牙语 | Português |
it |
意大利语 | Italiano |
ar |
阿拉伯语 | العربية |
th |
泰语 | ไทย |
vi |
越南语 | Tiếng Việt |
提交给
/api/translate的targetLang必须是上表中的code(如en),而非显示名(如「英语」「English」)。提交旧版中文名会被拒绝。
4. 字幕翻译 GET /api/languages
获取支持的目标语言列表,可按 UI 语言本地化显示名。
4.1 请求
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
lang |
query | 否 | UI 语言 code,决定 name 的显示语言;支持上述 13 种 code;未提供或为未知值时回退 zh;大小写不敏感 |
4.2 响应
data 为数组,每项形如 { "code": string, "name": string }:
code—— 稳定语言代码,作为/api/translate的targetLang契约值name—— 在lang指定的 UI 语言下的显示名
示例:GET /api/languages?lang=zh(默认)
{
"success": true,
"data": [
{ "code": "zh", "name": "中文" },
{ "code": "en", "name": "英语" },
{ "code": "ja", "name": "日语" },
{ "code": "ko", "name": "韩语" },
{ "code": "fr", "name": "法语" },
{ "code": "de", "name": "德语" },
{ "code": "es", "name": "西班牙语" },
{ "code": "ru", "name": "俄语" },
{ "code": "pt", "name": "葡萄牙语" },
{ "code": "it", "name": "意大利语" },
{ "code": "ar", "name": "阿拉伯语" },
{ "code": "th", "name": "泰语" },
{ "code": "vi", "name": "越南语" }
]
}
4.3 行为说明
| 场景 | 结果 |
|---|---|
缺省 / 空 lang |
等同 lang=zh |
已知 code(ko、ja、ru …) |
返回该 UI 语言的译名 |
大写(KO、En) |
大小写归一后查表,正常返回 |
未知 code(xx) |
该项 name 回退为中文(names.zh) |
5. 字幕翻译 POST /api/translate
上传 .srt / .vtt 字幕文件,返回翻译后的字幕内容。
5.1 请求
Content-Type: multipart/form-data
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file |
File | 是 | 字幕文件(.srt 或 .vtt,最大 10MB) |
targetLang |
string | 是 | 目标语言 code,如 en、zh、ja(取自 /api/languages 的 code);大小写不敏感 |
apiKey |
string | 否 | 翻译 API Key;不传则使用服务端 DEFAULT_API_KEY;两者皆无时返回 500 |
model |
string | 否 | 翻译模型;不传则用服务端 DEFAULT_MODEL(默认 qwen-mt-flash) |
batchSize |
number | 否 | 每批翻译条数,范围 1–50,默认 20;超出范围会被夹紧到区间内 |
outputFormat |
string | 否 | 输出格式:vtt(默认)或 srt;非 srt 一律按 vtt 处理 |
5.2 成功响应
{
"success": true,
"data": {
"content": "WEBVTT\n\n1\n00:00:01.000 --> 00:00:03.000\nHello World\n\n",
"format": "vtt",
"totalSubtitles": 120,
"filename": "movie.translated.vtt"
}
}
| 字段 | 说明 |
|---|---|
content |
翻译后的完整字幕文本 |
format |
输出格式:vtt 或 srt |
totalSubtitles |
字幕条数 |
filename |
建议的下载文件名(<原文件名>.translated.<格式>) |
5.3 状态码
| HTTP 状态 | 触发条件 |
|---|---|
200 |
翻译成功 |
400 |
未上传文件;targetLang 为空;targetLang 不是合法 code(含提交了旧版中文名的情况) |
422 |
字幕解析失败,或解析后内容为空 |
500 |
服务端未配置 DEFAULT_API_KEY 且请求未携带 apiKey |
502 |
上游翻译 API 调用失败 |
非法 targetLang 的错误信息会列出全部有效 code,便于客户端提示,例如:
{
"success": false,
"error": "不支持的目标语言代码 \"英语\",有效代码:zh、en、ja、ko、fr、de、es、ru、pt、it、ar、th、vi"
}
6. 提交 TTS 转换 POST /api/tts/conversions
上传 .srt / .vtt 字幕文件,将其转换为严格时间对齐的 M4A 音频。任务异步处理,立即返回 job_id。
可选上传参考音频触发声音复刻,或传入已有 voice_id 复用音色。生成的配音音频可作为 ZWPlayer 的外挂音轨使用(详见 音轨设置)。
9.1 请求
Content-Type: multipart/form-data
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
subtitle |
File | 是 | 字幕文件,.srt 或 .vtt |
audio |
File | 否 | 参考音频(WAV/MP3/M4A,建议 10–20s,≤ 10 MB),触发声音复刻 |
voice_id |
string | 否 | 已有音色 ID。传入后跳过复刻,优先级高于 audio |
language |
string | 否 | 目标合成语言,覆盖服务端默认值 |
声音优先级:
voice_id>audio> 服务端默认voice
language 取值
zh en ja ko fr de ru pt th id vi
9.2 成功响应
202 Accepted
{
"job_id": "5233b92a98ef49f68452edfade00d6f4"
}
9.3 错误
| HTTP 状态 | 触发条件 |
|---|---|
401 |
鉴权未通过 |
413 |
multipart 整体超限(~10 MB) |
422 |
未传 subtitle / 扩展名不合法 / audio 超过 10 MB / language 不在允许列表 |
7. 查询 TTS 任务 GET /api/tts/conversions/{id}
查询 TTS 转换任务的当前状态。
10.1 成功响应
200 OK
{
"id": "5233b92a98ef49f68452edfade00d6f4",
"status": "completed",
"created_at": 1783309806.19,
"started_at": 1783309806.20,
"finished_at": 1783309807.18,
"voice_id": "cosyvoice-v3-flash_gf_abc123",
"model_used": "cosyvoice-v3-flash",
"error": "",
"params": {
"subtitle_filename": "subtitle.srt",
"audio_filename": "voice_sample.wav",
"requested_voice_id": "",
"requested_language": "zh"
}
}
10.2 status 状态机
queued → parsing ─┬─→ cloning_voice → synthesizing → aligning → mixing → completed
└─→ synthesizing → aligning → mixing → completed
(未上传 audio 时跳过 cloning_voice)
任意阶段失败 → failed
DELETE → canceled + 删除工作目录
| status | 含义 | 终态 |
|---|---|---|
queued |
已入队 | 否 |
parsing |
正在解析字幕 | 否 |
cloning_voice |
正在复刻音色 | 否 |
synthesizing |
正在并发 TTS | 否 |
aligning |
正在对齐(atempo / apad) | 否 |
mixing |
正在拼接 + AAC 编码 | 否 |
completed |
成功,可下载 | 是 |
failed |
失败,error 字段有原因 |
是 |
canceled |
被 DELETE 取消 | 是 |
推荐每 1–2 秒轮询一次,遇到终态(
completed/failed/canceled)即停止。
8. 下载 TTS 音频 GET /api/tts/conversions/{id}/audio
下载已完成任务的 M4A 音频。
11.1 成功响应
200 OK
Content-Type: audio/mp4Content-Disposition: attachment; filename="{id}.m4a"- Body:二进制 AAC 音频
11.2 错误
| HTTP 状态 | 触发条件 |
|---|---|
404 |
job_id 不存在 / 输出文件丢失 |
409 |
任务未完成(status 不是 completed) |
9. 取消 TTS 任务 DELETE /api/tts/conversions/{id}
取消任务(如果还在运行)并删除其工作目录及所有中间产物。不可恢复。
12.1 成功响应
204 No Content(空 Body)
12.2 错误
| HTTP 状态 | 触发条件 |
|---|---|
404 |
job_id 不存在 |
10. 错误码速查
| HTTP 状态 | 含义 | 常见原因 |
|---|---|---|
400 |
参数错误 | 缺少必填字段、格式不支持、语言代码非法 |
401 |
未授权 | 未携带 X-ZWPlayer 且 IP 不在白名单 |
404 |
不存在 | TTS 任务 ID 不存在 / 文件丢失 |
409 |
冲突 | TTS 任务未完成时尝试下载音频 |
413 |
文件过大 | 超过上传限制(字幕 10 MB / TTS 参考音频 10 MB) |
422 |
解析失败 | 字幕格式不正确、扩展名不合法、TTS language 不在允许列表 |
500 |
服务端配置缺失 | 未配置 public_base_url / API Key |
502 |
上游调用失败 | DashScope / 翻译 API 返回错误 |
11. 与 ZWPlayer 对接
ZWPlayer 播放器通过初始化配置项 translateApi 指向字幕服务地址。该参数为服务的 base URL(即所有接口的共同前缀),播放器会在此基础上自动拼接各端点:
const player = new ZWPlayer({
url: 'http://example.com/vod/movie.mp4',
playerElm: '#player-holder',
translateApi: 'https://your-server.com/subtitle-api/api'
});
配置后,播放器字幕菜单(CC 菜单)会自动出现「字幕翻译」与「字幕转配音」两个入口(均仅当 translateApi 有效时显示)。字幕菜单的完整使用说明请参阅 字幕设置。
11.1 字幕翻译对接
播放器实际请求的地址(base URL + 端点):
GET https://your-server.com/subtitle-api/api/languages?lang=<UI语言>POST https://your-server.com/subtitle-api/api/translate
翻译入口的用户流程:选择目标语言 → 将当前字幕翻译后作为字幕轨道加载。
11.2 字幕转配音(TTS)对接
播放器实际请求的地址(base URL + 端点):
| 时机 | 方法 | 端点 | 说明 |
|---|---|---|---|
| 提交合成 | POST |
/tts/conversions |
上传字幕 + 可选参考音频,返回 job_id |
| 轮询状态 | GET |
/tts/conversions/{job_id} |
每 1.5 秒查询一次,直到终态 |
| 下载音频 | GET |
/tts/conversions/{job_id}/audio |
终态 completed 后下载 M4A |
| 取消任务 | DELETE |
/tts/conversions/{job_id} |
用户点"取消"时调用 |
提交请求的 FormData 字段
播放器用 multipart/form-data 提交,字段如下:
| 字段 | 是否发送 | 内容 |
|---|---|---|
subtitle |
始终 | 当前字幕序列化后的 SRT 文本,文件名固定 subtitle.srt,MIME text/plain |
language |
始终 | 目标配音语言 code(如 zh/en/ja),取自面板下拉框 |
audio |
仅当用户选择了参考音频 | 参考音频文件(WAV/MP3/M4A,≤10MB),用于声音复刻 |
字幕的 SRT 序列化由播放器内部完成(时间戳为毫秒精度
HH:MM:SS,mmm),服务端无需关心字幕格式转换。
客户端的状态机与轮询行为
播放器提交后进入轮询,UI 会根据服务端返回的 status 显示阶段文案。服务端 status 与用户可见文案的映射:
服务端 status |
用户可见提示 |
|---|---|
queued |
排队中 |
parsing |
解析字幕 |
cloning_voice |
复刻音色(仅上传了参考音频时出现) |
synthesizing |
合成语音 |
aligning |
时间对齐 |
mixing |
混音编码 |
completed |
✅ 合成完成(触发音频下载与音轨挂载) |
failed |
合成失败(显示 error 字段) |
canceled |
已取消 |
播放器遇到 completed / failed / canceled 任一终态即停止轮询。服务端只需在 GET /tts/conversions/{id} 的响应中返回上述 status 值之一即可。
音频下载与挂载
completed 后,播放器用 fetch(带 X-ZWPlayer 头)下载音频为 Blob,生成 blob URL 后调用内部的 addAudioTrack 挂载为外挂音轨并自动切换播放。
为什么用 fetch 而非直接
<audio src>:TTS 音频接口要求X-ZWPlayer鉴权头,而 HTML<audio>元素无法附加自定义请求头,因此必须先 fetch 下载为 Blob 再用URL.createObjectURL生成同源 blob URL。
TTS 服务端实现要点
第三方实现 TTS 服务端时,需满足以下契约:
POST /tts/conversions:接收multipart/form-data(subtitle+language+ 可选audio),返回202与{ "job_id": "<任务ID>" }。subtitle字段是 SRT 文本文件。GET /tts/conversions/{id}:返回当前任务状态,JSON 含status字段(取值见上表)。failed时附带error字段。completed时建议附带voice_id(供调试)。GET /tts/conversions/{id}/audio:任务completed后返回 M4A 音频(Content-Type: audio/mp4);未完成时返回409。DELETE /tts/conversions/{id}:取消任务,返回204。- 音频时间对齐:合成的音频须与字幕的时间轴严格对齐(每条 cue 的起止时间匹配),否则播放器同步会出现漂移。服务端通常用 atempo/apad 做时间拉伸。
服务端只要满足上述请求/响应结构即可被 ZWPlayer 的 TTS 功能调用。合成引擎(CosyVoice、Edge-TTS、Azure 等)可自由选择,播放器不关心。
11.3 协议兼容性提示
本文档描述的是目标协议规范(翻译使用 ISO 语言 code)。开发者在实现服务端时,需注意 ZWPlayer 客户端实际发送的 targetLang 取值以客户端版本为准:
- 目标协议要求
targetLang为语言 code(如en、zh),并通过GET /api/languages获取可选列表。 - 为保证服务端对各类客户端均健壮,建议在
/api/translate的语言校验中同时兼容 code 与显示名:既能识别en,也能兜底识别英语/English这类旧格式输入,给出明确的错误信息(见 §5.3)。
服务端只要满足本文档的请求/响应结构即可被 ZWPlayer 调用。