关于 videodb
videodb 是一项技能,指导通过 VideoDB 的 Python SDK 在其服务器上处理视频。它面向开发者和 Agent,涵盖导入、分析、搜索与编辑,并建议常见任务使用配套技能。
主要功能
- 01从网址或本地文件上传视频
- 02使用可配置分析器分析视频
- 03按语义或精确条件搜索视频
- 04生成转录文本和字幕
- 05合成、转码及重构视频画幅
适用场景
- 01在视频集合中查找相关片段
- 02制作字幕或翻译视频素材
- 03合成包含音频、图像和文字的片段
- 04在 Python 项目中运行视频处理
MCP 相关
该技能介绍了供 Claude Code 使用的托管 VideoDB MCP 服务,其授权与 SDK API 密钥分开。MCP 工具无需本地安装 Python 或 API 密钥即可执行视频操作;在该插件配置之外,文档所述路径为 SDK。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ video-db/ skills --skill videodb安装后在 Claude Code 中输入/videodb确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
.env.examplereference/api-reference.mdcapture-reference.mdcapture.mdeditor-reference.mdeditor.mdgenerative.mdindexing-reference.mdindexing.mdmigration.mdrtstream-reference.mdrtstream.mdsandbox-reference.mdsandbox.mdsearch-reference.mdsearch.mdstreaming.mduse-cases.mdreference/legacy/index-reference.mdindex.mdsearch.mdscripts/ws_listener.py详细介绍
VideoDB:设置与 Python SDK
VideoDB 会在其服务器上处理视频:导入、理解、索引、搜索、剪辑、添加字幕、合成和流式传输。本技能涵盖设置与 Python SDK。另有五个较小的技能涵盖常见任务;符合需求时应优先使用:
| 任务 | 技能 |
|---|---|
| 查找某个时刻、询问视频内容、统计画面中出现的事物 | video-search |
| 剪辑、精彩集锦、竖屏剪辑、MP4 文件 | video-clips |
| 转录文本、字幕、翻译 | video-subtitles |
| 音乐、叠加内容、字幕、合成 | video-edit |
| 摄像头、直播、会议、屏幕录制 | video-live |
选择路径
- 存在 VideoDB 连接器工具(名称如
upload_media、search_video、generate_clip):使用这些工具。这是 Claude 网页版、桌面版和移动版聊天,以及 Cowork 中的使用方式。无需安装,也无需 API 密钥。 - 没有 VideoDB 工具,但可以使用 shell(Claude Code、Cowork,或任何能运行 Python 的 Agent):用一句友好的话询问 VideoDB API 密钥(可在 https://console.videodb.io 免费获取,含 $20 额度,无需银行卡)。用户提供后,自行将其保存到项目的
.env文件中(见下文“设置”),切勿打印密钥,然后继续使用 SDK。在 Claude Code 中,也可以使用/mcp,然后授权已捆绑的 videodb 服务。 - 没有 VideoDB 工具,也无法使用 shell:你当前位于 Claude 聊天中。仅回复以下内容,并根据请求调整:“VideoDB 尚未连接。打开 Settings → Connectors,找到 VideoDB,点击 Connect 并登录(免费,无需银行卡)。然后再次发送此请求,我会继续处理。”此处不要提及 API 密钥、
.env、shell、Claude Code 或/mcp;这些都不适用于聊天,而且在聊天中粘贴的密钥也无法使用,因为这里不会运行代码。
代码、脚本、笔记本、桌面录制、AI 生成、配音和沙盒计算也只能通过 SDK 完成,连接器无法处理这些任务。与用户交流时:回复中绝不要写“索引”“已索引”或“正在索引”;应使用“可搜索”。
耗时较长的连接器任务会返回 status: processing。回复中会包含 output_url,并指出要轮询的工具(poll_with,旧版服务则为 retry_with)。使用该工具传入 output_url,直到状态变为 done;连续收到多次 processing 属于正常情况(上传一个 10 分钟的 YouTube 视频大约需要 5 分钟),而 done 回复中结果为空时,表示需要再轮询一次。绝不要重新运行写入操作,否则会启动第二个任务。
输出
- 流媒体 URL,可通过
https://console.videodb.io/player?url={STREAM_URL}播放;连接器工具也会返回player_url - 带时间戳和可播放证据的搜索结果
- 生成的素材:字幕、音频、图片、剪辑
- 直播的事件和警报载荷
- 桌面会话摘要和可搜索时间线
运行 Python 代码
**重要:**运行 Python 代码前,务必先 cd 到用户的项目目录。这样可以确保 load_dotenv(".env") 找到正确的 .env 文件。
from dotenv import load_dotenv
load_dotenv(".env")
import videodb
conn = videodb.connect()
它会按以下顺序读取 VIDEO_DB_API_KEY:
- 环境变量(如果已导出)
- 当前目录下项目的
.env文件
如果缺少密钥,videodb.connect() 会自动引发 AuthenticationError。
短小的内联命令即可完成时,不要编写脚本文件。
编写内联 Python(python -c "...")时,务必使用格式规范的代码——用分号分隔语句,并保持易读。超过约 3 条语句时,请改用 heredoc:
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
设置
用户询问“设置 videodb”或类似内容时:
1. 安装 SDK
pip install "videodb[capture]>=0.5.0" python-dotenv
如果在 Linux 上安装 videodb[capture] 失败,请在不添加 capture 扩展的情况下安装:
pip install "videodb>=0.5.0" python-dotenv
>=0.5.0 这一版本限制很重要——更早版本中没有 understand/index/ask/aggregate API。
2. 配置 API 密钥
请用一行向用户询问其 VideoDB API 密钥:可在 https://console.videodb.io 免费获取,含 $20 免费额度,无需信用卡。用户通过聊天或其他方式提供密钥后,替他们保存并继续操作:
- 将
VIDEO_DB_API_KEY=<key>写入项目的.env文件(如有需要则创建该文件),并确保.env已列在.gitignore中。 - 切勿打印、回显、记录或提交该密钥,也切勿通过 SDK 以外的途径发送该密钥。
- 如果用户想自行设置,也可以在终端中运行
export VIDEO_DB_API_KEY=<key>。
告知用户保存了密钥以及保存位置,但不要重复密钥。
3. 授权托管 MCP 服务(仅限 Claude Code 插件)
该插件捆绑了托管 MCP 服务 https://mcp.videodb.io/mcp,其授权与上述 SDK 密钥分开进行。请告知用户运行 /mcp,选择 videodb,并使用其 VideoDB 账号完成浏览器授权。通过“list my VideoDB collections”进行验证。
MCP 工具无需本地安装 Python,也无需 API 密钥。如果技能是通过 npx skills add 安装的,或者运行环境不是 Claude Code,则跳过此步骤——此时没有 MCP 服务,只有上述 SDK 方式可用。
快速参考
上传媒体
# 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")
理解 → 建立索引 → 检索(默认路径)
分为三个阶段。运行分析器以生成产物,为每个产物建立索引,然后进行检索。
import time
# 1. Understand. Naming each analyzer keeps `analyzer.name` meaningful downstream.
understanding = video.understand(
analyzers=[
{"type": "spoken_words", "name": "transcript"},
{"type": "vlm", "name": "scene",
"config": {"prompt": "Describe the scene and any on-screen text."}},
],
segmentation={"type": "shot", "threshold": 30},
)
# A run with a failed or skipped analyzer ends `partial`, which the SDK does not
# treat as terminal — wait_until_complete() would poll to TimeoutError. Poll the
# analyzers instead. The `analyzers and` guard is load-bearing: a refresh can
# transiently return an empty list, and all([]) is True, which would exit the
# loop while the run is still going.
deadline = time.time() + 3600
while time.time() < deadline:
analyzers = understanding.refresh().list_analyzers()
if analyzers and all(a.is_complete for a in analyzers):
break
time.sleep(15)
# 2. Index each artifact that succeeded.
for analyzer in understanding.list_analyzers():
if analyzer.is_successful:
video.index(source=analyzer, name=analyzer.name).wait_until_complete()
# 3. Retrieve.
response = video.search("discussion about pricing")
for shot in response.shots:
print(f"[{shot.start:.1f}s - {shot.end:.1f}s] {shot.text}")
if response.response_type in ("shots", "deepsearch") and len(response):
stream_url = response.compile() # raises SearchError otherwise
分析器类型:spoken_words(→ 产物 transcript)、vlm(→ scene)、object_detection(→ objects)、ocr、brand_detection(→ brands)、activity_recognition(→ activity)、location_detection(→ location)、faces、audio_event_detection。它们都是普通字符串——没有 SDK 枚举。
关于分段、采样、字段配置和成本调优,请参阅 reference/indexing.md。
检索
search(query) 是默认方式——它会规划检索并自行选择索引。需要特定内容时,可改用以下方法:
# Target a specific index, with a relevance floor
video.semantic_search("a customer holding the product", index_names=["scene"], score_threshold=0.7)
# Exact filtering, no natural-language interpretation
video.query(index_name="objects",
filter=[{"field": "frames.detections.label", "op": "contains", "value": "car"}])
# Counts and facets — returns the raw server payload, not a SearchResult
video.aggregate(index_name="objects", group_by="frames.detections.label", metric="count")
# A written answer plus the moments it came from
answer = video.ask("What did they say about pricing?", include_sources=True)
这五种方法在 Collection 上也都可用,并会遍历其中所有已建立索引的视频。请参阅 reference/search.md。
search() 现在返回 SearchResponse,而非 SearchResult。 get_shots()、compile()、play() 和迭代都可正常使用,但它没有 .stream_url——请改用 .compile()。
转录文本 + 字幕
video.generate_transcript() # returns the existing transcript if there is one; force=True re-runs it
text = video.get_transcript_text()
stream_url = video.add_subtitle()
add_subtitle() 和 CaptionAsset(src="auto") 会读取视频的转录文本。generate_transcript() 会创建转录文本,而从 understand() 运行 spoken_words 分析器也会创建转录文本。字幕或说明文字无需使用旧版 index_spoken_words()。
旧版索引(现有代码库)
# v1 API, legacy. Only for codebases that already use it; new code uses understand() + index() above.
from videodb import IndexType
video.index_spoken_words(force=True)
scene_index_id = video.index_scenes(prompt="Describe the visual content.")
results = video.legacy_search(
"person writing on a whiteboard",
index_type=IndexType.scene,
scene_index_id=scene_index_id,
)
在现有仓库中识别这种模式,除非用户要求迁移,否则不要改动——它仍然有效。关于移植,请参阅 reference/migration.md;关于维护,请参阅 reference/legacy/search.md。
时间线编辑
使用 Editor API 合成视频、图像、音频和文本。完整工作流请参阅 reference/editor.md。
from videodb.editor import Timeline, Track, Clip, VideoAsset, ImageAsset, AudioAsset, Fit
timeline = Timeline(conn)
timeline.resolution = "1280x720"
video_track = Track()
video_track.add_clip(0, Clip(asset=VideoAsset(id=video.id, start=10), duration=20))
audio_track = Track()
audio_track.add_clip(0, Clip(asset=AudioAsset(id=music.id, volume=0.2), duration=20))
timeline.add_track(video_track)
timeline.add_track(audio_track)
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),
)
重新调整宽高比(适用于社交平台)
警告:reframe() 是一项耗时的服务端操作。对于较长的视频,可能需要几分钟,并且可能超时。最佳实践:
- 尽可能始终使用
start/end限定较短片段 - 对于完整视频,使用
callback_url进行异步处理 - 先在
Timeline上裁剪视频,再对较短的结果重新调整宽高比
from 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",
)
沙盒计算(自托管/开放权重模型)
通过创建沙盒并将 sandbox_id 传递给受支持的任务,即可运行开放权重模型(Gemma、Qwen、Whisper、OmniVoice、FLUX、RT-DETR)。需要 videodb>=0.5.1。
from videodb import SandboxTier, SandboxModel
# 1. Create a sandbox sized for the largest model, then wait until active.
sandbox = conn.create_sandbox(
tier=SandboxTier.medium,
models=[SandboxModel.GEMMA_4_31B.value], # exact ID, NO -FP8 suffix
)
sandbox.wait_for_ready(timeout=300, interval=5)
# 2. Understanding: set config.model + config.sandbox_id on the analyzer.
understanding = video.understand(analyzers=[{
"type": "vlm", "name": "scene",
"config": {"model": "google/gemma-4-31B-it", "sandbox_id": sandbox.id,
"prompt": "Describe the scene."},
}])
# 2b. Generation: pass model_name + sandbox_id (jobs return GenerationJob → .wait()).
response = coll.generate_text(prompt="Summarize this.", model_name="Qwen/Qwen3.5-9B",
sandbox_id=sandbox.id, max_tokens=300)
job = coll.generate_image(prompt="a city at sunset", model_name="black-forest-labs/FLUX.1-dev",
sandbox_id=sandbox.id)
image = job.wait(timeout=900, interval=5)
# 3. Stop when done — provisioning/active/alert all count toward the tier limit.
sandbox.stop(); sandbox.wait_for_stop()
模型 ID 必须与目录完全一致(不能带 -FP8 后缀),否则 create_sandbox 会引发 Unsupported sandbox model。完整的模型目录、层级、类别、定价和注意事项,请参阅 reference/sandbox.md。
错误处理
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}")
常见注意事项
| 场景 | 错误消息 | 解决方案 |
|---|---|---|
| 搜索结果没有流 URL | AttributeError: 'SearchResponse' object has no attribute 'stream_url' | search() 在 0.5.0 中返回 SearchResponse。请使用 results.compile() |
search(score_threshold=) 搜索了错误的索引 | 无错误,但结果不符合预期 | score_threshold 不会路由到旧版索引。请使用 semantic_search(score_threshold=),或对 v1 索引使用 legacy_search() |
| 在对象检测上使用语义索引 | use_for includes semantic but no scene has embeddable text | 对象产物没有顶层文本。请省略 use_for(它会自动降级),或传入 ["query", "aggregate"] |
| 为不存在的字段编制索引 | fields.filter names not present in any scene's data | 错误会列出可用字段名,请查看该列表。也可检查 index.field_schema |
| 搜索没有匹配项 | v2 返回空的 SearchResponse;只有 legacy_search() 会引发 InvalidRequestError: No results found | 检查 len(response)。仅对旧版调用使用 try/except |
对已编制索引的视频(v1)调用旧版 index_spoken_words() | Spoken word index for video already exists | 传入 force=True,或删除该调用:字幕和说明文字只需要 generate_transcript() |
| Reframe 超时 | 处理较长视频时会无限期阻塞 | 使用 start/end 限制片段,或传入 callback_url 以异步执行 |
| Timeline 上的时间戳为负数 | 会悄无声息地生成损坏的流 | 创建 VideoAsset 前务必验证 start >= 0 |
generate_video()/create_collection() 失败 | Operation not allowed 或 maximum limit | 这些功能受套餐限制,请告知用户套餐限额 |
其他文档
参考文档位于 ${CLAUDE_SKILL_DIR}/reference/。请使用此前缀读取其中的文件;以下链接均相对于此 SKILL.md。
- reference/api-reference.md - 完整的 VideoDB Python SDK API 参考
- reference/indexing.md - 了解 → 索引流水线:分析器、工件、分段、字段配置
- reference/indexing-reference.md - 分析器目录及 Understanding/Index 类参考
- reference/search.md - 检索指南:search、ask、semantic_search、query、aggregate
- reference/search-reference.md - 检索签名、筛选语法、响应对象
- reference/migration.md - v1 → v2 映射及 SDK 0.5.0 重大变更。发现 v1 代码时请阅读
- reference/editor.md - 时间线编辑工作流指南(4 层模型、使用场景、示例)
- reference/editor-reference.md - 编辑器代码参考(构造函数、参数、枚举)
- reference/streaming.md - HLS 流式传输与即时播放
- reference/generative.md - AI 媒体生成(图像、视频、音频)
- reference/sandbox.md - Sandbox Compute 工作流(运行开放权重模型:Gemma、Qwen、Whisper、OmniVoice、FLUX、RT-DETR)
- reference/sandbox-reference.md - Sandbox 代码参考(创建/获取/列出/停止、层级、模型目录、支持 Sandbox 的生成)
- reference/rtstream.md - 实时流摄取工作流(RTSP/RTMP)
- reference/rtstream-reference.md - RTStream SDK 方法及 AI 流水线
- reference/capture.md - 桌面捕获工作流
- reference/capture-reference.md - Capture SDK 和 WebSocket 事件
- reference/use-cases.md - 常见视频处理模式和示例
旧版 v1 索引与搜索。这些 API 仍然可用,且未弃用,但仅在维护现有 v1 代码时阅读:
- reference/legacy/index.md - v1 场景索引和帧提取工作流
- reference/legacy/index-reference.md - v1 场景索引代码参考(SceneCollection/Scene/Frame)
- reference/legacy/search.md - v1 语音词语和场景搜索
屏幕录制(桌面捕获)
录制期间使用 ws_listener.py 捕获 WebSocket 事件。桌面捕获支持 macOS 和 Windows。
${CLAUDE_SKILL_DIR} 是此 skill 的安装目录,由 Claude Code 设置。对于未设置该变量的 Agent,请替换为存放此 SKILL.md 的目录。
快速开始
- 启动监听器:
python "${CLAUDE_SKILL_DIR}/scripts/ws_listener.py" --cwd=<PROJECT_ROOT> & - 获取 WebSocket ID:
cat /tmp/videodb_ws_id - 运行捕获代码(完整工作流请参阅 reference/capture.md)
- 事件写入位置:
/tmp/videodb_events.jsonl
查询事件
import 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]
实用脚本
${CLAUDE_SKILL_DIR}/scripts/ws_listener.py- WebSocket 事件监听器(将数据写入 JSONL)
完整的捕获工作流请参阅 reference/capture.md。
VideoDB 支持相关操作时,请勿使用 ffmpeg、moviepy 或本地编码工具。以下操作均由 VideoDB 在服务端处理:剪辑、合并片段、叠加音频或音乐、添加字幕、文本/图像叠加、转码、更改分辨率、转换宽高比、按平台要求调整大小、转录、音量控制、淡入淡出过渡和媒体生成。仅当操作列在 reference/editor.md 的 Limitations 部分时,才使用本地工具作为后备方案(变速、裁剪/缩放、调色、关键帧动画)。
何时使用什么
| 问题 | VideoDB 解决方案 |
|---|---|
| 让视频可搜索 | video.understand(analyzers=[...]),然后执行 video.index(source=analyzer) |
| 根据说过或展示的内容查找片段 | video.search(query),或使用 semantic_search(index_names=[...]) 指定索引 |
| 回答有关视频的问题 | video.ask(question, include_sources=True) |
| 统计或分组视频中出现的内容 | video.aggregate(index_name=..., group_by=..., metric="count") |
| 根据精确的字段值筛选片段 | video.query(index_name=..., filter={...}) |
| 平台拒绝视频的宽高比或分辨率 | 使用 VideoConfig 执行 video.reframe() 或 conn.transcode() |
| 需要调整视频尺寸以适配 Twitter/Instagram/TikTok | video.reframe(target="vertical") 或 target="square" |
| 需要更改分辨率(例如从 1080p 调整为 720p) | 使用 VideoConfig(resolution=720) 执行 conn.transcode() |
| 需要在视频上叠加音频/音乐 | 在 Editor Timeline 上使用 AudioAsset,并控制音量 |
| 需要添加字幕 | 使用 video.add_subtitle(),或在 Editor Timeline 上使用 CaptionAsset |
| 需要合并/剪辑片段 | 在 Editor Timeline 上使用 VideoAsset |
| 需要将图像与旁白合成 | 在单独的 Editor 音轨上使用 ImageAsset + AudioAsset |
| 需要生成旁白、音乐或音效 | coll.generate_voice()、generate_music()、generate_sound_effect() |