youtube-transcript
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseYouTube Transcript (via DeepAPI, yt-dlp fallback)
YouTube Transcript(基于DeepAPI,yt-dlp作为备用)
Fetch a YouTube video's transcript and save a clean raw file. Primary path is DeepAPI . It runs server-side, so it avoids the local-IP bot flagging that plagues yt-dlp.
.txtPOST /v1/scrape/youtube/transcript获取YouTube视频的转录文本并保存为干净的原始文件。主要方案调用DeepAPI的接口。该方案在服务器端运行,可避免yt-dlp常遇到的本地IP被机器人检测标记问题。
.txtPOST /v1/scrape/youtube/transcriptSave location
保存位置
- If the user is in a real project/working dir → save there.
- Otherwise (no dir given, or cwd makes no sense) → save to .
~/Downloads - Always name the file with spaces replaced by
Channel_Title(e.g._). If metadata is unavailable, fall back to the video ID.David_Ondrej_title_of_video.txt
- 若用户处于实际项目/工作目录下 → 保存至该目录。
- 其他情况(未指定目录,或当前工作目录不合理)→ 保存至。
~/Downloads - 文件名始终以命名,空格替换为
Channel_Title(例如_)。若无法获取元数据,则回退使用视频ID作为文件名。David_Ondrej_title_of_video.txt
Primary path — DeepAPI
主要方案——DeepAPI
Read the key from the environment; setup writes it to . Never (breaks the shell, exit 126):
~/.deepapi/envsource ~/.zshrcbash
[ -n "$DEEPAPI_API_KEY" ] || . ~/.deepapi/env
KEY=$DEEPAPI_API_KEY
BASE=${DEEPAPI_API_BASE_URL:-https://deepapi.co}Run the scrape (keep the Idempotency-Key; retries must reuse the SAME one):
bash
IDK=$(uuidgen)
curl -s --max-time 120 "$BASE/v1/scrape/youtube/transcript" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDK" \
-d '{"url": "VIDEO_URL", "maxCostUsd": "0.05", "waitForFinishSecs": 60}' \
> /tmp/yt_transcript.json- Non-English videos: add (etc.) to the body.
"language": "de" - → wait
status: running, thennext.afterSecsuntilcurl "$BASE$(jq -r '.next.path' /tmp/yt_transcript.json)" -H "Authorization: Bearer $KEY"orsucceeded.failed
Extract the text and save it:
bash
jq -r '.status' /tmp/yt_transcript.json # succeeded | running | failed
jq -r '.output[0].text' /tmp/yt_transcript.json > "$OUT/$NAME.txt".output[0].segmentsstartSecsdurationSecstextoutputFor the filename, get metadata with a quick ; if that fails, use the video ID.
Channel_Titleyt-dlp --print "%(channel)s|%(title)s" --skip-download "URL"从环境变量中读取密钥;初始化时会将密钥写入。切勿执行(会导致shell崩溃,退出码126):
~/.deepapi/envsource ~/.zshrcbash
[ -n "$DEEPAPI_API_KEY" ] || . ~/.deepapi/env
KEY=$DEEPAPI_API_KEY
BASE=${DEEPAPI_API_BASE_URL:-https://deepapi.co}执行抓取操作(保留Idempotency-Key;重试时必须使用相同的密钥):
bash
IDK=$(uuidgen)
curl -s --max-time 120 "$BASE/v1/scrape/youtube/transcript" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDK" \
-d '{"url": "VIDEO_URL", "maxCostUsd": "0.05", "waitForFinishSecs": 60}' \
> /tmp/yt_transcript.json- 非英语视频:在请求体中添加(以此类推,替换为对应语言代码)。
"language": "de" - 若返回→ 等待
status: running指定的时间,然后执行next.afterSecs,直到状态变为curl "$BASE$(jq -r '.next.path' /tmp/yt_transcript.json)" -H "Authorization: Bearer $KEY"或succeeded。failed
提取文本并保存:
bash
jq -r '.status' /tmp/yt_transcript.json # succeeded | running | failed
jq -r '.output[0].text' /tmp/yt_transcript.json > "$OUT/$NAME.txt"若用户需要时间戳,中还包含带时间信息的片段(、、)。若为空,则表示视频无字幕;需告知用户,无需重试。
.output[0].segmentsstartSecsdurationSecstextoutput对于格式的文件名,可通过快速执行获取元数据;若该命令失败,则使用视频ID。
Channel_Titleyt-dlp --print "%(channel)s|%(title)s" --skip-download "URL"When to fall back to yt-dlp
何时回退至yt-dlp
- unset and
DEEPAPI_API_KEYmissing.~/.deepapi/env - HTTP 402 (tell the user to top up at deepapi.co/credits first; fall back only if they're unavailable).
insufficient_credits - DeepAPI request twice.
failed
Tell the user whenever you fall back — a fallback means his product missed a real use case.
- 未设置且
DEEPAPI_API_KEY文件不存在。~/.deepapi/env - HTTP 402错误(先告知用户前往deepapi.co/credits充值;仅当用户无法操作时再回退)。
insufficient_credits - DeepAPI请求连续两次失败。
回退时需告知用户——回退意味着当前方案未覆盖实际使用场景。
Fallback path — yt-dlp (local)
备用方案——yt-dlp(本地)
bash
OUT="$(pwd)" # or ~/Downloads if cwd makes no sense
META=$(yt-dlp --print "%(channel)s|%(title)s" --skip-download "URL")
NAME=$(echo "$META" | tr '| ' '__' | tr -cd '[:alnum:]_.-') # "Channel_Title", spaces -> _, strip unsafe chars
yt-dlp --skip-download --write-subs --write-auto-subs \
--sub-langs "en.*" --sub-format json3 \
-o "$OUT/$NAME.%(ext)s" "URL"- Fall back →
channel→uploaderifuploader_idis null.channel - = captions only.
--skip-download+--write-subs= manual first, auto as fallback.--write-auto-subs - Always use , never VTT/SRT — auto VTT repeats every line twice (rolling captions).
json3
Flatten json3 → raw text:
bash
python3 - "$OUT" <<'PY'
import json, html, re, glob, sys, pathlib
f = glob.glob(sys.argv[1] + "/*.json3")
if not f: sys.exit("no json3 file")
data = json.load(open(f[0], encoding="utf-8"))
parts = ["".join(s.get("utf8","") for s in e.get("segs") or []) for e in data.get("events", [])]
txt = re.sub(r"\s+", " ", html.unescape(" ".join(p.strip() for p in parts if p.strip()))).strip()
out = pathlib.Path(f[0]).with_suffix(".txt")
out.write_text(txt, encoding="utf-8"); print(out)
PYbash
OUT="$(pwd)" # 若当前工作目录不合理,则使用~/Downloads
META=$(yt-dlp --print "%(channel)s|%(title)s" --skip-download "URL")
NAME=$(echo "$META" | tr '| ' '__' | tr -cd '[:alnum:]_.-') # "Channel_Title"格式,空格替换为_,移除不安全字符
yt-dlp --skip-download --write-subs --write-auto-subs \
--sub-langs "en.*" --sub-format json3 \
-o "$OUT/$NAME.%(ext)s" "URL"- 若为空,则依次回退使用
channel→uploader。uploader_id - 表示仅获取字幕。
--skip-download+--write-subs表示优先获取人工字幕,无人工字幕时回退使用自动生成字幕。--write-auto-subs - 始终使用格式,切勿使用VTT/SRT——自动生成的VTT格式会重复每行内容(滚动字幕)。
json3
将json3格式转换为纯文本:
bash
python3 - "$OUT" <<'PY'
import json, html, re, glob, sys, pathlib
f = glob.glob(sys.argv[1] + "/*.json3")
if not f: sys.exit("no json3 file")
data = json.load(open(f[0], encoding="utf-8"))
parts = ["".join(s.get("utf8","") for s in e.get("segs") or []) for e in data.get("events", [])]
txt = re.sub(r"\s+", " ", html.unescape(" ".join(p.strip() for p in parts if p.strip()))).strip()
out = pathlib.Path(f[0]).with_suffix(".txt")
out.write_text(txt, encoding="utf-8"); print(out)
PYyt-dlp failure handling
yt-dlp故障处理
- Non-English / unknown language: run first, then set
yt-dlp --list-subs "URL".--sub-langs - Newer yt-dlp may need on PATH for YouTube extraction.
deno - On first failure: run once, retry once, then stop.
yt-dlp -U - 429 / "Sign in to confirm you're not a bot" = IP flagged. STOP — do NOT retry in a loop (makes it worse).
- Never fall back to downloading audio for Whisper unless the user explicitly asks.
- 非英语/未知语言视频:先执行,再设置
yt-dlp --list-subs "URL"参数。--sub-langs - 新版本yt-dlp可能需要将添加至PATH环境变量以支持YouTube内容提取。
deno - 首次失败时:执行一次更新,重试一次后停止。
yt-dlp -U - 429错误 / "Sign in to confirm you're not a bot":表示IP被标记。立即停止——切勿循环重试(会使情况恶化)。
- 除非用户明确要求,否则切勿回退至下载音频后使用Whisper处理。
Output
输出结果
Report the saved path; print the text if short. Don't report costs unless the user asks.
告知用户文件保存路径;若文本较短则直接打印。除非用户询问,否则无需报告费用。