
Chanjing Tts
- 143 installs
- 18 repo stars
- Updated March 28, 2026
- chanjing-ai/chan-skills
Converts text to speech via the Chanjing TTS Open API by listing voices, creating tasks, polling, and downloading audio.
About
Uses the Chanjing TTS Open API to list voices, create text-to-speech tasks, poll them, and download the resulting audio. A developer uses it to generate spoken audio from text.
- List voices, create synthesis tasks, poll, download audio from returned URLs
- Python Open API client; no ffmpeg dependency
Chanjing Tts by the numbers
- 143 all-time installs (skills.sh)
- Ranked #729 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/chanjing-ai/chan-skills --skill chanjing-ttsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 143 |
|---|---|
| repo stars | ★ 18 |
| Last updated | March 28, 2026 |
| Repository | chanjing-ai/chan-skills ↗ |
What it does
Converts text to speech via the Chanjing TTS Open API by listing voices, creating tasks, polling, and downloading audio.
Files
Chanjing TTS
功能说明
调用蝉镜 TTS Open API:列举音色、创建合成任务、轮询并从接口返回 URL 下载音频。脚本不依赖 ffmpeg/ffprobe。
运行依赖
- python3 与同仓库
scripts/*.py - 无 ffmpeg/ffprobe 门控
环境变量与机器可读声明
- 环境变量键名与说明:`manifest.yaml`(
environment段)及本文 - 变量、凭据、合规 `permissions`、`clientPermissions`、`agentPolicy`:`manifest.yaml`
使用命令
- ClawHub(slug 以注册表为准):
clawhub run chanjing-tts - 本仓库:
python skills/chanjing-tts/scripts/create_task.py …(见正文 How to Use)
---
登记与审稿(单一事实来源)
主凭据、下载行为、`primaryEnv` 省略等:以 `manifest.yaml` 为准。本篇 How to Use 起为 API 步骤说明。
When to Use This Skill
Use this skill when the user needs to generate audio from text.
Chanjing TTS supports:
- both Chinese and English
- multiple system voices
- adjustment of speech speed
- sentence-level timestamp in result
How to Use This Skill
前置条件(权限验证):执行本 Skill 前,必须先通过 chanjing-credentials-guard 完成 AK/SK 与 Token 校验。本 Skill 与 guard 使用同一套凭证(~/.chanjing/credentials.json);脚本在无凭证时会执行 `open_login_page.py` 脚本,在默认浏览器打开 AK/SK 注册/登录页,并提示配置命令。凭据与审稿对表见 `manifest.yaml`。
Security & credentials(引用)
详见 `manifest.yaml` 中 `credentials` 与 `clientPermissions`(及合规顶层 `permissions`)。
Multiple APIs need to be invoked. All share the domain: "https://open-api.chanjing.cc". All requests communicate using json. You should use utf-8 to encode and decode text throughout this task.
1. Obtain an access_token, which is required for all subsequent API calls 2. List all voice IDs and select one to use 3. Call the Create Speech API, record task_id 4. Poll the Query Speech Status API until success, then download generated audio file using the url in response
Obtain AccessToken
从 ~/.chanjing/credentials.json 读取 app_id 和 secret_key,若无有效 Token 则调用:
POST /open/v1/access_token
Content-Type: application/json请求体(使用本地配置的 app_id、secret_key):
{
"app_id": "<从 credentials.json 读取>",
"secret_key": "<从 credentials.json 读取>"
}Response example:
{
"trace_id": "8ff3fcd57b33566048ef28568c6cee96",
"code": 0,
"msg": "success",
"data": {
"access_token": "1208CuZcV1Vlzj8MxqbO0kd1Wcl4yxwoHl6pYIzvAGoP3DpwmCCa73zmgR5NCrNu",
"expire_in": 1721289220
}
}Response field description:
| First-level Field | Second-level Field | Description |
|---|---|---|
| code | Response status code | |
| msg | Response message | |
| data | Response data | |
| access\_token | Valid for one day, previous token will be invalidated | |
| expire\_in | Token expiration time |
Response Status Code Description
| Code | Description |
|---|---|
| 0 | Success |
| 400 | Invalid parameter format |
| 40000 | Parameter error |
| 50000 | System internal error |
Select a Voice ID
Obtain all available voice IDs via API, and select one that fits the task at hand. The dialect/accent can be deduced from the voice name.
GET /open/v1/list_common_audio
access_token: {{access_token}}Use the following request body:
{
"page": 1,
"size": 100
}
Response example:
{ "trace_id": "25eb6794ffdaaf3672c25ed9efbe49c6", "code": 0, "msg": "success", "data": { "list": [ { "id": "f9248f3b1b42447fb9282829321cfcf2", "grade": 0, "name": "带货小芸", "gender": "female", "lang": "multilingual", "desc": "", "speed": 1, "pitch": 1, "audition": "https://res.chanjing.cc/chanjing/res/upload/ms/2025-06-05/7945e0474b8cb526e884ee7e28e4af8d.wav" }, { "id": "f5e69c1bbe414bec860da3294e177625", "grade": 0, "name": "方言口音老奶奶", "gender": "female", "lang": "multilingual", "desc": "", "speed": 1, "pitch": 1, "audition": "https://res.chanjing.cc/chanjing/res/upload/ms/2025-04-30/1b248ad05953028db5a6bcba9a951164.wav" }, ... ], "page_info": { "page": 1, "size": 100, "total_count": 98, "total_page": 1 } } }
Response field description:
| First-level Field | Second-level Field | Third-level Field | Description |
|---|---|---|---|
| code | | | Response status code |
| message | | | Response message |
| data | | | Response data |
| | list | List data | Public voice - list data |
| | | id | Voice ID |
| | | name | Voice name, if it includes a place name, the generated speech is in dialect |
| | | gender | Gender |
| | | lang | Language |
| | | desc | Description |
| | | speed | Speech speed |
| | | pitch | Pitch |
| | | audition | Audition link |
| | | grade | Grade |
Response status code description:
| Code | Description |
|---|---|
| 0 | Response successful |
| 10400 | AccessToken verification failed |
| 40000 | Parameter error |
| 50000 | System internal error |
| 51000 | System internal error |
### Create Speech API
Submit a speech creating task, which returns a task ID for polling later.
POST /open/v1/create_audio_task access_token: {{access_token}} Content-Type: application/json
Request body example:
{ "audio_man": "89843d52ccd04e2d854decd28d6143ce ", "speed": 1, "pitch": 1, "text": { "text": "Hello, I am your AI assistant." } }
Request field description:
| Parameter Name | Type | Nested Key | Required | Example | Description |
|---|---|---|---|---|---|
| audio\_man | string | | Yes | 89843d52ccd04e2d854decd28d6143ce | Voice ID |
| speed | number | | Yes | 1 | Speech speed: 0.5 (slow) - 2 (fast) |
| pitch | number | | Yes | 1 | Just set to 1 |
| text | object | text | Yes | Hello, I am your AI assistant. | Rich text, length must be less than 4000 characters |
| aigc\_watermark | bool | | No | false | Whether to add visible watermark to audio, default to false |
Response example:
{ "trace_id": "dd09f123a25b43cf2119a2449daea6de", "code": 0, "msg": "success", "data": { "task_id": "88f635dd9b8e4a898abb9d4679e0edc8" } }
Response field description:
| Field | Description |
|---|---|
| code | Response status code |
| msg | Response message |
| task\_id | Task ID, to be used in subsequent polling step |
Response status code description:
| code | Description |
| --- | --- |
| 0 | Response successful |
| 400 | Invalid parameter format |
| 10400 | AccessToken verification failed |
| 40000 | Parameter error |
| 40001 | Exceeds QPS limit |
| 40002 | Production duration reached limit |
| 50000 | System internal error |
#### Poll Query Speech Status API
Poll the following API until speech is generated.
POST /open/v1/audio_task_state access_token: {{access_token}} Content-Type: application/json
Request example:
{ "task_id": "88f635dd9b8e4a898abb9d4679e0edc8" }
Request field description:
| Parameter Name | Type | Required | Example | Description |
|---|---|---|---|---|
| task\_id | string | Yes | 88f789dd9b8e4a121abb9d4679e0edc8 | Speech synthesis task ID |
Response example:
{ "trace_id": "ab18b14574bbcc31df864099d474080e", "code": 0, "msg": "success", "data": { "id": "9546a0fb1f0a4ae3b5c7489b77e4a94d", "type": "tts", "status": 9, "text": [ "猫在跌落时能够在空中调整身体,通常能够四脚着地,这种”猫右自己“反射显示了它们惊人的身体协调能力和灵活性。核磁共振成像技术通过利用人体细胞中氢原子的磁性来生成详细的内部图像,为医学诊断提供了重要工具。" ], "full": { "url": "https://cy-cds-test-innovation.cds8.cn/chanjing/res/upload/tts/2025-04-08/093a59021d85a72d28a491f21820ece4.wav", "path": "093a59013d85a72d28a491f21820ece4.wav", "duration": 18.81 }, "slice": null, "errMsg": "", "errReason": "", "subtitles": [ { "key": "20c53ff8cce9831a8d9c347263a400a54d72be15", "start_time": 0, "end_time": 2.77, "subtitle": "猫在跌落时能够在空中调整身体" }, { "key": "e19f481b6cd2219225fa4ff67836448e054b2271", "start_time": 2.77, "end_time": 4.49, "subtitle": "通常能够四脚着地" }, { "key": "140beae4046bd7a99fbe4706295c19aedfeeb843", "start_time": 4.49, "end_time": 5.73, "subtitle": "这种,猫右自己" }, { "key": "e851881271876ab5a90f4be754fde2dc6b5498fd", "start_time": 5.73, "end_time": 7.97, "subtitle": "反射显示了它们惊人的身体" }, { "key": "fbb0b4138bad189b9fc02669fe1f95116e9991b4", "start_time": 7.97, "end_time": 9.45, "subtitle": "协调能力和灵活性" }, { "key": "f73404d135feaf84dd8fbea13af32eac847ac26d", "start_time": 9.45, "end_time": 12.49, "subtitle": "核磁共振成像技术通过利用人体" }, { "key": "e18827931223962e477b14b2b8046947039ac222", "start_time": 12.49, "end_time": 14.77, "subtitle": "细胞中氢原子的磁性来生成" }, { "key": "d137bf2b0c8b7a39e3f6753b7cf5d92bd877d2d9", "start_time": 14.77, "end_time": 15.97, "subtitle": "详细的内部图像" }, { "key": "0773911ae0dbaa763a64352abdb6bdac3ff8f149", "start_time": 15.97, "end_time": 18.41, "subtitle": "为医学诊断提供了重要工具" } ] } }
Response field description:
| First-level Field | Second-level Field | Third-level Field | Description |
|---|---|---|---|
| code | | | Response status code |
| msg | | | Response message |
| data | id | | Audio ID |
| | type | | |
| | status | | 1: generating; 9: completed |
| | text | | Speech text |
| | full | url | url to download the generated audio file |
| | | path | |
| | | duration | Audio duration |
| | slice | | |
| | errMsg | | Error message |
| | errReason | | Error reason |
| | subtitles (array type) | key | Subtitle ID |
| | | start\_time | Subtitle start time |
| | | end\_time | Subtitle end time |
| | | subtitle | Subtitle text |
Response status code description:
| code | Description |
|---|---|
| 0 | Response successful |
| 10400 | AccessToken verification failed |
| 40000 | Parameter error |
| 50000 | System internal error |
## Scripts
本 Skill 提供脚本(`skills/chanjing-tts/scripts/`),**带权限验证**:与 **chanjing-credentials-guard** 使用同一配置文件;无 AK/SK 时会**执行 guard 的 `open_login_page.py` 脚本**,在浏览器打开注册/登录页,并提示配置命令。
| 脚本 | 说明 |
|------|------|
| `list_voices.py` | 列出公共声音人,默认输出 id/name 表,可选 `--json` 输出完整数据 |
| `create_task.py` | 创建 TTS 任务,输出 task_id |
| `poll_task.py` | 轮询任务直到完成,输出音频下载 URL(full.url) |
示例(在项目根或 skill 目录下执行):
1. 列出可用声音,选取一个 id
python skills/chanjing-tts/scripts/list_voices.py
2. 创建合成任务
TASK_ID=$(python skills/chanjing-tts/scripts/create_task.py \ --audio-man "f9248f3b1b42447fb9282829321cfcf2" \ --text "Hello, I am your AI assistant.")
3. 轮询到完成,得到音频下载链接
python skills/chanjing-tts/scripts/poll_task.py --task-id "$TASK_ID"
# 合规:根目录 合规规则.md §1–§2
name: chanjing-tts
version: 0.1.0
vendor: chanjing
runtime:
interpreter: python3
dependencies: []
env:
required: []
optional:
- CHANJING_OPENAPI_CREDENTIALS_DIR
- CHANJING_OPENAPI_BASE_URL
permissions:
network_mode: allowlist
allowed_hosts:
- open-api.chanjing.cc
- www.chanjing.cc
filesystem:
read_roots:
- "${WORKSPACE_ROOT}"
- "${SKILL_DIR}"
- "${CHANJING_OPENAPI_CREDENTIALS_DIR}"
write_roots:
- "${WORKSPACE_ROOT}"
- "${CHANJING_OPENAPI_CREDENTIALS_DIR}"
allowed_commands:
- python3
schemaVersion: 1
skill:
id: chanjing-tts
author: chan-skills
category: 媒体处理
tags:
- TTS
- 语音合成
- ChanjingAPI
- 蝉镜
summary: >-
Chanjing TTS:列举音色、创建合成任务、轮询并从接口 URL 下载音频。
skillDoc: SKILL.md
environment:
variables:
- name: CHANJING_OPENAPI_CREDENTIALS_DIR
required: false
description: 存放 credentials.json 的目录(兼容 CHANJING_CONFIG_DIR),默认 ~/.chanjing
- name: CHANJING_OPENAPI_BASE_URL
required: false
description: Open API 基址(兼容 CHANJING_API_BASE),默认 https://open-api.chanjing.cc
credentials:
model: credentials_json
defaultPath: "~/.chanjing/credentials.json"
directoryEnv: CHANJING_OPENAPI_CREDENTIALS_DIR
fileName: credentials.json
sensitiveFields:
- app_id
- secret_key
- access_token
- expire_in
persistAccessTokenOnDisk: true
primaryEnvIntentionallyOmitted: true
doNotCommitToVcs:
- credentials.json
clientPermissions:
network:
httpsOutbound: true
documentedHosts:
- open-api.chanjing.cc
filesystem:
read:
- "${CHANJING_OPENAPI_CREDENTIALS_DIR or CHANJING_CONFIG_DIR or ~/.chanjing}/credentials.json"
write:
- "${CHANJING_OPENAPI_CREDENTIALS_DIR or CHANJING_CONFIG_DIR or ~/.chanjing}/credentials.json"
browser:
mayOpenForAuth: true
documentedHosts:
- www.chanjing.cc
subprocess:
allowedPatterns:
- python3
userContent:
mayDownloadFromApiResponseUrls: true
metadata:
openclaw:
homepage: https://doc.chanjing.cc
agentPolicy:
alwaysSkill: false
modifiesOtherSkillsOrGlobalAgent: false
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
import subprocess
import sys
import time
import urllib.request
from pathlib import Path
_DEFAULT_OPENAPI_BASE = "https://open-api.chanjing.cc"
def credentials_config_dir() -> Path:
raw = os.environ.get("CHANJING_OPENAPI_CREDENTIALS_DIR") or os.environ.get("CHANJING_CONFIG_DIR")
return Path(raw).expanduser() if raw else Path.home() / ".chanjing"
def openapi_base_url() -> str:
return (
os.environ.get("CHANJING_OPENAPI_BASE_URL")
or os.environ.get("CHANJING_API_BASE")
or _DEFAULT_OPENAPI_BASE
).rstrip("/")
CONFIG_DIR = credentials_config_dir()
CONFIG_FILE = CONFIG_DIR / "credentials.json"
API_BASE = openapi_base_url()
BUFFER_SECONDS = 300
LOGIN_URL = "https://www.chanjing.cc/openapi/login"
# 无 AK/SK 时先执行打开登录页脚本,再返回此提示
NO_CREDENTIALS_MSG = """已在浏览器打开蝉镜登录/注册页。
获取秘钥后请执行:
python skills/chanjing-credentials-guard/scripts/chanjing_config.py --ak <你的app_id> --sk <你的secret_key>
设置完毕后请重新执行您之前的操作。"""
# AK/SK 无效(获取 Token 接口返回错误)时:不重试获取 token,执行打开登录页并提示重新配置
INVALID_CREDENTIALS_MSG = """AK/SK 无效或已过期,已在浏览器打开蝉镜登录/注册页。
请重新登录获取秘钥后执行:
python skills/chanjing-credentials-guard/scripts/chanjing_config.py --ak <你的app_id> --sk <你的secret_key>
设置完毕后请重新执行您之前的操作。"""
def _run_open_login_page():
"""执行 credentials-guard 的 open_login_page.py,在默认浏览器打开注册/登录页。"""
try:
skills_dir = Path(__file__).resolve().parent.parent.parent
script = skills_dir / "chanjing-credentials-guard" / "scripts" / "open_login_page.py"
if script.exists():
subprocess.run([sys.executable, str(script)], check=False, timeout=5)
else:
import webbrowser
webbrowser.open(LOGIN_URL)
except Exception:
try:
import webbrowser
webbrowser.open(LOGIN_URL)
except Exception:
pass
def read_config():
if CONFIG_FILE.exists():
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
return json.load(f)
return {}
def write_config(data):
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
def get_token():
"""返回 (token, None) 或 (None, error_msg)。"""
data = read_config()
app_id = (data.get("app_id") or "").strip()
secret_key = (data.get("secret_key") or "").strip()
if not app_id or not secret_key:
_run_open_login_page()
return None, NO_CREDENTIALS_MSG
now = int(time.time())
token = data.get("access_token")
expire_in = data.get("expire_in")
try:
expire_in = int(expire_in) if expire_in is not None else 0
except (ValueError, TypeError):
expire_in = 0
if token and expire_in > now + BUFFER_SECONDS:
return token, None
url = API_BASE + "/open/v1/access_token"
req = urllib.request.Request(
url,
data=json.dumps({"app_id": app_id, "secret_key": secret_key}).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
body = json.loads(resp.read().decode("utf-8"))
except Exception as e:
return None, str(e)
if body.get("code") != 0:
_run_open_login_page()
return None, INVALID_CREDENTIALS_MSG
d = body.get("data", {})
new_token = d.get("access_token")
if not new_token:
_run_open_login_page()
return None, INVALID_CREDENTIALS_MSG
data["access_token"] = new_token
data["expire_in"] = d.get("expire_in")
write_config(data)
return new_token, None
#!/usr/bin/env python3
"""
创建 TTS 语音合成任务。与 chanjing-credentials-guard 使用同一配置文件获取 Token。
用法: create_task.py --audio-man <声音id> --text "要合成的文本" [--speed 1] [--pitch 1] [--aigc-watermark]
输出: task_id(一行)或错误到 stderr
"""
import argparse
import json
import sys
import urllib.request
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _auth import get_token
API_BASE = (__import__("os").environ.get("CHANJING_OPENAPI_BASE_URL") or __import__("os").environ.get("CHANJING_API_BASE") or "https://open-api.chanjing.cc").rstrip("/")
def main():
parser = argparse.ArgumentParser(description="创建 TTS 语音合成任务")
parser.add_argument("--audio-man", required=True, help="声音 ID(来自 list_voices)")
parser.add_argument("--text", required=True, help="要合成的文本,不超过 4000 字")
parser.add_argument("--speed", type=float, default=1, help="语速 0.5-2,默认 1")
parser.add_argument("--pitch", type=float, default=1, help="语调,默认 1")
parser.add_argument("--aigc-watermark", action="store_true", help="是否添加 AIGC 水印")
args = parser.parse_args()
if len(args.text) > 4000:
print("文本长度不能超过 4000 字符", file=sys.stderr)
sys.exit(1)
body = {
"audio_man": args.audio_man.strip(),
"speed": args.speed,
"pitch": args.pitch,
"text": {"text": args.text},
"aigc_watermark": args.aigc_watermark,
}
token, err = get_token()
if err:
print(err, file=sys.stderr)
sys.exit(1)
url = f"{API_BASE}/open/v1/create_audio_task"
req = urllib.request.Request(
url,
data=json.dumps(body).encode("utf-8"),
headers={"access_token": token, "Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
res = json.loads(resp.read().decode("utf-8"))
if res.get("code") != 0:
print(res.get("msg", res), file=sys.stderr)
sys.exit(1)
data = res.get("data", {})
task_id = data.get("task_id")
if not task_id:
print("响应无 task_id", file=sys.stderr)
sys.exit(1)
print(task_id)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
列出公共声音人。与 chanjing-credentials-guard 使用同一配置文件获取 Token。
用法: list_voices.py [--page 1] [--size 100] [--json]
输出: 默认打印 id/name 表;--json 时输出完整 data
"""
import argparse
import json
import sys
import urllib.parse
import urllib.request
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _auth import get_token
API_BASE = (__import__("os").environ.get("CHANJING_OPENAPI_BASE_URL") or __import__("os").environ.get("CHANJING_API_BASE") or "https://open-api.chanjing.cc").rstrip("/")
def main():
parser = argparse.ArgumentParser(description="列出蝉镜公共声音人")
parser.add_argument("--page", type=int, default=1, help="页码")
parser.add_argument("--size", type=int, default=100, help="每页数量")
parser.add_argument("--json", action="store_true", help="输出完整 JSON")
args = parser.parse_args()
token, err = get_token()
if err:
print(err, file=sys.stderr)
sys.exit(1)
qs = urllib.parse.urlencode({"page": args.page, "size": args.size})
url = f"{API_BASE}/open/v1/list_common_audio?{qs}"
req = urllib.request.Request(url, headers={"access_token": token}, method="GET")
with urllib.request.urlopen(req, timeout=30) as resp:
body = json.loads(resp.read().decode("utf-8"))
if body.get("code") != 0:
print(body.get("msg", body), file=sys.stderr)
sys.exit(1)
data = body.get("data", {})
if args.json:
print(json.dumps(data, ensure_ascii=False, indent=2))
return
items = data.get("list", [])
page_info = data.get("page_info", {})
print(f"# 共 {page_info.get('total_count', len(items))} 个声音 (page={args.page})")
print(f"{'id':<36} name")
print("-" * 60)
for v in items:
print(f"{v.get('id', ''):<36} {v.get('name', '')}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
轮询 TTS 任务状态,直到完成或失败。与 chanjing-credentials-guard 使用同一配置文件获取 Token。
用法: poll_task.py --task-id <task_id> [--interval 3]
输出: 成功时打印音频下载 URL(data.full.url),失败时打印错误到 stderr 并 exit 1
状态: 1-生成中 9-完成
"""
import argparse
import json
import sys
import time
import urllib.request
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _auth import get_token
API_BASE = (__import__("os").environ.get("CHANJING_OPENAPI_BASE_URL") or __import__("os").environ.get("CHANJING_API_BASE") or "https://open-api.chanjing.cc").rstrip("/")
def main():
parser = argparse.ArgumentParser(description="轮询 TTS 任务直到完成")
parser.add_argument("--task-id", required=True, help="任务 ID(来自 create_task)")
parser.add_argument("--interval", type=int, default=3, help="轮询间隔秒数,默认 3")
args = parser.parse_args()
token, err = get_token()
if err:
print(err, file=sys.stderr)
sys.exit(1)
url = f"{API_BASE}/open/v1/audio_task_state"
body = json.dumps({"task_id": args.task_id}).encode("utf-8")
while True:
req = urllib.request.Request(
url,
data=body,
headers={"access_token": token, "Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
res = json.loads(resp.read().decode("utf-8"))
if res.get("code") != 0:
print(res.get("msg", res), file=sys.stderr)
sys.exit(1)
data = res.get("data", {})
status = data.get("status")
if status == 9:
full = data.get("full") or {}
audio_url = full.get("url")
if audio_url:
print(audio_url)
return 0
print("任务完成但无 full.url", file=sys.stderr)
sys.exit(1)
if status not in (1, None):
err_msg = data.get("errMsg") or data.get("errReason") or f"status={status}"
print(f"任务失败: {err_msg}", file=sys.stderr)
sys.exit(1)
time.sleep(args.interval)
if __name__ == "__main__":
main()