# How to Use `--timestamps` to Extract Frames at Specific Moments with Claude-Video

> Extract video frames at specific moments using the --timestamps flag in Claude-Video. Learn how to precisely select and save frames from your videos based on transcript timestamps with this powerful FFmpeg tool.

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

---

**The `--timestamps` flag lets you extract exact video frames at user-specified moments by passing a comma-separated list of timestamps, which are parsed, deduplicated, and extracted via FFmpeg before being merged with any detail frames from the watch pipeline.**

The `watch` skill in [bradautomates/claude-video](https://github.com/bradautomates/claude-video) provides deterministic, transcript-aligned frame extraction through its `--timestamps` option. Whether you're highlighting key statements, capturing slide transitions, or building evidence reels, this feature guarantees specific moments survive any downstream downsampling.

## How `--timestamps` Works in the Watch Pipeline

The timestamp extraction flow spans four main stages: parsing, validation, extraction, and merging. Understanding each helps you predict behavior when combining cues with automated detail sampling.

### Parsing and Normalizing Timestamps

The `parse_timestamps` helper in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) (lines 295-304) handles all input formats:

- Splits on commas and trims whitespace
- Converts each token to seconds using `parse_time`
- Drops duplicates and empty entries
- Returns a sorted `List[float]`

Supported formats include bare seconds (`45`), compact (`1:30`), and full HH:MM:SS (`00:02:15`). Invalid tokens raise `ValueError` with context.

### Extracting Cue Frames with `extract_at_timestamps`

The core extraction logic lives in `extract_at_timestamps` (lines 324-390 of [`frames.py`](https://github.com/bradautomates/claude-video/blob/main/frames.py)). Called from [`watch.py`](https://github.com/bradautomates/claude-video/blob/main/watch.py) (lines 76-88), it:

1. **Filters by window** — timestamps outside `--start`/`--end` bounds are dropped (tracked in `dropped_out_of_window` metadata)
2. **Respects frame budgets** — if `--max-frames` is set, the list is evenly sub-sampled to fit
3. **Invokes FFmpeg** — each retained timestamp uses `-ss <t> -frames:v 1` for exact seeking
4. **Scales output** — applies the requested resolution (default 512px)

Cue frames are **pinned**: they extract first and count against any cap before detail frames allocate remaining budget.

### Merging Cue and Detail Frames

After scene-aware, keyframe, or uniform sampling completes, `merge_frames` (lines 312-321) concatenates cue frames with detail frames, preserving chronological order. This ensures your specified moments appear in the final set even when aggressive downsampling occurs.

### CLI Reporting

The markdown output distinguishes cue frames explicitly:

```

- **Cue frames:** 3 at transcript-flagged timestamps

## Frames

| # | Time | Reason      | File           |

|---|------|-------------|----------------|
| 1 | 0:05 | transcript-cue | frame_005.jpg |
| 2 | 1:15 | scene-change | frame_075.jpg |
| 3 | 2:30 | transcript-cue | frame_150.jpg |

```

## Command-Line Examples for `--timestamps`

### Basic Transcript-Aligned Extraction

```bash
watch https://example.com/video.mp4 \
  --detail balanced \
  --timestamps "1,3"

```

Grabs frames at 1 second and 3 seconds, plus balanced detail sampling for remaining budget.

### Frame-Limited Workflow

```bash
watch local_video.mov \
  --detail efficient \
  --max-frames 30 \
  --timestamps "00:00:05,00:02:10"

```

Cue frames count against the 30-frame cap. If the video warrants 28 detail frames, only 2 cue frames are kept; if efficient sampling yields 25 detail frames, all 5 specified moments extract.

### Windowed Extraction Without Detail

```bash
watch https://example.com/video.mp4 \
  --detail transcript \
  --timestamps "1:15,2:45" \
  --start "1:00" \
  --end "3:00"

```

Extracts only at 1:15 and 2:45 within the focus window. `--detail transcript` disables scene/keyframe sampling, yielding pure cue-frame output.

### Mixed Format Input

```bash
watch interview.mp4 \
  --timestamps "0:30, 90, 02:00, 3:45.500"

```

Demonstrates tolerance for spacing, multiple formats, and sub-second precision.

## Using `parse_timestamps` and `extract_at_timestamps` in Python

For custom pipelines, import the internal helpers directly:

```python
from pathlib import Path
from skills.watch.scripts.frames import parse_timestamps, extract_at_timestamps

# Parse flexible input

timestamps = parse_timestamps("00:00:10, 00:00:20, 0:30")

# Result: [10.0, 20.0, 30.0]

# Extract with full control

frames, meta = extract_at_timestamps(
    video_path="presentation.mp4",
    out_dir=Path("cue_frames"),
    timestamps=timestamps,
    resolution=768,           # taller frames for slides

    max_frames=10,            # hard cap

    start_seconds=5.0,        # ignore cues before 5s

    end_seconds=120.0,        # ignore cues after 2min

)

print(f"Extracted {len(frames)} frames")
print(f"Dropped out of window: {meta['dropped_out_of_window']}")

```

The function returns a tuple of `(frame_paths, metadata_dict)` where metadata includes parsing notes and window filtering results.

## Key Implementation Files in Claude-Video

| File | Lines | Purpose |
|------|-------|---------|
| [`skills/watch/scripts/watch.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/watch.py) | 76-88 | CLI argument parsing, orchestrates cue + detail extraction |
| [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) | 295-304 | `parse_timestamps` normalization |
| [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) | 312-321 | `merge_frames` cue/detail union |
| [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) | 324-390 | `extract_at_timestamps` FFmpeg invocation |
| [`tests/test_timestamps.py`](https://github.com/bradautomates/claude-video/blob/main/tests/test_timestamps.py) | — | Unit tests for parsing edge cases |
| [`tests/test_watch.py`](https://github.com/bradautomates/claude-video/blob/main/tests/test_watch.py) | — | Integration tests for full pipeline |

## Summary

- **`--timestamps`** accepts comma-separated times in multiple formats (seconds, MM:SS, HH:MM:SS)
- **Cue frames are pinned** and extract before detail frames, ensuring specific moments survive downsampling
- **Window filtering** drops out-of-bounds timestamps with metadata tracking
- **Frame budgets** apply to the combined cue + detail set, with even sub-sampling when needed
- **FFmpeg exact seeking** via `-ss` before input guarantees accurate frame capture

## Frequently Asked Questions

### What timestamp formats does `--timestamps` accept?

The parser accepts bare seconds (`45`), compact minutes (`1:30`), and full timestamps (`00:02:15.5`). Decimal seconds work. Commas separate multiple values; whitespace is trimmed. Invalid formats raise a clear `ValueError` rather than silently failing.

### Do cue frames count against `--max-frames`?

Yes. Cue frames are extracted first and consume budget before detail sampling occurs. If you specify 5 timestamps and set `--max-frames 10`, only 5 detail frames can be added. Use `--max-frames` with generous headroom or set it to `None` to preserve all cues.

### Can I extract cue frames without any automated detail sampling?

Yes. Pass `--detail transcript` (or any mode that yields zero detail frames for your content) to get only your specified timestamps. Combine with `--start` and `--end` to further restrict the effective window without affecting which cues parse successfully.

### Why are some of my timestamps missing from the output?

Timestamps outside the `--start`/`--end` window are filtered silently but tracked in metadata. Check the `dropped_out_of_window` count in the CLI report or Python metadata dict. Also verify you haven't exceeded `--max-frames`, which triggers even sub-sampling of the cue list.