Files
claude-skills/skills/using-yt-tools/SKILL.md
vitya b827d06d9b fix(using-yt-tools): resolve binaries via venv/winget fallback v0.3.0
Skill no longer aborts on bare `Get-Command yt-frames` miss — binaries
installed in project-local venv or winget cache (Gyan.FFmpeg_*) are
valid install sites, just not on PATH for fresh shells.

New Step 0 in both flows: probe PATH first, then known install
locations (venv Scripts/bin, winget Gyan.FFmpeg_*/ffmpeg-*-full_build/bin);
abort only if both empty. PATH-prepend pattern documented (full-path
to yt-frames.exe is insufficient — child yt-dlp/ffmpeg need prepend
too).

Failure modes table updated: removed 'restart CC session' advice —
agent can't restart itself, and resolve-fallback makes it unnecessary.

MINOR bump per project-discipline (behaviour change in resolve logic).
2026-05-20 14:55:57 +03:00

148 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: using-yt-tools
version: 0.3.0
description: Two flows for YouTube content. **Iterative-watch** (summary/exploration): transcript with [mm:ss] anchors → pick moments → extract frames. **Targeted-frames** (specific timestamps): extract frames directly, no transcript. Triggers: "что в ролике", "о чём видео", "video summary", "youtube transcript", "покажи кадр на N", или любой youtube.com URL. CLI в `~/projects/.common/lib/yt-tools/`. YouTube-only; для Vimeo/Twitch/local — другие тулзы.
---
# using-yt-tools
Iterative-watching YouTube для агента: clean-markdown транскрипт с `[mm:ss]`-якорями → агент решает, какие моменты интересны → targeted frame extraction по таймкодам → агент видит кадры через `Read`. Альтернативный flow — если юзер уже назвал таймкоды, идём прямо за кадрами без транскрипта.
## When to use
Два различных flow, выбор по user intent:
**Flow A — iterative-watch** (exploration / summary):
- Юзер спрашивает что в ролике, хочет summary, хочет узнать о чём видео.
- Шаги: fetch transcript → read → pick interesting moments → extract those frames → Read frames.
- Trigger phrases: «что в этом ролике», «о чём ролик», «расшифровка YouTube», «video summary», «youtube transcript», «watch this video».
**Flow B — targeted-frames** (specific moments):
- Юзер уже назвал конкретные таймкоды; транскрипт — лишняя работа.
- Шаги: extract frames at given timestamps → Read frames.
- Trigger phrases: «покажи кадр на N», «посмотри момент N», «что показано на N», «show frame at N».
Оба flow предполагают, что `yt-tools` CLI установлен из `~/projects/.common/lib/yt-tools/` (project-local venv). Бинари могут не быть на PATH текущей сессии — это норма, особенно после свежего `winget install`. **Никогда не abort'ить по голому `Get-Command yt-frames` / `which yt-frames`** — сначала прогнать резолв (см. Prerequisites → Locating binaries).
## Prerequisites
### Locating binaries
Скил ничего не предполагает про активный PATH. **Step 0 каждого flow** — резолв путей для `yt-frames`/`yt-transcript` и `ffmpeg` (+ `yt-dlp`, поставляется в том же venv). Если резолвится через fallback — используй PATH-prepend в каждом вызове (см. Invoke pattern ниже). Abort'ить **только** если бинаря нет ни на PATH, ни в известных install-локациях.
**yt-tools CLI** (один venv даёт все четыре: `yt-frames`, `yt-transcript`, `yt-watch`, `yt-tools`; туда же ставится `yt-dlp`):
1. PATH: `Get-Command yt-frames` (pwsh) / `command -v yt-frames` (bash)
2. Project-local venv:
- Windows: `~/projects/.common/lib/yt-tools/.venv/Scripts/yt-frames.exe`
- Linux/macOS: `~/projects/.common/lib/yt-tools/.venv/bin/yt-frames`
3. Если ни PATH, ни venv — install hint: `python -m venv ~/projects/.common/lib/yt-tools/.venv && ~/projects/.common/lib/yt-tools/.venv/{Scripts,bin}/pip install -e ~/projects/.common/lib/yt-tools/`; стоп.
**ffmpeg**:
1. PATH: `Get-Command ffmpeg` / `command -v ffmpeg`
2. Windows winget cache (версия плавающая, глобь):
`~/AppData/Local/Microsoft/WinGet/Packages/Gyan.FFmpeg_Microsoft.Winget.Source_*/ffmpeg-*-full_build/bin/ffmpeg.exe`
3. macOS Homebrew: `/opt/homebrew/bin/ffmpeg` (Apple Silicon) или `/usr/local/bin/ffmpeg` (Intel)
4. Linux: `/usr/bin/ffmpeg` (apt) или `/usr/local/bin/ffmpeg`
5. Если ни одна локация — install hint per-ОС (`winget install Gyan.FFmpeg` / `brew install ffmpeg` / `apt install ffmpeg`); стоп. **Не** проси юзера restart'ить CC — продолжай резолв-логику в той же сессии после установки, либо подскажи, что новой сессии PATH подхватится сам.
### Invoke pattern
`yt-frames` сам спавнит `yt-dlp` и `ffmpeg` через `subprocess.run([..., "ffmpeg", ...])` — full-path к самому `yt-frames.exe` **не хватит**, нужен PATH-prepend, чтобы child процессы тоже их видели.
```bash
# bash / git-bash — после резолва через fallback
FFDIR=$(dirname "$(ls ~/AppData/Local/Microsoft/WinGet/Packages/Gyan.FFmpeg_*/ffmpeg-*-full_build/bin/ffmpeg.exe 2>/dev/null | head -1)")
YTBIN=~/projects/.common/lib/yt-tools/.venv/Scripts # /bin на Linux/macOS
PATH="$FFDIR:$YTBIN:$PATH" yt-frames <url> --timestamps 1:23,4:56
```
```powershell
# pwsh — glob по плавающей версии ffmpeg
$ff = (Get-ChildItem "$HOME\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg_*\ffmpeg-*-full_build\bin\ffmpeg.exe" -ErrorAction SilentlyContinue | Select-Object -First 1).DirectoryName
$ytbin = "$HOME\projects\.common\lib\yt-tools\.venv\Scripts"
$env:PATH = "$ff;$ytbin;" + $env:PATH
yt-frames <url> --timestamps 1:23,4:56
```
Если оба нашлись напрямую на PATH (`Get-Command` вернул что-то) — pre-pend не нужен, зови как обычно.
## Inputs
| Flow | Required | Optional |
|---|---|---|
| A — iterative-watch | YouTube URL или bare 11-char video id | `--lang ru,en` для non-English subs; `--out PATH` |
| B — targeted-frames | YouTube URL + timestamps (`mm:ss`, `h:mm:ss`, или bare seconds: `123` → 2:03) | `--no-cache-source` (stream вместо кеша source.mp4); `--out DIR` |
Оба flow пишут в `<cwd>/yt-cache/<video-id>/` по умолчанию.
## Steps
### Flow A — iterative-watch
```
0. Резолв yt-frames + ffmpeg per Prerequisites → Locating binaries; собрать PATH-prepend если резолв через fallback
1. yt-transcript <url> → ./yt-cache/<vid>/transcript.md
2. Read transcript.md, find [mm:ss] anchors that match the question
3. yt-frames <url> --timestamps 1:23,4:56,… → ./yt-cache/<vid>/frames/frame_*.jpg
4. Read each frame_*.jpg via the vision tool
5. Answer the user, citing both transcript paragraph and frame contents
```
**Stdout contract per CLI** (бери последнюю строку, формат зависит от CLI):
- `yt-transcript`, `yt-watch` — одна строка на stdout: bare absolute path артефакта (`<abs>/transcript.md` или `<abs>/watch.md`).
- `yt-frames`**N строк** формата `Wrote: <abs path>`, одна на каждый извлечённый кадр. Strip префикс `"Wrote: "` чтобы получить путь. Дизайн осознан: per-line output чтобы caller'ы пайпили / скрейпили без парсинга summary в конце.
Warnings и errors уходят в stderr (`warning: …`, `error: …`); stdout остаётся машинно-парсимым.
### Flow B — targeted-frames
```
0. Резолв yt-frames + ffmpeg per Prerequisites → Locating binaries; собрать PATH-prepend если резолв через fallback
1. Parse user's timestamps (mm:ss / h:mm:ss / bare seconds — все работают)
2. yt-frames <url> --timestamps 1:23,4:56,… → ./yt-cache/<vid>/frames/frame_*.jpg
3. Read each frame_*.jpg
4. Answer the user, referencing each frame by its [mm:ss] label
```
Без transcript fetch. Если потом юзер спросит «что говорилось в тот момент?», переключайся на Flow A на том же URL — `source.mp4` cache переиспользуется, повторного download нет.
## Failure modes
Все failures abort cleanly; никогда не оставляй наполовину готовое состояние.
| Symptom | Cause | Action |
|---|---|---|
| `yt-transcript` / `yt-frames` not on PATH | yt-tools pkg не pip-installed **либо** venv создан, но не на PATH | Прогнать резолв per Prerequisites → Locating binaries. Abort только если и venv-локация пуста — тогда install-hint, стоп |
| `yt-dlp not found on PATH` (от child процесса) | `yt-dlp` есть в venv, но PATH-prepend не собран | Пересобери PATH-prepend (Prerequisites → Invoke pattern); `yt-dlp` ставится тем же `pip install -e` что и `yt-frames` |
| `ffmpeg not found on PATH` (от child процесса) | ffmpeg установлен, но в winget-кэше/Homebrew/etc., не на PATH сессии | Прогнать ffmpeg-резолв per Prerequisites, prepend в PATH. Abort только если ни одна локация не нашла бинаря — install per ОС. **Не** требовать restart CC session — резолв решает |
| `yt-dlp source download failed (exit N) \| stderr: …` | Network / private / age-gated / region-locked / malformed URL | Выведи captured stderr verbatim; не retry |
| `yt-dlp --dump-json failed` | То же, но на metadata step | То же |
| `Subtitles disabled` от `youtube-transcript-api` | Канал отключил CC | Для Flow A это fatal — скажи юзеру, предложи Flow B с явными таймкодами если уместно |
| Юзер дал не-YouTube URL (Vimeo / Twitch / local mp4) | Out of scope | Стоп; скажи что скил YouTube-only |
## Side effects
- Writes под `<cwd>/yt-cache/<video-id>/`:
- `transcript.md` (Flow A)
- `source.mp4` (≤720p, оба flow если без `--no-cache-source`)
- `frames/frame_<mmss>.jpg` per extracted frame
- Network: yt-dlp вытягивает metadata + опционально source.mp4; `youtube-transcript-api` вытягивает subs
- Никакого внешнего state не мутируется — чисто local-fs side effects
- `source.mp4` может быть ~50-200 MB на 720p / 10-минутный ролик; кеш переиспользуется между запусками. **Warning:** cumulative — 20 роликов = 1-4 GB на диске.
Cache hygiene: `yt-tools cache list` показывает usage, `yt-tools cache prune --older-than 7d` чистит.
## What NOT to do
- **Не запускай Flow A когда юзер уже дал таймкоды.** «Посмотри 1:23 и 4:56» → сразу Flow B. Fetching transcript first — чистая трата.
- **Не bulk-extract «на всякий случай».** Flow A берёт кадры из транскрипта, Flow B — из явного user input. Никогда `--mode interval --interval 5s` «to be safe».
- **Не используй `yt-watch` как default Flow A renderer.** `yt-watch` комбинирует transcript + scene-frames в один doc — тяжелее (требует ffmpeg scene-detect pass на source.mp4). Бери только когда юзер хочет один self-contained document.
- **Не shell-quote URLs в одну command строку.** Используй CLI list-form (он уже list-form в `subprocess.run`); YouTube URLs содержат `?` и `&`, которые ломают naive quoting.
- **Не retry на yt-dlp failures.** Failure здесь значит видео реально недоступно (private / region / age) или у юзера broken auth. Retries — трата токенов.
- **Не пересказывай / не переводи transcript в свой ответ молча.** Артефакт — для твоего reasoning; цитируй с `[mm:ss]`-якорем когда приводишь пассаж.
- **Не вызывай скил на non-YouTube URL.** Vimeo / Twitch / TikTok / local mp4 — out of scope. Бери другие тулзы (или yt-dlp напрямую).
- **Не пиши результаты в произвольные пути.** По умолчанию `<cwd>/yt-cache/<vid>/`; явный `--out` только если юзер просил.