watch

watch

热门

观看视频(URL 或本地路径)。使用 yt-dlp 下载,用 ffmpeg 提取自动缩放的帧,从字幕(或 Whisper API 后备)获取转录,并将结果交给 Claude,以便回答关于视频内容的问题。

9177Star
983Fork
更新于 2026/7/1
SKILL.md
readonly只读
name
watch
description

观看视频(URL 或本地路径)。使用 yt-dlp 下载,用 ffmpeg 提取自动缩放的帧,从字幕(或 Whisper API 后备)获取转录,并将结果交给 Claude,以便回答关于视频内容的问题。

version
0.2.0

/watch

你没有视频输入;这个技能给你一个。Python 脚本首先获取字幕,可选地下载视频,提取帧为 JPEG(场景感知,或在 efficient 细节下快速关键帧),获取带时间戳的转录(优先原生字幕,然后 Whisper API 作为后备),并打印帧路径。然后你 Read 每个帧路径以查看图像,并结合转录来回答用户。

解析 SKILL_DIR(在任何命令之前执行)

下面的每个 python3 ... 命令都运行 SKILL_DIR/scripts/ 下的捆绑脚本。将 SKILL_DIR 设置为包含你刚刚 Read 的 SKILL.md 的目录的绝对路径——你的工具在 Read 结果中告诉了你该路径。脚本始终是该文件的直接同级(SKILL_DIR/scripts/watch.py),在任何安装布局中:

Read ~/.claude/plugins/cache/claude-video/watch/<ver>/skills/watch/SKILL.md → SKILL_DIR=…/skills/watch
Read ~/.codex/skills/watch/SKILL.md                                          → SKILL_DIR=~/.codex/skills/watch
Read ~/.agents/skills/watch/SKILL.md                                         → SKILL_DIR=~/.agents/skills/watch

在每个命令中将该字面路径替换为 ${SKILL_DIR}。这适用于任何工具(Claude Code, Codex, Cursor, Gemini CLI, …),无需依赖任何工具特定的环境变量。在运行开始时检查一次:

SKILL_DIR="<包含你 Read 的 SKILL.md 的目录的绝对路径>"
if [ ! -f "$SKILL_DIR/scripts/watch.py" ]; then
  echo "错误:在 SKILL_DIR=$SKILL_DIR 下未找到 scripts/watch.py" >&2
  echo "请重新检查你 Read 的 SKILL.md 的目录,并将其替换为 SKILL_DIR。" >&2
  exit 1
fi

步骤 0 — 设置预检(每次 /watch 调用时运行,成功时静默)

Python 解释器: 此技能中的每个 python3 ... 命令适用于 macOS/Linux。在 Windows 上,替换为 python——Windows 上的 python3 命令是 Microsoft Store 存根,不会运行脚本。

在会话中第一次 /watch 调用时,使用结构化预检以便检测首次运行设置:

python3 "${SKILL_DIR}/scripts/setup.py" --json

根据两个字段分支:

  • can_proceed: truefirst_run: false → 设置已完成(用户可能故意跳过了 Whisper 密钥——这是允许的)。继续执行步骤 1,无需评论。
  • first_run: true → 真正的首次设置。按顺序执行以下操作:
    1. 如果 missing_binaries 非空,首先运行安装程序(它在 macOS 上自动安装 / 在其他系统上打印命令——见下文)并确认二进制文件已安装。不要跳过此步骤直接跳到偏好设置。
    2. 如果需要,再次运行安装程序,以便它创建 ~/.config/watch/.env(它只在文件不存在时写入模板,所以让它先创建文件,然后你再写入任何值)。
    3. 鼓励提供 Whisper API 密钥并询问下面的观看偏好问题,然后将选定的值写入 ~/.config/watch/.env 并设置 SETUP_COMPLETE=true
  • can_proceed: falsefirst_run: false → 之前已完成设置,但环境退化了(例如,操作系统更改后 missing_binaries)。运行安装程序进行修复,然后继续。不要重新询问偏好。

缺少 Whisper 密钥是鼓励修复,但不是必需的:在真正的首次运行时,即使二进制文件存在,status 也会显示 needs_key——这是你鼓励提供密钥的信号,而不是阻塞。

在同一会话中的后续 /watch 调用中,使用静默检查:

python3 "${SKILL_DIR}/scripts/setup.py" --check

这是一个 <100ms 的查找。退出码 0 表示 /watch 可以运行——这包括已完成设置但没有 Whisper 密钥的用户(无密钥是允许的)。退出码 0 时脚本不输出任何内容——继续执行步骤 1,无需评论。不要向用户宣布“设置完成”——他们不需要每次轮次都看到状态消息。步骤 0 唯一可接受的用户可见输出是当需要修复时。

对于非零退出码,遵循下表:

退出码 含义 操作
2 缺少二进制文件(ffmpeg / ffprobe / yt-dlp 运行安装程序
3 真正的首次运行,没有 Whisper API 密钥 运行安装程序以创建 .env,然后鼓励提供密钥(用户可以拒绝——继续使用 --no-whisper
4 两者都缺少 运行安装程序,然后鼓励提供密钥

退出码 3 仅在用户完成设置之前触发。一旦写入 SETUP_COMPLETE=true,无密钥的安装返回退出码 0,并且不再提示。

安装程序是幂等的——重新运行是安全的:

python3 "${SKILL_DIR}/scripts/setup.py"

在 macOS 上使用 Homebrew,它会自动安装 ffmpegyt-dlp。在 Linux/Windows 上,它会打印确切的安装命令供用户运行。它会创建 ~/.config/watch/.env,包含注释掉的占位符和默认观看设置,权限为 0600

如果安装后仍然缺少 API 密钥: 使用 AskUserQuestion 询问用户是否有 Groq API 密钥(首选——更便宜、更快)或 OpenAI 密钥。然后将其写入 ~/.config/watch/.env——设置相应的 GROQ_API_KEY=...OPENAI_API_KEY=... 行。如果他们不想设置 Whisper,则使用 --no-whisper 继续,并告诉他们没有原生字幕的视频将只返回帧。

首次运行观看偏好: 在安装程序创建了 ~/.config/watch/.env 后,使用 AskUserQuestion 询问一个问题:

  • 默认细节(单选)。按此确切顺序将以下选项呈现为 AskUserQuestion 选项——从最轻到最重——并将 (recommended) 保留在 balanced 上,即使它不是第一个(不要重新排序将推荐选项放在第一位):
    • transcript — 无帧,仅转录(当存在字幕时跳过视频下载)。
    • efficient — 快速关键帧通过(上限 50)。
    • balanced(推荐)— 场景感知帧(上限 100,默认)。
    • token-burner — 场景感知,无上限(最高保真度;高 token 成本)。

将答案直接写入 ~/.config/watch/.env,将裸键设置在其自己的行上——没有尾随内联注释(值后的 # note 可能会破坏解析):

WATCH_DETAIL=balanced

使用用户选择的值。如果他们跳过问题,则保留推荐的默认值。一旦处理了依赖项、API 密钥选择和此偏好,在同一文件中写入或更新 SETUP_COMPLETE=true。当 SETUP_COMPLETE=true 时,不要再次询问此偏好问题。

结构化模式(可选): python3 "${SKILL_DIR}/scripts/setup.py" --json 输出 {status, can_proceed, first_run, setup_complete, missing_binaries, whisper_backend, has_api_key, config_file, watch_detail, platform},其中 statusready | needs_install | needs_key | needs_install_and_key 之一。status 描述理想状态(鼓励提供密钥,因此无密钥的首次运行显示 needs_key);can_proceed 是操作门(二进制文件存在且密钥已设置,或者设置已完成)。根据 can_proceed/first_run 分支决定是否运行;使用 status 决定鼓励什么。

在单个会话中,你可以在后续的 /watch 调用中跳过步骤 0——一旦 --check 返回 0,环境在轮次之间不会改变。

何时使用

  • 用户粘贴视频 URL(YouTube, Vimeo, X, TikTok, Twitch 剪辑,大多数 yt-dlp 支持的网站)并询问相关内容。
  • 用户指向本地视频文件(.mp4, .mov, .mkv, .webm 等)并询问相关内容。
  • 用户输入 /watch <url-or-path> [question]

推荐限制

  • 最佳准确性:10 分钟以下的视频。 帧覆盖率与时长成反比。
  • 通用速率上限:2 fps。 脚本采样速度永远不会超过 2 fps,即使预算或 --fps 暗示更多。
  • 帧上限由细节模式决定~/.config/watch/.env 中的 WATCH_DETAIL,或 --detail),而不是单个全局上限:
    • transcript → 无帧
    • efficient → 最多 50(关键帧)
    • balanced(默认)→ 最多 100(场景感知)
    • token-burner无上限(场景感知;超过 250 帧时打印软警告)
    • --max-frames N 覆盖模式本应使用的上限。
  • 全视频帧预算按时长。 Token 成本随帧数增长,因此脚本按时长设定预算。此预算设置 fps 和均匀采样后备;场景感知选择可以填充到上述细节上限,取较低者:
    • ≤30s → ~12-30 帧
    • 30s-1min → ~40 帧
    • 1-3min → ~60 帧
    • 3-10min → ~80 帧
    • >10min → 最多细节上限,稀疏分布(打印警告)
  • 如果用户给你一个长视频,考虑询问他们是否想要特定部分,然后再在稀疏扫描上消耗 token。

如何调用

步骤 1 — 解析用户输入。 将视频源(URL 或路径)与用户提出的任何问题分开。示例:/watch https://youtu.be/abc what language is this in? → source = https://youtu.be/abc, question = what language is this in?

步骤 2 — 运行观看脚本。 直接传递源。不要自己进行 shell 转义,除了正常的引号:

python3 "${SKILL_DIR}/scripts/watch.py" "<source>"

可选标志:

  • --detail transcript|efficient|balanced|token-burner — 保真度/速度拨盘。transcript = 无帧(仅转录,当存在字幕时跳过视频下载);efficient = 快速关键帧(上限 50);balanced = 场景感知帧(上限 100);token-burner = 场景感知,无上限。
  • --start T / --end T — 聚焦于某个部分。接受 SS, MM:SS, 或 HH:MM:SS。当设置其中之一时,fps 自动缩放得更密集(见下文“聚焦于某个部分”)。
  • --timestamps T1,T2,… — 在每个绝对时间戳(SS, MM:SS, 或 HH:MM:SS)抓取一帧。在阅读转录后使用此选项,以捕获演示者标记的指示性时刻(“看这里”、“如你所见”、“注意这个”),这些时刻可能被视觉选择单独错过。见下面的“转录提示帧”。
  • --max-frames N — 覆盖预设上限以获得更紧的 token 预算(例如 --max-frames 40
  • --resolution W — 更改帧宽度(像素)(默认 512;仅当用户需要阅读屏幕上的文本时才增加到 1024)
  • --fps F — 覆盖自动 fps(上限为 2 fps)
  • --out-dir DIR — 将工作文件保存在特定位置(默认:自动生成的临时目录)
  • --whisper groq|openai — 强制使用特定的 Whisper 后端(默认:如果两个密钥都存在,优先使用 Groq)
  • --no-whisper — 完全禁用 Whisper 后备(如果没有字幕,则仅帧)
  • --no-dedup — 保留近乎重复的帧。默认情况下,帧差异传递会丢弃与上一个保留帧视觉上几乎相同的帧(静态幻灯片、屏幕录制、暂停的视频),以便帧预算用于不同的内容;报告的 Frames 行会注明丢弃了多少帧。仅当用户需要每个采样帧时才传递此选项(例如,判断帧间细微运动)。

聚焦于某个部分(更高帧率)

当用户询问特定时刻时——“2 分钟标记处发生了什么?”、“放大到 0:45 到 1:00”、“前 10 秒”——传递 --start 和/或 --end。脚本切换到聚焦模式预算,这比全视频预算更密集(仍然上限为 2 fps,并且仍然受细节模式上限限制——以下计数假设默认 balanced 上限为 100;efficient 上限为 50):

  • ≤5s → 2 fps(最多 10 帧)
  • 5-15s → 2 fps(最多 30 帧)
  • 15-30s → ~2 fps(最多 60 帧)
  • 30-60s → ~1.3 fps(最多 80 帧)
  • 60-180s → ~0.6 fps(100 帧,上限)

聚焦模式适用于:

  • 用户明确命名的任何时刻/范围(“大约 2:30”、“开头”、“最后 30 秒”)。
  • 任何时长超过约 10 分钟的视频,用户的问题是关于特定部分——在相关部分运行聚焦模式比稀疏扫描整个视频有用得多。
  • 在全扫描后某些区域细节不足时重新运行。

转录会自动过滤到相同范围。帧时间戳是绝对的(真实视频时间线,不是从开始偏移)。

示例:

# 1 分钟视频的最后 10 秒
python3 "${SKILL_DIR}/scripts/watch.py" video.mp4 --start 50 --end 60

# 放大到 2:15 → 2:45,2 fps(60 帧)
python3 "${SKILL_DIR}/scripts/watch.py" "$URL" --start 2:15 --end 2:45 --fps 2

# 从 1h12m 到视频结束
python3 "${SKILL_DIR}/scripts/watch.py" "$URL" --start 1:12:00

步骤 3 — Read 脚本列出的每个帧路径。 Read 工具直接将 JPEG 渲染为图像供你查看。在单条消息中 Read 所有帧(并行工具调用),以便你同时看到它们。帧按时间顺序排列,带有 t=MM:SS 时间戳,以便你可以将它们与转录对齐。

步骤 4 — 回答用户。 你现在有两组证据:

  • — 每个时间戳屏幕上显示的内容
  • 转录 — 每个时间戳所说的内容。报告的头部显示来源(captions = yt-dlp 拉取的原生字幕;whisper (groq)whisper (openai) = 由 API 转录)。

如果用户问了具体问题,直接回答并引用时间戳。如果他们没有问任何问题,总结视频中发生的事情——结构、关键时刻、值得注意的视觉内容、口头内容。

这也适用于 transcript 细节:即使没有帧,也要像其他模式一样生成摘要——不要将完整转录粘贴到聊天中。综合结构、关键时刻和口头内容,并附上时间戳;只引用重要的行。仅当用户明确要求时才提供原始转录。

步骤 5 — 清理。 脚本在末尾打印工作目录。如果用户不打算就此视频提出后续问题,使用 rm -rf <dir> 删除它。如果他们可能提出后续问题,则保留它。

细节和帧

默认行为来自 ~/.config/watch/.env

  • WATCH_DETAIL=transcript|efficient|balanced|token-burner(默认:balanced

transcript 细节下,字幕足以返回报告,无需下载视频。如果缺少字幕,脚本仅下载音频并尝试 Whisper。如果无法生成转录,它会清楚地报告限制;使用 --detail balanced 重新运行以获取帧。

efficient 细节下,脚本下载视频并仅提取关键帧ffmpeg -skip_frame nokey)——一个近乎即时的传递,在场景切换处获取帧。如果剪辑少于 4 个关键帧,则回退到均匀采样。

balanced / token-burner 细节下,脚本提取场景感知帧:首先使用 ffmpeg 场景变化选择,仅当视频实际上是静态时才回退到均匀采样。balanced 上限为 100 帧;token-burner 无上限。帧报告行包括时间戳和选择原因。提取的图像被限制为最大 1998px 高度,以兼容 Claude Read。

转录提示帧

视觉帧选择(场景/关键帧)可能会错过演示者明确标记的时刻——“看这里”、“如你所见”、“注意这个”、“看看会发生什么”——因为指向幻灯片通常是视觉变化。--timestamps 允许你在这些确切时刻强制获取帧。通过阅读转录来决定哪些时刻重要:

  1. 首先以 --detail transcript(或任何细节)运行一次,以获取带时间戳的转录。
  2. 扫描指示性提示——说话者将注意力引向屏幕上某物的短语。这是一个判断调用(忽略修辞性的“看,重点是…”);这就是为什么由你而不是正则表达式来完成。
  3. 使用 --timestamps 4:32,7:10,9:55(绝对源时间)重新运行。对于 URL,将第二次运行指向工作目录中的下载的本地文件,这样它就不会重新下载。

行为:

  • 默认是附加的。 提示帧(reason=transcript-cue)按时间顺序合并到 --detail 已选择的帧中。
  • 固定并首先计数。 提示帧在细节引擎运行之前从帧上限中预留,因此它们永远不会被均匀采样驱逐。
  • 尊重聚焦模式。 使用 --start/--end 时,窗口外的任何提示时间戳都会被丢弃(在摘要中报告)。坐标始终是绝对源时间。
  • 仅提示帧。 --detail transcript --timestamps … 跳过场景/关键帧采样,仅返回提示帧(它会下载视频以执行此操作,因为帧需要像素)。

转录

脚本通过两种方式之一获取带时间戳的转录:

  1. 原生字幕(免费,首选)。 yt-dlp 从源平台拉取手动或自动生成的字幕(如果可用)。
  2. Whisper API 后备。 如果没有返回字幕(或者源是本地文件),脚本提取音频(ffmpeg -vn -ac 1 -ar 16000 -b:a 64k,约 0.5 MB/分钟)并将其上传到配置了密钥的 Whisper API:

两个密钥都存储在 ~/.config/watch/.env 中。当两者都设置时,脚本优先使用 Groq;使用 --whisper openai 覆盖以强制使用 OpenAI。使用 --no-whisper 完全跳过后备。

失败模式和处理

  • 设置预检失败 → 运行 python3 "${SKILL_DIR}/scripts/setup.py"(在 macOS 上通过 brew 自动安装 ffmpeg/yt-dlp,创建 .env)。对于 API 密钥,通过 AskUserQuestion 询问用户并将其写入 ~/.config/watch/.env
  • 无可用转录 → 缺少字幕且(没有 Whisper 密钥或 Whisper API 失败)。脚本打印指向设置的提示。仅以帧模式继续并告知用户。
  • 打印长视频警告 → 在你的回答中承认它。提供通过 --start/--end 重新运行聚焦于特定部分,而不是稀疏的全视频扫描。
  • 下载失败 → yt-dlp 的错误输出到 stderr。如果是需要登录或区域限制的视频,直接告诉用户;不要继续重试。
  • Whisper 请求失败 → 错误打印到 stderr(可能是:无效密钥或速率限制)。超过 API 25 MB 上传上限的音频会自动分块并转录,因此仅长度不会导致失败;如果某些块失败,转录是部分的,丢弃的块会在 stderr 上注明。仅当每个块都失败时,报告才会显示“none available”。如果 Groq 失败,你可以使用 --whisper openai 重试(反之亦然)。

Token 效率

此技能主要消耗帧的 token。数量级:

  • 80 帧,512px 宽,大约 50-80k 图像 token,取决于宽高比。
  • 转录很便宜(对于 10 分钟的视频,最多几千个 token)。
  • --resolution 提高到 1024 大约使每帧的图像 token 翻两番。仅在必要时才这样做。

如果你在此会话中已经观看过视频,并且用户提出后续问题,不要重新运行脚本——你已经在上下文中拥有帧和转录。只需根据已有内容回答。

安全与权限

此技能做什么:

  • 本地运行 yt-dlp 以下载视频并在源支持时拉取原生字幕(公共数据;请求直接发送到 URL 指向的任何主机)
  • 本地运行 ffmpeg / ffprobe 以提取帧为 JPEG,并在需要 Whisper 时提取单声道 16 kHz 音频剪辑
  • 当设置了 GROQ_API_KEY 时,将提取的音频剪辑发送到 Groq 的 Whisper API(api.groq.com/openai/v1/audio/transcriptions)(首选——更便宜、更快)
  • 当设置了 OPENAI_API_KEY 且未设置 Groq,或强制使用 --whisper openai 时,将提取的音频剪辑发送到 OpenAI 的音频转录 API(api.openai.com/v1/audio/transcriptions
  • 将下载的视频、帧、音频和中间转录写入系统临时目录下的工作目录(或如果指定了 --out-dir),以便 Claude 可以 Read 它们
  • 读取/创建 ~/.config/watch/.env(模式 0600)以存储 Whisper API 密钥和 SETUP_COMPLETE 标记。作为后备,也读取当前工作目录中的 .env

此技能不做什么:

  • 不将视频本身上传到任何 API——只有提取的音频被发送出去,并且仅在缺少原生字幕且未使用 --no-whisper 禁用 Whisper 时
  • 不访问任何平台账户(无登录、无会话 cookie、无发布)——yt-dlp 仅请求公共数据
  • 不在提供商之间共享 API 密钥(Groq 密钥仅发送到 api.groq.com,OpenAI 密钥仅发送到 api.openai.com
  • 不记录、缓存或将 API 密钥写入 stdout、stderr 或输出文件
  • 不在工作目录和 ~/.config/watch/.env 之外持久化任何内容——完成后清理工作目录(步骤 5)

捆绑脚本: scripts/watch.py(入口点),scripts/download.py(yt-dlp 包装器),scripts/frames.py(ffmpeg 帧提取),scripts/transcribe.py(字幕选择 + Whisper 编排),scripts/whisper.py(Groq / OpenAI 客户端),scripts/setup.py(预检 + 安装程序)

首次使用前审查脚本以验证行为。