
Video Explorer
- 34 installs
- Updated January 1, 1970
- cygnusfear/agent-skills
Lets the agent explore and pull information out of video content so a solo builder can research or summarize footage without watching all of it.
About
A Claude Code skill that explores video content so the agent can extract, navigate, and summarize what a video contains. A solo builder reaches for it to research or digest footage without manually watching everything, turning long videos into information they can act on quickly.
- Explores video content programmatically
- Extracts and summarizes information
- Speeds up video research
Video Explorer by the numbers
- 34 all-time installs (skills.sh)
- Ranked #1,187 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cygnusfear/agent-skills --skill video-explorerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 34 |
|---|---|
| Last updated | January 1, 1970 |
| Repository | cygnusfear/agent-skills ↗ |
What it does
Lets the agent explore and pull information out of video content so a solo builder can research or summarize footage without watching all of it.
Who is it for?
Extracting information from video content
When should I use this skill?
You need to research or digest a video
Files
Video Explorer
Analyze video content through hierarchical frame extraction. Start wide, identify interesting regions, zoom in.
Workflow
1. Overview First
Extract quick thumbnails to see the video timeline:
./skills/video-explorer/scripts/videx overview <video>This creates small frames (320px) at 10-second intervals in ./videx-out/<name>/overview/.
Read all overview frames to understand video structure:
ls ./videx-out/<name>/overview/Then use the Read tool on the jpg files to see them.
⚠️ Triplet Rule: Never Single-Frame Sample
A single snapshot tells you nothing about change. Overview automatically extracts 3 frames per sample point (t-δ, t, t+δ) so you see motion at each checkpoint. Default δ=0.1s (~3 frames at 30fps) — tight enough to show micro-motion against the long interval between samples.
Triplet files are suffixed _a, _b, _c. Always compare the triplet together.
Configure delta for your needs:
--triplet=0.1(default) — subtle motion, UI transitions--triplet=0.5— broader change, scene transitions--triplet=1.0— wide spread, slow-moving content
2. Identify Regions of Interest
After viewing overview frames, identify timestamps where:
- Something interesting is happening
- More detail is needed
- Action is occurring that requires temporal resolution
3. Zoom In
For a time range (more frames, higher resolution):
./skills/video-explorer/scripts/videx range <video> <start>-<end>
# Example: videx range talk.mp4 5:30-6:00For higher temporal resolution (catch fast action):
./skills/video-explorer/scripts/videx range <video> <start>-<end> --fps=10For a single frame at full detail:
./skills/video-explorer/scripts/videx zoom <video> <time>
# Example: videx zoom talk.mp4 5:454. Iterate
Repeat zoom operations as needed until the question is answered.
Commands Reference
| Command | Purpose | Output |
|---|---|---|
videx overview <video> | Quick timeline scan | 320px triplets @ 10s intervals (±0.1s) |
videx overview <video> 5 480 | Denser timeline | 480px triplets @ 5s intervals |
videx overview <video> 10 320 0.5 | Wider triplet delta | 320px triplets @ 10s, ±0.5s spread |
videx range <video> <start>-<end> | Extract segment | 1280px frames @ 2fps (no triplets) |
videx range <video> <start>-<end> --fps=10 | Fast action | 1280px frames @ 10fps |
videx range <video> <start>-<end> --triplet=0.1 | Range with triplets | 1280px triplets @ 2fps |
videx zoom <video> <time> | Single frame detail | 1920px single frame |
videx zoom <video> <time> --hd | Maximum detail | Full resolution frame |
Time Formats
All commands accept these time formats:
1:30= 1 minute 30 seconds01:30:00= 1 hour 30 minutes90= 90 seconds1:30.5= 1 minute 30.5 seconds
Output Structure
Frames are saved with timestamps in filenames for easy reference:
./videx-out/
└── video-name/
├── overview/
│ ├── t_00-00-00.00.jpg
│ ├── t_00-00-10.00.jpg
│ └── ...
├── range_5-30_6-00/
│ ├── t_00-05-30.00.jpg
│ └── ...
└── zoom/
└── t_00-05-45.00.jpgExample Session
User asks: "What happens in this lecture video around the 10 minute mark?"
1. Run overview to understand video structure 2. View overview frames, note that slides change around 9:30-11:00 3. Extract that range: videx range lecture.mp4 9:30-11:00 4. View range frames, find the specific slide transition at 10:15 5. Zoom for detail: videx zoom lecture.mp4 10:15 6. Report findings with specific timestamps
#!/usr/bin/env bash
#
# videx - Video frame extraction for Claude analysis
#
# Usage:
# videx overview <video> # Quick thumbnails, 10s intervals, 320px
# videx range <video> <start>-<end> # Frames from range, 2fps, 720px
# videx range <video> <start>-<end> --fps=N # Custom fps
# videx zoom <video> <time> # Single frame, 1080px
# videx zoom <video> <time> --hd # Single frame, full resolution
#
# Output: Creates timestamped frames in ./videx-out/<video-basename>/
#
# Time formats: 1:30, 01:30, 1:30.5, 90 (seconds)
set -euo pipefail
die() { echo "error: $*" >&2; exit 1; }
# Normalize time to seconds (handles 1:30, 01:30:00, 90, etc.)
time_to_seconds() {
local t="$1"
if [[ "$t" =~ ^([0-9]+):([0-9]+):([0-9.]+)$ ]]; then
echo "$(( ${BASH_REMATCH[1]} * 3600 + ${BASH_REMATCH[2]} * 60 )) + ${BASH_REMATCH[3]}" | bc
elif [[ "$t" =~ ^([0-9]+):([0-9.]+)$ ]]; then
echo "$(( ${BASH_REMATCH[1]} * 60 )) + ${BASH_REMATCH[2]}" | bc
else
echo "$t"
fi
}
# Format seconds back to timestamp for filenames
seconds_to_timestamp() {
local s="$1"
local h m sec
h=$(echo "$s / 3600" | bc)
m=$(echo "($s % 3600) / 60" | bc)
sec=$(echo "$s % 60" | bc)
printf "%02d-%02d-%05.2f" "$h" "$m" "$sec"
}
# Get video duration in seconds
get_duration() {
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1" 2>/dev/null || echo "0"
}
cmd_overview() {
local video="$1"
local interval="${2:-10}"
local width="${3:-320}"
local triplet="${4:-0.1}"
[[ -f "$video" ]] || die "video not found: $video"
local name=$(basename "${video%.*}")
local outdir="./videx-out/$name/overview"
mkdir -p "$outdir"
local duration=$(get_duration "$video")
echo "Extracting overview triplets (${interval}s intervals, ±${triplet}s, ${width}px)..."
# Extract triplet frames at each sample point: t-δ, t, t+δ
local t=0
local frame_num=0
while (( $(echo "$t < $duration" | bc -l) )); do
for offset in "-$triplet" "0" "$triplet"; do
local sample=$(echo "$t + $offset" | bc -l)
# Clamp to 0
if (( $(echo "$sample < 0" | bc -l) )); then sample="0"; fi
if (( $(echo "$sample >= $duration" | bc -l) )); then continue; fi
local ts=$(seconds_to_timestamp "$sample")
local suffix=""
if [[ "$offset" == "-$triplet" ]]; then suffix="_a";
elif [[ "$offset" == "0" ]]; then suffix="_b";
else suffix="_c"; fi
ffmpeg -ss "$sample" -i "$video" \
-vf "select=eq(n\,0),scale=$width:-1" \
-q:v 3 \
-frames:v 1 \
"$outdir/t_${ts}${suffix}.jpg" \
-y -loglevel error
done
t=$(echo "$t + $interval" | bc -l)
done
local count=$(ls -1 "$outdir"/*.jpg 2>/dev/null | wc -l | tr -d ' ')
echo "Created $count frames ($((count / 3)) triplets) in $outdir"
echo "$outdir"
}
cmd_range() {
local video="$1"
local range="$2"
local fps="${3:-2}"
local width="${4:-1280}"
local triplet="${5:-0}"
[[ -f "$video" ]] || die "video not found: $video"
[[ "$range" =~ ^([^-]+)-(.+)$ ]] || die "invalid range format, use: start-end (e.g., 1:30-2:00)"
local start_time="${BASH_REMATCH[1]}"
local end_time="${BASH_REMATCH[2]}"
local start_sec=$(time_to_seconds "$start_time")
local end_sec=$(time_to_seconds "$end_time")
local duration=$(echo "$end_sec - $start_sec" | bc)
local name=$(basename "${video%.*}")
local range_safe="${start_time//:/-}_${end_time//:/-}"
local outdir="./videx-out/$name/range_${range_safe}"
mkdir -p "$outdir"
if [[ "$triplet" != "0" ]]; then
echo "Extracting range $start_time to $end_time (${fps}fps, ±${triplet}s triplets, ${width}px)..."
local t="$start_sec"
local step=$(echo "1 / $fps" | bc -l)
while (( $(echo "$t < $end_sec" | bc -l) )); do
for offset in "-$triplet" "0" "$triplet"; do
local sample=$(echo "$t + $offset" | bc -l)
if (( $(echo "$sample < $start_sec" | bc -l) )); then sample="$start_sec"; fi
if (( $(echo "$sample >= $end_sec" | bc -l) )); then continue; fi
local ts=$(seconds_to_timestamp "$sample")
local suffix=""
if [[ "$offset" == "-$triplet" ]]; then suffix="_a";
elif [[ "$offset" == "0" ]]; then suffix="_b";
else suffix="_c"; fi
ffmpeg -ss "$sample" -i "$video" \
-vf "select=eq(n\,0),scale=$width:-1" \
-q:v 2 \
-frames:v 1 \
"$outdir/t_${ts}${suffix}.jpg" \
-y -loglevel error
done
t=$(echo "$t + $step" | bc -l)
done
else
echo "Extracting range $start_time to $end_time (${fps}fps, ${width}px)..."
ffmpeg -ss "$start_sec" -i "$video" -t "$duration" \
-vf "fps=$fps,scale=$width:-1" \
-q:v 2 \
-vsync vfr \
"$outdir/frame_%04d.jpg" \
-y -loglevel error
# Rename with actual timestamps
local i=0
for f in "$outdir"/frame_*.jpg; do
[[ -f "$f" ]] || continue
local frame_sec=$(echo "$start_sec + ($i / $fps)" | bc -l)
local ts=$(seconds_to_timestamp "$frame_sec")
mv "$f" "$outdir/t_${ts}.jpg"
((i++)) || true
done
fi
local count=$(ls -1 "$outdir"/*.jpg 2>/dev/null | wc -l | tr -d ' ')
echo "Created $count frames in $outdir"
echo "$outdir"
}
cmd_zoom() {
local video="$1"
local time="$2"
local hd="${3:-}"
[[ -f "$video" ]] || die "video not found: $video"
local time_sec=$(time_to_seconds "$time")
local name=$(basename "${video%.*}")
local outdir="./videx-out/$name/zoom"
mkdir -p "$outdir"
local ts=$(seconds_to_timestamp "$time_sec")
local outfile="$outdir/t_${ts}.jpg"
local scale_filter=""
if [[ "$hd" != "--hd" ]]; then
scale_filter=",scale=1920:-1"
fi
echo "Extracting frame at $time..."
ffmpeg -ss "$time_sec" -i "$video" \
-vf "select=eq(n\,0)${scale_filter}" \
-q:v 1 \
-frames:v 1 \
"$outfile" \
-y -loglevel error
echo "Created: $outfile"
echo "$outfile"
}
cmd_help() {
cat << 'EOF'
videx - Video frame extraction for Claude analysis
USAGE:
videx <command> <video> [options]
COMMANDS:
overview <video> [interval] [width]
Extract thumbnail frames at regular intervals
Default: 10s intervals, 320px width
range <video> <start-end> [--fps=N] [--width=N]
Extract frames from a time range
Default: 2fps, 1280px width
Example: videx range video.mp4 1:30-2:00 --fps=5
zoom <video> <time> [--hd]
Extract a single frame at specific timestamp
Default: 1920px width
--hd: Full resolution (no scaling)
TIME FORMATS:
1:30 = 1 minute 30 seconds
01:30:00 = 1 hour 30 minutes
90 = 90 seconds
1:30.5 = 1 minute 30.5 seconds
OUTPUT:
Frames saved to ./videx-out/<video-name>/<command>/
Files named with timestamps: t_HH-MM-SS.SS.jpg
EXAMPLES:
videx overview talk.mp4 # Quick scan
videx overview talk.mp4 5 480 # 5s intervals, 480px
videx range talk.mp4 10:00-12:30 # Extract 2.5min segment
videx range talk.mp4 10:00-12:30 --fps=10 # Higher temporal resolution
videx zoom talk.mp4 11:45 # Single frame
videx zoom talk.mp4 11:45 --hd # Full resolution
EOF
}
# Parse arguments
[[ $# -lt 1 ]] && { cmd_help; exit 0; }
command="$1"
shift
case "$command" in
overview)
[[ $# -lt 1 ]] && die "usage: videx overview <video> [interval] [width]"
cmd_overview "$1" "${2:-10}" "${3:-320}"
;;
range)
[[ $# -lt 2 ]] && die "usage: videx range <video> <start-end> [--fps=N] [--width=N] [--triplet=N]"
video="$1"
range="$2"
shift 2
fps=2
width=1280
triplet=0
for arg in "$@"; do
case "$arg" in
--fps=*) fps="${arg#--fps=}" ;;
--width=*) width="${arg#--width=}" ;;
--triplet=*) triplet="${arg#--triplet=}" ;;
--triplet) triplet="0.1" ;;
esac
done
cmd_range "$video" "$range" "$fps" "$width" "$triplet"
;;
zoom)
[[ $# -lt 2 ]] && die "usage: videx zoom <video> <time> [--hd]"
cmd_zoom "$1" "$2" "${3:-}"
;;
help|--help|-h)
cmd_help
;;
*)
die "unknown command: $command (try: videx help)"
;;
esac