VideoDB 技能
感知 + 记忆 + 动作:面向视频、直播流和桌面会话。
何时使用
桌面感知
- 启动/停止一个捕获屏幕、麦克风和系统音频的桌面会话
- 流式传输实时上下文并存储情景会话记忆
- 针对语音内容和屏幕事件运行实时告警/触发器
- 生成会话摘要、可搜索的时间线以及可播放的证据链接
视频摄取 + 流
- 摄取文件或 URL 并返回一个可播放的网页流链接
- 转码/归一化:编码、比特率、帧率、分辨率、宽高比
索引 + 搜索(时间戳 + 证据)
- 构建视觉、语音和关键词索引
- 搜索并返回带有时间戳和可播证据的精确时刻
- 根据搜索结果自动创建剪辑
时间线编辑 + 生成
- 字幕:生成、翻译、烧录
- 叠加:文本/图片/品牌、动态字幕
- 音频:背景音乐、画外音、配音
- 通过时间线操作进行程序化合成与导出
直播流 (RTSP) + 监控
- 连接 RTSP/直播源
- 运行实时视觉和语音理解并发射事件/告警以用于监控工作流
工作原理
常见输入
- 本地文件路径、公共 URL 或 RTSP URL
- 桌面捕获请求:开始 / 停止 / 总结会话
- 所需操作:获取理解上下文、转码规格、索引规格、搜索查询、剪辑范围、时间线编辑、告警规则
常见输出
- 流 URL
- 带时间戳和证据链接的搜索结果
- 生成的资产:字幕、音频、图片、剪辑
- 针对直播流的事件/告警负载
- 桌面会话摘要和记忆条目
运行 Python 代码
在运行任何 VideoDB 代码之前,切换到项目目录并加载环境变量:
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"视频数: {len(coll.get_videos())}")
EOF
设置
当用户要求“setup videodb”或类似操作时:
1. 安装 SDK
pip install "videodb[capture]" python-dotenv
如果在 Linux 上 videodb[capture] 失败,请不安装 capture 额外依赖:
pip install videodb python-dotenv
2. 配置 API 密钥
用户必须使用任一方法设置 VIDEO_DB_API_KEY:
- 在终端中导出(在启动 Claude 之前):
export VIDEO_DB_API_KEY=你的密钥 - 项目
.env文件:将VIDEO_DB_API_KEY=你的密钥保存在项目的.env文件中
在 console.videodb.io 获取免费 API 密钥(50 次免费上传,无需信用卡)。
不要自己读取、写入或处理 API 密钥。始终让用户自行设置。
快速参考
上传媒体
# URL
video = coll.upload(url="https://example.com/video.mp4")
# YouTube
video = coll.upload(url="https://www.youtube.com/watch?v=视频ID")
# 本地文件
video = coll.upload(file_path="/path/to/video.mp4")
转录 + 字幕
# force=True 可在视频已索引时跳过错误
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() 在无结果时抛出 InvalidRequestError。
# 始终包裹在 try/except 中,并将“无结果”视为空结果。
try:
results = video.search("产品演示")
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() 没有 force 参数——如果场景索引已存在则会抛出错误。
# 从错误中提取已有的索引 ID。
try:
scene_index_id = video.index_scenes(
extraction_type=SceneExtractionType.shot_based,
prompt="描述此场景中的视觉内容。",
)
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
# 使用 score_threshold 过滤低相关性噪声(推荐:0.3 以上)
try:
results = video.search(
query="人在白板上写字",
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
时间线编辑
重要: 在构建时间线之前始终验证时间戳:
start必须 >= 0(负值会被静默接受但产生损坏输出)start必须 <endend必须 <=video.length
from 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="结束", duration=3, style=TextStyle(fontsize=36)))
stream_url = timeline.generate_stream()
转码视频(分辨率/质量更改)
from videodb import TranscodeMode, VideoConfig, AudioConfig
# 在服务端更改分辨率、质量或宽高比
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进行异步处理 - 先在时间线上修剪视频,然后对较短的结果进行重构
from videodb import ReframeMode
# 始终优先重构短片段:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
# 对全长视频的异步重构(返回 None,结果通过 webhook 返回):
video.reframe(target="vertical", callback_url="https://example.com/webhook")
# 预设:"vertical" (9:16), "square" (1:1), "landscape" (16:9)
reframed = video.reframe(start=0, end=60, target="square")
# 自定义尺寸
reframed = video.reframe(start=0, end=60, target={"width": 1280, "height": 720})
生成式媒体
image = coll.generate_image(
prompt="群山上的日落",
aspect_ratio="16:9",
)
错误处理
from videodb.exceptions import AuthenticationError, InvalidRequestError
try:
conn = videodb.connect()
except AuthenticationError:
print("请检查你的 VIDEO_DB_API_KEY")
try:
video = coll.upload(url="https://example.com/video.mp4")
except InvalidRequestError as e:
print(f"上传失败: {e}")
常见陷阱
| 场景 | 错误消息 | 解决方案 |
|---|---|---|
| 索引已索引过的视频 | 视频的语音词索引已存在 | 使用 video.index_spoken_words(force=True) 在已索引时跳过 |
| 场景索引已存在 | ID 为 XXXX 的场景索引已存在 | 使用 re.search(r"id\s+([a-f0-9]+)", str(e)) 从错误中提取已有的 scene_index_id |
| 搜索无匹配 | InvalidRequestError: No results found | 捕获异常并视为空结果 (shots = []) |
| 重构超时 | 长视频上无限阻塞 | 使用 start/end 限制片段,或传递 callback_url 进行异步 |
| 时间线上负时间戳 | 静默产生损坏流 | 在创建 VideoAsset 之前始终验证 start >= 0 |
generate_video() / create_collection() 失败 | 操作不允许 或 达到上限 | 计划限制的功能——告知用户计划限制 |
示例
典型提示
- "启动桌面捕获,并在密码字段出现时发出告警。"
- "录制我的会话,并在结束时生成一个可操作的摘要。"
- "摄取此文件并返回一个可播放的流链接。"
- "索引此文件夹并找到所有有人的场景,返回时间戳。"
- "生成字幕,烧录进去,并添加轻背景音乐。"
- "连接此 RTSP URL,并在有人进入区域时发出告警。"
屏幕录制(桌面捕获)
使用 ws_listener.py 在录制会话期间捕获 WebSocket 事件。桌面捕获仅支持 macOS。
快速开始
- 选择状态目录:
STATE_DIR="${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}" - 启动监听器:
VIDEODB_EVENTS_DIR="$STATE_DIR" python scripts/ws_listener.py --clear "$STATE_DIR" & - 获取 WebSocket ID:
cat "$STATE_DIR/videodb_ws_id" - 运行捕获代码(参见 reference/capture.md 获取完整工作流)
- 事件写入至:
$STATE_DIR/videodb_events.jsonl
每次开始新的捕获运行时使用 --clear,这样旧的转录和视觉事件就不会泄漏到新会话中。
查询事件
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
]
附加文档
参考文档位于此 SKILL.md 文件旁的 reference/ 目录中。如有需要,可以使用 Glob 工具定位它。
- reference/api-reference.md - 完整的 VideoDB Python SDK API 参考
- reference/search.md - 视频搜索(语音词和场景)的深入指南
- reference/editor.md - 时间线编辑、资产和合成
- reference/streaming.md - HLS 流媒体和即时回放
- reference/generative.md - AI 驱动的媒体生成(图片、视频、音频)
- reference/rtstream.md - 直播流摄取工作流(RTSP/RTMP)
- reference/rtstream-reference.md - RTStream SDK 方法和 AI 管道
- reference/capture.md - 桌面捕获工作流
- reference/capture-reference.md - 捕获 SDK 和 WebSocket 事件
- reference/use-cases.md - 常见视频处理模式和示例
当 VideoDB 支持某个操作时,不要使用 ffmpeg、moviepy 或本地编码工具。以下所有操作都由 VideoDB 在服务端处理——修剪、组合剪辑、叠加音频或音乐、添加字幕、文本/图片叠加、转码、分辨率更改、宽高比转换、为平台要求调整大小、转录音频和媒体生成。仅当操作在 reference/editor.md 的“限制”部分中列出时(转场、速度变化、裁剪/缩放、调色、音量混音),才回退到本地工具。
何时使用什么
| 问题 | VideoDB 解决方案 |
|---|---|
| 平台拒绝视频的宽高比或分辨率 | video.reframe() 或使用 VideoConfig 的 conn.transcode() |
| 需要为 Twitter/Instagram/TikTok 调整视频大小 | video.reframe(target="vertical") 或 target="square" |
| 需要更改分辨率(例如 1080p → 720p) | 使用 VideoConfig(resolution=720) 的 conn.transcode() |
| 需要叠加音频/音乐到视频上 | 在时间线上使用 AudioAsset |
| 需要添加字幕 | video.add_subtitle() 或 CaptionAsset |
| 需要组合/修剪剪辑 | 在时间线上使用 VideoAsset |
| 需要生成画外音、音乐或音效 | coll.generate_voice()、generate_music()、generate_sound_effect() |
来源
此技能的参考材料位于本地 skills/videodb/reference/ 目录下。
请在运行时使用上述本地副本,而不是跟随外部仓库链接。