首页 / 全部技能 / 内容创作 / baoyu-youtube-transcript
内容创作 · JimLiu/baoyu-skills

baoyu-youtube-transcript

Downloads YouTube video transcripts/subtitles and cover images by URL or video ID. Supports multiple languages, translation, chapters, and speaker identification. Caches raw data for fast re-formatting. Use when user asks to "get YouTube transcript", "download subtitles", "get captions", "YouTube字幕", "YouTube封面", "视频封面", "video thumbnail", "video cover image", or provides a YouTube URL and wants the transcript/subtitle text or cover image extracted.

L5 严管 · 隔离环境与严格审计V2 已复核逆向接口凭据/登录态执行命令联网读写文件Dual-pass Audit
Author JimLiuGitHub JimLiu/baoyu-skills ↗Stars 25926License MIT(仓库根 LICENSE:Copyright (c) 2026 Jim Liu;根 package.json 同声明 "license": "MIT")Commit 1567581c26
管控等级描述 skill 的能力与建议配套的管控措施,不代表质量或安全认证,也不表示可用或不可用。是否使用由用户和管理者决定。不同 agent 宿主对权限的约束各不相同,请勿假定宿主一定会提供安全兜底。第三方 skill 版权归原作者,使用风险由使用者自行承担。

1实现原理 · 为什么它能做到

字幕来源是 YouTube 自带的 InnerTube 播放器接口,不是官方 Data API:脚本先从 watch 页 HTML 里现场正则抓出 INNERTUBE_API_KEY,再带上它 POST 播放器接口。

skills/baoyu-youtube-transcript/scripts/youtube.ts
const apiKey = html.match(/"INNERTUBE_API_KEY":\s*"([a-zA-Z0-9_-]+)"/)?.[1];
这里在做什么:key 是公开网页 key,随取随用,所以 skill 能宣称『No API key』;同一段 HTML 里还抓 visitorData 当 X-Goog-Visitor-Id 用,属匿名会话身份,不需要登录。端点常量在同文件:INNERTUBE_URL = https://www.youtube.com/youtubei/v1/player —— 即自建对非公开接口的请求,而非走官方 Data API。

被 YouTube 判为机器人时不是硬重试,而是换客户端身份:依次用 ANDROID / WEB / IOS 三套 client 声明与 UA 重试,全失败才落到 yt-dlp。

skills/baoyu-youtube-transcript/scripts/youtube.ts
com.google.android.youtube/20.10.38 (Linux; U; Android 14; en_US; Pixel 8 Pro; Build/AP1A.240405.002)
这里在做什么:把请求伪装成 YouTube App 客户端(配 X-YouTube-Client-Name / X-YouTube-Client-Version / X-Goog-Visitor-Id 头),绕开网页端 bot 检测。

yt-dlp 回退是『先探测、再按能力挑命令』:逐个试 yt-dlp / uvx --from yt-dlp yt-dlp / python3 -m yt_dlp,并要求 --help 里出现 --js-runtimes 与 --remote-components 才算合格。

skills/baoyu-youtube-transcript/scripts/youtube.ts
helpText.includes("--js-runtimes") &&
不合格的旧版 yt-dlp 会被跳过——因为脚本要用 --js-runtimes bun 解签名、用 --remote-components ejs:github 拉 EJS 组件。SKILL.md 同时把缺件的处置权交给 agent 而不是用户。

最终调用是 spawnSync(命令, argv 数组),没有 shell 拼串;探测与执行都带 maxBuffer 上限。

skills/baoyu-youtube-transcript/scripts/youtube.ts
const result = spawnSync(command.command, args, {
执行面收窄为固定 argv(-J、--skip-download、--js-runtimes、bun、--remote-components、ejs:github,可选 --cookies-from-browser),因此不存在 shell 注入;视频 ID 参数来自用户输入。

--translate 不调用任何翻译 API,只是给字幕轨 URL 追 &tlang=<code>,用 YouTube 自己的机器翻译轨。

skills/baoyu-youtube-transcript/scripts/youtube.ts
url += `&tlang=${translateTo}`;
前置检查 info.isTranslatable 且目标语言在 translationLanguages 里;走 yt-dlp 分支时则改成重选语言列表而不是加 tlang。

--speakers 采用『脚本出料 + sub-agent 加工』:脚本只落一份带 SRT 时间戳的原始 md,再让 agent 派一个便宜模型的 sub-agent 做说话人标注与章节切分。

skills/baoyu-youtube-transcript/SKILL.md
spawn a sub-agent (use a cheaper model like Sonnet for cost efficiency)
提示词模板在 prompts/speaker-transcript.md,硬性要求逐字保真、用 SRT 既有时间戳合并成段落、以 **Speaker Name:** 起段。

缓存即数据面:首跑落 meta.json + transcript-raw.json + transcript-sentences.json + imgs/cover.jpg,`.index.json` 维护 videoId→目录映射,同视频再次运行零网络。

skills/baoyu-youtube-transcript/scripts/storage.ts
return existsSync(join(videoDir, "meta.json")) && existsSync(join(videoDir, "transcript-raw.json"));
换语言或 --refresh 会强制重取;SRT 由 raw snippets 生成、markdown 用 sentences,同一份缓存支持两种渲染。

文本输出不是原始 snippets 而是『句子』:按标点切句、时间戳按字符长度比例分配、CJK 合并。

skills/baoyu-youtube-transcript/SKILL.md
split by sentence-ending punctuation (`.?!…。?!` etc.), timestamps proportionally allocated by character length, CJK-aware text merging
这是为什么它的 markdown 输出比原生字幕好读:段落边界是句子而不是几秒一条的 cue。

2核心能力

01按 URL 或 video ID 抓字幕并输出 markdown / SRT(无需 API key、无需浏览器)
02多语言优先级与可用轨列举(--languages / --list)
03机翻字幕轨(--translate zh-Hans),走 YouTube 自己的翻译轨
04按视频描述里的时间戳做章节切分(--chapters)
05说话人识别(--speakers):脚本出原料、sub-agent 逐字标注说话人并分段
06抓取并本地缓存封面与元数据(imgs/cover.jpg + meta.json)
07反爬回退链:三套客户端身份重试 → yt-dlp
08缓存复用与强制刷新(--refresh / 换语言自动失效)

3外部依赖

KindDependency / Endpoint
apiYouTube InnerTube player API(非公开接口)
networkYouTube watch 页(抓 INNERTUBE_API_KEY / visitorData)
network字幕轨 baseUrl(InnerTube/yt-dlp 返回,fmt=json3/xml)
networkYouTube 缩略图 CDN(封面)
networkconsent.youtube.com 表单(仅字符串匹配,重试用 CONSENT cookie 再请求 watch 页)
cliyt-dlp(直接二进制)
cliuvx --from yt-dlp yt-dlp(免安装形态)
clipython3 -m yt_dlp(已安装模块形态)
clibun / npx -y bun(脚本运行时)
networkyt-dlp --remote-components ejs:github 拉取的 GitHub 远端 EJS 组件

4管控建议与判定依据

建议配套管控措施
面向 Agent
隔离环境,逐次授权
面向管理者
严格授权、全量审计、合规确认
判定依据(文件路径与关键语句摘要)
  • SKILL.md / 脚本: 调用未公开或逆向工程的私有 API / 伪造客户端标识
  • SKILL.md / 会话存储: 读取或复用浏览器用户登录态与会话 Cookie
  • 本地凭据 / 浏览器配置: 读取用户登录 Cookie/会话/密钥环: process.env.YOUTUBE_TRANSCRIPT_COOKIES_FROM_BROWSER(全 skill 唯一一处 env 读取)→ 作
  • scripts/: 执行本地脚本或命令: ${BUN_X} {baseDir}/scripts/main.ts <url|id>(bun,或 npx -y bun;skill 目录内只有 TS
  • 网络端点: 声明外部网络访问: https://www.youtube.com/watch?v=<id>&hl=en&persist_hl=1&has_verified=1&bpct
  • 本地工作目录: 文件读写与工件输出: youtube-transcript/(默认,可用 --output-dir 改)
审计说明: (属能力/策略级判定,非恶意植入:代码干净可读、无混淆、无硬编码密钥、无向非预期对象外发)。判红的三层理由:①【逆向/伪装】不调官方 Data API,而是自建对 youtubei/v1/player 的请求,并每次从 watch 页 HTML 正则抓 INNERTUBE_API_KEY('const apiKey = html.match(/"INNERTUBE_API_KEY":\s*"([a-zA-Z0-9_-]+)"/)?.[1];'),再以 ANDROID / IOS / WEB 三套客户端身份与移动端 UA 冒充官方客户端绕开 bot 检测(UA 原文:'com.google.android.youtube/20.10.38 (Linux; U; Android 14; en_US; Pixel 8 Pro; Build/AP1A.240405.002)')。②【凭证面】skill 的内建决策把 YOUTUBE_TRANSCRIPT_COOKIES_FROM_BROWSER 透传为 yt-dlp --cookies-from-browser,等于让第三方 CLI 读取本机浏览器 cookie 库并带真实登录态出网;程度说明:有 env 闸门、委派给第三方、不经 CDP、不落盘 token。③【自举第三方 CLI】回退路径可用 uvx --from yt-dlp yt-dlp / python3 -m yt_dlp 现取现用,并以 --js-runtimes bun --remote-components ejs:github 拉取 GitHub 远端组件执行,且 SKILL.md 要求 agent 自行取得缺失依赖而不询问用户。对照片内先例:bilibili-source 仅『接触 cookie』即判 orange,本条同时命中逆向伪装与凭证委派,故为红。默认(未设 env)路径本身只访问固定公开端點、无凭证,属黄级——按站内能力级范式,能力本身决定档位。

5第二遍独立确认

  • [ok] InnerTube 端点与 key 现场抓取(无硬编码 key) — youtube.ts 含 INNERTUBE_URL/WATCH_URL 常量与 html.match(/"INNERTUBE_API_KEY":...) 抓取逻辑;全仓无硬编码 API key,只有会随版本过期的客户端版本号与 UA 常量。
  • [ok] 三套客户端身份轮换(ANDROID/WEB/IOS)+ 对应头 — clientName "ANDROID"/"WEB"/"IOS"、X-YouTube-Client-Name / X-YouTube-Client-Version / X-Goog-Visitor-Id 头与 Pixel 8 Pro UA 均在源码中;与 SKILL.md 的『bot detected』说明一致。
  • [ok] yt-dlp 回退的探测与参数 — 三个候选命令(yt-dlp / uvx / python3 -m yt_dlp),--version 探测后还要 --help 命中 --js-runtimes 与 --remote-components;执行参数为 -J --skip-download --js-runtimes bun --remote-components ejs:github(可选 --cookies-from-browser)。
  • [ok] --translate 无任何外部翻译服务 — 只有 url += `&tlang=${translateTo}` 与 isTranslatable / translationLanguages 前置检查;无 Google Translate / LLM / HTTP 翻译调用,依赖面为零 npm 包。
  • [ok] 零 npm 依赖、skill 内无 package.json — skill 目录仅 .ts 与 prompt 模板;全部 import 为 node 内置(fs/path/child_process/node:test/node:assert)与相对 .ts;根 package.json 的 workspaces 只覆盖 packages/*。
  • [ok] 全 skill 唯一 env 读取 = YOUTUBE_TRANSCRIPT_COOKIES_FROM_BROWSER — 跨目录 grep process.env / env[ / YOUTUBE_ 仅命中该变量(读取一处、拼参数一处);未设置时回退路径不带 cookie,故凭证面为『闸门 + 委派』而非默认开启。
  • [ok] 写盘清单与 SKILL.md 输出树一致 — 代码写入 .index.json / meta.json / transcript-raw.json / transcript-sentences.json / imgs/cover.jpg / transcript.md|.srt,与 SKILL.md 目录树逐项对齐;不落盘任何 token。
  • [ok] 文档与代码的次要出入 — SKILL.md 工作流小节出现两个编号为 3 的步骤(文档笔误,不影响结论);--speakers 隐含 --chapters 的说明与代码分支一致。

6结论

  • 零依赖零构建:只用 bun + node 内置模块,克隆下来即可运行,供应链面几乎为零。
  • 不需要 API key 与浏览器就能拿字幕,且把『被反爬』做成可恢复路径(身份轮换 → yt-dlp)。
  • 输出质量优于原生字幕:按标点切句 + 章节 + 说话人 + 时间戳可组合,markdown 与 SRT 同源。
  • 缓存即数据面:首次抓取后重复格式化(先 text 再 SRT)不再联网,省时且对 YouTube 友好。
  • 文档纪律好:把 zsh 引号陷阱、可选 cookie 变量、错误码处置都写清楚。
  • 适合: 适合把访谈/讲座/播客的 YouTube 视频整理成可读文稿与字幕的个人与内容团队:要 markdown 长文、要 SRT 上播放器、要章节与说话人标注、要机翻对照,且希望重复处理同一视频不重复联网。Bun 环境一步可用;在知情其逆向/伪装与可选 cookie 行为的前提下自用最合适。
    不适合: 不适合企业合规敏感或个人隐私要求高的环境(伪造客户端身份、可选的浏览器 cookie 委派、拉取远端组件);不适合需要稳定官方接口承诺的生产管道;不适合把远端视频文本直接喂给有写权限的 agent 而无人复核;也不适合需要下载视频文件本身(本 skill 只取字幕与封面)。
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload baoyu-youtube-transcript.tar.gz
    sha256: dbf11afd644a4963…
    Option B · Copy Guide for Agent
    install.json
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit 1567581c26.