Skill VideoDB
Percepção + memória + ações para vídeo, streams ao vivo e sessões de desktop.
Quando usar
Percepção de Desktop
- Iniciar/parar uma sessão de desktop capturando tela, microfone e áudio do sistema
- Transmitir contexto ao vivo e armazenar memória episódica da sessão
- Executar alertas/gatilhos em tempo real sobre o que é falado e o que acontece na tela
- Produzir resumos de sessão, uma linha do tempo pesquisável e links de evidência reproduzíveis
Ingestão + Stream de Vídeo
- Ingerir um arquivo ou URL e retornar um link de stream web reproduzível
- Transcodificar/normalizar: codec, bitrate, FPS, resolução, proporção
Indexação + Busca (timestamps + evidência)
- Construir índices visual, falado e por palavras-chave
- Pesquisar e retornar momentos exatos com timestamps e evidência reproduzível
- Criar automaticamente clipes a partir dos resultados da busca
Edição de Linha do Tempo + Geração
- Legendas: gerar, traduzir, incorporar
- Sobreposições: texto/imagem/marca, legendas animadas
- Áudio: música de fundo, narração, dublagem
- Composição programática e exportação via operações de linha do tempo
Streams ao Vivo (RTSP) + Monitoramento
- Conectar feeds RTSP/ao vivo
- Executar compreensão visual e falada em tempo real e emitir eventos/alertas para fluxos de monitoramento
Como funciona
Entradas comuns
- Caminho de arquivo local, URL pública ou URL RTSP
- Solicitação de captura de desktop: iniciar / parar / resumir sessão
- Operações desejadas: obter contexto para entendimento, especificação de transcodificação, especificação de indexação, consulta de busca, intervalos de clipe, edições de linha do tempo, regras de alerta
Saídas comuns
- URL do stream
- Resultados de busca com timestamps e links de evidência
- Ativos gerados: legendas, áudio, imagens, clipes
- Payloads de eventos/alertas para streams ao vivo
- Resumos de sessão de desktop e entradas de memória
Executando código Python
Antes de executar qualquer código VideoDB, mude para o diretório do projeto e carregue as variáveis de ambiente:
from dotenv import load_dotenv
load_dotenv(".env")
import videodb
conn = videodb.connect()
Isso lê VIDEO_DB_API_KEY de:
- Ambiente (se já exportado)
- Arquivo
.envdo projeto no diretório atual
Se a chave estiver faltando, videodb.connect() levanta AuthenticationError automaticamente.
NÃO escreva um arquivo de script quando um comando inline curto funcionar.
Ao escrever Python inline (python -c "..."), sempre use código formatado corretamente — use ponto e vírgula para separar instruções e mantenha legível. Para qualquer coisa com mais de ~3 instruções, use um heredoc:
python << 'EOF'
from dotenv import load_dotenv
load_dotenv(".env")
import videodb
conn = videodb.connect()
coll = conn.get_collection()
print(f"Vídeos: {len(coll.get_videos())}")
EOF
Configuração
Quando o usuário pedir "configure videodb" ou similar:
1. Instalar SDK
pip install "videodb[capture]" python-dotenv
Se videodb[capture] falhar no Linux, instale sem o extra de captura:
pip install videodb python-dotenv
2. Configurar chave de API
O usuário deve definir VIDEO_DB_API_KEY usando um dos métodos:
- Exportar no terminal (antes de iniciar Claude):
export VIDEO_DB_API_KEY=sua-chave - Arquivo
.envdo projeto: SalveVIDEO_DB_API_KEY=sua-chaveno arquivo.envdo projeto
Obtenha uma chave de API gratuita em console.videodb.io (50 uploads gratuitos, sem cartão de crédito).
NÃO leia, escreva ou manipule a chave de API você mesmo. Sempre deixe o usuário defini-la.
Referência Rápida
Fazer upload de mídia
# URL
video = coll.upload(url="https://exemplo.com/video.mp4")
# YouTube
video = coll.upload(url="https://www.youtube.com/watch?v=VIDEO_ID")
# Arquivo local
video = coll.upload(file_path="/caminho/para/video.mp4")
Transcrição + legenda
# force=True pula o erro se o vídeo já estiver indexado
video.index_spoken_words(force=True)
texto = video.get_transcript_text()
url_stream = video.add_subtitle()
Busca dentro de vídeos
from videodb.exceptions import InvalidRequestError
video.index_spoken_words(force=True)
# search() levanta InvalidRequestError quando nenhum resultado é encontrado.
# Sempre envolva em try/except e trate "Nenhum resultado encontrado" como vazio.
try:
resultados = video.search("demonstração de produto")
cenas = resultados.get_shots()
url_stream = resultados.compile()
except InvalidRequestError as e:
if "Nenhum resultado encontrado" in str(e):
cenas = []
else:
raise
Busca por cenas
import re
from videodb import SearchType, IndexType, SceneExtractionType
from videodb.exceptions import InvalidRequestError
# index_scenes() não tem parâmetro force — levanta erro se um índice
# de cena já existir. Extraia o ID do índice existente do erro.
try:
id_indice_cena = video.index_scenes(
extraction_type=SceneExtractionType.shot_based,
prompt="Descreva o conteúdo visual nesta cena.",
)
except Exception as e:
match = re.search(r"id\s+([a-f0-9]+)", str(e))
if match:
id_indice_cena = match.group(1)
else:
raise
# Use score_threshold para filtrar ruído de baixa relevância (recomendado: 0.3+)
try:
resultados = video.search(
query="pessoa escrevendo em um quadro branco",
search_type=SearchType.semantic,
index_type=IndexType.scene,
scene_index_id=id_indice_cena,
score_threshold=0.3,
)
cenas = resultados.get_shots()
url_stream = resultados.compile()
except InvalidRequestError as e:
if "Nenhum resultado encontrado" in str(e):
cenas = []
else:
raise
Edição de linha do tempo
Importante: Sempre valide timestamps antes de construir uma linha do tempo:
startdeve ser >= 0 (valores negativos são aceitos silenciosamente mas produzem saída quebrada)startdeve ser <endenddeve ser <=video.length
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, TextAsset, TextStyle
linha_tempo = Timeline(conn)
linha_tempo.add_inline(VideoAsset(asset_id=video.id, start=10, end=30))
linha_tempo.add_overlay(0, TextAsset(text="Fim", duracao=3, style=TextStyle(fontsize=36)))
url_stream = linha_tempo.generate_stream()
Transcodificar vídeo (mudança de resolução/qualidade)
from videodb import TranscodeMode, VideoConfig, AudioConfig
# Altere resolução, qualidade ou proporção no servidor
job_id = conn.transcode(
source="https://exemplo.com/video.mp4",
callback_url="https://exemplo.com/webhook",
mode=TranscodeMode.economy,
video_config=VideoConfig(resolution=720, quality=23, aspect_ratio="16:9"),
audio_config=AudioConfig(mute=False),
)
Redimensionar proporção (para plataformas sociais)
Aviso: reframe() é uma operação lenta no servidor. Para vídeos longos pode levar vários minutos e pode expirar. Melhores práticas:
- Sempre limite a um segmento curto usando
start/endquando possível - Para vídeos completos, use
callback_urlpara processamento assíncrono - Corte o vídeo em um
Timelineprimeiro, depois redimensione o resultado mais curto
from videodb import ReframeMode
# Sempre prefira redimensionar um segmento curto:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
# Redimensionamento assíncrono para vídeos completos (retorna None, resultado via webhook):
video.reframe(target="vertical", callback_url="https://exemplo.com/webhook")
# Predefinições: "vertical" (9:16), "square" (1:1), "landscape" (16:9)
reframed = video.reframe(start=0, end=60, target="square")
# Dimensões personalizadas
reframed = video.reframe(start=0, end=60, target={"width": 1280, "height": 720})
Geração de mídia
imagem = coll.generate_image(
prompt="um pôr do sol sobre montanhas",
aspect_ratio="16:9",
)
Tratamento de erros
from videodb.exceptions import AuthenticationError, InvalidRequestError
try:
conn = videodb.connect()
except AuthenticationError:
print("Verifique sua VIDEO_DB_API_KEY")
try:
video = coll.upload(url="https://exemplo.com/video.mp4")
except InvalidRequestError as e:
print(f"Falha no upload: {e}")
Armadilhas comuns
| Cenário | Mensagem de erro | Solução |
|---|---|---|
| Indexar vídeo já indexado | Índice de palavra falada para o vídeo já existe | Use video.index_spoken_words(force=True) para pular se já indexado |
| Índice de cena já existe | Índice de cena com id XXXX já existe | Extraia o scene_index_id existente do erro com re.search(r"id\s+([a-f0-9]+)", str(e)) |
| Busca não encontra correspondências | InvalidRequestError: Nenhum resultado encontrado | Capture a exceção e trate como resultados vazios (cenas = []) |
| Redimensionamento expira | Bloqueia indefinidamente em vídeos longos | Use start/end para limitar o segmento, ou passe callback_url para assíncrono |
| Timestamps negativos no Timeline | Produz stream quebrado silenciosamente | Sempre valide start >= 0 antes de criar VideoAsset |
generate_video() / create_collection() falha | Operação não permitida ou limite máximo | Funcionalidades limitadas por plano — informe o usuário sobre os limites do plano |
Exemplos
Prompts canônicos
- "Inicie captura de desktop e alerte quando um campo de senha aparecer."
- "Grave minha sessão e produza um resumo acionável quando terminar."
- "Ingira este arquivo e retorne um link de stream reproduzível."
- "Indexe esta pasta e encontre toda cena com pessoas, retorne timestamps."
- "Gere legendas, incorpore-as e adicione música de fundo suave."
- "Conecte esta URL RTSP e alerte quando uma pessoa entrar na zona."
Gravação de Tela (Captura de Desktop)
Use ws_listener.py para capturar eventos WebSocket durante sessões de gravação. A captura de desktop suporta apenas macOS.
Início Rápido
- Escolha o diretório de estado:
STATE_DIR="${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}" - Inicie o listener:
VIDEODB_EVENTS_DIR="$STATE_DIR" python scripts/ws_listener.py --clear "$STATE_DIR" & - Obtenha o ID WebSocket:
cat "$STATE_DIR/videodb_ws_id" - Execute o código de captura (veja reference/capture.md para o fluxo completo)
- Eventos escritos em:
$STATE_DIR/videodb_events.jsonl
Use --clear sempre que iniciar uma nova captura para que eventos de transcrição e visuais antigos não vazem para a nova sessão.
Consultar Eventos
import json
import os
import time
from pathlib import Path
dir_eventos = Path(os.environ.get("VIDEODB_EVENTS_DIR", Path.home() / ".local" / "state" / "videodb"))
arquivo_eventos = dir_eventos / "videodb_events.jsonl"
eventos = []
if arquivo_eventos.exists():
with arquivo_eventos.open(encoding="utf-8") as handle:
for linha in handle:
try:
eventos.append(json.loads(linha))
except json.JSONDecodeError:
continue
transcricoes = [e["data"]["text"] for e in eventos if e.get("channel") == "transcript"]
corte = time.time() - 300
recente_visual = [
e for e in eventos
if e.get("channel") == "visual_index" and e["unix_ts"] > corte
]
Documentação adicional
A documentação de referência está no diretório reference/ adjacente a este arquivo SKILL.md. Use a ferramenta Glob para localizá-la se necessário.
- reference/api-reference.md - Referência completa da SDK Python VideoDB
- reference/search.md - Guia aprofundado de busca em vídeo (palavra falada e baseada em cena)
- reference/editor.md - Edição de linha do tempo, ativos e composição
- reference/streaming.md - Streaming HLS e reprodução instantânea
- reference/generative.md - Geração de mídia com IA (imagens, vídeo, áudio)
- reference/rtstream.md - Fluxo de ingestão de stream ao vivo (RTSP/RTMP)
- reference/rtstream-reference.md - Métodos da SDK RTStream e pipelines de IA
- reference/capture.md - Fluxo de captura de desktop
- reference/capture-reference.md - SDK de captura e eventos WebSocket
- reference/use-cases.md - Padrões e exemplos comuns de processamento de vídeo
Não use ffmpeg, moviepy ou ferramentas de codificação local quando o VideoDB suportar a operação. Os seguintes são todos tratados no servidor pelo VideoDB: cortar, combinar clipes, sobrepor áudio ou música, adicionar legendas, sobreposições de texto/imagem, transcodificação, alterações de resolução, conversão de proporção, redimensionamento para requisitos de plataforma, transcrição e geração de mídia. Recorra a ferramentas locais apenas para operações listadas em Limitações em reference/editor.md (transições, alterações de velocidade, corte/zoom, correção de cor, mixagem de volume).
Quando usar o quê
| Problema | Solução VideoDB |
|---|---|
| Plataforma rejeita proporção ou resolução do vídeo | video.reframe() ou conn.transcode() com VideoConfig |
| Precisa redimensionar vídeo para Twitter/Instagram/TikTok | video.reframe(target="vertical") ou target="square" |
| Precisa alterar resolução (ex.: 1080p → 720p) | conn.transcode() com VideoConfig(resolution=720) |
| Precisa sobrepor áudio/música no vídeo | AudioAsset em um Timeline |
| Precisa adicionar legendas | video.add_subtitle() ou CaptionAsset |
| Precisa combinar/cortar clipes | VideoAsset em um Timeline |
| Precisa gerar narração, música ou efeitos sonoros | coll.generate_voice(), generate_music(), generate_sound_effect() |
Proveniência
O material de referência para este skill é fornecido localmente em skills/videodb/reference/.
Use as cópias locais acima em vez de seguir links de repositórios externos em tempo de execução.