By reverse-engineering and wrapping the internal REST API of the Tingwu web client, cloud-based audio/video file transcription is achieved, outputting Markdown compatible with local-asr .
Features
- Upload local audio/video files to Alibaba Cloud OSS
- Cloud transcription, supporting speaker diarization (single/2 speakers/multiple speakers)
- Supports Chinese, English, Japanese, Cantonese, and mixed Chinese-English
- Outputs local-asr compatible Markdown, which can be directly used with
summary.pyto inject AI summaries
Dependencies
- Python 3.8+
requests(required) — HTTP requestsoss2(required) — Alibaba Cloud OSS SDK (STS direct upload)playwright(when logging in) — used by the built-in login scriptlogin_pw.py(pip3 install playwright && playwright install chromium)
Install:
pip3 install -r skills/tingwu-asr/config/requirements.txt
First-time use: login (preferred: built-in Playwright script)
Method 1 (recommended): the skill has built-in login_pw.py, one command completes "open browser → auto-fill credentials → wait for login → save cookie to disk":
python3 skills/tingwu-asr/scripts/login_pw.py
- Credentials are read from
config/.env(copy fromconfig/.env.exampleand fill in) - The browser opens in a visible window; if a slider/captcha appears, complete it manually in the window, and the script will automatically poll and wait
- The script retrieves cookies via
context.cookies()(including the HttpOnlylogin_aliyunid_ticket), written directly toconfig/cookies.json, cookie values do not pass through stdout/conversation logs - After completion, you can use
check_auth.pyto verify login status
Method 2 (fallback): Agent uses MCP Playwright browser tools for a manual process:
- Use MCP Playwright to open
https://tingwu.aliyun.com/home - If redirected to the login page, log in with account and password or QR code
- After successful login, save the extracted cookies to a file (note that
document.cookiecannot get HttpOnly cookies, only as a fallback):python3 skills/tingwu-asr/scripts/login.py --save-cookies '{"cna":"xxx",...}'
The account and password can be pre-configured in the config/.env file (copy from config/.env.example ).
Daily check-in (claim free quota)
Logging into the Tingwu web page every day allows you to claim 2 hours of free transcription quota. Agent check-in process:
- Run the built-in login script (it will open
https://tingwu.aliyun.com/hometo trigger the daily quota and save the Cookie):python3 skills/tingwu-asr/scripts/login_pw.py - Run the check script to confirm the status:
python3 skills/tingwu-asr/scripts/daily_checkin.py
You can configure a scheduled task in OpenClaw to let the Agent automatically execute this process every day.
Agent Workflow
When the user requests transcription of an audio/video file, perform the following steps:
1. Check login status
python3 skills/tingwu-asr/scripts/check_auth.py
If it returns "invalid", first run login.py。
2. Execute transcription
# 直接给链接(自动 yt-dlp 下载,默认多人分离):小宇宙/YouTube/B站等
python3 skills/tingwu-asr/scripts/transcribe.py "https://www.xiaoyuzhoufm.com/episode/xxx"
# 单文件转录(默认多人分离)
python3 skills/tingwu-asr/scripts/transcribe.py /path/to/audio.mp3 --lang cn
# 多文件并行转录(自动保存到文件所在目录 + archive 目录)
python3 skills/tingwu-asr/scripts/transcribe.py /path/to/audio1.mp3 /path/to/audio2.mp3 /path/to/video.mp4
# 批量转录目录下所有文件(并行)
python3 skills/tingwu-asr/scripts/transcribe.py /path/to/media_folder/ --batch
# 指定并行数(默认3)
python3 skills/tingwu-asr/scripts/transcribe.py /path/to/audio1.mp3 /path/to/audio2.mp3 --parallel 5
Parameter description:
pathsAudio/video file pathor link(http(s)-prefixed links are automatically downloaded to a temporary directory with yt-dlp; supports Xiaoyuzhou/YouTube/Bilibili, etc.; supports mixing multiple)--lang cnLanguage: cn(Chinese, default) / en(English) / ja(Japanese) / cant(Cantonese) / cn_en(mixed Chinese-English)--speakersSpeaker diarization: 2=distinguish speakers (default, the only verified effective value, distinguishes 2 speakers) / 0=do not distinguish / 1=single speaker.Note: both 3 and 4 have been empirically verified to be ineffective (no separation); the original "4=multi-speaker" was an erroneous note--batchBatch transcribe all files in the directory--parallel NMaximum number of files to transcribe in parallel (default: 3)--forceForce re-upload even if the file already has transcription results (by default, already-transcribed files are skipped)-o output.mdSpecify the output path (single-file mode)--no-archiveDo not save an archive--no-labDo not fetch smart analysis (keywords/agenda/highlights, etc.)--pptDownload PPT slide images and embed them in Markdown (video only)
About "transcribing from a link" and speaker separation (important design trade-off): When a link is passed in, the skill takes the "yt-dlp download → local upload" path to ensure speaker separation takes effect. Although the "podcast link transcription" on the Tingwu web side (the underlying net_source network source channel) can save local bandwidth, it has been empirically verified that its "distinguish speakers" option does not take effect (roleSplitNum is forced to 0 at submission, with no separation). In addition, it has been empirically verified that Tingwu roleSplitNum only 2 is a valid separation value (splitting into 2 speakers),0/1/3/4 neither separates (the original skill comment "4=multi-speaker" was an erroneous note and has been corrected). Therefore, by default the skill --speakers 2 takes the local upload channel in exchange for reliable 2-speaker separation, at the cost of download + upload bandwidth. If you only need a plain text transcript and do not care about separation, you can call the Tingwu web-side podcast link transcription yourself.
3. Output description
The transcription results are saved to two locations at the same time:
- The directory where the source file is located: for example
/path/to/audio.mp3→/path/to/audio.md - archive directory:
archive/YYYYMMDD_HHMMSS_audio/audio.md
The benefits of doing this are:
- The source file directory is convenient for direct access
- The archive directory is convenient for centralized management and backup
PPT slides: Video files automatically extract PPT slides, and the images are saved in the {文件名}_slides/ subdirectory (each file has its own separate directory to avoid conflicts among multiple videos in the same directory).
3. Generate an AI summary (reusing local-asr)
After transcription is complete, reuse the summary module of local-asr:
python3 skills/local-asr/scripts/summary.py inject transcript.md summary.json
python3 skills/local-asr/scripts/summary.py verify transcript.md
File structure
skills/tingwu-asr/
SKILL.md ← 本文件
scripts/
tingwu.py ← 核心 API 客户端
transcribe.py ← CLI 入口
format_output.py ← 听悟 JSON → Markdown 转换
login.py ← Cookie 保存工具
daily_checkin.py ← 额度检查 + 记录
check_auth.py ← 认证检查
config/
.env ← 账号密码凭证(gitignore,不提交)
.env.example ← 账号密码模板
cookies.json ← 登录 Cookie(gitignore,不提交)
cookie.example.json ← Cookie 文件模板
quota_history.jsonl ← 额度变更记录(gitignore,不提交)
requirements.txt ← Python 依赖
references/ ← API 文档和决策记录
archive/ ← 转录结果归档
Asynchronous transcription mode (recommended for long videos)
For long videos of more than 1 hour, transcription may take 20-30 minutes. Using asynchronous mode, it returns immediately after upload, and polling is done automatically in the background.
1. Asynchronous submission
python3 skills/tingwu-asr/scripts/transcribe.py /path/to/video.mp4 --async --speakers 2
After the upload is complete, the task ID is returned immediately, and the task information is saved to config/pending_tasks.json。
2. Background monitoring (choose according to the Agent runtime)
The lifecycle of the monitoring process is the key difference:An Agent's background processes are generally reclaimed when the session ends(the same applies to switching interfaces in a Hermes session or exiting a Claude Code session). Once the monitor dies, the Agent can no longer receive the "transcription complete" signal, and the long task silently strands. Therefore it is divided into two tiers:
Tier A — session-independent monitoring (recommended, first choice for long tasks): Make monitoring completely independent of the Agent session, write progress to a log file, and when the Agent comes back it only reads the status and does not hold the process.
# Claude Code / 任意运行时:nohup 脱离会话,日志落盘
nohup python3 skills/tingwu-asr/scripts/poll_tasks.py --monitor --timeout 7200 --interval 120 \
>> /tmp/tingwu_monitor.log 2>&1 &
# 单任务高频盯梢(含 macOS 原生通知):watch_active.sh 本身就是会话无关的
nohup bash skills/tingwu-asr/scripts/watch_active.sh <task_id> >> /tmp/tingwu_watch.log 2>&1 &
After the Agent resumes the session, it takes over in this order (without resubmitting the task):
cat skills/tingwu-asr/config/pending_tasks.json— if non-empty, there is an unfinished task; take the trans_idpython3 skills/tingwu-asr/scripts/poll_tasks.py(one-time) — if already completed, trigger finish_task on the spot to wrap up (download PPT/generate Markdown/archive); if not completed, print the status and estimated remaining time- finish_task wrap-up may take several minutes (185 slides download + compression measured at about 3 minutes):Run in the background and do not set a short timeout, or switch to watch_active.sh to take over
- If continuous monitoring is needed, then start tier A/B; reruns are safely idempotent (flock prevents concurrent duplicate archiving)
Tier B — Agent-managed background process: suitable for short waits when the Agent session is certain to persist.
- Claude Code: Bash tool
run_in_background: true+timeout: 600000(must be 10 minutes; the default 2 minutes will cut off finish_task) - Hermes:
terminal(background=true)submit + process_manage polling wait; after the session switches UI/exits, the process will be reclaimed, at which point return to step 1 of the recovery flow
No matter which tier, rerunning poll_tasks.py is always safe: task status is based on the cloud; pending_tasks.json has flock mutual exclusion, so it will not re-transcribe or re-archive. Process loss ≠ task loss.
3. Manual query
# 检查所有待处理任务的状态(已完成会当场收尾并打印输出路径)
python3 skills/tingwu-asr/scripts/poll_tasks.py
# 阻塞式监控
python3 skills/tingwu-asr/scripts/poll_tasks.py --monitor
Notes
- Cookies will expire; after expiration you need to rerun
login.py - The web version has limited free quota; large files or high-frequency use may trigger risk controls
- Supported formats: mp3/wav/m4a/wma/aac/ogg/amr/flac/aiff/mp4/wmv/mov/mkv/webm/avi, etc.
- Maximum audio 500M, maximum video 6G, maximum single file length 6 hours
- In proxy environments, OSS uploads now automatically connect directly (v0.4.6): when the shell persistently runs
HTTP_PROXY/HTTPS_PROXY(such as Clash family), oss2 multipart uploads used to go through the proxy and have long connections cut off (measured: a 771MB video at 50%ProxyError: Cannot connect to proxy). Now the upload session by defaulttrust_env=Falseignores the environment proxy and connects directly to domestic OSS nodes; if you really need to go overseas through a proxy, setTINGWU_OSS_USE_PROXY=1to restore. Note: yt-dlp download links (Xiaoyuzhou/YouTube/Bilibili) are not affected by this change; overseas sources still follow the environment proxy - On macOS Apple Silicon,
cryptography<=41's_rust.abi3.soduring OpenSSL CPU detection (_armv8_sve_probe) will hang in an infinite loop; the symptom is that any script importing oss2 hangs with no output. Already at thetingwu.pyentry point, automatically setOPENSSL_armcap=0to bypass; if you still encounter it, manually before runningexport OPENSSL_armcap=0, or upgrade cryptography>=42.