Communitygithub.com

VideoDB 스킬

비디오, 라이브 스트림 및 데스크톱 세션을 보고(see), 이해하고(understand), 조작(act)할 수 있는 AI 에이전트 스킬입니다.

VideoDB 스킬란 무엇인가요?

이 스킬은 VideoDB SDK를 통해 AI 에이전트(Claude Code, Cursor 등)가 비디오 데이터에 대한 완전한 퍼셉션-메모리-액션 파이프라인을 구현할 수 있도록 지원합니다. 에이전트는 로컬 파일, URL, RTSP 라이브 피드 또는 데스크톱 화면 캡처를 통해 비디오/오디오를 입력받아 스트리밍 링크를 생성하고, 실시간 컨텍스트와 세션 메모리를 제공합니다. 또한 시각/음성/키워드 인덱스를 구축하여 타임스탬프 기반 검색과 자동 클립 생성을 수행하며, 트랜스코딩, 해상도 변경, 자막/오버레이 추가, 오디오 더빙 등 서버사이드 편집 작업을 지원합니다. 라이브 스트림 모니터링과 실시간 이벤트 알림 기능을 통해 감시 및 워크플로우 자동화에도 활용할 수 있습니다. 이 스킬은 모든 주요 운영체제에서 사용 가능하며, 로컬 인코딩 도구(ffmpeg 등) 대신 VideoDB의 서버사이드 API를 우선 사용하도록 설계되었습니다.

지원 대상✓Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/Jamkris/everything-gemini-code/tree/HEAD/skills/videodb

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

VideoDB 스킬은(는) 무엇을 하나요?

비디오, 라이브 스트림 및 데스크톱 세션을 위한 퍼셉션 + 메모리 + 액션

사용 시기

데스크톱 퍼셉션

  • 화면, 마이크, 시스템 오디오를 캡처하는 데스크톱 세션 시작/중지
  • 실시간 컨텍스트 스트리밍 및 에피소드 세션 메모리 저장
  • 화면에 표시되는 내용과 음성을 기반으로 실시간 알림/트리거 실행
  • 세션 요약, 검색 가능한 타임라인 및 재생 가능한 증거 링크 생성

비디오 수집 + 스트리밍

  • 파일 또는 URL을 수집하고 재생 가능한 웹 스트림 링크 반환
  • 트랜스코딩/정규화: 코덱, 비트레이트, FPS, 해상도, 종횡비

인덱스 + 검색 (타임스탬프 + 증거)

  • 시각, 음성, 키워드 인덱스 구축
  • 정확한 순간을 타임스탬프와 재생 가능한 증거로 검색 및 반환
  • 검색 결과에서 클립 자동 생성

타임라인 편집 + 생성

  • 자막: 생성, 번역, 자막 박기
  • 오버레이: 텍스트/이미지/브랜딩, 모션 캡션
  • 오디오: 배경 음악, 내레이션, 더빙
  • 타임라인 연산을 통한 프로그래매틱 구성 및 내보내기

라이브 스트림(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를 다음 순서로 읽습니다:

  1. 환경 변수 (이미 내보낸 경우)
  2. 현재 디렉토리의 프로젝트 .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"Videos: {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를 설정해야 합니다:

  • 터미널에서 내보내기 (Gemini 시작 전): export VIDEO_DB_API_KEY=your-key
  • 프로젝트 .env 파일: VIDEO_DB_API_KEY=your-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=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("product demo")
    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에서 비디오를 자른 다음 더 짧은 결과를 reframe하십시오
from videodb import ReframeMode

# 항상 짧은 세그먼트를 reframe하는 것을 선호하십시오:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)

# 전체 길이 비디오의 비동기 reframe (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}")

일반적인 함정

시나리오오류 메시지해결 방법
이미 인덱싱된 비디오를 다시 인덱싱Spoken word index for video already existsvideo.index_spoken_words(force=True)를 사용하여 이미 인덱싱된 경우 건너뜁니다
장면 인덱스가 이미 존재Scene index with id XXXX already existsre.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요금제에 제한된 기능 — 사용자에게 요금제 제한 정보 제공

예제

표준 프롬프트

  • "데스크톱 캡처를 시작하고 비밀번호 필드가 나타나면 알림을 보내줘."
  • "내 세션을 녹화하고 종료 시 실행 가능한 요약을 생성해줘."
  • "이 파일을 수집하고 재생 가능한 스트림 링크를 반환해줘."
  • "이 폴더를 인덱싱하고 사람이 있는 모든 장면을 찾아 타임스탬프를 반환해줘."
  • "자막을 생성하고, 박아 넣고, 가벼운 배경 음악을 추가해줘."
  • "이 RTSP URL에 연결하고 사람이 구역에 들어오면 알림을 보내줘."

화면 녹화 (데스크톱 캡처)

녹화 세션 중 WebSocket 이벤트를 캡처하려면 ws_listener.py를 사용하십시오. 데스크톱 캡처는 macOS만 지원합니다.

빠른 시작

  1. 상태 디렉토리 선택: STATE_DIR="${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}"
  2. 리스너 시작: VIDEODB_EVENTS_DIR="$STATE_DIR" python scripts/ws_listener.py --clear "$STATE_DIR" &
  3. WebSocket ID 가져오기: cat "$STATE_DIR/videodb_ws_id"
  4. 캡처 코드 실행 (전체 워크플로우는 reference/capture.md 참조)
  5. 이벤트 기록 위치: $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 도구를 사용하여 찾으십시오.

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/ 아래에 로컬로 판매(vendored)됩니다.

런타임에 외부 저장소 링크를 따르는 대신 위의 로컬 복사본을 사용하십시오.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

관련 스킬