How to Use `--timestamps` to Extract Frames at Specific Moments with Claude-Video
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 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 (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). Called from watch.py (lines 76-88), it:
- Filters by window — timestamps outside
--start/--endbounds are dropped (tracked indropped_out_of_windowmetadata) - Respects frame budgets — if
--max-framesis set, the list is evenly sub-sampled to fit - Invokes FFmpeg — each retained timestamp uses
-ss <t> -frames:v 1for exact seeking - 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
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
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
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
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:
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 |
76-88 | CLI argument parsing, orchestrates cue + detail extraction |
skills/watch/scripts/frames.py |
295-304 | parse_timestamps normalization |
skills/watch/scripts/frames.py |
312-321 | merge_frames cue/detail union |
skills/watch/scripts/frames.py |
324-390 | extract_at_timestamps FFmpeg invocation |
tests/test_timestamps.py |
— | Unit tests for parsing edge cases |
tests/test_watch.py |
— | Integration tests for full pipeline |
Summary
--timestampsaccepts 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
-ssbefore 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →