Skip to main content

Video Types

VideoFrame

Video frame data received from the stream.
@dataclass(frozen=True, slots=True)
class VideoFrame:
    data: np.ndarray      # RGB uint8 array, shape (height, width, 3)
    width: int            # Frame width in pixels
    height: int           # Frame height in pixels
    timestamp_ms: int     # Presentation timestamp in milliseconds
PropertyTypeDescription
datanp.ndarrayRGB uint8 array with shape (height, width, 3)
widthintFrame width in pixels
heightintFrame height in pixels
timestamp_msintPresentation timestamp in milliseconds
Example usage:
import cv2
from PIL import Image

def on_frame(frame: VideoFrame) -> None:
    # OpenCV (note: OpenCV uses BGR)
    cv2.imshow("video", cv2.cvtColor(frame.data, cv2.COLOR_RGB2BGR))

    # PIL
    image = Image.fromarray(frame.data)

    # Headless processing
    processed = some_ml_model(frame.data)

Recording Types

Recording

Recording data with presigned URLs for a stream.
@dataclass(frozen=True, slots=True)
class Recording:
    stream_id: str              # Unique stream identifier
    video_url: str | None       # Presigned URL for video file
    events_url: str | None      # Presigned URL for events JSON
    thumbnail_url: str | None   # Presigned URL for thumbnail image
    preview_url: str | None     # Presigned URL for preview video
    frame_count: int | None     # Total number of frames
    duration_seconds: float | None  # Duration in seconds
PropertyTypeDescription
stream_idstrUnique stream identifier
video_urlstr | NonePresigned URL for video file (MP4)
events_urlstr | NonePresigned URL for events log (JSONL)
thumbnail_urlstr | NonePresigned URL for thumbnail image (JPEG)
preview_urlstr | NonePresigned URL for preview video (MP4)
frame_countint | NoneTotal number of frames
duration_secondsfloat | NoneDuration in seconds
URLs are valid for a limited time (typically 1 hour).

StreamRecordingInfo

Summary info for a stream recording in a list.
@dataclass(frozen=True, slots=True)
class StreamRecordingInfo:
    stream_id: str              # Unique stream identifier
    width: int                  # Video width in pixels
    height: int                 # Video height in pixels
    started_at: str             # ISO 8601 timestamp
    ended_at: str | None        # ISO 8601 timestamp or None if active
    duration_seconds: float | None  # Duration in seconds
PropertyTypeDescription
stream_idstrUnique stream identifier
widthintVideo width in pixels
heightintVideo height in pixels
started_atstrISO 8601 timestamp when stream started
ended_atstr | NoneISO 8601 timestamp when stream ended
duration_secondsfloat | NoneDuration in seconds

StreamRecordingsList

Paginated list of stream recordings.
@dataclass(frozen=True, slots=True)
class StreamRecordingsList:
    recordings: list[StreamRecordingInfo]  # List of recording info
    total: int                              # Total recordings available
    limit: int                              # Max per request
    offset: int                             # Recordings skipped
PropertyTypeDescription
recordingslist[StreamRecordingInfo]List of recording summaries
totalintTotal recordings available
limitintLimit used in request
offsetintOffset used in request

Simulate API Types

Simulate API types were added in v1.0.0

ScriptEntry

An entry in a simulation script.
from typing import TypedDict, NotRequired

class StartAction(TypedDict):
    prompt: str
    image: NotRequired[str | bytes]  # Optional image for image-to-video

class InteractAction(TypedDict):
    prompt: str

class ScriptEntry(TypedDict):
    timestamp_ms: int                    # When this action occurs (milliseconds from start)
    start: NotRequired[StartAction]      # Begin a new stream
    interact: NotRequired[InteractAction] # Send an interaction
    end: NotRequired[dict]               # End the stream (empty dict)
PropertyTypeDescription
timestamp_msintWhen this action occurs (milliseconds from start)
start{ prompt: str, image?: str | bytes }Begin a new stream with initial prompt
interact{ prompt: str }Send an interaction prompt
end{}End the current stream (empty dict)

SimulationStream

Output stream from a simulation job, including recording artifact URLs.
@dataclass(frozen=True, slots=True)
class SimulationStream:
    stream_id: str                    # Unique stream identifier
    video_url: str | None             # Presigned URL for the video file
    events_url: str | None            # Presigned URL for the events JSON
    thumbnail_url: str | None         # Presigned URL for the thumbnail image
    preview_url: str | None           # Presigned URL for the preview video
    frame_count: int | None           # Total number of frames
    duration_seconds: float | None    # Duration of the video in seconds
    script_index: int                 # Index of the script in batch mode (0 for single script)
PropertyTypeDescription
stream_idstrUnique stream identifier
video_urlstr | NonePresigned URL for the video file (MP4)
events_urlstr | NonePresigned URL for the events log (JSONL)
thumbnail_urlstr | NonePresigned URL for the thumbnail image (JPEG)
preview_urlstr | NonePresigned URL for the preview video (MP4)
frame_countint | NoneTotal number of frames in the video
duration_secondsfloat | NoneDuration of the video in seconds
script_indexintIndex of the script in batch mode (0 for single script)

SimulationJobInfo

Summary information for a simulation job in a list.
@dataclass(frozen=True, slots=True)
class SimulationJobInfo:
    job_id: str                       # Unique identifier for the job
    status: str                       # Current job status
    priority: str                     # Job priority
    created_at: str                   # ISO timestamp when job was created
    completed_at: str | None          # ISO timestamp when job completed
    error_message: str | None         # Error message if job failed
PropertyTypeDescription
job_idstrUnique identifier for the job
statusstrCurrent job status (pending, dispatched, processing, completed, failed, cancelled)
prioritystrJob priority
created_atstrISO timestamp when job was created
completed_atstr | NoneISO timestamp when job completed
error_messagestr | NoneError message if job failed

SimulationJobDetail

Detailed information about a simulation job.
@dataclass(frozen=True, slots=True)
class SimulationJobDetail:
    job_id: str                       # Unique identifier for the job
    status: SimulationJobStatus       # Current job status
    priority: str                     # Job priority
    created_at: str                   # ISO timestamp when job was created
    dispatched_at: str | None         # ISO timestamp when job was dispatched
    started_at: str | None            # ISO timestamp when processing started
    completed_at: str | None          # ISO timestamp when job completed
    error_message: str | None         # Error message if job failed
    assigned_region: str | None       # Region where the job is being processed
    retry_count: int                  # Number of times the job has been retried
    streams: list[SimulationStream]   # Output streams from the simulation
    estimated_wait_minutes: float | None  # Estimated wait time in minutes
PropertyTypeDescription
job_idstrUnique identifier for the job
statusSimulationJobStatusCurrent job status (pending, dispatched, processing, completed, failed, cancelled)
prioritystrJob priority
created_atstrISO timestamp when job was created
dispatched_atstr | NoneISO timestamp when the job was dispatched to a worker
started_atstr | NoneISO timestamp when processing started
completed_atstr | NoneISO timestamp when job completed
error_messagestr | NoneError message if job failed
assigned_regionstr | NoneRegion where the job is being processed
retry_countintNumber of times the job has been retried
streamslist[SimulationStream]Output streams from the simulation
estimated_wait_minutesfloat | NoneEstimated wait time in minutes

SimulationJobsList

Paginated list of simulation jobs.
@dataclass(frozen=True, slots=True)
class SimulationJobsList:
    jobs: list[SimulationJobInfo]     # List of simulation job summaries
    total: int                        # Total jobs available
    limit: int                        # Limit used in request
    offset: int                       # Offset used in request
PropertyTypeDescription
jobslist[SimulationJobInfo]List of simulation job summaries
totalintTotal jobs available
limitintLimit used in request
offsetintOffset used in request

Broadcast Types

BroadcastInfo

Broadcast playback URLs for spectators, received via the on_broadcast_ready callback when broadcast=True is passed to start_stream().
@dataclass(frozen=True, slots=True)
class BroadcastInfo:
   webrtc_url: str
   spectator_token: str
   hls_url: str | None = None
PropertyTypeDescription
webrtc_urlstrWebRTC (WHEP) endpoint URL for spectator playback.
spectator_tokenstrToken required to authenticate a spectator connection.
hls_urlstr | NoneOptional HLS playback URL, if available.

SpectatorConnection

Handle for an active spectator connection. Returned by connect_to_stream().
@dataclass(frozen=True, slots=True)
class SpectatorConnection:
   @property
   def is_connected(self) -> bool
   @property
   def peer_connection(self) -> object | None
   async def disconnect(self) -> None
PropertyTypeDescription
is_connectedboolIndicates whether the spectator connection is currently active.
peer_connectionObject | NoneThe underlying RTCPeerConnection. Can be used to retrieve WebRTC statistics (e.g., via getStats()).
MethodDescription
disconnectAsynchronously close the spectator connection and release resources.
from odyssey import connect_to_stream

def handle_frame(frame):
   cv2.imshow("Broadcast", frame.data)
   cv2.waitKey(1)

connection = await connect_to_stream(
   webrtc_url="http://localhost:8889/live/stream123",
   spectator_token="spectator_abc...",
   on_video_frame=handle_frame,
)

# Later
await connection.disconnect()

Status Types

ConnectionStatus

class ConnectionStatus(str, Enum):
    AUTHENTICATING = "authenticating"  # Authenticating with Odyssey API
    CONNECTING = "connecting"          # Connecting to signaling server
    RECONNECTING = "reconnecting"      # Reconnecting after disconnect
    CONNECTED = "connected"            # Connected and ready
    DISCONNECTED = "disconnected"      # Disconnected (clean)
    FAILED = "failed"                  # Connection failed (fatal)

Configuration Types

ClientConfig

Configuration for the Odyssey client.
@dataclass
class ClientConfig:
    api_key: str                        # API key for authentication (required)
    api_url: str = "https://api.odyssey.ml"  # API URL
    dev: DevConfig = DevConfig()        # Development settings
    advanced: AdvancedConfig = AdvancedConfig()  # Advanced settings

DevConfig

Development/debug settings.
@dataclass
class DevConfig:
    signaling_url: str | None = None  # Direct signaling URL (bypasses API)
    session_id: str | None = None     # Session ID for direct connection
    debug: bool = False               # Enable debug logging

AdvancedConfig

Advanced connection settings.
@dataclass
class AdvancedConfig:
    max_retries: int = 5              # Max retry attempts
    initial_retry_delay_ms: int = 1000  # Initial retry delay
    max_retry_delay_ms: int = 2000    # Max retry delay
    retry_backoff_multiplier: float = 2.0  # Backoff multiplier
    queue_timeout_s: int = 30         # Queue timeout in seconds

Exceptions

OdysseyError

Base exception for all Odyssey errors.
class OdysseyError(Exception):
    pass

OdysseyAuthError

Raised when authentication fails.
class OdysseyAuthError(OdysseyError):
    pass

OdysseyConnectionError

Raised when connection fails.
class OdysseyConnectionError(OdysseyError):
    pass

OdysseyStreamError

Raised when a stream operation fails.
class OdysseyStreamError(OdysseyError):
    pass

Error Handling

Fatal vs Non-Fatal Errors

The on_error handler receives a fatal boolean parameter:
FatalDescriptionAction Required
TrueConnection cannot continueReconnect or exit
FalseRecoverable errorMay retry or notify user

Common Errors

ErrorDescription
OdysseyAuthErrorAPI key is invalid or expired
OdysseyConnectionError: No streamers availableNo streamers available, try again later
OdysseyConnectionError: Timed out waiting for a streamerQueue timeout expired
OdysseyStreamError: Cannot start stream: client is disconnectedAttempted operation while disconnected

Environment Variables

VariableDescription
ODYSSEY_API_URLOverride default API URL
ODYSSEY_API_KEYDefault API key (used by examples)