Best for
- Desktop Perception
- Video ingest + stream
- Index + search (timestamps + evidence)
affaan-m/ECC/skills/videodb/SKILL.md
See, Understand, Act on video and audio. See- ingest from local files, URLs, RTSP/live feeds, or live record desktop; return realtime context and playable stream links. Understand- extract frames, build visual/semantic/temporal indexes, and search moments with timestamps and auto-clips. Act- transcode and normalize (codec, fps, resolution, aspect ratio), perform timeline edits (subtitles, text/image overlays, branding, audio overlays, dubbing, translation), generate media assets (image, audio, v
Decision brief
Perception + memory + actions for video, live streams, and desktop sessions.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/affaan-m/ECC --skill "skills/videodb"Inspect the Agent Skill "videodb" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/videodb/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
When the user asks to "setup videodb" or similar:
1. Choose state dir: STATEDIR="${VIDEODBEVENTSDIR:-$HOME/.local/state/videodb}" 2. Start listener: VIDEODBEVENTSDIR="$STATEDIR" python scripts/wslistener.py --clear "$STATEDIR" & 3. Get WebSocket ID: cat "$STATEDIR/videodbwsid" 4. Run capture code (see reference/capture.md for t…
Start/stop a desktop session capturing screen, mic, and system audio
Start/stop a desktop session capturing screen, mic, and system audio
Ingest a file or URL and return a playable web stream link
Permission review
The documentation asks the agent to create, modify, or delete local files.
Do NOT write a script file when a short inline command works.The documentation asks the agent to run terminal commands or scripts.
python << 'EOF'The documentation includes network, browsing, or remote request actions.
video = coll.upload(url="https://example.com/video.mp4")The documentation includes network, browsing, or remote request actions.
video = coll.upload(url="https://www.youtube.com/watch?v=VIDEO_ID")Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 234,327 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Perception + memory + actions for video, live streams, and desktop sessions.
Before running any VideoDB code, change to the project directory and load environment variables:
from dotenv import load_dotenv
load_dotenv(".env")
import videodb
conn = videodb.connect()
This reads VIDEO_DB_API_KEY from:
.env file in current directoryIf the key is missing, videodb.connect() raises AuthenticationError automatically.
Do NOT write a script file when a short inline command works.
When writing inline Python (python -c "..."), always use properly formatted code — use semicolons to separate statements and keep it readable. For anything longer than ~3 statements, use a heredoc instead:
python << 'EOF'
from dotenv import load_dotenv
load_dotenv(".env")
import videodb
conn = videodb.connect()
coll = conn.get_collection()
print(f"Videos: {len(coll.get_videos())}")
EOF
When the user asks to "setup videodb" or similar:
pip install "videodb[capture]" python-dotenv
If videodb[capture] fails on Linux, install without the capture extra:
pip install videodb python-dotenv
The user must set VIDEO_DB_API_KEY using either method:
export VIDEO_DB_API_KEY=your-key.env file: Save VIDEO_DB_API_KEY=your-key in the project's .env fileGet a free API key at console.videodb.io (50 free uploads, no credit card).
Do NOT read, write, or handle the API key yourself. Always let the user set it.
# URL
video = coll.upload(url="https://example.com/video.mp4")
# YouTube
video = coll.upload(url="https://www.youtube.com/watch?v=VIDEO_ID")
# Local file
video = coll.upload(file_path="/path/to/video.mp4")
# force=True skips the error if the video is already indexed
video.index_spoken_words(force=True)
text = video.get_transcript_text()
stream_url = video.add_subtitle()
from videodb.exceptions import InvalidRequestError
video.index_spoken_words(force=True)
# search() raises InvalidRequestError when no results are found.
# Always wrap in try/except and treat "No results found" as empty.
try:
results = video.search("product demo")
shots = results.get_shots()
stream_url = results.compile()
except InvalidRequestError as e:
if "No results found" in str(e):
shots = []
else:
raise
import re
from videodb import SearchType, IndexType, SceneExtractionType
from videodb.exceptions import InvalidRequestError
# index_scenes() has no force parameter — it raises an error if a scene
# index already exists. Extract the existing index ID from the error.
try:
scene_index_id = video.index_scenes(
extraction_type=SceneExtractionType.shot_based,
prompt="Describe the visual content in this scene.",
)
except Exception as e:
match = re.search(r"id\s+([a-f0-9]+)", str(e))
if match:
scene_index_id = match.group(1)
else:
raise
# Use score_threshold to filter low-relevance noise (recommended: 0.3+)
try:
results = video.search(
query="person writing on a whiteboard",
search_type=SearchType.semantic,
index_type=IndexType.scene,
scene_index_id=scene_index_id,
score_threshold=0.3,
)
shots = results.get_shots()
stream_url = results.compile()
except InvalidRequestError as e:
if "No results found" in str(e):
shots = []
else:
raise
Important: Always validate timestamps before building a timeline:
start must be >= 0 (negative values are silently accepted but produce broken output)start must be < endend must be <= video.lengthfrom videodb.timeline import Timeline
from videodb.asset import VideoAsset, TextAsset, TextStyle
timeline = Timeline(conn)
timeline.add_inline(VideoAsset(asset_id=video.id, start=10, end=30))
timeline.add_overlay(0, TextAsset(text="The End", duration=3, style=TextStyle(fontsize=36)))
stream_url = timeline.generate_stream()
from videodb import TranscodeMode, VideoConfig, AudioConfig
# Change resolution, quality, or aspect ratio server-side
job_id = conn.transcode(
source="https://example.com/video.mp4",
callback_url="https://example.com/webhook",
mode=TranscodeMode.economy,
video_config=VideoConfig(resolution=720, quality=23, aspect_ratio="16:9"),
audio_config=AudioConfig(mute=False),
)
Warning: reframe() is a slow server-side operation. For long videos it can take
several minutes and may time out. Best practices:
start/end when possiblecallback_url for async processingTimeline first, then reframe the shorter resultfrom videodb import ReframeMode
# Always prefer reframing a short segment:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
# Async reframe for full-length videos (returns None, result via webhook):
video.reframe(target="vertical", callback_url="https://example.com/webhook")
# Presets: "vertical" (9:16), "square" (1:1), "landscape" (16:9)
reframed = video.reframe(start=0, end=60, target="square")
# Custom dimensions
reframed = video.reframe(start=0, end=60, target={"width": 1280, "height": 720})
image = coll.generate_image(
prompt="a sunset over mountains",
aspect_ratio="16:9",
)
from videodb.exceptions import AuthenticationError, InvalidRequestError
try:
conn = videodb.connect()
except AuthenticationError:
print("Check your VIDEO_DB_API_KEY")
try:
video = coll.upload(url="https://example.com/video.mp4")
except InvalidRequestError as e:
print(f"Upload failed: {e}")
| Scenario | Error message | Solution |
|---|---|---|
| Indexing an already-indexed video | Spoken word index for video already exists | Use video.index_spoken_words(force=True) to skip if already indexed |
| Scene index already exists | Scene index with id XXXX already exists | Extract the existing scene_index_id from the error with re.search(r"id\s+([a-f0-9]+)", str(e)) |
| Search finds no matches | InvalidRequestError: No results found | Catch the exception and treat as empty results (shots = []) |
| Reframe times out | Blocks indefinitely on long videos | Use start/end to limit segment, or pass callback_url for async |
| Negative timestamps on Timeline | Silently produces broken stream | Always validate start >= 0 before creating VideoAsset |
generate_video() / create_collection() fails | Operation not allowed or maximum limit | Plan-gated features — inform the user about plan limits |
Use ws_listener.py to capture WebSocket events during recording sessions. Desktop capture supports macOS only.
STATE_DIR="${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}"VIDEODB_EVENTS_DIR="$STATE_DIR" python scripts/ws_listener.py --clear "$STATE_DIR" &cat "$STATE_DIR/videodb_ws_id"$STATE_DIR/videodb_events.jsonlUse --clear whenever you start a fresh capture run so stale transcript and visual events do not leak into the new session.
import json
import os
import time
from pathlib import Path
events_dir = Path(os.environ.get("VIDEODB_EVENTS_DIR", Path.home() / ".local" / "state" / "videodb"))
events_file = events_dir / "videodb_events.jsonl"
events = []
if events_file.exists():
with events_file.open(encoding="utf-8") as handle:
for line in handle:
try:
events.append(json.loads(line))
except json.JSONDecodeError:
continue
transcripts = [e["data"]["text"] for e in events if e.get("channel") == "transcript"]
cutoff = time.time() - 300
recent_visual = [
e for e in events
if e.get("channel") == "visual_index" and e["unix_ts"] > cutoff
]
Reference documentation is in the reference/ directory adjacent to this SKILL.md file. Use the Glob tool to locate it if needed.
Do not use ffmpeg, moviepy, or local encoding tools when VideoDB supports the operation. The following are all handled server-side by VideoDB — trimming, combining clips, overlaying audio or music, adding subtitles, text/image overlays, transcoding, resolution changes, aspect-ratio conversion, resizing for platform requirements, transcription, and media generation. Only fall back to local tools for operations listed under Limitations in reference/editor.md (transitions, speed changes, crop/zoom, colour grading, volume mixing).
| Problem | VideoDB solution |
|---|---|
| Platform rejects video aspect ratio or resolution | video.reframe() or conn.transcode() with VideoConfig |
| Need to resize video for Twitter/Instagram/TikTok | video.reframe(target="vertical") or target="square" |
| Need to change resolution (e.g. 1080p → 720p) | conn.transcode() with VideoConfig(resolution=720) |
| Need to overlay audio/music on video | AudioAsset on a Timeline |
| Need to add subtitles | video.add_subtitle() or CaptionAsset |
| Need to combine/trim clips | VideoAsset on a Timeline |
| Need to generate voiceover, music, or SFX | coll.generate_voice(), generate_music(), generate_sound_effect() |
Reference material for this skill is vendored locally under skills/videodb/reference/.
Use the local copies above instead of following external repository links at runtime.
Alternatives
affaan-m/ECC
Use it for engineering tasks; the detail page covers purpose, installation, and practical steps.
affaan-m/ECC
Use it for engineering tasks; the detail page covers purpose, installation, and practical steps.
wshobson/agents
Brand-first landing page designer — runs a brand-identity interview (colors, typography, shape language), then generates and iterates on a polished landing page via Stitch with deployment-ready HTML. Use when the user asks to create, design, or build a landing page, homepage, or marketing page and has no established visual direction. Skip when they have a design mockup, need a dashboard or app UI, are working at component level, building a multi-page app, or restyling with known design tokens —
dpearson2699/swift-ios-skills
Implement, review, or improve localization and internationalization in iOS/macOS apps — String Catalogs (.xcstrings), generated localizable symbols, stable key naming, LocalizedStringKey, LocalizedStringResource, pluralization, FormatStyle for numbers/dates/measurements, right-to-left layout, Dynamic Type, and locale-aware formatting. Use when adding multi-language support, setting up String Catalogs, enabling generated symbols for compile-time-safe localization keys, handling plural forms, form