Skip to content

Releases: muckybuzzwoo/claude-code-youtube-extract

v1.10.1

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 08 Jul 22:49

Fixed

  • Screenshot extraction falsely reported as failed on long output paths.
    extract_screenshots() checked os.path.exists()/os.path.getsize() on
    the raw constructed path to confirm each frame was written. On Windows,
    paths beyond the legacy 260-character MAX_PATH limit silently fail that
    check — ffmpeg had already written the frame correctly, but the stat call
    returned as if the file didn't exist, so every frame was logged as
    Frame at HH:MM:SS failed: unknown error and the run reported 0 successfully extracted despite valid PNGs on disk. New _long_path()
    helper applies the \?\ extended-length prefix on Windows before the
    check; no-op on macOS/Linux, which do not have this limit. Only manifests
    with long --output-base trees (e.g. deeply nested temp/session dirs) —
    the default --output-base "." case was unaffected.

v1.10.0

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 08 Jul 21:36

Added

  • --visual flag: the summarizer worker looks at 4 evenly-spaced, ephemeral
    keyframes so the summary can address on-screen content (diagrams, code,
    slides). Frames are extracted to a temp dir, read by the worker, then
    deleted — nothing is saved to the output folder. A "Visually checked at:"
    line in the summary records which timestamps were inspected. Requires ffmpeg;
    ignored in --full-transcript / --transcript-only modes. Adoption #3 from
    the claude-video roadmap.

v1.9.0 - Perceptual frame-dedup for scene screenshots

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 08 Jul 19:16

Added

Perceptual frame-dedup for scene-detection screenshots. After extraction, scenes mode compares 16×16 grayscale thumbnails of consecutive captures (mean-absolute-difference against the last kept frame) and drops near-duplicates — a slide held across several detected scene changes now collapses to a single frame instead of many near-identical ones. Comparing against the last kept frame (not the previous one) keeps a gradual pan while removing static repeats.

  • Runs only in scene-detection mode; --screenshots chapters and explicit-timestamp captures are intentional and left untouched.
  • No new flag; the threshold is fixed (mean-abs-diff ≤ 2.0 on a 0–255 scale).
  • Pure ffmpeg + stdlib (no PIL). Approach ported from frames.py in bradautomates/claude-video.

Changed

  • ### Screenshot Status now reports dedup explicitly: when frames are removed the count line reads N screenshots requested, M successfully extracted, D near-duplicate(s) removed (K kept). Previously deduped frames would have been indistinguishable from extraction failures in the count. With zero dedup the wording is unchanged.
  • Version bumped 1.8.3 → 1.9.0.

Tests

Added unit coverage for the pure helpers frame_delta and dedupe_perceptual_indices (drift-aware keep logic, threshold boundary, length-mismatch fail-open) and for the render_screenshot_status dedup clause. Suite: 81 passing. The ffmpeg-backed glue (compute_thumbnail, dedupe_screenshots) stays outside the unit suite per the project's I/O policy and was verified with an offline ffmpeg smoke check.

Full changelog: CHANGELOG.md

v1.8.3 - release workflow docs

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 08 Jul 16:10

Documentation-only release. No functional, output, or CLI change — extraction behavior, sentinels, and folder layout are identical to 1.8.2.

Docs

Added

  • SKILL.md "Extending this skill" now documents the release workflow: which files carry the version string, the CHANGELOG + SemVer convention, the python -m pytest tests/ gate, and the tag / GitHub Release / claude plugin update steps. The contributor section previously covered adding flags, install targets, sentinels, and Markdown sections but never how to cut a release.

Changed

  • Version bumped 1.8.2 → 1.8.3 across .claude-plugin/plugin.json, .claude-plugin/marketplace.json, CLAUDE.md, and README.md.

v1.8.2 — robust OUTPUT_FOLDER parse + scene threshold 0.04

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 13 Jun 17:29

Changed

  • Scene-detection default threshold raised 0.0250.04. On talking-head and slide-heavy videos the old default tripped on minor frame changes and often hit the 50-capture cap (then thinned); 0.04 yields fewer, more meaningful captures by default. Tune per video with --screenshots scenes=<0..1> (lower = more).

Fixed

  • Robust OUTPUT_FOLDER: parsing. When a worker subagent returns, the harness can append an agentId: <hex> trailer directly onto the trailing OUTPUT_FOLDER: line with no separating newline (e.g. …_my-slugagentId: a2ba…). The orchestrator now extracts the folder by cutting at the first whitespace or the literal agentId substring instead of taking the raw last line, so the trailer can no longer be folded into the saved folder/file name. Documented in SKILL.md Step 3, mirrored in the CLAUDE.md sentinel registry, and covered by a new static contract test.

Full changelog: https://github.com/muckybuzzwoo/claude-code-youtube-extract/blob/main/CHANGELOG.md

v1.8.1 — fix recursive subagent loop

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 13 Jun 16:48

Critical bugfix: runaway recursive subagent loop. /yt-extract <url> (default and --full-transcript modes — any path that dispatches a subagent) could spawn an ever-deepening chain of general-purpose subagents (~one new level every ~30 s), burning tokens and never producing output. Each dispatched worker's first action was Skill(yt-extract) — it re-invoked this skill instead of running the Python command it was handed, and Step 1 then dispatched the next worker, ad infinitum.

Root cause

The recursion needed two things, both introduced in v1.6.0:

  • v1.6.0 removed disable-model-invocation: true to make the skill callable programmatically by other skills. That flag was also the hard structural guard that kept a subagent from reaching /yt-extract through the Skill tool. Its removal was compensated only by a tightened description — a soft, model-interpreted guard. But the worker's own task ("Extract all data for this YouTube video…") reads as an explicit extract request, so the soft guard matched exactly the case it was meant to exclude.
  • The skill dispatched subagent_type: general-purpose, which inherits all tools — including Skill and Agent. With the skill now model-invocable, the worker could (and did) re-invoke it.

Fixed

  • New restricted worker agent agents/extract-worker.mdtools allowlist is Bash, Read, Glob, Grep only, so the worker has no Skill and no Agent tool and physically cannot re-invoke the skill or spawn subagents.
  • SKILL.md Step 1 now dispatches subagent_type: "yt-extract:extract-worker" instead of general-purpose. The skill stays model-invocable (programmatic invocation by other skills is preserved).
  • Both Step 1 subagent prompts gained a "you are a LEAF worker — run only the Bash command, never invoke a skill or dispatch a subagent" guard (defense-in-depth).
  • Worker no longer asks the user directly. The subagent prompts used to instruct the worker to call AskUserQuestion for FOLDER_EXISTS and SCREENSHOTS_ASK_USER, but subagents have no AskUserQuestion tool (it depends on the main-chat UI). The worker now returns these states; the orchestrator (main context) asks the user and re-dispatches with --force / explicit timestamps. The --transcript-only path is unaffected.
  • New test tests/test_skill_contract.py — static contract test locking the dispatch target (restricted worker, not general-purpose), the worker tool allowlist (excludes Skill/Agent/Task), and that no worker prompt instructs AskUserQuestion. Closes the previously untested orchestration-layer gap.

Upgrade note

Adds a fourth plugin component type (an internal agent). No user-facing CLI change. To restore the hard block instead — at the cost of programmatic invocability — re-add disable-model-invocation: true to the SKILL.md frontmatter.

v1.8.0 — scene-detection screenshot mode

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 12 Jun 16:59

New screenshot mode: ffmpeg scene detection — capture a frame whenever the
screen changes (slide flips, screen shares, demo cuts). Built for tutorial
videos, where slides change far more often than chapter markers suggest.

⚠ BREAKING

  • Bare --screenshots now means scene detection, not chapter markers. Use
    --screenshots chapters to restore the pre-1.8.0 behavior. The legacy
    internal value auto is kept as an alias for chapters, so direct script
    callers passing --screenshots auto are unaffected.

yt-extract.py (backend)

Added

  • Two-pass scene detection: pass 1 decodes a ≤360p stream once with ffmpeg's
    select='gt(scene,T)' filter (default threshold 0.025) and collects only
    timestamps; pass 2 extracts those frames at ≤1080p through the existing
    extract_screenshots() path. Rendering, filenames, and markdown embedding
    are unchanged (scene runs use the inline-with-heading layout).
  • Flood protection: 4s minimum gap between detections, then even thinning to
    max 50 screenshots with a WARNING in ### Screenshot Status (suggests a
    higher threshold). Zero detections above threshold → opening frame only +
    WARNING suggesting a lower threshold.
  • --screenshots scenes=0.05 — tunable detection threshold in (0, 1];
    invalid values fall back to the default with a WARNING.
  • New stage marker Detecting scene changes (stderr, [k/N] stays adaptive).
    Detection decodes the whole video at low resolution and gets a
    duration-scaled timeout (min 5, max 30 minutes) plus HTTP reconnect flags.
  • New pure helpers (unit-tested): parse_screenshots_mode,
    parse_scene_timestamps, apply_min_gap, thin_evenly; new subprocess
    helpers get_lowres_stream_url, detect_scene_timestamps.

Fixed

  • Screenshot seeks no longer truncate timestamps to whole seconds
    (-ss int(ts)-ss ts:.2f). For scene mode this is essential — a
    truncated seek could land before the detected change and capture the
    previous screen. Side effect: explicit fractional timestamps like 1:30.5
    now seek accurately instead of silently flooring to 1:30.

yt-extract skill

Changed

  • Step 0.4 flag grammar: --screenshots [scenes[=t]|chapters|timestamps];
    both subagent prompts pass the user's value through verbatim and announce
    the new detection stage.
  • SCREENSHOTS_ASK_USER now only fires for --screenshots chapters on a
    chapterless video — scene mode (the default) needs no chapters, so the
    no-chapters Rückfrage disappears from the happy path.
  • No new sentinel: thinning/zero-detection conditions travel as - WARNING:
    lines inside ### Screenshot Status.

Tests

Added

  • tests/test_scene_detection.py — pure-Python coverage for the four new
    helpers, including an ffmpeg metadata=print fixture verified against real
    output (pts_time: values may lack a decimal part).

Docs

Changed

  • Version bumped 1.7.0 → 1.8.0 across CLAUDE.md, README.md,
    .claude-plugin/plugin.json, and .claude-plugin/marketplace.json;
    README gets a breaking-change callout and a scene-mode row in the flags
    and modes tables.

v1.7.0 — transcript-only mode + guided help

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 12 Jun 14:35

New lean --transcript-only mode: fetch and output just the raw transcript — no metadata, description, chapters, comments, screenshots, or summary. It runs the Python script directly (no subagent), since a subagent would only relay the raw transcript back into context and pay its token cost twice. It also skips the heavy yt-dlp --dump-json metadata call entirely, naming the output folder by the video ID parsed from the URL.

Plus: a no-URL invocation now always answers with a guided text message instead of failing.

Highlights

  • --transcript-only — raw transcript only, fast; no subagent; folder/file named by video ID. Combinable with --no-save. Multi-URL supported (no synthesis — it's raw data). The post-run "What next?" offers an in-context summary, so "transcript then summarize" needs no separate mode.
  • No-URL guard/yt-extract with no URL (and without --check) prints the full guided help when no flags were given, or a short flag-preserving "paste a URL" message when flags were passed. Plain text only — never an AskUserQuestion — so it stays safe in programmatic skill-to-skill calls.
  • extract_video_id() — parses the 11-char ID from watch?v=, youtu.be/, /shorts/, /embed/, and /live/ URL forms.

The happy path is unchanged: /yt-extract <url> still runs the default structured summary immediately.

See CHANGELOG.md for full details.

v1.6.0 — skill invocable from other skills

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 03 Jun 13:45

The skill can now be invoked programmatically by other skills via the Skill tool, not only manually via /yt-extract. Removing disable-model-invocation: true is what unblocks this — the Skill tool cannot reach a skill that declares that flag. Because the skill is now model-invocable, its description was tightened to fire only on an explicit extract/analyze request.

Changed

  • SKILL.md frontmatter — removed disable-model-invocation: true. Callable from another skill as yt-extract:yt-extract <url> [flags]. Stays user-invocable via /yt-extract.
  • SKILL.md description — tightened to fire only on an explicit extract/analyze request; no auto-trigger on incidental YouTube URL mentions.
  • SKILL.md line-count-budget note — no longer references the removed flag.

Behavior change

  • The hard block against model-invocation is gone. Auto-trigger restraint now relies on the tightened description + user-level gating, not a frontmatter flag.

Full changelog: https://github.com/muckybuzzwoo/claude-code-youtube-extract/blob/main/CHANGELOG.md

v1.5.0 — Windows PATH Recovery

Choose a tag to compare

@muckybuzzwoo muckybuzzwoo released this 05 May 18:43

Windows-only fix for the "freshly installed but not on PATH" UX dead-end. winget installs into %LOCALAPPDATA%\Microsoft\WinGet\Packages with a shim under \Microsoft\WinGet\Links\ — when that Links dir is empty or absent from the Bash tool's PATH, yt-dlp --version returned exit 127 even though the binary was on disk, and the skill's only response was "restart your terminal".
v1.5.0 adds an automatic two-stage PATH-recovery step that locates the binary via merged-registry PATH and copies it into a directory already on the current shell's PATH (Stage 1 — Python's Scripts dir) or, as a fallback, into WinGet\Links while permanently adding that directory to the user PATH (Stage 2). One AskUserQuestion confirmation covers the full recovery chain.

yt-extract skill

Added

  • references/install-helper.md — new Step W (Windows PATH Recovery) with two recovery stages:
    • Stage 1 copies the binary to Python's Scripts directory (already on PATH per the Step 0.3a Python check) — works in the current shell session, no Claude Code restart required.
    • Stage 2 (auto-fallback when Stage 1 mechanically fails, or sole option when Stage 1 is unavailable, e.g. MS-Store Python) copies the binary to %LOCALAPPDATA%\Microsoft\WinGet\Links AND
      adds that directory to the user PATH via Registry — fixes the winget Links shim permanently for all future winget installs. Requires a Claude Code restart.
    • Step W locates the binary via PowerShell merged-registry Get-Command lookup with Get-ChildItem fallback under %LOCALAPPDATA%\Microsoft\WinGet\Packages.
  • SKILL.md Step 0.3b (yt-dlp) and Step 0.5 (ffmpeg) — Windows pre-check fallback that invokes Step W BEFORE loading the install-helper. On Stage 1 success, the install-helper is skipped entirely
    (recovers the case where the binary is already on disk via winget but Bash cannot see it).
  • install-helper.md Step D — Windows post-install fallback that invokes Step W BEFORE Step E. On Stage 1 success, returns success without "restart your terminal".
  • ffmpeg recovery copies BOTH ffmpeg.exe and ffprobe.exe — yt-dlp invokes ffprobe internally for stream selection, so copying only ffmpeg.exe would yield silent screenshot failures.

Fixed

  • Windows + winget yt-dlp / ffmpeg: skill no longer asks the user to install something that is already present on disk (Fall A — empty or absent WinGet\Links shim).
  • Combined with the existing winget exit-code-43 normalization (install-helper.md Step C), winget install yt-dlp now resolves transparently when the binary is in the WinGet packages dir.

Docs

Changed

  • README.md Quick Start and Troubleshooting — rewrote the "expected first-time experience on Windows" guidance. The Claude Code restart is now the rare-case fallback, not the default ritual.
  • CLAUDE.md — version reference bumped 1.4.0 → 1.5.0.

Known limitations

  • After a future winget upgrade of a binary recovered via Stage 1 / Stage 2, the local copy goes stale until a fresh recovery runs. Mitigated by yt-dlp's built-in -U self-update; ffmpeg upgrades
    are rare. /yt-extract --check re-triggers recovery whenever needed.
  • MS-Store Python users — both sysconfig.get_paths()['scripts'] and os.path.dirname(sys.executable) resolve under \WindowsApps\ with stub-redirect ACLs that reject Copy-Item. Step W detects
    this, suppresses Stage 1, and offers Stage 2 only (which writes to WinGet\Links and is unaffected by the ACL issue).