docs: fix zh-CN parity — add 44 missing files to ja-JP

Add files present in zh-CN but missing from ja-JP:
- commands: claw, context-budget, devfleet, docs, projects, prompt-optimize, rules-distill (7 files)
- skills: regex-vs-llm-structured-text, remotion-video-creation, repo-scan, research-ops,
  returns-reverse-logistics, rules-distill, rust-patterns, rust-testing, skill-comply,
  skill-stocktake, social-graph-ranker, swift-actor-persistence, swift-concurrency-6-2,
  swift-protocol-di-testing, swiftui-patterns, team-builder, terminal-ops, token-budget-advisor,
  ui-demo, unified-notifications-ops, video-editing, videodb (+reference/*), visa-doc-translate,
  workspace-surface-audit, x-api (37 files)

Result: ja-JP now has 517 files vs zh-CN 412 files.
zh-CN parity: 0 missing files (complete parity achieved).
This commit is contained in:
Claude
2026-05-17 08:51:06 +09:00
committed by Affaan Mustafa
parent 5a5a47e710
commit d66b5fa480
44 changed files with 9336 additions and 0 deletions

View File

@@ -0,0 +1,550 @@
# 完全APIリファレンス
VideoDBスキルの参考資料。使用ガイドとワークフロー選択については、[../SKILL.md](../SKILL.md) から始めること。
## 接続
```python
import videodb
conn = videodb.connect(
api_key="your-api-key", # or set VIDEO_DB_API_KEY env var
base_url=None, # custom API endpoint (optional)
)
```
**戻り値:** `Connection` オブジェクト
### 接続メソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `conn.get_collection(collection_id="default")` | `Collection` | コレクションを取得するIDなしの場合はデフォルトコレクションを取得 |
| `conn.get_collections()` | `list[Collection]` | すべてのコレクションを一覧表示する |
| `conn.create_collection(name, description, is_public=False)` | `Collection` | 新しいコレクションを作成する |
| `conn.update_collection(id, name, description)` | `Collection` | コレクションを更新する |
| `conn.check_usage()` | `dict` | アカウントの使用状況統計を取得する |
| `conn.upload(source, media_type, name, ...)` | `Video\|Audio\|Image` | デフォルトコレクションにアップロードする |
| `conn.record_meeting(meeting_url, bot_name, ...)` | `Meeting` | ミーティングを録画する |
| `conn.create_capture_session(...)` | `CaptureSession` | キャプチャセッションを作成する([capture-reference.md](capture-reference.md)参照) |
| `conn.youtube_search(query, result_threshold, duration)` | `list[dict]` | YouTubeを検索する |
| `conn.transcode(source, callback_url, mode, ...)` | `str` | ビデオをトランスコードするジョブIDを返す |
| `conn.get_transcode_details(job_id)` | `dict` | トランスコードジョブの状態と詳細を取得する |
| `conn.connect_websocket(collection_id)` | `WebSocketConnection` | WebSocketに接続する[capture-reference.md](capture-reference.md)参照) |
### トランスコード
カスタム解像度、品質、オーディオ設定でURLからビデオをトランスコードする。処理はサーバーサイドで行われる——ローカルのffmpegは不要。
```python
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),
audio_config=AudioConfig(mute=False),
)
```
#### transcodeのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `source` | `str` | 必須 | トランスコードするビデオURLダウンロード可能なURLが望ましい |
| `callback_url` | `str` | 必須 | トランスコード完了時にコールバックを受信するURL |
| `mode` | `TranscodeMode` | `TranscodeMode.economy` | トランスコード速度:`economy` または `lightning` |
| `video_config` | `VideoConfig` | `VideoConfig()` | ビデオエンコード設定 |
| `audio_config` | `AudioConfig` | `AudioConfig()` | オーディオエンコード設定 |
ジョブID (`str`) を返す。`conn.get_transcode_details(job_id)` を使用してジョブの状態を確認する。
```python
details = conn.get_transcode_details(job_id)
```
#### VideoConfig
```python
from videodb import VideoConfig, ResizeMode
config = VideoConfig(
resolution=720, # Target resolution height (e.g. 480, 720, 1080)
quality=23, # Encoding quality (lower = better, default 23)
framerate=30, # Target framerate
aspect_ratio="16:9", # Target aspect ratio
resize_mode=ResizeMode.crop, # How to fit: crop, fit, or pad
)
```
| フィールド | 型 | デフォルト | 説明 |
|-------|------|---------|-------------|
| `resolution` | `int\|None` | `None` | ターゲット解像度の高さ(ピクセル) |
| `quality` | `int` | `23` | エンコード品質(低いほど高品質) |
| `framerate` | `int\|None` | `None` | ターゲットフレームレート |
| `aspect_ratio` | `str\|None` | `None` | ターゲットアスペクト比(例:`"16:9"`, `"9:16"` |
| `resize_mode` | `str` | `ResizeMode.crop` | リサイズ戦略:`crop`, `fit`, または `pad` |
#### AudioConfig
```python
from videodb import AudioConfig
config = AudioConfig(mute=False)
```
| フィールド | 型 | デフォルト | 説明 |
|-------|------|---------|-------------|
| `mute` | `bool` | `False` | オーディオトラックをミュートする |
## コレクション
```python
coll = conn.get_collection()
```
### コレクションメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `coll.get_videos()` | `list[Video]` | すべてのビデオを一覧表示する |
| `coll.get_video(video_id)` | `Video` | 特定のビデオを取得する |
| `coll.get_audios()` | `list[Audio]` | すべてのオーディオを一覧表示する |
| `coll.get_audio(audio_id)` | `Audio` | 特定のオーディオを取得する |
| `coll.get_images()` | `list[Image]` | すべての画像を一覧表示する |
| `coll.get_image(image_id)` | `Image` | 特定の画像を取得する |
| `coll.upload(url=None, file_path=None, media_type=None, name=None)` | `Video\|Audio\|Image` | メディアをアップロードする |
| `coll.search(query, search_type, index_type, score_threshold, namespace, scene_index_id, ...)` | `SearchResult` | コレクション内を検索する(セマンティック検索のみ;キーワードとシーン検索は `NotImplementedError` を発生させる) |
| `coll.generate_image(prompt, aspect_ratio="1:1")` | `Image` | AIで画像を生成する |
| `coll.generate_video(prompt, duration=5)` | `Video` | AIでビデオを生成する |
| `coll.generate_music(prompt, duration=5)` | `Audio` | AIで音楽を生成する |
| `coll.generate_sound_effect(prompt, duration=2)` | `Audio` | 効果音を生成する |
| `coll.generate_voice(text, voice_name="Default")` | `Audio` | テキストから音声を生成する |
| `coll.generate_text(prompt, model_name="basic", response_type="text")` | `dict` | LLMテキスト生成——`["output"]` で結果にアクセス |
| `coll.dub_video(video_id, language_code)` | `Video` | ビデオを別の言語に吹き替える |
| `coll.record_meeting(meeting_url, bot_name, ...)` | `Meeting` | ライブミーティングを録画する |
| `coll.create_capture_session(...)` | `CaptureSession` | キャプチャセッションを作成する([capture-reference.md](capture-reference.md)参照) |
| `coll.get_capture_session(...)` | `CaptureSession` | キャプチャセッションを取得する([capture-reference.md](capture-reference.md)参照) |
| `coll.connect_rtstream(url, name, ...)` | `RTStream` | ライブストリームに接続する([rtstream-reference.md](rtstream-reference.md)参照) |
| `coll.make_public()` | `None` | コレクションを公開にする |
| `coll.make_private()` | `None` | コレクションを非公開にする |
| `coll.delete_video(video_id)` | `None` | ビデオを削除する |
| `coll.delete_audio(audio_id)` | `None` | オーディオを削除する |
| `coll.delete_image(image_id)` | `None` | 画像を削除する |
| `coll.delete()` | `None` | コレクションを削除する |
### アップロードのパラメータ
```python
video = coll.upload(
url=None, # Remote URL (HTTP, YouTube)
file_path=None, # Local file path
media_type=None, # "video", "audio", or "image" (auto-detected if omitted)
name=None, # Custom name for the media
description=None, # Description
callback_url=None, # Webhook URL for async notification
)
```
## ビデオオブジェクト
```python
video = coll.get_video(video_id)
```
### ビデオ属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `video.id` | `str` | 一意のビデオID |
| `video.collection_id` | `str` | 親コレクションID |
| `video.name` | `str` | ビデオ名 |
| `video.description` | `str` | ビデオの説明 |
| `video.length` | `float` | 長さ(秒) |
| `video.stream_url` | `str` | デフォルトのストリームURL |
| `video.player_url` | `str` | プレーヤー埋め込みURL |
| `video.thumbnail_url` | `str` | サムネイルURL |
### ビデオメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `video.generate_stream(timeline=None)` | `str` | ストリームURLを生成するオプションの `[(start, end)]` タプルタイムライン) |
| `video.play()` | `str` | ブラウザでストリームを開き、プレーヤーURLを返す |
| `video.index_spoken_words(language_code=None, force=False)` | `None` | 音声検索用にインデックスを作成する。既にインデックス済みの場合は `force=True` でスキップ。 |
| `video.index_scenes(extraction_type, prompt, extraction_config, metadata, model_name, name, scenes, callback_url)` | `str` | ビジュアルシーンをインデックス化するscene\_index\_idを返す |
| `video.index_visuals(prompt, batch_config, ...)` | `str` | ビジュアルコンテンツをインデックス化するscene\_index\_idを返す |
| `video.index_audio(prompt, model_name, ...)` | `str` | LLMを使用してオーディオをインデックス化するscene\_index\_idを返す |
| `video.get_transcript(start=None, end=None)` | `list[dict]` | タイムスタンプ付きのトランスクリプトを取得する |
| `video.get_transcript_text(start=None, end=None)` | `str` | 完全なトランスクリプトテキストを取得する |
| `video.generate_transcript(force=None)` | `dict` | トランスクリプトを生成する |
| `video.translate_transcript(language, additional_notes)` | `list[dict]` | トランスクリプトを翻訳する |
| `video.search(query, search_type, index_type, filter, **kwargs)` | `SearchResult` | ビデオ内を検索する |
| `video.add_subtitle(style=SubtitleStyle())` | `str` | 字幕を追加するストリームURLを返す |
| `video.generate_thumbnail(time=None)` | `str\|Image` | サムネイルを生成する |
| `video.get_thumbnails()` | `list[Image]` | すべてのサムネイルを取得する |
| `video.extract_scenes(extraction_type, extraction_config)` | `SceneCollection` | シーンを抽出する |
| `video.reframe(start, end, target, mode, callback_url)` | `Video\|None` | ビデオのアスペクト比を調整する |
| `video.clip(prompt, content_type, model_name)` | `str` | プロンプトに基づいてクリップを生成するストリームURLを返す |
| `video.insert_video(video, timestamp)` | `str` | タイムスタンプにビデオを挿入する |
| `video.download(name=None)` | `dict` | ビデオをダウンロードする |
| `video.delete()` | `None` | ビデオを削除する |
### アスペクト比の調整
ビデオを異なるアスペクト比に変換する。オプションでスマートオブジェクト追跡を使用。処理はサーバーサイドで行われる。
> **警告:** アスペクト比の調整は低速なサーバーサイド操作。長いビデオでは数分かかる場合があり、タイムアウトする可能性がある。常に `start`/`end` でセグメントを制限するか、非同期処理のために `callback_url` を渡すこと。
```python
from videodb import ReframeMode
# Always prefer short segments to avoid timeouts:
reframed = video.reframe(start=0, end=60, target="vertical", mode=ReframeMode.smart)
# Async reframe for full-length videos (returns None, result via webhook):
video.reframe(target="vertical", callback_url="https://example.com/webhook")
# Custom dimensions
reframed = video.reframe(start=0, end=60, target={"width": 1080, "height": 1080})
```
#### reframeのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `start` | `float\|None` | `None` | 開始時間None = 開始) |
| `end` | `float\|None` | `None` | 終了時間None = ビデオ終了) |
| `target` | `str\|dict` | `"vertical"` | プリセット文字列(`"vertical"`, `"square"`, `"landscape"`)または `{"width": int, "height": int}` |
| `mode` | `str` | `ReframeMode.smart` | `"simple"`(中央クロップ)または `"smart"`(オブジェクト追跡) |
| `callback_url` | `str\|None` | `None` | 非同期通知のWebhook URL |
`callback_url` が提供されない場合は `Video` オブジェクトを返し、そうでない場合は `None` を返す。
## オーディオオブジェクト
```python
audio = coll.get_audio(audio_id)
```
### オーディオ属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `audio.id` | `str` | 一意のオーディオID |
| `audio.collection_id` | `str` | 親コレクションID |
| `audio.name` | `str` | オーディオ名 |
| `audio.length` | `float` | 長さ(秒) |
### オーディオメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `audio.generate_url()` | `str` | 再生用の署名付きURLを生成する |
| `audio.get_transcript(start=None, end=None)` | `list[dict]` | タイムスタンプ付きのトランスクリプトを取得する |
| `audio.get_transcript_text(start=None, end=None)` | `str` | 完全なトランスクリプトテキストを取得する |
| `audio.generate_transcript(force=None)` | `dict` | トランスクリプトを生成する |
| `audio.delete()` | `None` | オーディオを削除する |
## 画像オブジェクト
```python
image = coll.get_image(image_id)
```
### 画像属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `image.id` | `str` | 一意の画像ID |
| `image.collection_id` | `str` | 親コレクションID |
| `image.name` | `str` | 画像名 |
| `image.url` | `str\|None` | 画像URL生成された画像の場合は `None` になる可能性がある——代わりに `generate_url()` を使用) |
### 画像メソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `image.generate_url()` | `str` | 署名付きURLを生成する |
| `image.delete()` | `None` | 画像を削除する |
## タイムラインとエディター
### タイムライン
```python
from videodb.timeline import Timeline
timeline = Timeline(conn)
```
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `timeline.add_inline(asset)` | `None` | メイントラックに `VideoAsset` を順番に追加する |
| `timeline.add_overlay(start, asset)` | `None` | タイムスタンプに `AudioAsset``ImageAsset`、または `TextAsset` をオーバーレイする |
| `timeline.generate_stream()` | `str` | コンパイルしてストリームURLを取得する |
### アセットタイプ
#### VideoAsset
```python
from videodb.asset import VideoAsset
asset = VideoAsset(
asset_id=video.id,
start=0, # trim start (seconds)
end=None, # trim end (seconds, None = full)
)
```
#### AudioAsset
```python
from videodb.asset import AudioAsset
asset = AudioAsset(
asset_id=audio.id,
start=0,
end=None,
disable_other_tracks=True, # mute original audio when True
fade_in_duration=0, # seconds (max 5)
fade_out_duration=0, # seconds (max 5)
)
```
#### ImageAsset
```python
from videodb.asset import ImageAsset
asset = ImageAsset(
asset_id=image.id,
duration=None, # display duration (seconds)
width=100, # display width
height=100, # display height
x=80, # horizontal position (px from left)
y=20, # vertical position (px from top)
)
```
#### TextAsset
```python
from videodb.asset import TextAsset, TextStyle
asset = TextAsset(
text="Hello World",
duration=5,
style=TextStyle(
fontsize=24,
fontcolor="black",
boxcolor="white", # background box colour
alpha=1.0,
font="Sans",
text_align="T", # text alignment within box
),
)
```
#### CaptionAssetエディターAPI
CaptionAssetはエディターAPIに属し、独自のタイムライン、トラック、クリップシステムを持つ
```python
from videodb.editor import CaptionAsset, FontStyling
asset = CaptionAsset(
src="auto", # "auto" or base64 ASS string
font=FontStyling(name="Clear Sans", size=30),
primary_color="&H00FFFFFF",
)
```
完全なCaptionAssetの使用方法については、[editor.md](../../../../../skills/videodb/reference/editor.md#caption-overlays) のエディターAPIを参照。
## ビデオ検索パラメータ
```python
results = video.search(
query="your query",
search_type=SearchType.semantic, # semantic, keyword, or scene
index_type=IndexType.spoken_word, # spoken_word or scene
result_threshold=None, # max number of results
score_threshold=None, # minimum relevance score
dynamic_score_percentage=None, # percentage of dynamic score
scene_index_id=None, # target a specific scene index (pass via **kwargs)
filter=[], # metadata filters for scene search
)
```
> **注意:** `filter` は `video.search()` の明示的な名前付きパラメータ。`scene_index_id` は `**kwargs` を通じてAPIに渡される。
>
> **重要:** `video.search()` は一致するものがない場合に `"No results found"` というメッセージとともに `InvalidRequestError` を発生させる。常に検索呼び出しをtry/exceptで包むこと。シーン検索には低関連性のイズをフィルタリングするために `score_threshold=0.3` 以上を使用する。
シーン検索には `search_type=SearchType.semantic` を使用し `index_type=IndexType.scene` を設定する。特定のシーンインデックスを対象にする場合は `scene_index_id` を渡す。詳細は [search.md](search.md) を参照。
## SearchResultオブジェクト
```python
results = video.search("query", search_type=SearchType.semantic)
```
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `results.get_shots()` | `list[Shot]` | 一致したクリップのリストを取得する |
| `results.compile()` | `str` | すべてのショットをストリームURLにコンパイルする |
| `results.play()` | `str` | ブラウザでコンパイルされたストリームを開く |
### Shot属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `shot.video_id` | `str` | ソースビデオID |
| `shot.video_length` | `float` | ソースビデオの長さ |
| `shot.video_title` | `str` | ソースビデオのタイトル |
| `shot.start` | `float` | 開始時間(秒) |
| `shot.end` | `float` | 終了時間(秒) |
| `shot.text` | `str` | 一致したテキストコンテンツ |
| `shot.search_score` | `float` | 検索関連スコア |
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `shot.generate_stream()` | `str` | この特定のショットをストリーミングする |
| `shot.play()` | `str` | ブラウザでショットストリームを開く |
## Meetingオブジェクト
```python
meeting = coll.record_meeting(
meeting_url="https://meet.google.com/...",
bot_name="Bot",
callback_url=None, # Webhook URL for status updates
callback_data=None, # Optional dict passed through to callbacks
time_zone="UTC", # Time zone for the meeting
)
```
### Meeting属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `meeting.id` | `str` | 一意のミーティングID |
| `meeting.collection_id` | `str` | 親コレクションID |
| `meeting.status` | `str` | 現在の状態 |
| `meeting.video_id` | `str` | 録画ビデオID完了後 |
| `meeting.bot_name` | `str` | ボット名 |
| `meeting.meeting_title` | `str` | ミーティングタイトル |
| `meeting.meeting_url` | `str` | ミーティングURL |
| `meeting.speaker_timeline` | `dict` | 発言者タイムラインデータ |
| `meeting.is_active` | `bool` | 初期化中または処理中の場合はtrue |
| `meeting.is_completed` | `bool` | 完了した場合はtrue |
### Meetingメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `meeting.refresh()` | `Meeting` | サーバーからデータをリフレッシュする |
| `meeting.wait_for_status(target_status, timeout=14400, interval=120)` | `bool` | 指定された状態になるまでポーリングする |
## RTStreamとCapture
RTStreamライブ取り込み、インデックス作成、転写については [rtstream-reference.md](rtstream-reference.md) を参照。
キャプチャセッションデスクトップ録画、CaptureClient、チャネルについては [capture-reference.md](capture-reference.md) を参照。
## 列挙型と定数
### SearchType
```python
from videodb import SearchType
SearchType.semantic # Natural language semantic search
SearchType.keyword # Exact keyword matching
SearchType.scene # Visual scene search (may require paid plan)
SearchType.llm # LLM-powered search
```
### SceneExtractionType
```python
from videodb import SceneExtractionType
SceneExtractionType.shot_based # Automatic shot boundary detection
SceneExtractionType.time_based # Fixed time interval extraction
SceneExtractionType.transcript # Transcript-based scene extraction
```
### SubtitleStyle
```python
from videodb import SubtitleStyle
style = SubtitleStyle(
font_name="Arial",
font_size=18,
primary_colour="&H00FFFFFF",
bold=False,
# ... see SubtitleStyle for all options
)
video.add_subtitle(style=style)
```
### SubtitleAlignmentとSubtitleBorderStyle
```python
from videodb import SubtitleAlignment, SubtitleBorderStyle
```
### TextStyle
```python
from videodb import TextStyle
# or: from videodb.asset import TextStyle
style = TextStyle(
fontsize=24,
fontcolor="black",
boxcolor="white",
font="Sans",
text_align="T",
alpha=1.0,
)
```
### その他の定数
```python
from videodb import (
IndexType, # spoken_word, scene
MediaType, # video, audio, image
Segmenter, # word, sentence, time
SegmentationType, # sentence, llm
TranscodeMode, # economy, lightning
ResizeMode, # crop, fit, pad
ReframeMode, # simple, smart
RTStreamChannelType,
)
```
## 例外
```python
from videodb.exceptions import (
AuthenticationError, # Invalid or missing API key
InvalidRequestError, # Bad parameters or malformed request
RequestTimeoutError, # Request timed out
SearchError, # Search operation failure (e.g. not indexed)
VideodbError, # Base exception for all VideoDB errors
)
```
| 例外 | よくある原因 |
|-----------|-------------|
| `AuthenticationError` | 欠落または無効な `VIDEO_DB_API_KEY` |
| `InvalidRequestError` | 無効なURL、サポートされていないフォーマット、不正なパラメータ |
| `RequestTimeoutError` | サーバーの応答に時間がかかりすぎた |
| `SearchError` | インデックス化前の検索、無効な検索タイプ |
| `VideodbError` | サーバーエラー、ネットワーク問題、一般的な障害 |

View File

@@ -0,0 +1,416 @@
# キャプチャリファレンス
VideoDBキャプチャセッションのコードレベルの詳細。ワークフローガイドは [capture.md](capture.md) を参照。
***
## WebSocketイベント
キャプチャセッションとAIパイプラインからのリアルタイムイベント。WebhookやポーリングLiveEventを使用しない。
[scripts/ws\_listener.py](../../../../../skills/videodb/scripts/ws_listener.py) を使用して接続し、イベントを `${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}/videodb_events.jsonl` にダンプする。
### イベントチャネル
| チャネル | ソース | コンテンツ |
|---------|--------|---------|
| `capture_session` | セッションライフサイクル | 状態変更 |
| `transcript` | `start_transcript()` | 音声テキスト変換 |
| `visual_index` / `scene_index` | `index_visuals()` | ビジュアル分析 |
| `audio_index` | `index_audio()` | オーディオ分析 |
| `alert` | `create_alert()` | アラート通知 |
### セッションライフサイクルイベント
| イベント | 状態 | 主要データ |
|-------|--------|----------|
| `capture_session.created` | `created` | — |
| `capture_session.starting` | `starting` | — |
| `capture_session.active` | `active` | `rtstreams[]` |
| `capture_session.stopping` | `stopping` | — |
| `capture_session.stopped` | `stopped` | — |
| `capture_session.exported` | `exported` | `exported_video_id`, `stream_url`, `player_url` |
| `capture_session.failed` | `failed` | `error` |
### イベント構造
**転写イベント:**
```json
{
"channel": "transcript",
"rtstream_id": "rts-xxx",
"rtstream_name": "mic:default",
"data": {
"text": "Let's schedule the meeting for Thursday",
"is_final": true,
"start": 1710000001234,
"end": 1710000002345
}
}
```
**ビジュアルインデックスイベント:**
```json
{
"channel": "visual_index",
"rtstream_id": "rts-xxx",
"rtstream_name": "display:1",
"data": {
"text": "User is viewing a Slack conversation with 3 unread messages",
"start": 1710000012340,
"end": 1710000018900
}
}
```
**オーディオインデックスイベント:**
```json
{
"channel": "audio_index",
"rtstream_id": "rts-xxx",
"rtstream_name": "mic:default",
"data": {
"text": "Discussion about scheduling a team meeting",
"start": 1710000021500,
"end": 1710000029200
}
}
```
**セッションアクティブイベント:**
```json
{
"event": "capture_session.active",
"capture_session_id": "cap-xxx",
"status": "active",
"data": {
"rtstreams": [
{ "rtstream_id": "rts-1", "name": "mic:default", "media_types": ["audio"] },
{ "rtstream_id": "rts-2", "name": "system_audio:default", "media_types": ["audio"] },
{ "rtstream_id": "rts-3", "name": "display:1", "media_types": ["video"] }
]
}
}
```
**セッションエクスポートイベント:**
```json
{
"event": "capture_session.exported",
"capture_session_id": "cap-xxx",
"status": "exported",
"data": {
"exported_video_id": "v_xyz789",
"stream_url": "https://stream.videodb.io/...",
"player_url": "https://console.videodb.io/player?url=..."
}
}
```
> 最新の詳細については [VideoDB リアルタイムコンテキストドキュメント](https://docs.videodb.io/pages/ingest/capture-sdks/realtime-context.md) を参照。
***
## イベント永続化
`ws_listener.py` を使用してすべてのWebSocketイベントをJSONLファイルにダンプして後で分析する。
### リスナーを起動してWebSocket IDを取得する
```bash
# Start with --clear to clear old events (recommended for new sessions)
python scripts/ws_listener.py --clear &
# Append to existing events (for reconnects)
python scripts/ws_listener.py &
```
またはカスタム出力ディレクトリを指定する:
```bash
python scripts/ws_listener.py --clear /path/to/output &
# Or via environment variable:
VIDEODB_EVENTS_DIR=/path/to/output python scripts/ws_listener.py --clear &
```
スクリプトは最初の行に `WS_ID=<connection_id>` を出力し、その後無限にリッスンする。
**ws\_idを取得する**
```bash
cat "${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}/videodb_ws_id"
```
**リスナーを停止する:**
```bash
kill "$(cat "${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}/videodb_ws_pid")"
```
**`ws_connection_id` を受け入れる関数:**
| 関数 | 目的 |
|----------|---------|
| `conn.create_capture_session()` | セッションライフサイクルイベント |
| RTStreamメソッド | [rtstream-reference.md](rtstream-reference.md) を参照 |
**出力ファイル**(出力ディレクトリ内、デフォルトは `${XDG_STATE_HOME:-$HOME/.local/state}/videodb`
* `videodb_ws_id` - WebSocket接続ID
* `videodb_events.jsonl` - すべてのイベント
* `videodb_ws_pid` - 停止用のプロセスID
**機能:**
* 起動時にイベントファイルをクリアするための `--clear` フラグ(新しいセッション用)
* 接続が切れた場合の指数バックオフによる自動再接続
* SIGINT/SIGTERMでのグレースフルシャットダウン
* 接続状態のログ記録
### JSONLフォーマット
各行はタイムスタンプが付加されたJSONオブジェクト
```json
{"ts": "2026-03-02T10:15:30.123Z", "unix_ts": 1772446530.123, "channel": "visual_index", "data": {"text": "..."}}
{"ts": "2026-03-02T10:15:31.456Z", "unix_ts": 1772446531.456, "event": "capture_session.active", "capture_session_id": "cap-xxx"}
```
### イベントの読み取り
```python
import json
import time
from pathlib import Path
events_path = Path.home() / ".local" / "state" / "videodb" / "videodb_events.jsonl"
transcripts = []
recent = []
visual = []
cutoff = time.time() - 600
with events_path.open(encoding="utf-8") as handle:
for line in handle:
event = json.loads(line)
if event.get("channel") == "transcript":
transcripts.append(event)
if event.get("unix_ts", 0) > cutoff:
recent.append(event)
if (
event.get("channel") == "visual_index"
and "code" in event.get("data", {}).get("text", "").lower()
):
visual.append(event)
```
***
## WebSocket接続
転写とインデックスパイプラインからリアルタイムのAI結果を受信するために接続する。
```python
ws_wrapper = conn.connect_websocket()
ws = await ws_wrapper.connect()
ws_id = ws.connection_id
```
| 属性 / メソッド | 型 | 説明 |
|-------------------|------|-------------|
| `ws.connection_id` | `str` | 一意の接続IDAIパイプラインメソッドに渡す |
| `ws.receive()` | `AsyncIterator[dict]` | リアルタイムメッセージを生成する非同期イテレータ |
***
## CaptureSession
### 接続メソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `conn.create_capture_session(end_user_id, collection_id, ws_connection_id, metadata)` | `CaptureSession` | 新しいキャプチャセッションを作成する |
| `conn.get_capture_session(capture_session_id)` | `CaptureSession` | 既存のキャプチャセッションを取得する |
| `conn.generate_client_token()` | `str` | クライアント認証トークンを生成する |
### キャプチャセッションの作成
```python
from pathlib import Path
ws_id = (Path.home() / ".local" / "state" / "videodb" / "videodb_ws_id").read_text().strip()
session = conn.create_capture_session(
end_user_id="user-123", # required
collection_id="default",
ws_connection_id=ws_id,
metadata={"app": "my-app"},
)
print(f"Session ID: {session.id}")
```
> **注意:** `end_user_id` は必須で、キャプチャを開始するユーザーを識別するために使用される。テストやデモ目的には任意の一意の文字列識別子が有効(例:`"demo-user"`、`"test-123"`)。
### CaptureSession属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `session.id` | `str` | 一意のキャプチャセッションID |
### CaptureSessionメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `session.get_rtstream(type)` | `list[RTStream]` | タイプ別にRTStreamを取得`"mic"``"screen"`、または `"system_audio"` |
### クライアントトークンの生成
```python
token = conn.generate_client_token()
```
***
## CaptureClient
クライアントはユーザーのマシン上で動作し、権限、チャネルの発見、ストリーミングを処理する。
```python
from videodb.capture import CaptureClient
client = CaptureClient(client_token=token)
```
### CaptureClientメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `await client.request_permission(type)` | `None` | デバイスの権限をリクエストする(`"microphone"``"screen_capture"` |
| `await client.list_channels()` | `Channels` | 利用可能なオーディオ/ビデオチャネルを発見する |
| `await client.start_capture_session(capture_session_id, channels, primary_video_channel_id)` | `None` | 選択したチャネルのストリーミングを開始する |
| `await client.stop_capture()` | `None` | キャプチャセッションをグレースフルに停止する |
| `await client.shutdown()` | `None` | クライアントリソースをクリーンアップする |
### 権限のリクエスト
```python
await client.request_permission("microphone")
await client.request_permission("screen_capture")
```
### セッションの開始
```python
selected_channels = [c for c in [mic, display, system_audio] if c]
await client.start_capture_session(
capture_session_id=session.id,
channels=selected_channels,
primary_video_channel_id=display.id if display else None,
)
```
### セッションの停止
```python
await client.stop_capture()
await client.shutdown()
```
***
## チャネル
`client.list_channels()` によって返される。利用可能なデバイスをタイプ別にグループ化する。
```python
channels = await client.list_channels()
for ch in channels.all():
print(f" {ch.id} ({ch.type}): {ch.name}")
mic = channels.mics.default
display = channels.displays.default
system_audio = channels.system_audio.default
```
### チャネルグループ
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `channels.mics` | `ChannelGroup` | 利用可能なマイク |
| `channels.displays` | `ChannelGroup` | 利用可能な画面ディスプレイ |
| `channels.system_audio` | `ChannelGroup` | 利用可能なシステムオーディオソース |
### ChannelGroupメソッドと属性
| メンバー | 型 | 説明 |
|--------|------|-------------|
| `group.default` | `Channel` | グループのデフォルトチャネル(または `None` |
| `group.all()` | `list[Channel]` | グループのすべてのチャネル |
### チャネル属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `ch.id` | `str` | 一意のチャネルID |
| `ch.type` | `str` | チャネルタイプ(`"mic"``"display"``"system_audio"` |
| `ch.name` | `str` | 人間が読めるチャネル名 |
| `ch.store` | `bool` | 録画を永続化するかどうか(保存するには `True` に設定) |
`store = True` がない場合、ストリームはリアルタイムで処理されるが保存されない。
***
## RTStreamとAIパイプライン
セッションがアクティブになったら、`session.get_rtstream()` を使用してRTStreamオブジェクトを取得する。
RTStreamメソッドインデックス作成、転写、アラート、バッチ設定については [rtstream-reference.md](rtstream-reference.md) を参照。
***
## セッションライフサイクル
```
create_capture_session()
v
┌───────────────┐
│ created │
└───────┬───────┘
│ client.start_capture_session()
v
┌───────────────┐ WebSocket: capture_session.starting
│ starting │ ──> Capture channels connect
└───────┬───────┘
v
┌───────────────┐ WebSocket: capture_session.active
│ active │ ──> Start AI pipelines
└───────┬──────────────┐
│ │
│ v
│ ┌───────────────┐ WebSocket: capture_session.failed
│ │ failed │ ──> Inspect error payload and retry setup
│ └───────────────┘
│ unrecoverable capture error
│ client.stop_capture()
v
┌───────────────┐ WebSocket: capture_session.stopping
│ stopping │ ──> Finalize streams
└───────┬───────┘
v
┌───────────────┐ WebSocket: capture_session.stopped
│ stopped │ ──> All streams finalized
└───────┬───────┘
│ (if store=True)
v
┌───────────────┐ WebSocket: capture_session.exported
│ exported │ ──> Access video_id, stream_url, player_url
└───────────────┘
```

View File

@@ -0,0 +1,104 @@
# キャプチャガイド
## 概要
VideoDB CaptureはAI処理機能を備えたリアルタイムの画面とオーディオの録画をサポートする。デスクトップキャプチャは現在**macOS**のみサポートされている。
コードレベルの詳細SDKメソッド、イベント構造、AIパイプラインについては [capture-reference.md](capture-reference.md) を参照。
## クイックスタート
1. **WebSocketリスナーを起動する**`python scripts/ws_listener.py --clear &`
2. **キャプチャコードを実行する**(以下の完全なキャプチャワークフローを参照)
3. **イベントの書き込み先**`/tmp/videodb_events.jsonl`
***
## 完全なキャプチャワークフロー
WebhookやポーリングLiveEventは不要。WebSocketがセッションライフサイクルイベントを含むすべてのイベントを配信する。
> **重要な注意事項:** `CaptureClient` はキャプチャ全体を通じて実行し続ける必要がある。ローカルレコーダーバイナリを実行し、画面/オーディオデータをVideoDBにストリーミングする。`CaptureClient` を作成したPythonプロセスが終了すると、レコーダーバイナリが終了し、キャプチャが静かに停止する。常にキャプチャコードを**長期実行バックグラウンドプロセス**として実行し(例:`nohup python capture_script.py &`)、明示的に停止するまで生き続けるようにシグナル処理(`asyncio.Event` + `SIGINT`/`SIGTERM`)を使用すること。
1. バックグラウンドで**WebSocketリスナーを起動する**。古いイベントをクリアするために `--clear` フラグを使用する。WebSocket IDファイルが作成されるまで待つ。
2. **WebSocket IDを読み取る**。このIDはキャプチャセッションとAIパイプラインに必要。
3. **キャプチャセッションを作成する**。デスクトップクライアント用のクライアントトークンを生成する。
4. トークンを使用して**CaptureClientを初期化する**。マイクと画面キャプチャの権限をリクエストする。
5. **チャネルをリストアップして選択する**(マイク、ディスプレイ、システムオーディオ)。ビデオとして永続化したいチャネルに `store = True` を設定する。
6. 選択したチャネルで**セッションを開始する**。
7. `capture_session.active` が見えるまでイベントを読み取ることで**セッションがアクティブになるまで待つ**。このイベントには `rtstreams` 配列が含まれる。セッション情報セッションID、RTStream IDをファイルに保存する`/tmp/videodb_capture_info.json`)。他のスクリプトがそれを読み取れるようにする。
8. **プロセスを生かし続ける**。明示的に停止されるまでプロセスをブロックするために、`SIGINT`/`SIGTERM` のシグナルハンドラーで `asyncio.Event` を使用する。後で `kill $(cat /tmp/videodb_capture_pid)` でプロセスを停止できるようにPIDファイルを書く`/tmp/videodb_capture_pid`。PIDファイルは実行のたびに上書きして、再実行時に常に正しいPIDを持つようにする。
9. 各RTStreamの音声インデックスとビジュアルインデックスを作成する**AIパイプラインを起動する**(別のコマンド/スクリプトで。保存されたセッション情報ファイルからRTStream IDを読み取る。
10. ユースケースに応じてリアルタイムイベントを読み取る**カスタムイベント処理ロジックを書く**(別のコマンド/スクリプトで)。例:
* `visual_index` が「Slack」を言及したときにSlackアクティビティをログに記録する
* `audio_index` イベントが到着したときに議論をサマリーする
* `transcript` に特定のキーワードが現れたときにアラートをトリガーする
* 画面の説明からアプリの使用状況を追跡する
11. **キャプチャを停止する** - 完了したら、キャプチャプロセスにSIGTERMを送信する。シグナルハンドラーで `client.stop_capture()``client.shutdown()` を呼び出すべき。
12. **エクスポートを待つ** - `capture_session.exported` が見えるまでイベントを読み取る。このイベントには `exported_video_id``stream_url``player_url` が含まれる。キャプチャを停止した後、これには数秒かかる場合がある。
13. **WebSocketリスナーを停止する** - エクスポートイベントを受信したら、`kill $(cat /tmp/videodb_ws_pid)` でクリーンに終了させる。
***
## シャットダウンシーケンス
すべてのイベントがキャプチャされることを確認するために、適切なシャットダウンシーケンスが重要:
1. **キャプチャセッションを停止する**`client.stop_capture()` 次に `client.shutdown()`
2. **エクスポートイベントを待つ**`capture_session.exported``/tmp/videodb_events.jsonl` でポーリングする
3. **WebSocketリスナーを停止する**`kill $(cat /tmp/videodb_ws_pid)`
エクスポートイベントを受信する前にWebSocketリスナーを**停止しないこと**。そうしないと最終的なビデオURLを受け取れなくなる。
***
## スクリプト
| スクリプト | 説明 |
|--------|-------------|
| `scripts/ws_listener.py` | WebSocketイベントリスナーJSONLにダンプ |
### ws\_listener.pyの使用方法
```bash
# Start listener in background (append to existing events)
python scripts/ws_listener.py &
# Start listener with clear (new session, clears old events)
python scripts/ws_listener.py --clear &
# Custom output directory
python scripts/ws_listener.py --clear /path/to/events &
# Stop the listener
kill $(cat /tmp/videodb_ws_pid)
```
**オプション:**
* `--clear`:起動前にイベントファイルをクリアする。新しいキャプチャセッションを開始するときに使用する。
**出力ファイル:**
* `videodb_events.jsonl` - すべてのWebSocketイベント
* `videodb_ws_id` - WebSocket接続ID`ws_connection_id` パラメータに使用)
* `videodb_ws_pid` - プロセスIDリスナーの停止に使用
**機能:**
* 接続が切れた場合の指数バックオフによる自動再接続
* SIGINT/SIGTERMでのグレースフルシャットダウン
* プロセス管理のためのPIDファイル
* 接続状態のログ記録

View File

@@ -0,0 +1,443 @@
# タイムライン編集ガイド
VideoDBは、複数のクリップからビデオを合成し、テキストや画像のオーバーレイを追加し、オーディオトラックをミックスし、クリップをトリミングするための非破壊的なタイムラインエディターを提供する——すべてサーバーサイドで、再エンコードやローカルツールは不要。トリミング、クリップのマージ、ビデオへのオーディオ/音楽のオーバーレイ、字幕の追加、テキストや画像のオーバーレイに使用できる。
## 前提条件
ビデオ、オーディオ、画像は、タイムラインアセットとして使用するために**コレクションにアップロードされている必要がある**。字幕オーバーレイには、ビデオも**音声単語のインデックスが作成されている必要がある**。
## コアコンセプト
### タイムライン
`Timeline` は仮想合成レイヤーである。アセットはタイムラインに**インライン**(メイントラックに順番に配置)または**オーバーレイ**(特定のタイムスタンプにレイヤーとして配置)として配置できる。元のメディアは変更されない;最終ストリームはオンデマンドでコンパイルされる。
```python
from videodb.timeline import Timeline
timeline = Timeline(conn)
```
### アセット
タイムライン上の各要素は**アセット**である。VideoDBは5種類のアセットタイプを提供する
| アセット | インポート | 主な用途 |
|-------|--------|-------------|
| `VideoAsset` | `from videodb.asset import VideoAsset` | ビデオクリップ(トリミング、順序付け) |
| `AudioAsset` | `from videodb.asset import AudioAsset` | 音楽、効果音、ナレーション |
| `ImageAsset` | `from videodb.asset import ImageAsset` | ロゴ、サムネイル、オーバーレイ |
| `TextAsset` | `from videodb.asset import TextAsset, TextStyle` | タイトル、字幕、ローワーサード |
| `CaptionAsset` | `from videodb.editor import CaptionAsset` | 自動レンダリング字幕エディターAPI |
## タイムラインの構築
### ビデオクリップをインラインで追加する
インラインアセットはメインビデオトラックに順番に再生される。`add_inline` メソッドは `VideoAsset` のみを受け入れる:
```python
from videodb.asset import VideoAsset
video_a = coll.get_video(video_id_a)
video_b = coll.get_video(video_id_b)
timeline = Timeline(conn)
timeline.add_inline(VideoAsset(asset_id=video_a.id))
timeline.add_inline(VideoAsset(asset_id=video_b.id))
stream_url = timeline.generate_stream()
```
### トリミング / サブクリップ
`VideoAsset``start``end` を使用して一部を抽出する:
```python
# Take only seconds 1030 from the source video
clip = VideoAsset(asset_id=video.id, start=10, end=30)
timeline.add_inline(clip)
```
### VideoAssetのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `asset_id` | `str` | 必須 | ビデオメディアID |
| `start` | `float` | `0` | トリミング開始時間(秒) |
| `end` | `float\|None` | `None` | トリミング終了時間(`None` = 完全なビデオ) |
> **警告:** SDKは負のタイムスタンプを検証しない。`start=-5` を渡すと静かに受け入れられるが、破損したまたは予期しない出力を生成する。`VideoAsset` を作成する前に常に `start >= 0`、`start < end`、`end <= video.length` を確認すること。
## テキストオーバーレイ
タイムラインの任意の点にタイトル、ローワーサード、またはアノテーションを追加する:
```python
from videodb.asset import TextAsset, TextStyle
title = TextAsset(
text="Welcome to the Demo",
duration=5,
style=TextStyle(
fontsize=36,
fontcolor="white",
boxcolor="black",
alpha=0.8,
font="Sans",
),
)
# Overlay the title at the very start (t=0)
timeline.add_overlay(0, title)
```
### TextStyleのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `fontsize` | `int` | `24` | フォントサイズ(ピクセル) |
| `fontcolor` | `str` | `"black"` | CSSカラー名または16進数値 |
| `fontcolor_expr` | `str` | `""` | 動的フォントカラー式 |
| `alpha` | `float` | `1.0` | テキストの不透明度0.0〜1.0 |
| `font` | `str` | `"Sans"` | フォントファミリー |
| `box` | `bool` | `True` | 背景ボックスを有効にする |
| `boxcolor` | `str` | `"white"` | 背景ボックスカラー |
| `boxborderw` | `str` | `"10"` | ボックスの境界線幅 |
| `boxw` | `int` | `0` | ボックス幅のオーバーライド |
| `boxh` | `int` | `0` | ボックス高さのオーバーライド |
| `line_spacing` | `int` | `0` | 行間隔 |
| `text_align` | `str` | `"T"` | ボックス内のテキスト整列 |
| `y_align` | `str` | `"text"` | 垂直整列の基準 |
| `borderw` | `int` | `0` | テキスト境界線幅 |
| `bordercolor` | `str` | `"black"` | テキスト境界線カラー |
| `expansion` | `str` | `"normal"` | テキスト展開モード |
| `basetime` | `int` | `0` | 時間ベースの式の基準時間 |
| `fix_bounds` | `bool` | `False` | テキスト境界を固定する |
| `text_shaping` | `bool` | `True` | テキストシェーピングを有効にする |
| `shadowcolor` | `str` | `"black"` | シャドウカラー |
| `shadowx` | `int` | `0` | シャドウXオフセット |
| `shadowy` | `int` | `0` | シャドウYオフセット |
| `tabsize` | `int` | `4` | タブサイズ(スペース数) |
| `x` | `str` | `"(main_w-text_w)/2"` | 水平位置の式 |
| `y` | `str` | `"(main_h-text_h)/2"` | 垂直位置の式 |
## オーディオオーバーレイ
バックグラウンドミュージック、効果音、またはナレーションをメインビデオトラックの上にオーバーレイする:
```python
from videodb.asset import AudioAsset
music = coll.get_audio(music_id)
audio_layer = AudioAsset(
asset_id=music.id,
disable_other_tracks=False,
fade_in_duration=2,
fade_out_duration=2,
)
# Start the music at t=0, overlaid on the video track
timeline.add_overlay(0, audio_layer)
```
### AudioAssetのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `asset_id` | `str` | 必須 | オーディオメディアID |
| `start` | `float` | `0` | トリミング開始時間(秒) |
| `end` | `float\|None` | `None` | トリミング終了時間(`None` = 完全なオーディオ) |
| `disable_other_tracks` | `bool` | `True` | Trueの場合、他のオーディオトラックをミュートする |
| `fade_in_duration` | `float` | `0` | フェードイン秒数最大5 |
| `fade_out_duration` | `float` | `0` | フェードアウト秒数最大5 |
## 画像オーバーレイ
ロゴ、ウォーターマーク、または生成された画像をオーバーレイとして追加する:
```python
from videodb.asset import ImageAsset
logo = coll.get_image(logo_id)
logo_overlay = ImageAsset(
asset_id=logo.id,
duration=10,
width=120,
height=60,
x=20,
y=20,
)
timeline.add_overlay(0, logo_overlay)
```
### ImageAssetのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `asset_id` | `str` | 必須 | 画像メディアID |
| `width` | `int\|str` | `100` | 表示幅 |
| `height` | `int\|str` | `100` | 表示高さ |
| `x` | `int` | `80` | 水平位置(左からのピクセル) |
| `y` | `int` | `20` | 垂直位置(上からのピクセル) |
| `duration` | `float\|None` | `None` | 表示時間(秒) |
## 字幕オーバーレイ
ビデオに字幕を追加する方法は2つある。
### 方法1字幕ワークフロー最もシンプル
`video.add_subtitle()` を使用してビデオストリームに字幕を直接バーンインする。これは内部で `videodb.timeline.Timeline` を使用する:
```python
from videodb import SubtitleStyle
# Video must have spoken words indexed first (force=True skips if already done)
video.index_spoken_words(force=True)
# Add subtitles with default styling
stream_url = video.add_subtitle()
# Or customise the subtitle style
stream_url = video.add_subtitle(style=SubtitleStyle(
font_name="Arial",
font_size=22,
primary_colour="&H00FFFFFF",
bold=True,
))
```
### 方法2エディターAPI高度
エディターAPI`videodb.editor`)は、`CaptionAsset``Clip``Track`、独自の `Timeline` を持つトラックベースの合成システムを提供する。これは上記で使用した `videodb.timeline.Timeline` とは独立したAPIである。
```python
from videodb.editor import (
CaptionAsset,
Clip,
Track,
Timeline as EditorTimeline,
FontStyling,
BorderAndShadow,
Positioning,
CaptionAnimation,
)
# Video must have spoken words indexed first (force=True skips if already done)
video.index_spoken_words(force=True)
# Create a caption asset
caption = CaptionAsset(
src="auto",
font=FontStyling(name="Clear Sans", size=30),
primary_color="&H00FFFFFF",
back_color="&H00000000",
border=BorderAndShadow(outline=1),
position=Positioning(margin_v=30),
animation=CaptionAnimation.box_highlight,
)
# Build an editor timeline with tracks and clips
editor_tl = EditorTimeline(conn)
track = Track()
track.add_clip(start=0, clip=Clip(asset=caption, duration=video.length))
editor_tl.add_track(track)
stream_url = editor_tl.generate_stream()
```
### CaptionAssetのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `src` | `str` | `"auto"` | 字幕ソース(`"auto"` またはbase64 ASS文字列 |
| `font` | `FontStyling\|None` | `FontStyling()` | フォントスタイリング(名前、サイズ、太字、斜体など) |
| `primary_color` | `str` | `"&H00FFFFFF"` | メインテキストカラーASSフォーマット |
| `secondary_color` | `str` | `"&H000000FF"` | サブテキストカラーASSフォーマット |
| `back_color` | `str` | `"&H00000000"` | 背景カラーASSフォーマット |
| `border` | `BorderAndShadow\|None` | `BorderAndShadow()` | 境界線とシャドウのスタイル |
| `position` | `Positioning\|None` | `Positioning()` | 字幕の整列とマージン |
| `animation` | `CaptionAnimation\|None` | `None` | アニメーション効果(例:`box_highlight``reveal``karaoke` |
## コンパイルとストリーミング
タイムラインを組み立てたら、ストリーミング可能なURLにコンパイルする。ストリームはオンザフライで生成される——レンダリングの待ち時間はない。
```python
stream_url = timeline.generate_stream()
print(f"Stream: {stream_url}")
```
追加のストリーミングオプション(セグメントストリーム、検索からストリーム、オーディオ再生)については [streaming.md](streaming.md) を参照。
## 完全なワークフロー例
### タイトルカード付きのハイライトリール
```python
import videodb
from videodb import SearchType
from videodb.exceptions import InvalidRequestError
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, TextAsset, TextStyle
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("your-video-id")
# 1. Search for key moments
video.index_spoken_words(force=True)
try:
results = video.search("product announcement", search_type=SearchType.semantic)
shots = results.get_shots()
except InvalidRequestError as exc:
if "No results found" in str(exc):
shots = []
else:
raise
# 2. Build timeline
timeline = Timeline(conn)
# Title card
title = TextAsset(
text="Product Launch Highlights",
duration=4,
style=TextStyle(fontsize=48, fontcolor="white", boxcolor="#1a1a2e", alpha=0.95),
)
timeline.add_overlay(0, title)
# Append each matching clip
for shot in shots:
asset = VideoAsset(asset_id=shot.video_id, start=shot.start, end=shot.end)
timeline.add_inline(asset)
# 3. Generate stream
stream_url = timeline.generate_stream()
print(f"Highlight reel: {stream_url}")
```
### バックグラウンドミュージック付きロゴオーバーレイ
```python
import videodb
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, AudioAsset, ImageAsset
conn = videodb.connect()
coll = conn.get_collection()
main_video = coll.get_video(main_video_id)
music = coll.get_audio(music_id)
logo = coll.get_image(logo_id)
timeline = Timeline(conn)
# Main video track
timeline.add_inline(VideoAsset(asset_id=main_video.id))
# Background music — disable_other_tracks=False to mix with video audio
timeline.add_overlay(
0,
AudioAsset(asset_id=music.id, disable_other_tracks=False, fade_in_duration=3),
)
# Logo in top-right corner for first 10 seconds
timeline.add_overlay(
0,
ImageAsset(asset_id=logo.id, duration=10, x=1140, y=20, width=120, height=60),
)
stream_url = timeline.generate_stream()
print(f"Final video: {stream_url}")
```
### 複数のビデオからのマルチクリップモンタージュ
```python
import videodb
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, TextAsset, TextStyle
conn = videodb.connect()
coll = conn.get_collection()
clips = [
{"video_id": "vid_001", "start": 5, "end": 15, "label": "Scene 1"},
{"video_id": "vid_002", "start": 0, "end": 20, "label": "Scene 2"},
{"video_id": "vid_003", "start": 30, "end": 45, "label": "Scene 3"},
]
timeline = Timeline(conn)
timeline_offset = 0.0
for clip in clips:
# Add a label as an overlay on each clip
label = TextAsset(
text=clip["label"],
duration=2,
style=TextStyle(fontsize=32, fontcolor="white", boxcolor="#333333"),
)
timeline.add_inline(
VideoAsset(asset_id=clip["video_id"], start=clip["start"], end=clip["end"])
)
timeline.add_overlay(timeline_offset, label)
timeline_offset += clip["end"] - clip["start"]
stream_url = timeline.generate_stream()
print(f"Montage: {stream_url}")
```
## 2つのタイムラインAPI
VideoDBには2つの独立したタイムラインシステムがある。それらは**互換性がない**
| | `videodb.timeline.Timeline` | `videodb.editor.Timeline`エディターAPI |
|---|---|---|
| **インポート** | `from videodb.timeline import Timeline` | `from videodb.editor import Timeline as EditorTimeline` |
| **アセット** | `VideoAsset``AudioAsset``ImageAsset``TextAsset` | `CaptionAsset``Clip``Track` |
| **メソッド** | `add_inline()``add_overlay()` | `add_track()``Track` / `Clip` の組み合わせ |
| **最適な用途** | ビデオ合成、オーバーレイ、マルチクリップ編集 | アニメーション付き字幕/キャプションスタイリング |
一方のAPIのアセットをもう一方に混在させない。`CaptionAsset` はエディターAPIのみで機能する。`VideoAsset` / `AudioAsset` / `ImageAsset` / `TextAsset``videodb.timeline.Timeline` のみで機能する。
## 制限と制約
タイムラインエディターは**非破壊的な線形合成**向けに設計されている。以下の操作は**サポートされていない**
### サポートされていない操作
| 制限 | 詳細 |
|---|---|
| **トランジションやエフェクトなし** | クリップ間のクロスフェード、ワイプ、ディゾルブ、トランジションはない。すべてのカットはハードカット。 |
| **ビデオへのビデオオーバーレイなし(ピクチャーインピクチャー)** | `add_inline()``VideoAsset` のみを受け入れる。別のビデオストリームの上に1つのビデオストリームをオーバーレイすることはできない。画像オーバーレイは静的なピクチャーインピクチャーを近似できるが、ライブビデオではない。 |
| **速度や再生制御なし** | スローモーション、早送り、逆再生、タイムリマッピングはない。`VideoAsset` には `speed` パラメータがない。 |
| **クロップ、ズーム、パンなし** | ビデオフレームの領域をクロップしたり、ズームエフェクトを適用したり、フレームでパンすることはできない。`video.reframe()` はアスペクト比変換のみ。 |
| **ビデオフィルターやカラーグレーディングなし** | 輝度、コントラスト、彩度、色相、カラーコレクション調整はない。 |
| **アニメーションテキストなし** | `TextAsset` はその全持続時間にわたって静的。フェードイン/アウト、移動、アニメーションはない。アニメーション字幕にはエディターAPIで `CaptionAsset` を使用する。 |
| **混合テキストスタイルなし** | 単一の `TextAsset` は1つの `TextStyle` のみを持つ。単一のテキストブロック内で太字、斜体、カラーを混在させることはできない。 |
| **ブランクまたは単色クリップなし** | 単色フレーム、ブラックスクリーン、スタンドアロンタイトルカードを作成することはできない。テキストと画像のオーバーレイは、インライントラックに基礎として `VideoAsset` が必要。 |
| **オーディオ音量コントロールなし** | `AudioAsset` には `volume` パラメータがない。オーディオはフルボリュームか、`disable_other_tracks` でミュートかのどちらか。低音量でミックスすることはできない。 |
| **キーフレームアニメーションなし** | 時間をかけてオーバーレイプロパティを変更することはできない画像を位置Aから位置Bに移動。 |
### 制約
| 制約 | 詳細 |
|---|---|
| **オーディオフェードは最大5秒** | `fade_in_duration``fade_out_duration` はそれぞれ最大5秒。 |
| **オーバーレイの位置は絶対タイムライン基準** | オーバーレイはタイムライン開始からの絶対タイムスタンプを使用する。インラインクリップの再配置によってオーバーレイは移動しない。 |
| **インライントラックはビデオのみ** | `add_inline()``VideoAsset` のみを受け入れる。オーディオ、画像、テキストは `add_overlay()` を使用する必要がある。 |
| **オーバーレイはクリップにバインドされない** | オーバーレイは固定されたタイムラインタイムスタンプに配置される。オーバーレイを特定のインラインクリップに添付してそれと一緒に移動させることはできない。 |
## ヒント
* **非破壊的**:タイムラインはソースメディアを変更しない。同じアセットを使用して複数のタイムラインを作成できる。
* **オーバーレイスタッキング**:複数のオーバーレイを同じタイムスタンプで開始できる。オーディオオーバーレイはミックスされる;画像/テキストオーバーレイは追加された順にレイヤー化される。
* **インライントラックはVideoAssetのみ**`add_inline()``VideoAsset` のみを受け入れる。`AudioAsset``ImageAsset``TextAsset` には `add_overlay()` を使用する。
* **クリップ精度**`VideoAsset``AudioAsset``start`/`end` は秒単位。
* **ビデオオーディオのミュート**:音楽やナレーションをオーバーレイするときに元のビデオオーディオをミュートするために `AudioAsset``disable_other_tracks=True` を設定する。
* **フェード制限**`AudioAsset``fade_in_duration``fade_out_duration` は最大5秒。
* **メディアの生成**`coll.generate_music()``coll.generate_sound_effect()``coll.generate_voice()``coll.generate_image()` を使用してタイムラインアセットとしてすぐに使用できるメディアを作成する。

View File

@@ -0,0 +1,331 @@
# 生成メディアガイド
VideoDBはAI駆動の画像、ビデオ、音楽、効果音、音声、テキストコンテンツ生成を提供する。すべての生成メソッドは**Collection**オブジェクト上にある。
## 前提条件
生成メソッドを呼び出す前に、接続とコレクションの参照が必要:
```python
import videodb
conn = videodb.connect()
coll = conn.get_collection()
```
## 画像生成
テキストプロンプトから画像を生成する:
```python
image = coll.generate_image(
prompt="a futuristic cityscape at sunset with flying cars",
aspect_ratio="16:9",
)
# Access the generated image
print(image.id)
print(image.generate_url()) # returns a signed download URL
```
### generate\_imageのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `prompt` | `str` | 必須 | 生成する画像のテキスト説明 |
| `aspect_ratio` | `str` | `"1:1"` | アスペクト比:`"1:1"`, `"9:16"`, `"16:9"`, `"4:3"`, または `"3:4"` |
| `callback_url` | `str\|None` | `None` | 非同期コールバックを受信するURL |
`.id``.name``.collection_id` を含む `Image` オブジェクトを返す。生成された画像の `.url` 属性は `None` になる可能性がある——信頼できる署名付きダウンロードURLを取得するには常に `image.generate_url()` を使用すること。
> **注意:** `Video` オブジェクト(`.generate_stream()` を使用)と異なり、`Image` オブジェクトは画像URLを取得するために `.generate_url()` を使用する。`.url` 属性は特定の画像タイプ(例:サムネイル)に対してのみ設定される。
## ビデオ生成
テキストプロンプトから短いビデオクリップを生成する:
```python
video = coll.generate_video(
prompt="a timelapse of a flower blooming in a garden",
duration=5,
)
stream_url = video.generate_stream()
video.play()
```
### generate\_videoのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `prompt` | `str` | 必須 | 生成するビデオのテキスト説明 |
| `duration` | `int` | `5` | 長さ整数値、5〜8でなければならない |
| `callback_url` | `str\|None` | `None` | 非同期コールバックを受信するURL |
`Video` オブジェクトを返す。生成されたビデオは自動的にコレクションに追加され、アップロードされたビデオと同様にタイムライン、検索、コンパイルで使用できる。
## オーディオ生成
VideoDBは異なるオーディオタイプのために3つの独立したメソッドを提供する。
### 音楽
テキスト説明からバックグラウンドミュージックを生成する:
```python
music = coll.generate_music(
prompt="upbeat electronic music with a driving beat, suitable for a tech demo",
duration=30,
)
print(music.id)
```
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `prompt` | `str` | 必須 | 音楽のテキスト説明 |
| `duration` | `int` | `5` | 長さ(秒) |
| `callback_url` | `str\|None` | `None` | 非同期コールバックを受信するURL |
### 効果音
特定の効果音を生成する:
```python
sfx = coll.generate_sound_effect(
prompt="thunderstorm with heavy rain and distant thunder",
duration=10,
)
```
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `prompt` | `str` | 必須 | 効果音のテキスト説明 |
| `duration` | `int` | `2` | 長さ(秒) |
| `config` | `dict` | `{}` | 追加設定 |
| `callback_url` | `str\|None` | `None` | 非同期コールバックを受信するURL |
### 音声(テキスト読み上げ)
テキストから音声を生成する:
```python
voice = coll.generate_voice(
text="Welcome to our product demo. Today we'll walk through the key features.",
voice_name="Default",
)
```
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `text` | `str` | 必須 | 音声に変換するテキスト |
| `voice_name` | `str` | `"Default"` | 使用する音声 |
| `config` | `dict` | `{}` | 追加設定 |
| `callback_url` | `str\|None` | `None` | 非同期コールバックを受信するURL |
3つのオーディオメソッドはすべて `.id``.name``.length``.collection_id` を含む `Audio` オブジェクトを返す。
## テキスト生成LLM統合
`coll.generate_text()` を使用してLLM分析を実行する。これは**コレクションレベル**のメソッド——プロンプト文字列に任意のコンテキスト(トランスクリプト、説明)を直接渡す。
```python
# Get transcript from a video first
transcript_text = video.get_transcript_text()
# Generate analysis using collection LLM
result = coll.generate_text(
prompt=f"Summarize the key points discussed in this video:\n{transcript_text}",
model_name="pro",
)
print(result["output"])
```
### generate\_textのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `prompt` | `str` | 必須 | LLMコンテキストを含むプロンプト |
| `model_name` | `str` | `"basic"` | モデル層:`"basic"``"pro"`、または `"ultra"` |
| `response_type` | `str` | `"text"` | レスポンスフォーマット:`"text"` または `"json"` |
`output` キーを持つ `dict` を返す。`response_type="text"` の場合、`output``str``response_type="json"` の場合、`output``dict`
```python
result = coll.generate_text(prompt="Summarize this", model_name="pro")
print(result["output"]) # access the actual text/dict
```
### LLMを使用したシーン分析
シーン抽出とテキスト生成を組み合わせる:
```python
from videodb import SceneExtractionType
# First index scenes
scenes = video.index_scenes(
extraction_type=SceneExtractionType.time_based,
extraction_config={"time": 10},
prompt="Describe the visual content in this scene.",
)
# Get transcript for spoken context
transcript_text = video.get_transcript_text()
scene_descriptions = []
for scene in scenes:
if isinstance(scene, dict):
description = scene.get("description") or scene.get("summary")
else:
description = getattr(scene, "description", None) or getattr(scene, "summary", None)
scene_descriptions.append(description or str(scene))
scenes_text = "\n".join(scene_descriptions)
# Analyze with collection LLM
result = coll.generate_text(
prompt=(
f"Given this video transcript:\n{transcript_text}\n\n"
f"And these visual scene descriptions:\n{scenes_text}\n\n"
"Based on the spoken and visual content, describe the main topics covered."
),
model_name="pro",
)
print(result["output"])
```
## 吹き替えと翻訳
### ビデオの吹き替え
コレクションメソッドを使用してビデオを別の言語に吹き替える:
```python
dubbed_video = coll.dub_video(
video_id=video.id,
language_code="es", # Spanish
)
dubbed_video.play()
```
### dub\_videoのパラメータ
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `video_id` | `str` | 必須 | 吹き替えるビデオのID |
| `language_code` | `str` | 必須 | ターゲット言語コード(例:`"es"``"fr"``"de"` |
| `callback_url` | `str\|None` | `None` | 非同期コールバックを受信するURL |
吹き替えられたコンテンツを含む `Video` オブジェクトを返す。
### トランスクリプトの翻訳
吹き替えなしでビデオのトランスクリプトを翻訳する:
```python
translated = video.translate_transcript(
language="Spanish",
additional_notes="Use formal tone",
)
for entry in translated:
print(entry)
```
**サポートされる言語**`en``es``fr``de``it``pt``ja``ko``zh``hi``ar` など。
## 完全なワークフロー例
### ビデオのナレーション生成
```python
import videodb
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("your-video-id")
# Get transcript
transcript_text = video.get_transcript_text()
# Generate narration script using collection LLM
result = coll.generate_text(
prompt=(
f"Write a professional narration script for this video content:\n"
f"{transcript_text[:2000]}"
),
model_name="pro",
)
script = result["output"]
# Convert script to speech
narration = coll.generate_voice(text=script)
print(f"Narration audio: {narration.id}")
```
### プロンプトからサムネイルを生成する
```python
thumbnail = coll.generate_image(
prompt="professional video thumbnail showing data analytics dashboard, modern design",
aspect_ratio="16:9",
)
print(f"Thumbnail URL: {thumbnail.generate_url()}")
```
### ビデオに生成された音楽を追加する
```python
import videodb
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, AudioAsset
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("your-video-id")
# Generate background music
music = coll.generate_music(
prompt="calm ambient background music for a tutorial video",
duration=60,
)
# Build timeline with video + music overlay
timeline = Timeline(conn)
timeline.add_inline(VideoAsset(asset_id=video.id))
timeline.add_overlay(0, AudioAsset(asset_id=music.id, disable_other_tracks=False))
stream_url = timeline.generate_stream()
print(f"Video with music: {stream_url}")
```
### 構造化JSON出力
```python
transcript_text = video.get_transcript_text()
result = coll.generate_text(
prompt=(
f"Given this transcript:\n{transcript_text}\n\n"
"Return a JSON object with keys: summary, topics (array), action_items (array)."
),
model_name="pro",
response_type="json",
)
# result["output"] is a dict when response_type="json"
print(result["output"]["summary"])
print(result["output"]["topics"])
```
## ヒント
* **生成されたメディアは永続的**:すべての生成されたコンテンツはコレクションに保存され、再利用できる。
* **3つのオーディオメソッド**:バックグラウンドミュージックには `generate_music()`、効果音には `generate_sound_effect()`、テキスト読み上げには `generate_voice()` を使用する。統一された `generate_audio()` メソッドはない。
* **テキスト生成はコレクションレベル**`coll.generate_text()` はビデオコンテンツに自動的にアクセスしない。`video.get_transcript_text()` でトランスクリプトを取得し、プロンプトに渡す。
* **モデル層**`"basic"` が最速、`"pro"` がバランスの取れたオプション、`"ultra"` が最高品質。ほとんどの分析タスクには `"pro"` を使用する。
* **生成タイプを組み合わせる**:オーバーレイ用に画像を生成し、バックグラウンド用に音楽を生成し、ナレーション用に音声を生成し、タイムラインを使用してそれらを組み合わせる([editor.md](editor.md) を参照)。
* **プロンプトの品質が重要**:説明的で具体的なプロンプトはすべての生成タイプでより良い結果を生む。
* **画像のアスペクト比**`"1:1"``"9:16"``"16:9"``"4:3"`、または `"3:4"` から選択する。

View File

@@ -0,0 +1,567 @@
# RTStreamリファレンス
RTStream操作のコードレベルの詳細。ワークフローガイドは [rtstream.md](rtstream.md) を参照。
使用ガイダンスとフロー選択については、[../SKILL.md](../SKILL.md) から始めること。
[docs.videodb.io](https://docs.videodb.io/pages/ingest/live-streams/realtime-apis.md) に基づく。
***
## CollectionのRTStreamメソッド
`Collection` 上でRTStreamを管理するメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `coll.connect_rtstream(url, name, ...)` | `RTStream` | RTSP/RTMP URLから新しいRTStreamを作成する |
| `coll.get_rtstream(id)` | `RTStream` | IDで既存のRTStreamを取得する |
| `coll.list_rtstreams(limit, offset, status, name, ordering)` | `List[RTStream]` | コレクション内のすべてのRTStreamをリストする |
| `coll.search(query, namespace="rtstream")` | `RTStreamSearchResult` | すべてのRTStreamで検索する |
### RTStreamへの接続
```python
import videodb
conn = videodb.connect()
coll = conn.get_collection()
rtstream = coll.connect_rtstream(
url="rtmp://your-stream-server/live/stream-key",
name="My Live Stream",
media_types=["video"], # or ["audio", "video"]
sample_rate=30, # optional
store=True, # enable recording storage for export
enable_transcript=True, # optional
ws_connection_id=ws_id, # optional, for real-time events
)
```
### 既存のRTStreamを取得する
```python
rtstream = coll.get_rtstream("rts-xxx")
```
### RTStreamをリストする
```python
rtstreams = coll.list_rtstreams(
limit=10,
offset=0,
status="connected", # optional filter
name="meeting", # optional filter
ordering="-created_at",
)
for rts in rtstreams:
print(f"{rts.id}: {rts.name} - {rts.status}")
```
### キャプチャセッションから取得する
キャプチャセッションがアクティブになったら、RTStreamオブジェクトを取得する
```python
session = conn.get_capture_session(session_id)
mics = session.get_rtstream("mic")
displays = session.get_rtstream("screen")
system_audios = session.get_rtstream("system_audio")
```
または `capture_session.active` WebSocketイベントの `rtstreams` データを使用する:
```python
for rts in rtstreams:
rtstream = coll.get_rtstream(rts["rtstream_id"])
```
***
## RTStreamメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `rtstream.start()` | `None` | 取り込みを開始する |
| `rtstream.stop()` | `None` | 取り込みを停止する |
| `rtstream.generate_stream(start, end)` | `str` | 録画されたセグメントをストリーミングするUnixタイムスタンプ |
| `rtstream.export(name=None)` | `RTStreamExportResult` | 永続的なビデオとしてエクスポートする |
| `rtstream.index_visuals(prompt, ...)` | `RTStreamSceneIndex` | AI分析付きのビジュアルインデックスを作成する |
| `rtstream.index_audio(prompt, ...)` | `RTStreamSceneIndex` | LLMサマリー付きのオーディオインデックスを作成する |
| `rtstream.list_scene_indexes()` | `List[RTStreamSceneIndex]` | ストリーム上のすべてのシーンインデックスをリストする |
| `rtstream.get_scene_index(index_id)` | `RTStreamSceneIndex` | 特定のシーンインデックスを取得する |
| `rtstream.search(query, ...)` | `RTStreamSearchResult` | インデックス化されたコンテンツを検索する |
| `rtstream.start_transcript(ws_connection_id, engine)` | `dict` | リアルタイム転写を開始する |
| `rtstream.get_transcript(page, page_size, start, end, since)` | `dict` | 転写ページを取得する |
| `rtstream.stop_transcript(engine)` | `dict` | 転写を停止する |
***
## 開始と停止
```python
# Begin ingestion
rtstream.start()
# ... stream is being recorded ...
# Stop ingestion
rtstream.stop()
```
***
## ストリームの生成
秒数オフセットではなくUnixタイムスタンプを使用して録画から再生ストリームを生成する
```python
import time
start_ts = time.time()
rtstream.start()
# Let it record for a while...
time.sleep(60)
end_ts = time.time()
rtstream.stop()
# Generate a stream URL for the recorded segment
stream_url = rtstream.generate_stream(start=start_ts, end=end_ts)
print(f"Recorded stream: {stream_url}")
```
***
## ビデオとしてエクスポートする
録画されたストリームをコレクション内の永続的なビデオとしてエクスポートする:
```python
export_result = rtstream.export(name="Meeting Recording 2024-01-15")
print(f"Video ID: {export_result.video_id}")
print(f"Stream URL: {export_result.stream_url}")
print(f"Player URL: {export_result.player_url}")
print(f"Duration: {export_result.duration}s")
```
### RTStreamExportResult属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `video_id` | `str` | エクスポートされたビデオのID |
| `stream_url` | `str` | HLSストリームURL |
| `player_url` | `str` | Webプレーヤー URL |
| `name` | `str` | ビデオ名 |
| `duration` | `float` | 長さ(秒) |
***
## AIパイプライン
AIパイプラインはライブストリームを処理し、WebSocket経由で結果を送信する。
### RTStream AIパイプラインメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `rtstream.index_audio(prompt, batch_config, ...)` | `RTStreamSceneIndex` | LLMサマリー付きのオーディオインデックスを開始する |
| `rtstream.index_visuals(prompt, batch_config, ...)` | `RTStreamSceneIndex` | 画面コンテンツのビジュアルインデックスを開始する |
### オーディオインデックス
一定間隔でオーディオコンテンツのLLMサマリーを生成する
```python
audio_index = rtstream.index_audio(
prompt="Summarize what is being discussed",
batch_config={"type": "word", "value": 50},
model_name=None, # optional
name="meeting_audio", # optional
ws_connection_id=ws_id,
)
```
**オーディオのbatch\_configオプション**
| タイプ | 値 | 説明 |
|------|-------|-------------|
| `"word"` | count | N単語ごとにセグメント化 |
| `"sentence"` | count | N文ごとにセグメント化 |
| `"time"` | seconds | N秒ごとにセグメント化 |
例:
```python
{"type": "word", "value": 50} # every 50 words
{"type": "sentence", "value": 5} # every 5 sentences
{"type": "time", "value": 30} # every 30 seconds
```
結果は `audio_index` WebSocketチャネル経由で届く。
### ビジュアルインデックス
ビジュアルコンテンツのAI説明を生成する
```python
scene_index = rtstream.index_visuals(
prompt="Describe what is happening on screen",
batch_config={"type": "time", "value": 2, "frame_count": 5},
model_name="basic",
name="screen_monitor", # optional
ws_connection_id=ws_id,
)
```
**パラメータ:**
| パラメータ | 型 | 説明 |
|-----------|------|-------------|
| `prompt` | `str` | AIモデルへの指示構造化JSON出力をサポート |
| `batch_config` | `dict` | フレームサンプリングを制御する(以下を参照) |
| `model_name` | `str` | モデル層:`"mini"``"basic"``"pro"``"ultra"` |
| `name` | `str` | インデックス名(オプション) |
| `ws_connection_id` | `str` | 結果を受信するWebSocket接続ID |
**ビジュアルのbatch\_config**
| キー | 型 | 説明 |
|-----|------|-------------|
| `type` | `str` | ビジュアルインデックスでは `"time"` のみサポート |
| `value` | `int` | ウィンドウサイズ(秒) |
| `frame_count` | `int` | 各ウィンドウで抽出するフレーム数 |
例:`{"type": "time", "value": 2, "frame_count": 5}` は2秒ごとに5フレームをサンプリングしてモデルに送信する。
**構造化JSON出力**
構造化されたレスポンスを得るためにJSONフォーマットをリクエストするプロンプトを使用する
```python
scene_index = rtstream.index_visuals(
prompt="""Analyze the screen and return a JSON object with:
{
"app_name": "name of the active application",
"activity": "what the user is doing",
"ui_elements": ["list of visible UI elements"],
"contains_text": true/false,
"dominant_colors": ["list of main colors"]
}
Return only valid JSON.""",
batch_config={"type": "time", "value": 3, "frame_count": 3},
model_name="pro",
ws_connection_id=ws_id,
)
```
結果は `scene_index` WebSocketチャネル経由で届く。
***
## バッチ設定のサマリー
| インデックスタイプ | `type` オプション | `value` | 追加キー |
|---------------|----------------|---------|------------|
| **オーディオ** | `"word"``"sentence"``"time"` | words/sentences/seconds | - |
| **ビジュアル** | `"time"` のみ | seconds | `frame_count` |
例:
```python
# Audio: every 50 words
{"type": "word", "value": 50}
# Audio: every 30 seconds
{"type": "time", "value": 30}
# Visual: 5 frames every 2 seconds
{"type": "time", "value": 2, "frame_count": 5}
```
***
## 転写
WebSocket経由のリアルタイム転写
```python
# Start live transcription
rtstream.start_transcript(
ws_connection_id=ws_id,
engine=None, # optional, defaults to "assemblyai"
)
# Get transcript pages (with optional filters)
transcript = rtstream.get_transcript(
page=1,
page_size=100,
start=None, # optional: start timestamp filter
end=None, # optional: end timestamp filter
since=None, # optional: for polling, get transcripts after this timestamp
engine=None,
)
# Stop transcription
rtstream.stop_transcript(engine=None)
```
転写結果は `transcript` WebSocketチャネル経由で届く。
***
## RTStreamSceneIndex
`index_audio()` または `index_visuals()` を呼び出すと、メソッドは `RTStreamSceneIndex` オブジェクトを返す。このオブジェクトは実行中のインデックスを表し、シーンとアラートを管理するためのメソッドを提供する。
```python
# index_visuals returns an RTStreamSceneIndex
scene_index = rtstream.index_visuals(
prompt="Describe what is on screen",
ws_connection_id=ws_id,
)
# index_audio also returns an RTStreamSceneIndex
audio_index = rtstream.index_audio(
prompt="Summarize the discussion",
ws_connection_id=ws_id,
)
```
### RTStreamSceneIndex属性
| 属性 | 型 | 説明 |
|----------|------|-------------|
| `rtstream_index_id` | `str` | インデックスの一意ID |
| `rtstream_id` | `str` | 親RTStreamのID |
| `extraction_type` | `str` | 抽出タイプ(`time` または `transcript` |
| `extraction_config` | `dict` | 抽出設定 |
| `prompt` | `str` | 分析に使用するプロンプト |
| `name` | `str` | インデックス名 |
| `status` | `str` | 状態(`connected``stopped` |
### RTStreamSceneIndexメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `index.get_scenes(start, end, page, page_size)` | `dict` | インデックス化されたシーンを取得する |
| `index.start()` | `None` | インデックスを開始/再開する |
| `index.stop()` | `None` | インデックスを停止する |
| `index.create_alert(event_id, callback_url, ws_connection_id)` | `str` | イベント検出アラートを作成する |
| `index.list_alerts()` | `list` | このインデックスのすべてのアラートをリストする |
| `index.enable_alert(alert_id)` | `None` | アラートを有効にする |
| `index.disable_alert(alert_id)` | `None` | アラートを無効にする |
### シーンの取得
インデックスからインデックス化されたシーンをポーリングする:
```python
result = scene_index.get_scenes(
start=None, # optional: start timestamp
end=None, # optional: end timestamp
page=1,
page_size=100,
)
for scene in result["scenes"]:
print(f"[{scene['start']}-{scene['end']}] {scene['text']}")
if result["next_page"]:
# fetch next page
pass
```
### シーンインデックスの管理
```python
# List all indexes on the stream
indexes = rtstream.list_scene_indexes()
# Get a specific index by ID
scene_index = rtstream.get_scene_index(index_id)
# Stop an index
scene_index.stop()
# Restart an index
scene_index.start()
```
***
## イベント
イベントは再利用可能な検出ルール。一度作成すれば、アラートを通じて任意のインデックスに添付できる。
### 接続イベントメソッド
| メソッド | 戻り値 | 説明 |
|--------|---------|-------------|
| `conn.create_event(event_prompt, label)` | `str` (event\_id) | 検出イベントを作成する |
| `conn.list_events()` | `list` | すべてのイベントをリストする |
### イベントの作成
```python
event_id = conn.create_event(
event_prompt="User opened Slack application",
label="slack_opened",
)
```
### イベントのリスト
```python
events = conn.list_events()
for event in events:
print(f"{event['event_id']}: {event['label']}")
```
***
## アラート
アラートはイベントをインデックスに接続してリアルタイム通知を実現する。AIがイベントの説明に一致するコンテンツを検出すると、アラートが送信される。
### アラートの作成
```python
# Get the RTStreamSceneIndex from index_visuals
scene_index = rtstream.index_visuals(
prompt="Describe what application is open on screen",
ws_connection_id=ws_id,
)
# Create an alert on the index
alert_id = scene_index.create_alert(
event_id=event_id,
callback_url="https://your-backend.com/alerts", # for webhook delivery
ws_connection_id=ws_id, # for WebSocket delivery (optional)
)
```
**注意:** `callback_url` は必須。WebSocket配信のみを使用する場合は空文字列 `""` を渡す。
### アラートの管理
```python
# List all alerts on an index
alerts = scene_index.list_alerts()
# Enable/disable alerts
scene_index.disable_alert(alert_id)
scene_index.enable_alert(alert_id)
```
### アラート配信
| 方法 | 遅延 | ユースケース |
|--------|---------|----------|
| WebSocket | リアルタイム | ダッシュボード、ライブUI |
| Webhook | < 1秒 | サーバー間、自動化 |
### WebSocketアラートイベント
```json
{
"channel": "alert",
"rtstream_id": "rts-xxx",
"data": {
"event_label": "slack_opened",
"timestamp": 1710000012340,
"text": "User opened Slack application"
}
}
```
### Webhookペイロード
```json
{
"event_id": "event-xxx",
"label": "slack_opened",
"confidence": 0.95,
"explanation": "User opened the Slack application",
"timestamp": "2024-01-15T10:30:45Z",
"start_time": 1234.5,
"end_time": 1238.0,
"stream_url": "https://stream.videodb.io/v3/...",
"player_url": "https://console.videodb.io/player?url=..."
}
```
***
## WebSocket統合
すべてのリアルタイムAI結果はWebSocket経由で配信される。以下に `ws_connection_id` を渡す:
* `rtstream.start_transcript()`
* `rtstream.index_audio()`
* `rtstream.index_visuals()`
* `scene_index.create_alert()`
### WebSocketチャネル
| チャネル | ソース | コンテンツ |
|---------|--------|---------|
| `transcript` | `start_transcript()` | リアルタイム音声テキスト変換 |
| `scene_index` | `index_visuals()` | ビジュアル分析結果 |
| `audio_index` | `index_audio()` | オーディオ分析結果 |
| `alert` | `create_alert()` | アラート通知 |
WebSocketイベント構造とws\_listenerの使用については [capture-reference.md](capture-reference.md) を参照。
***
## 完全なワークフロー
```python
import time
import videodb
from videodb.exceptions import InvalidRequestError
conn = videodb.connect()
coll = conn.get_collection()
# 1. Connect and start recording
rtstream = coll.connect_rtstream(
url="rtmp://your-stream-server/live/stream-key",
name="Weekly Standup",
store=True,
)
rtstream.start()
# 2. Record for the duration of the meeting
start_ts = time.time()
time.sleep(1800) # 30 minutes
end_ts = time.time()
rtstream.stop()
# Generate an immediate playback URL for the captured window
stream_url = rtstream.generate_stream(start=start_ts, end=end_ts)
print(f"Recorded stream: {stream_url}")
# 3. Export to a permanent video
export_result = rtstream.export(name="Weekly Standup Recording")
print(f"Exported video: {export_result.video_id}")
# 4. Index the exported video for search
video = coll.get_video(export_result.video_id)
video.index_spoken_words(force=True)
# 5. Search for action items
try:
results = video.search("action items and next steps")
stream_url = results.compile()
print(f"Action items clip: {stream_url}")
except InvalidRequestError as exc:
if "No results found" in str(exc):
print("No action items were detected in the recording.")
else:
raise
```

View File

@@ -0,0 +1,59 @@
# RTStreamガイド
## 概要
RTStreamはライブビデオストリームRTSP/RTMPとデスクトップキャプチャセッションのリアルタイム取り込みをサポートする。接続後は、ライブフィードのコンテンツを録画、インデックス作成、検索、エクスポートできる。
コードレベルの詳細SDKメソッド、パラメータ、例については [rtstream-reference.md](rtstream-reference.md) を参照。
## ユースケース
* **セキュリティと監視**RTSPカメラに接続し、イベントを検出し、アラートをトリガーする
* **ライブブロードキャスト**RTMPストリームを取り込み、リアルタイムでインデックス化し、即時検索を実現する
* **会議録画**:デスクトップ画面とオーディオをキャプチャし、リアルタイムで転写し、録画をエクスポートする
* **イベント処理**ライブビデオストリームを監視し、AI分析を実行し、検出されたコンテンツに応答する
## クイックスタート
1. **ライブストリームに接続する**RTSP/RTMP URLまたはキャプチャセッションからRTStreamを取得する
2. **取り込みを開始する**ことでライブコンテンツの録画を始める
3. **AIパイプラインを起動する**ことでリアルタイムインデックス作成(オーディオ、ビジュアル、転写)を行う
4. **WebSocketでイベントを監視する**ことでリアルタイムAI結果とアラートを取得する
5. **完了したら取り込みを停止する**
6. **ビデオとしてエクスポートする**ことで永続ストレージとさらなる処理を行う
7. **録画を検索する**ことで特定のモーメントを見つける
## RTStreamソース
### RTSP/RTMPストリームから
ライブビデオフィードに直接接続する:
```python
rtstream = coll.connect_rtstream(
url="rtmp://your-stream-server/live/stream-key",
name="My Live Stream",
)
```
### キャプチャセッションから
デスクトップキャプチャマイク、画面、システムオーディオからRTStreamを取得する
```python
session = conn.get_capture_session(session_id)
mics = session.get_rtstream("mic")
displays = session.get_rtstream("screen")
system_audios = session.get_rtstream("system_audio")
```
キャプチャセッションのワークフローについては [capture.md](capture.md) を参照。
***
## スクリプト
| スクリプト | 説明 |
|--------|-------------|
| `scripts/ws_listener.py` | リアルタイムAI結果のためのWebSocketイベントリスナー |

View File

@@ -0,0 +1,230 @@
# 検索とインデックスガイド
検索機能を使用すると、自然言語クエリ、正確なキーワード、またはビジュアルシーンの説明でビデオ内の特定のモーメントを見つけることができる。
## 前提条件
ビデオは検索の前に**インデックス化されている必要がある**。各インデックスタイプは各ビデオに対して1回だけ実行が必要。
## インデックス作成
### 音声単語インデックス
セマンティック検索とキーワード検索をサポートするためにビデオの転写音声コンテンツをインデックス化する:
```python
video = coll.get_video(video_id)
# force=True makes indexing idempotent — skips if already indexed
video.index_spoken_words(force=True)
```
この操作はオーディオトラックを転写し、音声コンテンツ上に検索可能なインデックスを構築する。セマンティック検索とキーワード検索に必要。
**パラメータ:**
| パラメータ | 型 | デフォルト | 説明 |
|-----------|------|---------|-------------|
| `language_code` | `str\|None` | `None` | ビデオの言語コード |
| `segmentation_type` | `SegmentationType` | `SegmentationType.sentence` | セグメンテーションタイプ(`sentence` または `llm` |
| `force` | `bool` | `False` | `True` に設定すると既にインデックス化済みをスキップする(「既に存在」エラーを回避) |
| `callback_url` | `str\|None` | `None` | 非同期通知のWebhook URL |
### シーンインデックス
シーンのAI説明を生成することでビジュアルコンテンツをインデックス化する。音声単語インデックスと同様に、シーンインデックスが既に存在する場合はこの操作がエラーを発生させる。エラーメッセージから既存の `scene_index_id` を抽出する。
```python
import re
from videodb import SceneExtractionType
try:
scene_index_id = video.index_scenes(
extraction_type=SceneExtractionType.shot_based,
prompt="Describe the visual content, objects, actions, and setting 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
```
**抽出タイプ:**
| タイプ | 説明 | 最適な用途 |
|------|-------------|----------|
| `SceneExtractionType.shot_based` | ビジュアルショット境界に基づいてセグメント化 | 汎用、アクションコンテンツ |
| `SceneExtractionType.time_based` | 固定間隔でセグメント化 | 均一なサンプリング、長い静的コンテンツ |
| `SceneExtractionType.transcript` | トランスクリプトセグメントに基づいてセグメント化 | 音声駆動のシーン境界 |
**`time_based` のパラメータ:**
```python
video.index_scenes(
extraction_type=SceneExtractionType.time_based,
extraction_config={"time": 5, "select_frames": ["first", "last"]},
prompt="Describe what is happening in this scene.",
)
```
## 検索タイプ
### セマンティック検索
自然言語クエリを使用して音声コンテンツを照合する:
```python
from videodb import SearchType
results = video.search(
query="explaining the benefits of machine learning",
search_type=SearchType.semantic,
)
```
クエリとセマンティックに一致する音声コンテンツのランク付けされたクリップを返す。
### キーワード検索
転写された音声内で正確な用語照合を行う:
```python
results = video.search(
query="artificial intelligence",
search_type=SearchType.keyword,
)
```
正確なキーワードまたはフレーズを含むクリップを返す。
### シーン検索
ビジュアルコンテンツクエリをインデックス化されたシーンの説明と照合する。事前に `index_scenes()` の呼び出しが必要。
`index_scenes()``scene_index_id` を返す。`video.search()` に渡して特定のシーンインデックスを対象にする(ビデオに複数のシーンインデックスがある場合に特に重要):
```python
from videodb import SearchType, IndexType
from videodb.exceptions import InvalidRequestError
# Search using semantic search against the scene index.
# Use score_threshold to filter low-relevance noise (recommended: 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()
except InvalidRequestError as e:
if "No results found" in str(e):
shots = []
else:
raise
```
**重要な注意事項:**
* `SearchType.semantic``index_type=IndexType.scene` を組み合わせて使用する——これはすべてのプランで機能する最も信頼性の高い組み合わせ。
* `SearchType.scene` は存在するが、すべてのプラン(例:無料プラン)で利用可能ではない可能性がある。`IndexType.scene``SearchType.semantic` を使用することを推奨する。
* `scene_index_id` パラメータはオプション。省略すると、検索はビデオ上のすべてのシーンインデックスに対して実行される。特定のインデックスを対象にするためにこのパラメータを渡す。
* 各ビデオに対して複数のシーンインデックスを作成し(異なるプロンプトや抽出タイプを使用して)、`scene_index_id` を使用して独立して検索できる。
### メタデータフィルター付きシーン検索
カスタムメタデータでシーンをインデックス化する場合、セマンティック検索とメタデータフィルターを組み合わせて使用できる:
```python
from videodb import SearchType, IndexType
results = video.search(
query="a skillful chasing scene",
search_type=SearchType.semantic,
index_type=IndexType.scene,
scene_index_id=scene_index_id,
filter=[{"camera_view": "road_ahead"}, {"action_type": "chasing"}],
)
```
カスタムメタデータインデックスとフィルター検索の完全な例については、[scene\_level\_metadata\_indexing 例](https://github.com/video-db/videodb-cookbook/blob/main/quickstart/scene_level_metadata_indexing.ipynb) を参照。
## 結果の処理
### クリップを取得する
個々の結果クリップにアクセスする:
```python
results = video.search("your query")
for shot in results.get_shots():
print(f"Video: {shot.video_id}")
print(f"Start: {shot.start:.2f}s")
print(f"End: {shot.end:.2f}s")
print(f"Text: {shot.text}")
print("---")
```
### コンパイルされた結果を再生する
すべての一致するクリップを単一のコンパイルされたビデオとしてストリーミング再生する:
```python
results = video.search("your query")
stream_url = results.compile()
results.play() # opens compiled stream in browser
```
### クリップを抽出する
特定の結果クリップをダウンロードまたはストリーミングする:
```python
for shot in results.get_shots():
stream_url = shot.generate_stream()
print(f"Clip: {stream_url}")
```
## コレクション横断検索
コレクション内のすべてのビデオを横断して検索する:
```python
coll = conn.get_collection()
# Search across all videos in the collection
results = coll.search(
query="product demo",
search_type=SearchType.semantic,
)
for shot in results.get_shots():
print(f"Video: {shot.video_id} [{shot.start:.1f}s - {shot.end:.1f}s]")
```
> **注意:** コレクションレベルの検索は `SearchType.semantic` のみをサポートする。`SearchType.keyword` または `SearchType.scene` を `coll.search()` と組み合わせると `NotImplementedError` が発生する。キーワードやシーン検索には代わりに個々のビデオで `video.search()` を使用する。
## 検索 + コンパイル
一致するクリップをインデックス化、検索し、単一の再生可能なストリームにコンパイルする:
```python
video.index_spoken_words(force=True)
results = video.search(query="your query", search_type=SearchType.semantic)
stream_url = results.compile()
print(stream_url)
```
## ヒント
* **一度インデックス化、何度も検索**:インデックス作成は高コストな操作。一度インデックスが作成されれば、検索は速くなる。
* **インデックスタイプを組み合わせる**:音声単語とシーンの両方をインデックス化して、同じビデオですべての検索タイプを有効にする。
* **クエリの最適化**:セマンティック検索は単一のキーワードではなく説明的な自然言語フレーズで最もよく機能する。
* **精度向上のためにキーワード検索を使用**:正確な用語照合が必要なときは、キーワード検索でセマンティックドリフトを避けられる。
* **「結果なし」の処理**:一致するものがない場合、`video.search()``InvalidRequestError` を発生させる。常に検索呼び出しをtry/exceptで包み、`"No results found"` を空の結果セットとして扱うこと。
* **シーン検索ノイズのフィルタリング**:あいまいなクエリの場合、セマンティックシーン検索は低関連性の結果を返す可能性がある。ノイズをフィルタリングするために `score_threshold=0.3`(またはより高い値)を使用する。
* **べき等なインデックス作成**`index_spoken_words(force=True)` を使用すると安全に再インデックス化できる。`index_scenes()` には `force` パラメータがない——try/exceptで包み、`re.search(r"id\s+([a-f0-9]+)", str(e))` を使用してエラーメッセージから既存の `scene_index_id` を抽出する。

View File

@@ -0,0 +1,406 @@
# ストリーミングと再生
VideoDBはオンデマンドでストリーミングを生成し、任意の標準ビデオプレーヤーで即時再生できるHLS互換のURLを返す。レンダリング時間やエクスポート待ちは不要——編集、検索、合成されたコンテンツは即座にストリーミングできる。
## 前提条件
ビデオはストリーミングを生成するために**コレクションにアップロードされている必要がある**。検索ベースのストリーミングには、ビデオも**インデックス化されている必要がある**(音声単語および/またはシーン)。インデックス作成の詳細については [search.md](search.md) を参照。
## コアコンセプト
### ストリーミング生成
VideoDBのすべてのビデオ、検索結果、タイムラインは**ストリームURL**を生成できる。このURLはオンデマンドでコンパイルされるHLSHTTPライブストリーミングマニフェストを指す。
```python
# From a video
stream_url = video.generate_stream()
# From a timeline
stream_url = timeline.generate_stream()
# From search results
stream_url = results.compile()
```
## 単一ビデオのストリーミング
### 基本再生
```python
import videodb
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("your-video-id")
# Generate stream URL
stream_url = video.generate_stream()
print(f"Stream: {stream_url}")
# Open in default browser
video.play()
```
### 字幕付き
```python
# Index and add subtitles first
video.index_spoken_words(force=True)
stream_url = video.add_subtitle()
# Returned URL already includes subtitles
print(f"Subtitled stream: {stream_url}")
```
### 特定のセグメント
タイムスタンプ範囲のタイムラインを渡すことでビデオの一部のみをストリーミングする:
```python
# Stream seconds 10-30 and 60-90
stream_url = video.generate_stream(timeline=[(10, 30), (60, 90)])
print(f"Segment stream: {stream_url}")
```
## タイムラインコンポジションのストリーミング
マルチアセットコンポジションを構築してリアルタイムでストリーミングする:
```python
import videodb
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, AudioAsset, ImageAsset, TextAsset, TextStyle
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video(video_id)
music = coll.get_audio(music_id)
timeline = Timeline(conn)
# Main video content
timeline.add_inline(VideoAsset(asset_id=video.id))
# Background music overlay (starts at second 0)
timeline.add_overlay(0, AudioAsset(asset_id=music.id))
# Text overlay at the beginning
timeline.add_overlay(0, TextAsset(
text="Live Demo",
duration=3,
style=TextStyle(fontsize=48, fontcolor="white", boxcolor="#000000"),
))
# Generate the composed stream
stream_url = timeline.generate_stream()
print(f"Composed stream: {stream_url}")
```
**重要な注意事項:** `add_inline()``VideoAsset` のみを受け入れる。`AudioAsset``ImageAsset``TextAsset` には `add_overlay()` を使用する。
詳細なタイムライン編集については [editor.md](editor.md) を参照。
## 検索結果のストリーミング
すべての一致するクリップを含む単一のストリームに検索結果をコンパイルする:
```python
from videodb import SearchType
from videodb.exceptions import InvalidRequestError
video.index_spoken_words(force=True)
try:
results = video.search("key announcement", search_type=SearchType.semantic)
# Compile all matching shots into one stream
stream_url = results.compile()
print(f"Search results stream: {stream_url}")
# Or play directly
results.play()
except InvalidRequestError as exc:
if "No results found" in str(exc):
print("No matching announcement segments were found.")
else:
raise
```
### 個別の検索結果をストリーミングする
```python
from videodb.exceptions import InvalidRequestError
try:
results = video.search("product demo", search_type=SearchType.semantic)
for i, shot in enumerate(results.get_shots()):
stream_url = shot.generate_stream()
print(f"Hit {i+1} [{shot.start:.1f}s-{shot.end:.1f}s]: {stream_url}")
except InvalidRequestError as exc:
if "No results found" in str(exc):
print("No product demo segments matched the query.")
else:
raise
```
## オーディオ再生
オーディオコンテンツの署名付き再生URLを取得する
```python
audio = coll.get_audio(audio_id)
playback_url = audio.generate_url()
print(f"Audio URL: {playback_url}")
```
## 完全なワークフロー例
### 検索からストリーミングへのパイプライン
単一のワークフローで検索、タイムラインコンポジション、ストリーミングを組み合わせる:
```python
import videodb
from videodb import SearchType
from videodb.exceptions import InvalidRequestError
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, TextAsset, TextStyle
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("your-video-id")
video.index_spoken_words(force=True)
# Search for key moments
queries = ["introduction", "main demo", "Q&A"]
timeline = Timeline(conn)
timeline_offset = 0.0
for query in queries:
try:
results = video.search(query, search_type=SearchType.semantic)
shots = results.get_shots()
except InvalidRequestError as exc:
if "No results found" in str(exc):
shots = []
else:
raise
if not shots:
continue
# Add the section label where this batch starts in the compiled timeline
timeline.add_overlay(timeline_offset, TextAsset(
text=query.title(),
duration=2,
style=TextStyle(fontsize=36, fontcolor="white", boxcolor="#222222"),
))
for shot in shots:
timeline.add_inline(
VideoAsset(asset_id=shot.video_id, start=shot.start, end=shot.end)
)
timeline_offset += shot.end - shot.start
stream_url = timeline.generate_stream()
print(f"Dynamic compilation: {stream_url}")
```
### マルチビデオストリーム
異なるビデオからのクリップを単一のストリームに組み合わせる:
```python
import videodb
from videodb.timeline import Timeline
from videodb.asset import VideoAsset
conn = videodb.connect()
coll = conn.get_collection()
video_clips = [
{"id": "vid_001", "start": 0, "end": 15},
{"id": "vid_002", "start": 10, "end": 30},
{"id": "vid_003", "start": 5, "end": 25},
]
timeline = Timeline(conn)
for clip in video_clips:
timeline.add_inline(
VideoAsset(asset_id=clip["id"], start=clip["start"], end=clip["end"])
)
stream_url = timeline.generate_stream()
print(f"Multi-video stream: {stream_url}")
```
### 条件付きストリーミングアセンブリ
検索結果の可用性に基づいてストリームを動的に構築する:
```python
import videodb
from videodb import SearchType
from videodb.exceptions import InvalidRequestError
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, TextAsset, TextStyle
conn = videodb.connect()
coll = conn.get_collection()
video = coll.get_video("your-video-id")
video.index_spoken_words(force=True)
timeline = Timeline(conn)
# Try to find specific content; fall back to full video
topics = ["opening remarks", "technical deep dive", "closing"]
found_any = False
timeline_offset = 0.0
for topic in topics:
try:
results = video.search(topic, search_type=SearchType.semantic)
shots = results.get_shots()
except InvalidRequestError as exc:
if "No results found" in str(exc):
shots = []
else:
raise
if shots:
found_any = True
timeline.add_overlay(timeline_offset, TextAsset(
text=topic.title(),
duration=2,
style=TextStyle(fontsize=32, fontcolor="white", boxcolor="#1a1a2e"),
))
for shot in shots:
timeline.add_inline(
VideoAsset(asset_id=shot.video_id, start=shot.start, end=shot.end)
)
timeline_offset += shot.end - shot.start
if found_any:
stream_url = timeline.generate_stream()
print(f"Curated stream: {stream_url}")
else:
# Fall back to full video stream
stream_url = video.generate_stream()
print(f"Full video stream: {stream_url}")
```
### ライブイベントのリキャップ
イベント録音を複数のセクションを持つストリーミング可能なリキャップに処理する:
```python
import videodb
from videodb import SearchType
from videodb.exceptions import InvalidRequestError
from videodb.timeline import Timeline
from videodb.asset import VideoAsset, AudioAsset, ImageAsset, TextAsset, TextStyle
conn = videodb.connect()
coll = conn.get_collection()
# Upload event recording
event = coll.upload(url="https://example.com/event-recording.mp4")
event.index_spoken_words(force=True)
# Generate background music
music = coll.generate_music(
prompt="upbeat corporate background music",
duration=120,
)
# Generate title image
title_img = coll.generate_image(
prompt="modern event recap title card, dark background, professional",
aspect_ratio="16:9",
)
# Build the recap timeline
timeline = Timeline(conn)
timeline_offset = 0.0
# Main video segments from search
try:
keynote = event.search("keynote announcement", search_type=SearchType.semantic)
keynote_shots = keynote.get_shots()[:5]
except InvalidRequestError as exc:
if "No results found" in str(exc):
keynote_shots = []
else:
raise
if keynote_shots:
keynote_start = timeline_offset
for shot in keynote_shots:
timeline.add_inline(
VideoAsset(asset_id=shot.video_id, start=shot.start, end=shot.end)
)
timeline_offset += shot.end - shot.start
else:
keynote_start = None
try:
demo = event.search("product demo", search_type=SearchType.semantic)
demo_shots = demo.get_shots()[:5]
except InvalidRequestError as exc:
if "No results found" in str(exc):
demo_shots = []
else:
raise
if demo_shots:
demo_start = timeline_offset
for shot in demo_shots:
timeline.add_inline(
VideoAsset(asset_id=shot.video_id, start=shot.start, end=shot.end)
)
timeline_offset += shot.end - shot.start
else:
demo_start = None
# Overlay title card image
timeline.add_overlay(0, ImageAsset(
asset_id=title_img.id, width=100, height=100, x=80, y=20, duration=5
))
# Overlay section labels at the correct timeline offsets
if keynote_start is not None:
timeline.add_overlay(max(5, keynote_start), TextAsset(
text="Keynote Highlights",
duration=3,
style=TextStyle(fontsize=40, fontcolor="white", boxcolor="#0d1117"),
))
if demo_start is not None:
timeline.add_overlay(max(5, demo_start), TextAsset(
text="Demo Highlights",
duration=3,
style=TextStyle(fontsize=36, fontcolor="white", boxcolor="#0d1117"),
))
# Overlay background music
timeline.add_overlay(0, AudioAsset(
asset_id=music.id, fade_in_duration=3
))
# Stream the final recap
stream_url = timeline.generate_stream()
print(f"Event recap: {stream_url}")
```
***
## ヒント
* **HLS互換性**ストリームURLはHLSマニフェスト`.m3u8`を返す。Safariでネイティブに動作し、他のブラウザではhls.jsや類似のライブラリで動作する。
* **オンデマンドコンパイル**:ストリーミングはリクエスト時にサーバーサイドでコンパイルされる。初回再生には短いコンパイル遅延が発生する場合がある;同じコンポジションの後続再生はキャッシュされる。
* **キャッシング**`video.generate_stream()`引数なしの2回目の呼び出しは再コンパイルせずにキャッシュされたストリームURLを返す。
* **セグメントストリーム**`video.generate_stream(timeline=[(start, end)])` は完全な `Timeline` オブジェクトを構築せずに特定のクリップをストリーミングする最速の方法。
* **インラインとオーバーレイ**`add_inline()``VideoAsset` のみを受け入れ、アセットをメイントラックに順番に配置する。`add_overlay()``AudioAsset``ImageAsset``TextAsset` を受け入れ、指定された開始時間にそれらを上にオーバーレイする。
* **TextStyleのデフォルト**`TextStyle` のデフォルトは `font='Sans'``fontcolor='black'`。テキストの背景色には `boxcolor``bgcolor` ではない)を使用する。
* **生成との組み合わせ**`coll.generate_music(prompt, duration)``coll.generate_image(prompt, aspect_ratio)` を使用してタイムラインコンポジションのアセットを作成する。
* **再生**`.play()` はデフォルトのシステムブラウザでストリームURLを開く。プログラム的な使用には直接URLの文字列を処理する。

View File

@@ -0,0 +1,142 @@
# ユースケース
VideoDBが実現する一般的なワークフローと機能。コードの詳細については [api-reference.md](api-reference.md)、[capture.md](capture.md)、[editor.md](editor.md)、[search.md](search.md) を参照。
***
## ビデオ検索とハイライト
### ハイライトリールの作成
長いビデオ会議講演、講義、カンファレンス録画をアップロードし、トピック別「製品発表」「Q&Aセッション」「デモ」に主要なモーメントを検索し、一致するクリップを共有可能なハイライトリールに自動的にアセンブルする。
### 検索可能なビデオライブラリの構築
ビデオをコレクションに一括アップロードし、検索のために音声コンテンツをインデックス化し、ライブラリ全体でクエリを実行する。数百時間のコンテンツから特定のトピックを即座に見つける。
### 特定のクリップの抽出
クエリに一致するセグメントを検索し「予算の議論」「アクションアイテム」、各一致するセグメントを独自のストリームURLを持つ独立したクリップとして抽出する。
***
## ビデオの強化
### プロフェッショナルな仕上げを追加する
生の素材を取得して強化する:
* 音声に基づいて字幕を自動生成する
* 特定のタイムスタンプにカスタムサムネイルを追加する
* バックグラウンドミュージックオーバーレイ
* 生成された画像を使用したオープニング/エンディングシーケンス
### AIで強化されたコンテンツ
既存のビデオと生成AIを組み合わせる
* トランスクリプトコンテンツからテキストサマリーを生成する
* ビデオの長さに合わせたバックグラウンドミュージックを作成する
* タイトルカードとオーバーレイ画像を生成する
* すべての要素を磨き上げた最終出力にミックスする
***
## リアルタイム録画(デスクトップ/会議)
### AIによる画面+オーディオ録画
画面、マイク、システムオーディオを同時にキャプチャする。リアルタイムで取得:
* **ライブ転写** - 音声を即座にテキストに変換
* **オーディオサマリー** - 定期的に生成されるAIによる議論サマリー
* **ビジュアルインデックス** - 画面アクティビティのAI説明
### サマリー機能付き会議録画
会議を録画して全参加者の発言をリアルタイムで転写する。主要な議論のポイント、決定事項、アクションアイテムを含む定期的なサマリーをリアルタイムで受け取る。
### 画面アクティビティ追跡
AIが生成した説明で画面アクティビティを追跡する
* 「ユーザーはGoogle Sheetsでスプレッドシートを閲覧しています」
* 「ユーザーはPythonファイルを含むコードエディターに切り替えました」
* 「画面共有付きのビデオ通話が進行中です」
### セッション後の処理
録画が終わると、録音は永続的なビデオとしてエクスポートされる。その後:
* 検索可能なトランスクリプトを生成する
* 録画内の特定のトピックを検索する
* 重要なモーメントのクリップを抽出する
* ストリームURLまたはプレーヤーリンクで共有する
***
## ライブストリームインテリジェンスRTSP/RTMP
### 外部ストリームへの接続
RTSPまたはRTMPソースセキュリティカメラ、エンコーダー、ブロードキャストからライブビデオを取り込む。コンテンツをリアルタイムで処理してインデックス化する。
### リアルタイムイベント検出
ライブストリームで検出するイベントを定義する:
* 「制限区域に人が入った」
* 「交差点での交通違反」
* 「棚に製品が見える」
イベントが発生したときにWebSocketまたはWebhook経由でアラートを受け取る。
### ライブストリーム検索
録画されたライブストリームコンテンツ内を検索する。数時間の連続した素材から特定のモーメントを見つけてクリップを生成する。
***
## コンテンツモデレーションとセキュリティ
### 自動化されたコンテンツレビュー
AIを使用してビデオシーンをインデックス化し、問題のあるコンテンツを検索する。暴力、不適切なコンテンツ、またはポリシー違反を含むビデオにフラグを立てる。
### 不適切な言葉の検出
オーディオ内の不適切な言葉を検出して特定する。検出されたタイムスタンプにビープ音をオーバーレイするオプションもある。
***
## プラットフォーム統合
### ソーシャルメディア向けフォーマット変換
異なるプラットフォーム向けにビデオのフォーマットを変換する:
* 縦型9:16TikTok、Reels、Shorts向け
* 正方形1:1Instagramフィード向け
* 横型16:9YouTube向け
### 配信のためのトランスコード
異なる配信ターゲットに向けて解像度、ビットレート、品質を変更する。Web、モバイル、ブロードキャスト出力に最適化されたストリーム。
### 共有可能なリンクの生成
すべての操作は再生可能なストリームURLを生成する。Webプレーヤーへの埋め込み、直接共有、または既存のプラットフォームとの統合が可能。
***
## ワークフローのサマリー
| 目標 | VideoDBのアプローチ |
|------|------------------|
| ビデオ内のセグメントを見つける | 音声/シーンをインデックス化 → 検索 → クリップをアセンブル |
| ハイライトリールを作成する | 複数のトピックを検索 → タイムラインを構築 → ストリームを生成 |
| 字幕を追加する | 音声をインデックス化 → 字幕オーバーレイを追加 |
| 画面録画 + AI | 録画を開始 → AIパイプラインを実行 → ビデオをエクスポート |
| ライブストリームを監視する | RTSPに接続 → シーンをインデックス化 → アラートを作成 |
| ソーシャルメディア向けにフォーマット変換 | ターゲットのアスペクト比にリフレーム |
| クリップをマージする | 複数のアセットでタイムラインを構築 → ストリームを生成 |