YouTube 视频 → 转录 + 智能翻译 + 摘要 + 标题 + clip 建议
POST https://youtube-research-workflow.hb67egcim4.workers.dev/
Authorization: Bearer yt-research-token-2026
Content-Type: application/json
{
"url": "https://www.youtube.com/watch?v=XXX", # 必填
"language": "zh", # 可选,分析摘要的输出语言,默认 zh
"translate_to": "zh", # 可选,强制翻译目标语言,不传则自动判断
"tts": false, # 可选,是否生成中文配音,默认 false
"clip_count": 5 # 可选,clip 建议数量,不传 AI 自行判断
}
返回 HTTP 202:
{ "job_id": "uuid", "status": "pending", "translate_to": "auto", "tts": false }
异步任务,需轮询 GET /status/{job_id}。
┌─────────────────────────────────────────────────────────────┐
│ Step 1: download-audio │
│ ───────────────────────── │
│ 调 youtube-audio worker: │
│ - yt-dlp 下载 mp4 音频 │
│ - 用 ffmpeg 按 chunk_minutes=5 切片 │
│ - 每片传 R2 (my-images bucket) │
│ - 完成后 POST callback_url 回调本 workflow │
│ 状态: status = "downloading" │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 2: wait-audio-done (event) │
│ 挂起等容器回调,15 分钟超时 │
│ 收到 event payload: { video_id, chunks[], total_chunks } │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 3: enqueue-chunks │
│ 把每个 chunk 写 D1 表 transcription_chunks (status=pending) │
│ 把每个 chunk 入 Queue whisper-chunks │
│ 状态: status = "transcribing" │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 4: Queue consumer (并发跑,本 workflow 等) │
│ 对每条 Queue 消息: │
│ - 从 R2 读 chunk 音频 │
│ - env.AI.run('@cf/openai/whisper-large-v3-turbo') │
│ → 出 transcript + segments[] (带时间戳) │
│ - 写回 D1.transcription_chunks.result │
│ 最后一个 chunk done 时唤醒 workflow │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 5: merge-transcription + 语言判定 │
│ 合并 transcript + segments[] │
│ ★ Whisper 给 language code 时直接用 │
│ ★ 否则用 transcript 文本 CJK 字符占比兜底: │
│ CJK > 15% → zh,平假名 > 10% → ja,韩字 > 10% → ko,else en │
└─────────────────────────────────────────────────────────────┘
↓
┌────────────┴────────────┐
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ ★ 中文视频(zh) │ │ ★ 英文视频(en) │
│ 跳过 translate │ │ 走 translate │
└──────────────────┘ └──────────────────┘
│ │
│ ┌─────────────────────────────┐
│ │ Step 6: translate │
│ │ 状态: status = "translating"│
│ │ segments 分 10 组并行 │
│ │ 调 youtube-translate │
│ │ → vertex-gateway │
│ │ → openapi-deepseek-gateway │
│ │ → DeepSeek v4 Pro 翻译 │
│ └─────────────────────────────┘
│ │
│ ┌─────────────────────────────┐
│ │ Step 7: tts (可选,tts:true) │
│ │ 用翻译后文本调 youtube-tts │
│ └─────────────────────────────┘
│ │
└────────────┬─────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 8: analyze │
│ 状态: status = "analyzing" │
│ 调 youtube-analysis worker │
│ → vertex-gateway → DeepSeek │
│ → 出 summary/key_points/titles/description/tags/clips │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Step 9: save-results │
│ 状态: status = "completed" │
└─────────────────────────────────────────────────────────────┘
| Step | 中文视频(zh) | 英文视频(en) |
|---|---|---|
| 1 download | ✅ 一样 | ✅ 一样 |
| 2 wait audio | ✅ 一样 | ✅ 一样 |
| 3 enqueue chunks | ✅ 一样 | ✅ 一样 |
| 4 Whisper 转写 | ✅ 出中文 transcript | ✅ 出英文 transcript |
| 5 lang 判定 | CJK > 15% → zh | 默认 → en |
| 6 translate | ⏭️ 跳过 | ✅ DeepSeek 译成 zh |
| 7 tts(可选) | 直接用原文 | 用翻译后文本 |
| 8 analyze | 输入中文 → 中文摘要 | 输入英文 → 中文摘要 |
| 9 save | ✅ 一样 | ✅ 一样 |
| 总时间 | ~50 秒 | ~2 分钟 |
| Token 成本 | 1 次 DeepSeek 推理 | 2 次 DeepSeek 推理 |
// 兜底语言判定
const finalDetectedLang = (() => {
if (detected_lang && detected_lang !== 'unknown') return detected_lang;
const cjk = (transcript.match(/[一-鿿]/g) || []).length;
if (cjk / transcript.length > 0.15) return 'zh';
return 'en';
})();
// 是否要翻译
const ZH_LANGS = new Set(['zh', 'yue', 'wuu', 'hak', 'nan']);
const effectiveTranslateTo =
translate_to // 用户指定 → 用它
?? (!ZH_LANGS.has(finalDetectedLang) ? 'zh' : null); // 非中文 → 译成中文
const shouldTranslate =
effectiveTranslateTo !== null
&& effectiveTranslateTo !== finalDetectedLang; // 目标≠源 才执行
pending → downloading → transcribing → [translating] → [tts] → analyzing → completed
↑ ↑
英文视频才有 tts:true 才有
↘ errored
youtube-research-workflow
├─ Step 1 → youtube-audio (yt-dlp + ffmpeg)
├─ Step 4 → env.AI.run(whisper) (Workers AI)
├─ Step 6 → youtube-translate
│ └─→ vertex-gateway (开关 BYPASS_VERTEX_TEXT)
│ └─→ openapi-deepseek-gateway → DeepSeek v4 Pro
├─ Step 7 → youtube-tts (Edge TTS / MeloTTS)
└─ Step 8 → youtube-analysis
└─→ vertex-gateway (开关 BYPASS_VERTEX_TEXT)
└─→ openapi-deepseek-gateway → DeepSeek v4 Pro
POST https://api-proxy.chxyka.ccwu.cc/v1/chat/completions,
使用 model: "deepseek-v4-pro"、max_tokens: 8192(防止 reasoning token 占满)、
默认 stream: false。
status: pending → downloading → transcribing → [translating] → [tts] → analyzing → completeddetected_lang: zh / en / ja / komodel_used: gateway(走 vertex-gateway) / gemini / llama(fallback)transcript: 原文全文translated_transcript: 翻译后全文(仅英文视频有)segments: 带时间戳的 segment 数组summary / titles / key_points / description / tags / clip_suggestionschunks: 每个分片的转写状态files: transcript 多格式下载链接(json/txt/srt/vtt/csv/md)