| name | framedex |
| description | Build a portable knowledge base of your video (and eventually photo) archive across multiple SSDs or directly from an Apple Photos library. For each clip: GPS + reverse-geocoded place, speaker-diarized multi-lingual transcript with English translation, face detection + embeddings for later named-person queries, Claude/Gemma structured assessment (keep/review/cull rating + technical quality + lighting + time of day + dominant colors + audio quality + people count + keywords + notable timestamps), and prose scene description. Writes plain-text sidecars next to originals (or to a mirror tree for Photos libraries — never inside the .photoslibrary bundle) + persistent face DB. Photos-library mode also threads through albums, named persons, and keywords from the Photos database. Non-destructive, idempotent, resumable. Use whenever you want to: index videos, tag footage, organize a drive, build the video knowledge base, transcribe audio, describe clips, rate clips, find clips by location/lighting/person/keyword, generate folder summaries, identify duplicates or cull pile, index the Apple Photos library directly. Trigger phrases: 'index this drive', 'tag my videos', 'index my Photos library', 'index Apple Photos videos', 'what's on this SSD', 'rate these clips', 'find me clips of X', 'what should I cull', 'build the video knowledge base'. |
framedex — Video Archive Knowledge Base
Cross-project, cross-drive. An entire video archive turned into a portable plain-text knowledge base + queryable face DB.
Per-clip pipeline
ffprobe — metadata
exiftool — GPS lat/lon/altitude (iPhone, DJI, drone all supported)
- Nominatim — reverse-geocoded place name (rate-limited 1/sec, free, no key)
ffmpeg — 5 content-diverse JPEG frames @ 1920px max (most mutually different, sharpest moments from a thumbnail pool; --frame-sampling even for legacy even spacing)
ffmpeg — audio extraction → WhisperX transcribe + diarization + alignment
- WhisperX translate-mode — English translation for non-English clips
insightface (RetinaFace + ArcFace) — face detection + 512-dim embeddings on the same frames
- Vision model (Claude Haiku/Sonnet via Max CLI / API, OR local Gemma via LM Studio) → structured YAML + prose description in one call
- Write
[filename].description.md sidecar + insert face rows into ~/.framedex/faces.db
Output schema
Each sidecar's YAML frontmatter:
file: IMG_4827.mov
path: /Volumes/SSD-2024/...
parent_folder: drone
duration_seconds: 12.3
resolution: 3840x2160
codec: hvc1
size_bytes: 245678912
creation_time: 2024-08-14T07:23:11Z
location:
lat: 37.7456
lon: -119.5936
altitude_m: 1842.5
place: "Yosemite Valley, Mariposa County, USA"
language_detected: es
speaker_count: 2
rating: keep
cull_reason: ""
technical:
focus: sharp
exposure: strong
stability: smooth
motion_blur: clean
lighting: golden_hour
time_of_day: golden_hour
dominant_color_palette: "warm dusk: amber, ochre, dusty olive"
dominant_colors: [amber, ochre, olive, sky-blue]
audio_quality: clean_speech
people_count: 3
keywords: [drone, landscape, construction, golden-hour, wide-shot, speech, workers]
notable_timestamp: ""
faces:
- cluster_id: tmp_a3f78c
frame_time: 1.2
bbox: [120, 80, 180, 240]
detection_quality: high
face_count: 2
indexed_at: 2026-05-17T14:32:01
When indexed via fdx-photos, the sidecar also carries Photos-side fields:
original_filename: IMG_4827.MOV
photos_uuid: ABCD1234-EF56-7890-ABCD-1234567890AB
photos_persons: [Mom, Dad]
photos_albums: [Yosemite 2024]
photos_keywords: [sunset]
photos_edited: true
Body follows: ## Description (Scene/Subjects/Action/Mood/Shot type/Use cases prose), ## Transcript (with speaker labels if diarized), ## English translation (if applicable).
Three vision backends
| Backend | Quality | Speed | Cost | Privacy |
|---|
cli (default) | Claude Haiku/Sonnet via Max | ~10-30s per clip | $0 (Max subscription) | Cloud (frames sent to Anthropic) |
api | Claude Haiku/Sonnet via API | ~2-3s per clip | ~$0.002 (Haiku) / ~$0.008 (Sonnet) per clip | Cloud (frames sent to Anthropic) |
local | Local model via LM Studio (Gemma 4, Qwen2-VL, etc.) | ~3-90s depending on model | $0 | Fully local |
--vision-model haiku|sonnet picks Claude model for cli/api. --local-model NAME picks LM Studio model. The script auto-strips ANTHROPIC_API_KEY from claude -p subprocess env so CLI mode hits Max OAuth even if API key is set globally.
Face detection
Always on by default. ~/.framedex/faces.db is the single shared face database across all drives. Per-clip embeddings stored as 512 float32 vectors + bbox + detection score. Temporary cluster IDs (tmp_<hash>) get replaced with real names by the (not-yet-built) fdx-faces clustering tool — that tool will be a follow-up that doesn't require re-running the indexing pass, because all embeddings are captured here.
Skip with --no-faces if you don't want face data.
Companion scripts / aliases
| Alias | Script | Purpose |
|---|
fdx | framedex.index_videos | Main indexer for folder trees — videos and still photos (--media images|videos|all) |
fdx-photos | framedex.photos_indexer | Index videos directly from an Apple Photos library (macOS, requires [photos] extra) |
fdx-summary | framedex.trip_summary | Recursive folder summaries (_folder-summary.md in each ≥5-clip folder) |
fdx-master | framedex.master_index | Drive-level _INDEX.md + _INDEX.json |
fdx-query | framedex.query | Filter sidecars by metadata (rating, lighting, person, keyword, etc.) |
fdx-xmp | framedex.xmp_export | Export ratings/keywords to Lightroom via .xmp sidecars (proprietary RAW; regenerable, non-destructive) |
Set up once
cd ~/.claude/skills/framedex
uv pip install -e '.[all]'
python3 scripts/setup.py
export HF_TOKEN=hf_...
Common run patterns
fdx /Volumes/SSD-2024 --max-files 5
fdx /Volumes/SSD-2024
fdx /Volumes/SSD-2024 --vision-model sonnet
fdx /Volumes/SSD-2024 --backend local
fdx /Volumes/SSD-2024 --max-duration 30
fdx /Volumes/SSD-2024 --force --vision-model sonnet
fdx-summary /Volumes/SSD-2024
fdx-master /Volumes/SSD-2024
fdx-query /Volumes/SSD-2024 --rating keep --time-of-day golden_hour
fdx-query /Volumes/SSD-2024 --rating cull
fdx-query /Volumes/SSD-2024 --place-contains California --language es
fdx-query /Volumes/SSD-2024 --keyword drone --keyword landscape
fdx-query /Volumes/SSD-2024 --stability smooth --people-count 0
fdx-query /Volumes/SSD-2024 --rating keep --json | jq '.[] | .path'
Still photos (fdx --media)
fdx indexes still photos (RAW / JPEG / HEIC) through the same pipeline as
video, minus the audio half, plus an EXIF camera block. One command handles
mixed photo + video folders.
fdx /Volumes/SSD-photos --max-files 5
fdx /Volumes/SSD-photos
fdx /Volumes/SSD-photos --media images
fdx /Volumes/SSD-photos --media videos
fdx-query /Volumes/SSD-photos --media images --place-contains Mara --keyword giraffe
Photo sidecars mirror the video schema with the audio/motion fields removed and
a camera block added:
file: DSC_4827.RAF
media_type: image
dimensions: 7728x5152
camera: {make: FUJIFILM, model: X-T5, lens: "XF16-80mmF4", focal_length: "80.0 mm", aperture: 4.0, shutter: "1/1000", iso: 400}
location: {lat: -1.40, lon: 35.01, place: "Maasai Mara National Reserve, Narok County, Kenya"}
rating: keep
technical: {focus: sharp, exposure: strong, composition: strong}
lighting: golden_hour
time_of_day: golden_hour
scene_type: wildlife
keywords: [giraffe, waterhole, drinking, savanna]
RAW is read from the embedded full-res JPEG preview (no libraw). Requires the
[images] extra (Pillow + pillow-heif); video indexing needs [video].
Apple Photos library
If the library is local on disk (no iCloud, or iCloud without Optimize Storage),
this is one command — no flags, no permission prompts:
fdx-photos
The full menu:
.venv/bin/python scripts/diagnose_photos.py
fdx-photos --max-files 5
fdx-photos
fdx-photos --album "Yosemite 2024" --since 2024-01-01
fdx-photos --person "Mom" --keyword sunset
fdx-photos --output ~/Documents/photos-kb
fdx-photos --uuid ABCD1234-EF56-7890-ABCD-1234567890AB --force
fdx-photos --download
fdx-query ~/framedex-photos --rating keep --person "Mom"
fdx-master ~/framedex-photos
Optional folder context
Drop .video-context.md at the root of any scan target with a paragraph describing what's on that drive ("construction site, 2023-2026", "family travel, 2024", etc). The vision prompt prepends it for context-aware descriptions.
Privacy
| Component | Local or cloud? |
|---|
| ffmpeg / exiftool / Whisper / pyannote / insightface | Local |
| Nominatim reverse geocoding | Sends lat/lon (not video). Skip with --no-geocode. |
Vision (--backend cli/api) | Frames sent to Anthropic. By default not used for training. |
Vision (--backend local) | Fully local, fully offline. |
Face DB (~/.framedex/faces.db) | Local only, never uploaded. Back up the file manually if you care. |
Multiple SSDs
Run on each drive separately. Sidecars travel with the data; the face DB is centralized at ~/.framedex/faces.db so cross-drive person queries work.
Known limitations (v1)
- pyannote diarization degrades on heavy ambient noise (wind, music, crowd)
- WhisperX runs on CPU on Apple Silicon (CTranslate2 doesn't have M-series GPU acceleration yet; 64GB CPU is still plenty)
fdx-faces (clustering + labeling tool) not built yet — face embeddings are captured but cluster IDs are temporary hashes until that tool ships
- RAW image format support not yet (videos only; photos are coming)
fdx-photos --download relies on PhotoKit, which requires the parent terminal to have "Photos" access in macOS Privacy & Security. Unsigned Python (the default .venv install) often can't trigger the OS prompt, so the terminal may never appear in that panel. The supported workaround is to turn off "Optimize Mac Storage" in Photos → Settings → iCloud, which makes all originals local and removes the need for --download entirely.
File layout
~/.claude/skills/framedex/
├── SKILL.md # this file
├── README.md
├── pyproject.toml # deps, ruff/mypy config, entry points
├── .pre-commit-config.yaml # pre-commit hooks
├── .github/workflows/ci.yml # CI (ruff + mypy)
├── scripts/
│ ├── setup.py # system binaries + model pre-download
│ └── diagnose_photos.py # one-shot Photos-library state report
└── src/framedex/
├── __init__.py # package init, version from pyproject.toml
├── index_videos.py # main worker (fdx) — also exports
│ # ProcessOptions/ProcessContext/process_one_video
│ # so fdx-photos can reuse the per-clip pipeline
├── photos.py # Apple Photos library adapter (osxphotos)
├── photos_indexer.py # Photos library indexer (fdx-photos)
├── face_db.py # face detection + SQLite face DB module
├── trip_summary.py # recursive folder summaries (fdx-summary)
├── master_index.py # drive-level KB (fdx-master)
└── query.py # filter sidecars (fdx-query)