VideoDB 스킬
비디오, 라이브 스트림, 데스크톱 세션을 위한 인지 + 메모리 + 액션.
사용 시기
데스크톱 인지
- 데스크톱 세션을 시작/중지하여 화면, 마이크, 시스템 오디오 캡처
- 실시간 컨텍스트 스트리밍 및 에피소드 세션 메모리 저장
- 화면에서 말해지는 내용과 발생하는 상황에 대한 실시간 알림/트리거 실행
- 세션 요약, 검색 가능한 타임라인 및 재생 가능한 증거 링크 생성
비디오 수집 + 스트리밍
- 파일 또는 URL을 수집하고 재생 가능한 웹 스트림 링크 반환
- 트랜스코딩/정규화: 코덱, 비트레이트, fps, 해상도, 종횡비
인덱싱 + 검색 (타임스탬프 + 증거)
- 시각적, 음성, 키워드 인덱스 구축
- 정확한 순간을 타임스탬프와 재생 가능한 증거로 검색 및 반환
- 검색 결과에서 클립 자동 생성
타임라인 편집 + 생성
- 자막: 생성, 번역, 하드코딩(burn-in)
- 오버레이: 텍스트/이미지/브랜딩, 모션 캡션
- 오디오: 배경 음악, 보이스오버, 더빙
- 타임라인 연산을 통한 프로그래매틱 합성 및 내보내기
라이브 스트림 (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
설정
사용자가 "videodb 설정" 또는 이와 유사한 요청을 하면:
1. SDK 설치
pip install "videodb[capture]" python-dotenv
Linux에서 videodb[capture]가 실패하면 capture extra 없이 설치하세요:
pip install videodb python-dotenv
2. API 키 구성
사용자는 다음 방법 중 하나로 VIDEO_DB_API_KEY를 설정해야 합니다:
- 터미널에 내보내기 (Claude 시작 전):
export VIDEO_DB_API_KEY=your-key - 프로젝트
.env파일: 프로젝트의.env파일에VIDEO_DB_API_KEY=your-key저장
무료 API 키는 console.videodb.io에서 받으세요 (50회 무료 업로드, 신용카드 불필요).
API 키를 직접 읽거나, 쓰거나, 처리하지 마세요. 항상 사용자가 설정하도록 하세요.
빠른 참조
미디어 업로드
# URL
video = coll.upload(url="https://example.com/video.mp4")
# YouTube
video = coll.upload(url="https://www.youtube.com/watch?v=VIDEO_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는 <end여야 함end는 <=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을 사용하세요 - 먼저
Timeline에서 동영상을 자른 후, 더 짧은 결과를 재구성하세요
from videodb import ReframeMode
# 항상 짧은 세그먼트를 재구성하는 것을 선호:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
# 전체 길이 동영상의 비동기 재구성 (None 반환, 웹훅을 통해 결과 수신):
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() 실패 | Operation not allowed 또는 maximum limit | 요금제 제한 기능 — 사용자에게 요금제 제한에 대해 알림 |
예제
일반적인 프롬프트
- "데스크톱 캡처를 시작하고 비밀번호 필드가 나타나면 알림을 보내."
- "내 세션을 녹화하고 종료 시 실행 가능한 요약을 생성해."
- "이 파일을 수집하고 재생 가능한 스트림 링크를 반환해."
- "이 폴더를 인덱싱하고 사람이 있는 모든 장면을 찾아 타임스탬프를 반환해."
- "자막을 생성하고 하드코딩한 후, 가벼운 배경 음악을 추가해."
- "이 RTSP URL을 연결하고 사람이 구역에 들어오면 알림을 보내."
화면 녹화 (데스크톱 캡처)
녹화 세션 중 WebSocket 이벤트를 캡처하려면 ws_listener.py를 사용하세요. 데스크톱 캡처는 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() 사용 |
| 비디오에 오디오/음악 오버레이 필요 | Timeline의 AudioAsset 사용 |
| 자막 추가 필요 | video.add_subtitle() 또는 CaptionAsset 사용 |
| 클립 결합/트리밍 필요 | Timeline의 VideoAsset 사용 |
| 보이스오버, 음악 또는 SFX 생성 필요 | coll.generate_voice(), generate_music(), generate_sound_effect() 사용 |
출처
이 스킬의 참조 자료는 skills/videodb/reference/ 아래에 로컬로 제공됩니다. 런타임에 외부 저장소 링크를 따르지 말고 위의 로컬 복사본을 사용하세요.