videodb

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

VideoDB 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

场景与对应解决方案

ProblemVideoDB 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()
问题VideoDB解决方案
平台拒绝视频的宽高比或分辨率使用
video.reframe()
或搭配
VideoConfig
conn.transcode()
需要为Twitter/Instagram/TikTok调整视频尺寸使用
video.reframe(target="vertical")
target="square"
需要更改分辨率(如1080p → 720p)搭配
VideoConfig(resolution=720)
conn.transcode()
需要在视频上叠加音频/音乐
Timeline
中使用
AudioAsset
需要添加字幕使用
video.add_subtitle()
CaptionAsset
需要合并/剪辑片段
Timeline
中使用
VideoAsset
需要生成旁白、音乐或音效使用
coll.generate_voice()
generate_music()
generate_sound_effect()

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/.env

2. 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/.env

4. 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
~/.videodb/.env
or local
.env
file.
python
import videodb
from dotenv import load_dotenv

load_dotenv()
conn = videodb.connect()
coll = conn.get_collection()
API密钥会自动从
~/.videodb/.env
或本地
.env
文件加载。

Quick Reference

快速参考

Upload media

上传媒体

python
undefined
python
undefined

URL

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")
undefined
video = coll.upload(file_path="/path/to/video.mp4")
undefined

Transcript + subtitle

转录 + 字幕

python
undefined
python
undefined

force=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()
undefined
video.index_spoken_words(force=True) text = video.get_transcript_text() stream_url = video.add_subtitle()
undefined

Search 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
undefined
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
undefined

Scene search

场景搜索

python
import re
from videodb import SearchType, IndexType, SceneExtractionType
from videodb.exceptions import InvalidRequestError
python
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_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
undefined
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
undefined

Timeline editing

时间线编辑

Important: Always validate timestamps before building a timeline:
  • start
    must be >= 0 (negative values are silently accepted but produce broken output)
  • start
    must be <
    end
  • end
    must be <=
    video.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()
重要提示: 在构建时间线前务必验证时间戳:
  • start
    必须 >= 0(负值会被静默接受,但会产生损坏的输出)
  • start
    必须 <
    end
  • end
    必须 <=
    video.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, AudioConfig
python
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), )
undefined
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), )
undefined

Reframe aspect ratio (for social platforms)

宽高比适配(面向社交平台)

Warning:
reframe()
is a slow server-side operation. For long videos it can take several minutes and may time out. Best practices:
  • Always limit to a short segment using
    start
    /
    end
    when possible
  • For full-length videos, use
    callback_url
    for async processing
  • Trim the video on a
    Timeline
    first, then reframe the shorter result
python
from videodb import ReframeMode
警告:
reframe()
是一项较慢的服务器端操作。对于长视频,可能需要数分钟甚至超时。最佳实践:
  • 尽可能使用
    start
    /
    end
    限制为短片段
  • 对于完整长度的视频,使用
    callback_url
    进行异步处理
  • 先在
    Timeline
    上剪辑视频,再对较短的结果进行适配
python
from videodb import ReframeMode

Always 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})
undefined
reframed = video.reframe(start=0, end=60, target={"width": 1280, "height": 720})
undefined

Generative 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

常见陷阱

ScenarioError messageSolution
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 outBlocks indefinitely on long videosUse
start
/
end
to limit segment, or pass
callback_url
for async
Negative timestamps on TimelineSilently produces broken streamAlways 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
场景错误信息解决方案
为已索引的视频重新索引
Spoken word index for video already exists
使用
video.index_spoken_words(force=True)
跳过已索引的视频
场景索引已存在
Scene index with id XXXX already exists
使用
re.search(r"id\s+([a-f0-9]+)", str(e))
从错误信息中提取已有的
scene_index_id
搜索无匹配结果
InvalidRequestError: No results found
捕获异常并视为空结果(
shots = []
Reframe操作超时长时间阻塞无响应使用
start
/
end
限制片段,或传入
callback_url
进行异步处理
时间线使用负时间戳静默生成损坏的流创建
VideoAsset
前务必验证
start >= 0
generate_video()
/
create_collection()
失败
Operation not allowed
maximum limit
这些是受套餐限制的功能——告知用户套餐限制

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