youtube-research-workflow

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}

8 个 Step 全流程

┌─────────────────────────────────────────────────────────────┐
│  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"                                  │
└─────────────────────────────────────────────────────────────┘

中文视频 vs 英文视频 — 路径差异

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 推理

关键决策点(代码 215-251 行)

// 兜底语言判定
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
最底层 DeepSeek 调用就是 POST https://api-proxy.chxyka.ccwu.cc/v1/chat/completions, 使用 model: "deepseek-v4-pro"max_tokens: 8192(防止 reasoning token 占满)、 默认 stream: false

结果字段(GET /status/{job_id})