How to Extract Frames at Specific Timestamps with `--timestamps` in claude-video

The claude-video skill extracts exact frames at user-specified timestamps via the --timestamps flag, converting time strings like "4:32,7:10,9:55" into sorted, deduplicated seconds and invoking ffmpeg to capture JPEGs at each cue point.

The bradautomates/claude-video repository provides a frame extraction engine that supports precise timestamp-based sampling through its CLI and Python API. When you pass the --timestamps argument to the watch skill, the system parses your time cues, filters them against optional start/end windows, and generates individual frame files marked with "reason": "transcript-cue" for identification in the final output.

Understanding the Frame Engine Architecture

The timestamp extraction logic lives in skills/watch/scripts/frames.py, which orchestrates parsing, filtering, and image generation. The workflow moves through four distinct phases before merging results into the final frame set.

Parsing Time Strings with parse_timestamps()

At lines 95–103 of frames.py, the parse_timestamps() function handles the initial conversion of the raw CLI string. It accepts comma-separated values in formats like "4:32" or "7:10", converts each to floating-point seconds, and returns a sorted, deduplicated list.

from skills.watch.scripts.frames import parse_timestamps

# Converts "4:32,7:10,9:55" to [272.0, 430.0, 595.0]

timestamps = parse_timestamps("4:32,7:10,9:55")

Filtering and Sampling with extract_at_timestamps()

The extract_at_timestamps() function (lines 124–158) manages the core selection logic. This routine first validates each timestamp against user-provided --start and --end boundaries, dropping any cues that fall outside the active range. If the remaining count exceeds max_frames, the engine applies the _even_indices sampling strategy to evenly thin the list while preserving temporal distribution.

ffmpeg Integration for Frame Capture

For every retained timestamp t, the engine constructs and executes an ffmpeg command at lines 160–175. The command seeks to the exact second (-ss), extracts a single video frame (-frames:v 1), and writes it as a JPEG file named cue_*.jpg (e.g., cue_0000.jpg, cue_0001.jpg).

Merging Cue Frames into the Primary Set

After extraction, merge_frames() (lines 112–119) combines the timestamp-based frames with those generated by other detail engines (scene detection, keyframe analysis, or uniform sampling). This function preserves chronological order, re-indexes the combined list, and ensures cue frames are never dropped by subsequent sampling stages.

Using the --timestamps Flag from the Command Line

Invoke the watch.py entry point with the --timestamps argument to request specific frames from a video URL or local file. The skill downloads the video if necessary, processes your time cues, and includes the resulting frames in the final JSON payload.


# Assume $SKILL_DIR points to the skill directory

python3 "$SKILL_DIR/scripts/watch.py" "https://youtu.be/abc123" \
  --detail balanced \
  --timestamps 4:32,7:10,9:55

The execution flow performs four actions:

  1. Downloads the video to a temporary cache if not already present.
  2. Parses the three timestamps into seconds and sorts them.
  3. Extracts JPEGs for each cue point, naming them cue_0000.jpg, cue_0001.jpg, and cue_0002.jpg.
  4. Returns a JSON object where each frame dictionary contains "reason": "transcript-cue".

Programmatic Frame Extraction with Python

You can bypass the CLI and call the frame engine directly from Python code. This approach is useful when integrating timestamp extraction into larger workflows or when you need fine-grained control over output directories and resolution.

from pathlib import Path
from skills.watch.scripts.frames import (
    parse_timestamps,
    extract_at_timestamps,
)

video_path = "/tmp/video.mp4"
out_dir = Path("/tmp/frames")

# Parse the timestamp string

timestamps = parse_timestamps("4:32,7:10,9:55")

# Extract frames at 512px resolution, keeping all cues

frames, meta = extract_at_timestamps(
    video_path,
    out_dir,
    timestamps,
    resolution=512,
    max_frames=None,  # Retain every requested timestamp

)

print(frames)  # List of dicts with path, timestamp_seconds, reason

print(meta)    # {'engine': 'timestamps', ...}

Combining Cue Frames with Scene Detection

To ensure timestamp frames appear alongside automatically selected scene frames, use the merge_frames() utility:

from skills.watch.scripts.frames import merge_frames

# scene_frames from extract_scene_or_uniform()

# cue_frames from extract_at_timestamps()

all_frames = merge_frames(scene_frames, cue_frames)

The merged list maintains chronological order and is the version rendered to users, guaranteeing that your explicitly requested timestamps are preserved even when scene-based sampling occurs.

Key Implementation Details

Time Format Flexibility. The parse_timestamps() implementation handles standard time notation and converts to floating-point seconds, enabling sub-second precision if your input includes decimal values.

Window Constraints. When using --start or --end flags in conjunction with --timestamps, the extract_at_timestamps() function filters cues before any sampling occurs. This ensures you only extract frames within your specified analysis window.

Even Sampling Fallback. If you request more timestamps than max_frames allows, the _even_indices logic evenly distributes the selected cues across your time range rather than simply truncating the list.

Documentation Reference. The SKILL.md file at lines 45–48 explicitly documents the --timestamps flag and its intended use as a transcript cue mechanism.

Summary

  • The --timestamps flag in bradautomates/claude-video enables extraction of exact frames at user-specified times via the watch.py CLI.
  • parse_timestamps() (lines 95–103) converts comma-separated time strings into sorted, deduplicated seconds.
  • extract_at_timestamps() (lines 124–158) filters cues against --start/--end windows and applies even sampling if frame limits are exceeded.
  • The engine invokes ffmpeg at lines 160–175 to generate cue_*.jpg files marked with "reason": "transcript-cue".
  • merge_frames() (lines 112–119) integrates cue frames with scene or keyframe selections while preserving chronological order.

Frequently Asked Questions

What timestamp formats does the --timestamps flag accept?

The parse_timestamps() function accepts comma-separated time strings in standard notation like "4:32" (minutes:seconds) or "7:10". These are converted to floating-point seconds, allowing for precise positioning and optional sub-second decimal values.

How does the engine handle timestamps outside the --start or --end range?

The extract_at_timestamps() function automatically filters out any timestamps that fall before the --start value or after the --end value before extraction begins. This ensures you only generate frames within your specified analysis window, preventing unnecessary ffmpeg invocations.

Can I combine timestamp-based frames with scene detection?

Yes. Use the merge_frames() function (lines 112–119) to combine the list from extract_at_timestamps() with frames from extract_scene_or_uniform(). The merge preserves chronological order and guarantees that your explicitly requested cue frames are included in the final output.

What file naming convention is used for timestamp frames?

The engine generates JPEG files named cue_0000.jpg, cue_0001.jpg, etc., corresponding to the sorted order of your input timestamps. Each frame dictionary in the output JSON includes "reason": "transcript-cue" to distinguish these from scene-selected or keyframe-selected frames.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →