- Python 100%
| docs | ||
| src/yt_digest | ||
| .gitignore | ||
| .python-version | ||
| AGENTS.md | ||
| pyproject.toml | ||
| README.md | ||
| SKILL.md | ||
| uv.lock | ||
yt-digest
Status: 🟡 ACTIVE (toolchain) — un-parked by owner 2026-10-20: uv project
with all dependencies installed and end-to-end verified; the pipeline runs
via the SKILL.md scaffold. The CLI is not yet implemented — the design
history below documents every decision.
What it is
A small, harness-agnostic tool that turns a YouTube video into a formatted abstract, in three stages:
- Acquire a transcript — captions if they exist, local speech-to-text if not
- Abstract — remote LLM by default (1M ctx, seconds, cents per abstract); local LLM (RTX 3060 via ollama) as the offline/privacy fallback
- Format & deliver — clean abstract plus optional ways to work with it (report, note, slides)
HeadlessX is explicitly not part of this (see Why not HeadlessX).
Pipeline shape
URL
└─ probe captions (yt-dlp --list-subs)
├─ captions exist (most of YouTube)
│ └─ yt-dlp --write-auto-subs / --write-subs --skip-download
│ → parse caption file → clean text (+ timestamps)
└─ no captions
└─ yt-dlp -f bestaudio -x → faster-whisper (local GPU)
→ text (+ timestamps)
→ REMOTE abstract (default): zai-glm-5-3 via Mistral API, 1M ctx — seconds, cents
└─ fallback: offline | privacy | API outage | budget cap
→ LOCAL ollama — Gemma 4 12B QAT @64K (~5 h ceiling; E2B tier for
short structured runs)
→ format abstract (markdown is canonical)
→ optional deliverables: report (.odt) · note (SilverBullet) · slides (.odp)
Runtime decision rule: always probe captions first — it's a 30-second win; Whisper is the fallback, never the default.
Stage 1 — transcript acquisition (options evaluated 2026-10-19)
| Path | Tool | Key options | Notes |
|---|---|---|---|
| Captions | yt-dlp | --list-subs (probe) · --write-subs (creator-uploaded) · --write-auto-subs (YouTube ASR) · --skip-download · --sub-langs en,-live_chat · --sub-format json3 (word-level timing) or vtt · --convert-subs srt |
Returns caption files, not text — needs a parse step to strip timestamps/cues |
| Captions (simpler) | youtube-transcript-api |
plain text per language, one call | can replace yt-dlp entirely for the captioned path |
| No captions | yt-dlp → faster-whisper | -f bestaudio -x, then STT |
faster-whisper — GPU (RTX 3060) preferred for batch; Mistral voxtral (API ASR) is fallback only: its cost compounds under batch extraction while the 3060 absorbs it at zero marginal cost. Engine ladder incl. WhisperX (batched, word-aligned, diarization): see SKILL.md |
Auto-generated captions are YouTube's own ASR: decent quality, available for most videos, no local compute. Word-level timing (json3) and Whisper segments both survive into stage 3 so citations/quotes stay precise.
Stage 2 — abstract
- Default: remote LLM (owner-set 2026-10-20 — the local-hardware path was
walked before, deliberately not again). Any capable mid-tier, 256K+ ctx is
plenty (owner-verified: huge transcripts abstract fine on 256K medium
models);
zai-glm-5-3is the known-good default in this setup, and the OpenCode Zen free tier (cat-jam-free/longcat-2.5-preview-free/space-bunny-free) is $0 when the harness mediates the abstract step. Cost: noted, not worried about — an abstract is a one-shot job (tokens in, tokens out, end of run, end of session), so even frontier-ish paid tiers are trivially cheap. Full options + analysis indocs/model-planning.md. - Fallback: local (RTX 3060 12 GB, ollama) for offline / privacy-sensitive
content / API outage / hard budget cap. Local plan (all retained in
docs/model-planning.md): Gemma 4 12B QAT @ 64K ctx (~8.6 GB incl. KV; ~5 h video ceiling); E2B-class only as a structured extractive tier for short videos; fill ≤ 75%; never trust ollama's default context — setnum_ctxexplicitly and assert transcript < window. - Parked sub-decision: for very long transcripts where privacy matters more than simplicity, chunk + map-reduce locally instead of going remote.
Stage 3 — format & deliverables
Markdown is the canonical abstract format. Later, optional subcommands ("ways to work with it"):
telegram— LIVE (2026-10-20, v0.1.1): short technical abstract in paste-safe Telegram format — bold hook line +•bullets, no headings/ tables/[label](url)syntax (regular markdown breaks Telegram), target ≤ ~800 chars, paste-safety lint + one corrective retry; printed to stdout and written totelegram.txt. Verified on the Proa video (713 chars, lint-clean first generation).report— .odt document. Owner convention: run both pandoc and UNO/pyuno routes, compare renders, keep the better one (see the md→ODT rule in~/.pi/agent/AGENTS.md);odtskill in~/.agents/skills/covers it.note— SilverBullet markdown with wikilinks (owner's note system).slides— .odp via theodpskill (stretch goal).text/json— plain outputs, always available.
Delivery surface (harness-agnostic)
- CLI + personal skill (recommended) — one tool,
yt-digest <url>(subcommands later), shipped as a skill in~/.agents/skills/yt-digest/per the skill-sharing convention (opencode + pi read it natively; hermes via symlink). Any harness, any model, can drive it. ~100 lines of glue. Scaffold drafted:SKILL.md— v0.1 preliminary, project-internal only; publish to~/.agents/skills/at implementation time, not before. - MCP server exposing
transcribe/abstractas first-class tools for any MCP client — only worth it if it becomes a routine cross-client agent tool; otherwise ceremony. - Agent-decides pattern works under either: captions-first, Whisper fallback, remote-abstract-default, local-LLM-fallback. The wrapper encodes the decisions so agents don't re-derive them.
Environment facts (verified 2026-10-19)
- ffmpeg 9.0.1 ✓ present · uv 0.12.1 ✓ present
- yt-dlp, faster-whisper, youtube-transcript-api — not installed yet;
each is a
uv tool install/uv addone-liner on pickup - Local LLM: ollama + NVIDIA RTX 3060 (12 GB) available
- Remote LLM (default for abstracts): Mistral API (
zai-glm-5-3, 1M ctx — pricing + cheaper flash-tier options indocs/model-planning.md) - Python on PATH is 3.14 — pin a 3.12 venv if faster-whisper deps lag
- Owner prefers user-level installs (
~/-prefixed, avoid sudo)
Why not HeadlessX
Transcripts are the weakest reason to self-host HeadlessX. Its yt-engine only lists subtitle track URLs (manual + YouTube ASR), ships no STT engine, and has no audio-only download preset — even uncaptioned videos would need this exact pipeline bolted on anyway. HeadlessX "someday" triggers remain the ones from the 2026-10-19 analysis: Google AI Overview extraction, anti-detect scraping past bot defenses, queue-managed YouTube pipelines at scale.
Validation (2026-10-20)
Stage-1 smoke-tested end-to-end on both paths (captions and
audio→Whisper) with a throwaway uvx/uv run — reference video, transcripts,
timings, and seven hard-won findings (incl. the mandatory av<13 pin) in
docs/validation-2026-10-20.md. The
stage-2 agent-driven abstract flow was demonstrated in the same run. The
project remains parked; no pipeline code committed.
Pickup checklist (when this becomes active)
✅ DONE 2026-10-20 —uvproject init (venv pinned 3.12); addyt-dlp,faster-whisper,youtube-transcript-api, andav<13as a pyproject constraintuv add 'yt-dlp[default,deno]' faster-whisper 'av<13' youtube-transcript-api, verified against the reference video (transcription parity, no JS-runtime warning). Pinning gotcha found:uv init --python 3.12sets only the lower bound — also runuv python pin 3.12and boundrequires-pythonto<3.13, or uv sync picks the newest system interpreter (3.14) andav<13attempts a source build that fails on missinglibav*-dev. It's a pinning bug, never a missing system dependency — do not "fix" withsudo apt libavfilter-dev.- Re-verify
docs/model-planning.mdagainst the live ollama catalog (Gemma 4 tags, QAT sizes, default context length all move); wirenum_ctxexplicitly — never trust ollama's default Implement stage 1: probe → captions → parse | audio → Whisper✅ DONE 2026-10-20 —src/yt_digest: probe (yt-dlp as a library), captions (track picker manual>auto, json3 + vtt parsers with the event-boundary space fix), transcribe (audio download + faster-whisper)Implement stage 2: abstract — remote by default, local fallback✅ DONE 2026-10-20 — remote via OpenAI-compatible API (defaultzai-glm-5-3@api.mistral.ai; key resolution:--api-key>MISTRAL_API_KEYenv >~/.pi/agent/auth.json, never printed); local via ollama chat (num_ctxset explicitly, fill ≤ 75% guard);--engine autoimplements the documented outage-fallback policy (network/5xx → local; 4xx auth errors stop with a clear message)Wrap as CLI✅ DONE 2026-10-20 —yt-digest {probe,transcript, run,abstract}(entry point installed byuv sync; run viauv run yt-digest …). Verified live on all paths: captions + remote abstract 9.6 s; audio→Whisper 132 words (validation parity); local ollama fallback OK. Skill publishing: ✅ DONE 2026-10-20 — CLI-first skill at~/.agents/skills/yt-digest/+ hermes symlink; Forgejo description updated to "CLI y skill para integrar en el flujo de trabajo de un LLM".- Deliverables last: report/note/slides subcommands — future
Renaming this repo is one git mv-free operation (rename dir + update
references) — the name was chosen for descriptiveness, not attachment.