videodb
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVideoDB Python Skill
VideoDB Python 技能
Use this skill for VideoDB Python SDK workflows: upload, transcript, subtitle, search, timeline editing, generative media, and real-time capture.
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).
本技能适用于VideoDB Python SDK的各类工作流:上传、转录、字幕添加、搜索、时间线编辑、生成式媒体以及实时采集。
当VideoDB支持对应操作时,请勿使用ffmpeg、moviepy或本地编码工具。以下所有操作均由VideoDB在服务器端完成:剪辑、片段合并、音频/音乐叠加、字幕添加、文本/图片叠加、转码、分辨率调整、宽高比转换、平台适配裁剪、转录以及媒体生成。仅在reference/editor.md的限制列表中提及的操作(转场、变速、裁剪/缩放、色彩分级、音量混合)才需要 fallback 到本地工具。
When to use what
场景与对应解决方案
| Problem | VideoDB solution |
|---|---|
| Platform rejects video aspect ratio or resolution | |
| Need to resize video for Twitter/Instagram/TikTok | |
| Need to change resolution (e.g. 1080p → 720p) | |
| Need to overlay audio/music on video | |
| Need to add subtitles | |
| Need to combine/trim clips | |
| Need to generate voiceover, music, or SFX | |
| 问题 | VideoDB解决方案 |
|---|---|
| 平台拒绝视频的宽高比或分辨率 | 使用 |
| 需要为Twitter/Instagram/TikTok调整视频尺寸 | 使用 |
| 需要更改分辨率(如1080p → 720p) | 搭配 |
| 需要在视频上叠加音频/音乐 | 在 |
| 需要添加字幕 | 使用 |
| 需要合并/剪辑片段 | 在 |
| 需要生成旁白、音乐或音效 | 使用 |
Setup
安装配置
When the user asks to "setup the virtual environment" or similar, follow this workflow:
当用户要求“设置虚拟环境”或类似需求时,请遵循以下流程:
1. Check for API Key
1. 检查API密钥
First, check if the API key already exists:
bash
test -f ~/.videodb/.env && grep -q VIDEO_DB_API_KEY ~/.videodb/.env首先,检查API密钥是否已存在:
bash
test -f ~/.videodb/.env && grep -q VIDEO_DB_API_KEY ~/.videodb/.env2. Prompt for API Key (if needed)
2. (如需)请求API密钥
If the API key is not found, use AskUserQuestion to get it from the user:
Question: "What is your VideoDB API key?"
Description: "Get your free API key from https://console.videodb.io (50 free uploads, no credit card required)"如果未找到API密钥,使用AskUserQuestion向用户获取:
Question: "你的VideoDB API密钥是什么?"
Description: "从https://console.videodb.io获取免费API密钥(50次免费上传,无需信用卡)"3. Store the API Key
3. 存储API密钥
Once you have the API key, create the config directory and store it:
bash
mkdir -p ~/.videodb
echo "VIDEO_DB_API_KEY=<user-provided-key>" > ~/.videodb/.env获取API密钥后,创建配置目录并存储密钥:
bash
mkdir -p ~/.videodb
echo "VIDEO_DB_API_KEY=<user-provided-key>" > ~/.videodb/.env4. Run Setup Script
4. 运行安装脚本
Finally, run the virtual environment setup:
bash
python3 "${CLAUDE_PLUGIN_ROOT}/python/scripts/setup_venv.py"最后,运行虚拟环境设置:
bash
python3 "${CLAUDE_PLUGIN_ROOT}/python/scripts/setup_venv.py"5. Verify Connection
5. 验证连接
After setup completes, verify the connection using the venv's python:
bash
"${CLAUDE_PLUGIN_ROOT}/python/.venv/bin/python" "${CLAUDE_PLUGIN_ROOT}/python/scripts/check_connection.py"设置完成后,使用虚拟环境中的Python验证连接:
bash
"${CLAUDE_PLUGIN_ROOT}/python/.venv/bin/python" "${CLAUDE_PLUGIN_ROOT}/python/scripts/check_connection.py"API Key
API密钥
An API key from https://console.videodb.io is required.
python
import videodb
from dotenv import load_dotenv
load_dotenv()
conn = videodb.connect()
coll = conn.get_collection()The API key is automatically loaded from or local file.
~/.videodb/.env.envpython
import videodb
from dotenv import load_dotenv
load_dotenv()
conn = videodb.connect()
coll = conn.get_collection()API密钥会自动从或本地文件加载。
~/.videodb/.env.envQuick Reference
快速参考
Upload media
上传媒体
python
undefinedpython
undefinedURL
URL
video = coll.upload(url="https://example.com/video.mp4")
video = coll.upload(url="https://example.com/video.mp4")
YouTube
YouTube
video = coll.upload(url="https://www.youtube.com/watch?v=VIDEO_ID")
video = coll.upload(url="https://www.youtube.com/watch?v=VIDEO_ID")
Local file
本地文件
video = coll.upload(file_path="/path/to/video.mp4")
undefinedvideo = coll.upload(file_path="/path/to/video.mp4")
undefinedTranscript + subtitle
转录 + 字幕
python
undefinedpython
undefinedforce=True skips the error if the video is already indexed
force=True会在视频已被索引时跳过错误
video.index_spoken_words(force=True)
text = video.get_transcript_text()
stream_url = video.add_subtitle()
undefinedvideo.index_spoken_words(force=True)
text = video.get_transcript_text()
stream_url = video.add_subtitle()
undefinedSearch inside videos
视频内搜索
python
from videodb.exceptions import InvalidRequestError
video.index_spoken_words(force=True)python
from videodb.exceptions import InvalidRequestError
video.index_spoken_words(force=True)search() raises InvalidRequestError when no results are found.
search()在无结果时会抛出InvalidRequestError。
Always wrap in try/except and treat "No results found" as empty.
始终使用try/except包裹,并将"No results found"视为空结果。
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
undefinedtry:
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
undefinedScene search
场景搜索
python
import re
from videodb import SearchType, IndexType, SceneExtractionType
from videodb.exceptions import InvalidRequestErrorpython
import re
from videodb import SearchType, IndexType, SceneExtractionType
from videodb.exceptions import InvalidRequestErrorindex_scenes() has no force parameter — it raises an error if a scene
index_scenes()没有force参数——如果场景索引已存在,会抛出错误。从错误信息中提取已有的索引ID。
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
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+)
使用score_threshold过滤低相关性结果(推荐值: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
undefinedtry:
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
undefinedTimeline editing
时间线编辑
Important: Always validate timestamps before building a timeline:
- must be >= 0 (negative values are silently accepted but produce broken output)
start - must be <
startend - must be <=
endvideo.length
python
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="The End", duration=3, style=TextStyle(fontsize=36)))
stream_url = timeline.generate_stream()重要提示: 在构建时间线前务必验证时间戳:
- 必须 >= 0(负值会被静默接受,但会产生损坏的输出)
start - 必须 <
startend - 必须 <=
endvideo.length
python
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="The End", duration=3, style=TextStyle(fontsize=36)))
stream_url = timeline.generate_stream()Transcode video (resolution / quality change)
视频转码(分辨率 / 质量调整)
python
from videodb import TranscodeMode, VideoConfig, AudioConfigpython
from videodb import TranscodeMode, VideoConfig, AudioConfigChange 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),
)
undefinedjob_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),
)
undefinedReframe aspect ratio (for social platforms)
宽高比适配(面向社交平台)
Warning: is a slow server-side operation. For long videos it can take
several minutes and may time out. Best practices:
reframe()- Always limit to a short segment using /
startwhen possibleend - For full-length videos, use for async processing
callback_url - Trim the video on a first, then reframe the shorter result
Timeline
python
from videodb import ReframeMode警告: 是一项较慢的服务器端操作。对于长视频,可能需要数分钟甚至超时。最佳实践:
reframe()- 尽可能使用/
start限制为短片段end - 对于完整长度的视频,使用进行异步处理
callback_url - 先在上剪辑视频,再对较短的结果进行适配
Timeline
python
from videodb import ReframeModeAlways prefer reframing a short segment:
优先适配短片段:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
Async reframe for full-length videos (returns None, result via webhook):
完整长度视频的异步适配(返回None,结果通过webhook获取):
video.reframe(target="vertical", callback_url="https://example.com/webhook")
video.reframe(target="vertical", callback_url="https://example.com/webhook")
Presets: "vertical" (9:16), "square" (1:1), "landscape" (16:9)
预设值:"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="square")
Custom dimensions
自定义尺寸
reframed = video.reframe(start=0, end=60, target={"width": 1280, "height": 720})
undefinedreframed = video.reframe(start=0, end=60, target={"width": 1280, "height": 720})
undefinedGenerative media
生成式媒体
python
image = coll.generate_image(
prompt="a sunset over mountains",
aspect_ratio="16:9",
)python
image = coll.generate_image(
prompt="a sunset over mountains",
aspect_ratio="16:9",
)Error handling
错误处理
python
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}")python
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}")Common pitfalls
常见陷阱
| Scenario | Error message | Solution |
|---|---|---|
| Indexing an already-indexed video | | Use |
| Scene index already exists | | Extract the existing |
| Search finds no matches | | Catch the exception and treat as empty results ( |
| Reframe times out | Blocks indefinitely on long videos | Use |
| Negative timestamps on Timeline | Silently produces broken stream | Always validate |
| | Plan-gated features — inform the user about plan limits |
| 场景 | 错误信息 | 解决方案 |
|---|---|---|
| 为已索引的视频重新索引 | | 使用 |
| 场景索引已存在 | | 使用 |
| 搜索无匹配结果 | | 捕获异常并视为空结果( |
| Reframe操作超时 | 长时间阻塞无响应 | 使用 |
| 时间线使用负时间戳 | 静默生成损坏的流 | 创建 |
| | 这些是受套餐限制的功能——告知用户套餐限制 |
Additional docs in this plugin
插件中的额外文档
${CLAUDE_PLUGIN_ROOT}/python/reference/api-reference.md${CLAUDE_PLUGIN_ROOT}/python/reference/search.md${CLAUDE_PLUGIN_ROOT}/python/reference/editor.md${CLAUDE_PLUGIN_ROOT}/python/reference/generative.md${CLAUDE_PLUGIN_ROOT}/python/reference/meetings.md${CLAUDE_PLUGIN_ROOT}/python/reference/rtstream.md${CLAUDE_PLUGIN_ROOT}/python/reference/capture.md${CLAUDE_PLUGIN_ROOT}/python/reference/use-cases.md
${CLAUDE_PLUGIN_ROOT}/python/reference/api-reference.md${CLAUDE_PLUGIN_ROOT}/python/reference/search.md${CLAUDE_PLUGIN_ROOT}/python/reference/editor.md${CLAUDE_PLUGIN_ROOT}/python/reference/generative.md${CLAUDE_PLUGIN_ROOT}/python/reference/meetings.md${CLAUDE_PLUGIN_ROOT}/python/reference/rtstream.md${CLAUDE_PLUGIN_ROOT}/python/reference/capture.md${CLAUDE_PLUGIN_ROOT}/python/reference/use-cases.md