# How to Debug Issues with watch.skill Video Processing: A Complete Guide

> Debug watch.skill video processing by tracing the 10-step pipeline from configuration to frame extraction. Check stderr and temporary files for artifacts to quickly resolve issues.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: how-to-guide
- Published: 2026-08-06

---

**To debug watch.skill video processing issues, trace the deterministic 10-step pipeline from configuration through frame extraction, checking stderr output and the temporary working directory for intermediate artifacts.**

The `watch.skill` in the bradautomates/claude-video repository is a pure-Python orchestration layer that coordinates `yt-dlp`, `ffmpeg`, and optional Whisper API calls to download videos, extract representative frames, and generate transcripts. When processing fails or produces unexpected results, systematic debugging requires understanding how data flows through `skills/watch/scripts/` and where each stage reports diagnostic information. This guide covers the exact file locations, function behaviors, and diagnostic flags you need to isolate and resolve video processing errors.

## Understanding the watch.skill Processing Pipeline

The pipeline executes deterministically across ten distinct stages, each implemented in specific modules within `skills/watch/scripts/` according to the bradautomates/claude-video source code:

1. **Configuration Loading** – [`config.py`](https://github.com/bradautomates/claude-video/blob/main/config.py) calls `get_config()` ([L48-L62](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/config.py#L48-L62)) to load user defaults from `~/.config/watch/.env` or environment variables, determining detail level and frame caps.

2. **Metadata & Captions** – For URL sources, [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py)'s `fetch_captions()` queries `yt-dlp` for metadata and embedded subtitles, called from [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) ([L97-L105](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py#L97-L105)).

3. **Video Download** – [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py)'s `download()` ([L115-L126](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py#L115-L126)) fetches the full video or audio-only streams.

4. **Metadata Extraction** – [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py)'s `get_metadata()` ([L86-L119](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L86-L119)) runs `ffprobe` to capture duration, resolution, codec, and audio presence.

5. **Time-range Handling** – [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) uses `parse_time()` and `parse_timestamps()` ([L55-L74](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L55-L74) & [L95-L109](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L95-L109)) to convert `--start`, `--end`, and `--timestamps` arguments into seconds.

6. **FPS Budgeting** – `auto_fps()` and `auto_fps_focus()` in [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) ([L22-L39](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L22-L39)) calculate frame rates respecting the `--max-frames` cap and focus windows.

7. **Cue-frame Extraction** – `extract_at_timestamps()` ([L24-L34](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L24-L34)) in [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) pulls single frames at specified timestamps (never dropped).

8. **Detail-engine Extraction** – Based on `--detail`, it runs one of three engines:
   - **efficient**: `extract_keyframes()` ([L75-L85](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L75-L85)) for key-frame only extraction
   - **balanced / token-burner**: `extract_scene_or_uniform()` ([L110-L124](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L110-L124)) for scene-change detection → dedup → even-sample
   - **transcript**: Skips frame extraction entirely

9. **Deduplication** – `dedupe_perceptual()` in [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) ([L64-L71](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py#L64-L71)) collapses near-identical frames using perceptual thumbnails (`DEDUP_THUMB=16`).

10. **Transcription & Reporting** – [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py) handles API transcription when subtitles are missing, and [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) generates the final Markdown summary ([L70-L89](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py#L70-L89)), sending all diagnostics to **stderr**.

## Common Failure Points and Solutions

When the `watch` command fails, match your symptoms to the specific pipeline stage:

- **"ffprobe is not installed"** – Occurs during metadata extraction at `frames.py:L86`. Verify `ffprobe` is on your `$PATH` before running the command.

- **"subtitle parse failed"** – Check the VTT file under `work/download/`; the path is printed in `watch.py:L99-L107`.

- **Zero frames extracted** – The engine may have dropped everything due to a very low cap defined in `config.py:L65-L74`. Inspect the `frames/` directory in the working directory to confirm.

- **Whisper fallback not triggered** – Ensure `~/.config/watch/.env` contains `WATCH_WHISPER_OPENAI` or `WATCH_WHISPER_GROQ`. The script hints to run [`setup.py`](https://github.com/bradautomates/claude-video/blob/main/setup.py) when keys are missing (`watch.py:L60-L64`).

- **Unexpected timestamps** – `extract_at_timestamps()` drops timestamps outside the focus window (`frames.py:L48-L52`). Check the summary for drop counts (`watch.py:L88-L94`).

- **Slow or crashing extraction** – The script uses `-loglevel error`. For deeper logs, temporarily change the level in `frames.py:L80` and `frames.py:L118`.

## Enabling Runtime Diagnostics

Increase visibility into the pipeline using these specific techniques.

### Print Internal Variables

Insert debug statements that write to stderr, matching the script's `[watch]` prefix format:

```python
import sys
print("[debug] fps_budget=", fps_budget, file=sys.stderr)

```

### Force FFmpeg Verbosity

Edit the `cmd` lists in [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) within functions like `extract()`, `extract_scene_candidates`, `extract_keyframes`, or `extract_at_timestamps`. Replace `"-loglevel", "error"` with `"info"` or `"debug"` to see frame-by-frame processing.

### Enable Debug Mode

Run with the debug environment variable to preserve intermediate files and verbose output:

```bash
WATCH_DEBUG=1 watch https://youtu.be/abcd1234 \
    --detail balanced \
    --out-dir ./debug-run \
    --max-frames 30 \
    2> debug.log

```

## Isolating Components for Targeted Debugging

Test individual pipeline stages without running the full orchestration.

### Run the Scene Engine Directly

Invoke the frame extraction module independently to see raw FFmpeg stderr:

```bash
python -m skills.watch.scripts.frames extract_scene_candidates \
    ./sample.mp4 ./tmp/scene_frames \
    --resolution 512

```

This dumps `showinfo` output enumerating detected scene changes with timestamps.

### Inspect Working Directories

After execution, [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) prints the working directory path ([L84-L89](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py#L84-L89)). List contents to verify intermediate artifacts:

```bash
ls -R /path/to/working/dir/

```

Check for downloaded videos in `work/download/` and extracted frames in `frames/`.

### Validate Configuration

Test your environment file parsing:

```python
from skills.watch.scripts.config import get_config
print(get_config())

```

If the `detail` value is unexpected, check `WATCH_DETAIL` environment variables or `~/.config/watch/.env` formatting (requires `KEY=VALUE` without extra spaces).

### Force Specific Whisper Backends

Override automatic backend selection to verify API connectivity:

```bash
watch my_video.mp4 \
    --whisper openai \
    --no-dedup \
    --out-dir ./whisper-test

```

The script prints the selected backend ([L40-L45](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py#L40-L45)). If the API key is missing, the script guides you to run [`setup.py`](https://github.com/bradautomates/claude-video/blob/main/setup.py).

## Summary

- The **watch.skill** pipeline in bradautomates/claude-video follows a deterministic 10-stage flow from configuration to final report generation, implemented across [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py), [`config.py`](https://github.com/bradautomates/claude-video/blob/main/config.py), [`download.py`](https://github.com/bradautomates/claude-video/blob/main/download.py), [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py), and [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py).
- All diagnostic messages are sent to **stderr**, and the working directory path is printed at the end of execution for artifact inspection.
- Common issues include missing `ffprobe` installations, invalid API keys in `~/.config/watch/.env`, and frame caps set too low in [`config.py`](https://github.com/bradautomates/claude-video/blob/main/config.py).
- You can isolate problems by running individual modules like [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) directly and temporarily increasing FFmpeg's log level from `error` to `debug`.

## Frequently Asked Questions

### Why does watch.skill report "ffprobe is not installed" even though I have FFmpeg?

The script requires `ffprobe` specifically to be available on your system `$PATH` for metadata extraction at `frames.py:L86`. Verify installation by running `which ffprobe` or `ffprobe -version`. If installed in a non-standard location, symlink it to `/usr/local/bin` or add its directory to your PATH before executing the watch command.

### How do I prevent the frame extraction engine from dropping frames?

The engine drops frames when your `--max-frames` cap (defined in `config.py:L65-L74` or via CLI) is lower than the calculated sample count. Increase the cap with `--max-frames 50` or switch to `detail=efficient` which uses `extract_keyframes()` and produces fewer frames. Check the `frames/` directory in the working directory to verify what was actually extracted.

### Where should I store my Whisper API key for transcription fallback?

Create or edit `~/.config/watch/.env` and add either `WATCH_WHISPER_OPENAI=sk-xxxxxxxx` or `WATCH_WHISPER_GROQ=your-groq-key`. The script loads these via [`whisper.py`](https://github.com/bradautomates/claude-video/blob/main/whisper.py)'s `load_api_key()` function. If the key is missing, [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) directs you to run [`setup.py`](https://github.com/bradautomates/claude-video/blob/main/setup.py) to create the configuration file template.

### Can I extract frames only at specific timestamps without processing the entire video?

Yes. Use the `--timestamps` flag with `detail=transcript` to skip standard frame extraction and only pull cue frames:

```bash
watch ./video.mp4 --detail transcript --timestamps "00:10,00:45,01:20"

```

The `extract_at_timestamps()` function in [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py) handles these extractions and reports if any timestamps fall outside your specified `--start` or `--end` windows.