字幕服务 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 为准,请按实际部署替换。端口由服务端 .envPORT / 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/translatetargetLang 必须是上表中的 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/translatetargetLang 契约值
  • 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(kojaru …) 返回该 UI 语言的译名
大写(KOEn 大小写归一后查表,正常返回
未知 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,如 enzhja(取自 /api/languagescode);大小写不敏感
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 输出格式:vttsrt
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/mp4
  • Content-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 服务端时,需满足以下契约:

  1. POST /tts/conversions:接收 multipart/form-datasubtitle + language + 可选 audio),返回 202{ "job_id": "<任务ID>" }subtitle 字段是 SRT 文本文件。
  2. GET /tts/conversions/{id}:返回当前任务状态,JSON 含 status 字段(取值见上表)。failed 时附带 error 字段。completed 时建议附带 voice_id(供调试)。
  3. GET /tts/conversions/{id}/audio:任务 completed 后返回 M4A 音频(Content-Type: audio/mp4);未完成时返回 409
  4. DELETE /tts/conversions/{id}:取消任务,返回 204
  5. 音频时间对齐:合成的音频须与字幕的时间轴严格对齐(每条 cue 的起止时间匹配),否则播放器同步会出现漂移。服务端通常用 atempo/apad 做时间拉伸。

服务端只要满足上述请求/响应结构即可被 ZWPlayer 的 TTS 功能调用。合成引擎(CosyVoice、Edge-TTS、Azure 等)可自由选择,播放器不关心。

11.3 协议兼容性提示

本文档描述的是目标协议规范(翻译使用 ISO 语言 code)。开发者在实现服务端时,需注意 ZWPlayer 客户端实际发送的 targetLang 取值以客户端版本为准:

  • 目标协议要求 targetLang 为语言 code(如 enzh),并通过 GET /api/languages 获取可选列表。
  • 为保证服务端对各类客户端均健壮,建议在 /api/translate 的语言校验中同时兼容 code 与显示名:既能识别 en,也能兜底识别 英语 / English 这类旧格式输入,给出明确的错误信息(见 §5.3)。

服务端只要满足本文档的请求/响应结构即可被 ZWPlayer 调用。