Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Video and audio perception, indexing, and editing. Ingest files/URLs/live streams, build visual/spoken indexes, search with timestamps, edit timelines, add overlays/subtitles, generate media, and create real-time alerts.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 67% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 244% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 192% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 139% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 243% | 0% |
Perception + memory + actions for video, live streams, and desktop sessions.
Use this skill when you need to:
Before running any VideoDB code, change to the project directory and load environment variables:
pythonfrom 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:
bashpython << '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:
bashpip install "videodb[capture]" python-dotenv
If videodb[capture] fails on Linux, install without the capture extra:
bashpip 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 https://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.
python# 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")
python# 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()
pythonfrom 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
pythonimport 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.lengthpythonfrom 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()
pythonfrom 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 resultpythonfrom 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})
pythonimage = coll.generate_image( prompt="a sunset over mountains", aspect_ratio="16:9", )
pythonfrom 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 |
Reference documentation is in the reference/ directory adjacent to this SKILL.md file. Use the Glob tool to locate it if needed.
Use ws_listener.py to capture WebSocket events during recording sessions. Desktop capture supports macOS only.
python scripts/ws_listener.py &cat /tmp/videodb_ws_id/tmp/videodb_events.jsonlpythonimport json events = [json.loads(l) for l in open("/tmp/videodb_events.jsonl")] # Get all transcripts transcripts = [e["data"]["text"] for e in events if e.get("channel") == "transcript"] # Get visual descriptions from last 5 minutes import time cutoff = time.time() - 300 recent_visual = [e for e in events if e.get("channel") == "visual_index" and e["unix_ts"] > cutoff]
For complete capture workflow, see reference/capture.md.
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() |
https://github.com/video-db/skills
Maintained By: VideoDB
Other measured skills in the registry, with their headline benchmark lift.