Jellyfish Python Server SDK

Jellyfish Python Server SDK

CircleCI

Python server SDK for the Jellyfish Media Server.

Read the docs here

Installation

pip install jellyfish-server-sdk

Usage

The SDK exports two main classes for interacting with Jellyfish server: RoomApi and Notifier.

RoomApi wraps http REST api calls, while Notifier is responsible for receiving real-time updates from the server.

RoomApi

Create a RoomApi instance, providing the jellyfish server address and api token

from jellyfish import RoomApi

room_api = RoomApi(server_address="localhost:5002", server_api_token="development")

You can use it to interact with Jellyfish, manage rooms, peers and components

# Create a room
jellyfish_address, room = room_api.create_room(video_codec="h264", webhook_url="http://localhost:5000/webhook")
# '127.0.0.1:5002', Room(components=[], config=RoomConfig(max_peers=None, video_codec=<RoomConfigVideoCodec.H264: 'h264'>, webhook_url='http://localhost:5000/webhook'), id='1d905478-ccfc-44d6-a6e7-8ccb1b38d955', peers=[])

# Add peer to the room
from jellyfish import PeerOptionsWebRTC

peer_token, peer_webrtc = room_api.add_peer(room.id, options=PeerOptionsWebRTC())
# 'M8TUGhj-L11KpyG-2zBPIo', Peer(id='b1232c7e-c969-4450-acdf-ea24f3cdd7f6', status=<PeerStatus.DISCONNECTED: 'disconnected'>, type='webrtc')

# Add component to the room
from jellyfish import ComponentOptionsHLS

component_hls = room_api.add_component(room.id, options=ComponentOptionsHLS())
# ComponentHLS(id='5f062447-a9f7-45ed-8d1b-511f77dc78ae', properties=ComponentPropertiesHLS(low_latency=False, persistent=False, playable=False, subscribe_mode=<ComponentPropertiesHLSSubscribeMode.AUTO: 'auto'>, target_window_duration=None), type='hls')

All methods in RoomApi may raise one of the exceptions deriving from jellyfish.errors.HTTPError. They are defined in jellyfish.errors.

Notifier

Notifier allows for receiving real-time updates from the Jellyfish Server.

You can read more about notifications in the Jellyfish Docs.

Create Notifier instance

from jellyfish import Notifier

notifier = Notifier(server_address='localhost:5002', server_api_token='development')

Then define handlers for incoming messages

@notifier.on_server_notification
def handle_notification(server_notification):
    print(f'Received a notification: {server_notification}')

@notifier.on_metrics
def handle_metrics(metrics_report):
    print(f'Received WebRTC metrics: {metrics_report}')

After that you can start the notifier

async def test_notifier():
    notifier_task = asyncio.create_task(notifier.connect())

    # Wait for notifier to be ready to receive messages
    await notifier.wait_ready()

    # Create a room to trigger a server notification
    room_api = RoomApi()
    room_api.create_room()

    await notifier_task

asyncio.run(test_notifier())

# Received a notification: ServerMessageRoomCreated(room_id='69a3fd1a-6a4d-47bc-ae54-0c72b0d05e29')
# Received WebRTC metrics: ServerMessageMetricsReport(metrics='{}')

Testing

You can test the SDK by running

poetry run ci_test

In local development you can use

poetry run local_test

Format & Lint

You can format code by running

poetry run format

You can check linter by running

poetry run lint

Copyright 2023, Software Mansion

Software Mansion

Licensed under the Apache License, Version 2.0

 1"""
 2    .. include:: ../README.md
 3"""
 4
 5# pylint: disable=locally-disabled, no-name-in-module, import-error
 6
 7# Exceptions and Server Messages
 8from jellyfish import errors, events
 9
10# Models
11from jellyfish._openapi_client.models import (
12    ComponentFile,
13    ComponentHLS,
14    ComponentOptionsFile,
15    ComponentOptionsHLS,
16    ComponentOptionsHLSSubscribeMode,
17    ComponentOptionsRTSP,
18    ComponentPropertiesFile,
19    ComponentPropertiesHLS,
20    ComponentPropertiesHLSSubscribeMode,
21    ComponentPropertiesRTSP,
22    ComponentRTSP,
23    Peer,
24    PeerOptionsWebRTC,
25    PeerStatus,
26    Room,
27    RoomConfig,
28    RoomConfigVideoCodec,
29)
30
31# API
32from jellyfish._webhook_notifier import receive_json
33from jellyfish._ws_notifier import Notifier
34from jellyfish.api._recording_api import RecordingApi
35from jellyfish.api._room_api import RoomApi
36
37__all__ = [
38    "RoomApi",
39    "RecordingApi",
40    "Notifier",
41    "receive_json",
42    "Room",
43    "RoomConfig",
44    "RoomConfigVideoCodec",
45    "Peer",
46    "PeerOptionsWebRTC",
47    "PeerStatus",
48    "ComponentHLS",
49    "ComponentOptionsHLS",
50    "ComponentOptionsHLSSubscribeMode",
51    "ComponentPropertiesHLS",
52    "ComponentPropertiesHLSSubscribeMode",
53    "ComponentRTSP",
54    "ComponentOptionsRTSP",
55    "ComponentPropertiesRTSP",
56    "ComponentFile",
57    "ComponentOptionsFile",
58    "ComponentPropertiesFile",
59    "events",
60    "errors",
61]
62__docformat__ = "restructuredtext"
class RoomApi(jellyfish.api._base_api.BaseApi):
 36class RoomApi(BaseApi):
 37    """Allows for managing rooms"""
 38
 39    def __init__(
 40        self,
 41        server_address: str = "localhost:5002",
 42        server_api_token: str = "development",
 43        secure: bool = False,
 44    ):
 45        """
 46        Create RoomApi instance, providing the jellyfish address and api token.
 47        Set secure to `True` for `https` and `False` for `http` connection (default).
 48        """
 49        super().__init__(
 50            server_address=server_address,
 51            server_api_token=server_api_token,
 52            secure=secure,
 53        )
 54
 55    def create_room(
 56        self,
 57        room_id: str = None,
 58        max_peers: int = None,
 59        video_codec: Literal["h264", "vp8"] = None,
 60        webhook_url: str = None,
 61    ) -> (str, Room):
 62        """
 63        Creates a new room
 64
 65        Returns a tuple (`jellyfish_address`, `Room`) - the address of the Jellyfish
 66        in which the room has been created and the created `Room`
 67
 68        The returned address may be different from the current `RoomApi` instance.
 69        In such case, a new `RoomApi` instance has to be created using
 70        the returned address in order to interact with the room.
 71        """
 72
 73        if video_codec is not None:
 74            video_codec = RoomConfigVideoCodec(video_codec)
 75        else:
 76            video_codec = None
 77
 78        room_config = RoomConfig(
 79            room_id=room_id,
 80            max_peers=max_peers,
 81            video_codec=video_codec,
 82            webhook_url=webhook_url,
 83        )
 84
 85        resp = self._request(room_create_room, json_body=room_config)
 86        return (resp.data.jellyfish_address, resp.data.room)
 87
 88    def delete_room(self, room_id: str) -> None:
 89        """Deletes a room"""
 90
 91        return self._request(room_delete_room, room_id=room_id)
 92
 93    def get_all_rooms(self) -> list:
 94        """Returns list of all rooms"""
 95
 96        return self._request(room_get_all_rooms).data
 97
 98    def get_room(self, room_id: str) -> Room:
 99        """Returns room with the given id"""
100
101        return self._request(room_get_room, room_id=room_id).data
102
103    def add_peer(self, room_id: str, options: PeerOptionsWebRTC) -> (str, Peer):
104        """
105        Creates peer in the room
106
107        Currently only `webrtc` peer is supported
108
109        Returns a tuple (`peer_token`, `Peer`) - the token needed by Peer
110        to authenticate to Jellyfish and the new `Peer`
111        """
112
113        peer_type = "webrtc"
114        json_body = AddPeerJsonBody(type=peer_type, options=options)
115
116        resp = self._request(room_add_peer, room_id=room_id, json_body=json_body)
117        return (resp.data.token, resp.data.peer)
118
119    def delete_peer(self, room_id: str, peer_id: str) -> None:
120        """Deletes peer"""
121
122        return self._request(room_delete_peer, id=peer_id, room_id=room_id)
123
124    def add_component(
125        self,
126        room_id: str,
127        options: Union[ComponentOptionsFile, ComponentOptionsHLS, ComponentOptionsRTSP],
128    ) -> Union[ComponentFile, ComponentHLS, ComponentRTSP]:
129        """Creates component in the room"""
130
131        if isinstance(options, ComponentOptionsFile):
132            component_type = "file"
133        elif isinstance(options, ComponentOptionsHLS):
134            component_type = "hls"
135        elif isinstance(options, ComponentOptionsRTSP):
136            component_type = "rtsp"
137        else:
138            raise ValueError(
139                "options must be ComponentFile, ComponentOptionsHLS"
140                "or ComponentOptionsRTSP"
141            )
142
143        json_body = AddComponentJsonBody(type=component_type, options=options)
144
145        return self._request(
146            room_add_component, room_id=room_id, json_body=json_body
147        ).data
148
149    def delete_component(self, room_id: str, component_id: str) -> None:
150        """Deletes component"""
151
152        return self._request(room_delete_component, id=component_id, room_id=room_id)
153
154    def hls_subscribe(self, room_id: str, origins: [str]):
155        """
156        In order to subscribe to HLS peers/components,
157        the HLS component should be initialized with the subscribe_mode set to manual.
158        This mode proves beneficial when you do not wish to record or stream
159        all the available streams within a room via HLS.
160        It allows for selective addition instead –
161        you can manually select specific streams.
162        For instance, you could opt to record only the stream of an event's host.
163        """
164
165        return self._request(
166            hls_subscribe_hls_to,
167            room_id=room_id,
168            json_body=SubscriptionConfig(origins=origins),
169        )

Allows for managing rooms

RoomApi( server_address: str = 'localhost:5002', server_api_token: str = 'development', secure: bool = False)
39    def __init__(
40        self,
41        server_address: str = "localhost:5002",
42        server_api_token: str = "development",
43        secure: bool = False,
44    ):
45        """
46        Create RoomApi instance, providing the jellyfish address and api token.
47        Set secure to `True` for `https` and `False` for `http` connection (default).
48        """
49        super().__init__(
50            server_address=server_address,
51            server_api_token=server_api_token,
52            secure=secure,
53        )

Create RoomApi instance, providing the jellyfish address and api token. Set secure to True for https and False for http connection (default).

def create_room( self, room_id: str = None, max_peers: int = None, video_codec: Literal['h264', 'vp8'] = None, webhook_url: str = None) -> (<class 'str'>, <class 'Room'>):
55    def create_room(
56        self,
57        room_id: str = None,
58        max_peers: int = None,
59        video_codec: Literal["h264", "vp8"] = None,
60        webhook_url: str = None,
61    ) -> (str, Room):
62        """
63        Creates a new room
64
65        Returns a tuple (`jellyfish_address`, `Room`) - the address of the Jellyfish
66        in which the room has been created and the created `Room`
67
68        The returned address may be different from the current `RoomApi` instance.
69        In such case, a new `RoomApi` instance has to be created using
70        the returned address in order to interact with the room.
71        """
72
73        if video_codec is not None:
74            video_codec = RoomConfigVideoCodec(video_codec)
75        else:
76            video_codec = None
77
78        room_config = RoomConfig(
79            room_id=room_id,
80            max_peers=max_peers,
81            video_codec=video_codec,
82            webhook_url=webhook_url,
83        )
84
85        resp = self._request(room_create_room, json_body=room_config)
86        return (resp.data.jellyfish_address, resp.data.room)

Creates a new room

Returns a tuple (jellyfish_address, Room) - the address of the Jellyfish in which the room has been created and the created Room

The returned address may be different from the current RoomApi instance. In such case, a new RoomApi instance has to be created using the returned address in order to interact with the room.

def delete_room(self, room_id: str) -> None:
88    def delete_room(self, room_id: str) -> None:
89        """Deletes a room"""
90
91        return self._request(room_delete_room, room_id=room_id)

Deletes a room

def get_all_rooms(self) -> list:
93    def get_all_rooms(self) -> list:
94        """Returns list of all rooms"""
95
96        return self._request(room_get_all_rooms).data

Returns list of all rooms

def get_room(self, room_id: str) -> Room:
 98    def get_room(self, room_id: str) -> Room:
 99        """Returns room with the given id"""
100
101        return self._request(room_get_room, room_id=room_id).data

Returns room with the given id

def add_peer( self, room_id: str, options: PeerOptionsWebRTC) -> (<class 'str'>, <class 'Peer'>):
103    def add_peer(self, room_id: str, options: PeerOptionsWebRTC) -> (str, Peer):
104        """
105        Creates peer in the room
106
107        Currently only `webrtc` peer is supported
108
109        Returns a tuple (`peer_token`, `Peer`) - the token needed by Peer
110        to authenticate to Jellyfish and the new `Peer`
111        """
112
113        peer_type = "webrtc"
114        json_body = AddPeerJsonBody(type=peer_type, options=options)
115
116        resp = self._request(room_add_peer, room_id=room_id, json_body=json_body)
117        return (resp.data.token, resp.data.peer)

Creates peer in the room

Currently only webrtc peer is supported

Returns a tuple (peer_token, Peer) - the token needed by Peer to authenticate to Jellyfish and the new Peer

def delete_peer(self, room_id: str, peer_id: str) -> None:
119    def delete_peer(self, room_id: str, peer_id: str) -> None:
120        """Deletes peer"""
121
122        return self._request(room_delete_peer, id=peer_id, room_id=room_id)

Deletes peer

def add_component( self, room_id: str, options: Union[ComponentOptionsFile, ComponentOptionsHLS, ComponentOptionsRTSP]) -> Union[ComponentFile, ComponentHLS, ComponentRTSP]:
124    def add_component(
125        self,
126        room_id: str,
127        options: Union[ComponentOptionsFile, ComponentOptionsHLS, ComponentOptionsRTSP],
128    ) -> Union[ComponentFile, ComponentHLS, ComponentRTSP]:
129        """Creates component in the room"""
130
131        if isinstance(options, ComponentOptionsFile):
132            component_type = "file"
133        elif isinstance(options, ComponentOptionsHLS):
134            component_type = "hls"
135        elif isinstance(options, ComponentOptionsRTSP):
136            component_type = "rtsp"
137        else:
138            raise ValueError(
139                "options must be ComponentFile, ComponentOptionsHLS"
140                "or ComponentOptionsRTSP"
141            )
142
143        json_body = AddComponentJsonBody(type=component_type, options=options)
144
145        return self._request(
146            room_add_component, room_id=room_id, json_body=json_body
147        ).data

Creates component in the room

def delete_component(self, room_id: str, component_id: str) -> None:
149    def delete_component(self, room_id: str, component_id: str) -> None:
150        """Deletes component"""
151
152        return self._request(room_delete_component, id=component_id, room_id=room_id)

Deletes component

def hls_subscribe(self, room_id: str, origins: [<class 'str'>]):
154    def hls_subscribe(self, room_id: str, origins: [str]):
155        """
156        In order to subscribe to HLS peers/components,
157        the HLS component should be initialized with the subscribe_mode set to manual.
158        This mode proves beneficial when you do not wish to record or stream
159        all the available streams within a room via HLS.
160        It allows for selective addition instead –
161        you can manually select specific streams.
162        For instance, you could opt to record only the stream of an event's host.
163        """
164
165        return self._request(
166            hls_subscribe_hls_to,
167            room_id=room_id,
168            json_body=SubscriptionConfig(origins=origins),
169        )

In order to subscribe to HLS peers/components, the HLS component should be initialized with the subscribe_mode set to manual. This mode proves beneficial when you do not wish to record or stream all the available streams within a room via HLS. It allows for selective addition instead – you can manually select specific streams. For instance, you could opt to record only the stream of an event's host.

Inherited Members
jellyfish.api._base_api.BaseApi
client
class RecordingApi(jellyfish.api._base_api.BaseApi):
10class RecordingApi(BaseApi):
11    """Allows for managing recordings"""
12
13    def __init__(
14        self,
15        server_address: str = "localhost:5002",
16        server_api_token: str = "development",
17        secure: bool = False,
18    ):
19        """
20        Create RecordingApi instance, providing the jellyfish address and api token.
21        Set secure to `True` for `https` and `False` for `http` connection (default).
22        """
23
24        super().__init__(
25            server_address=server_address,
26            server_api_token=server_api_token,
27            secure=secure,
28        )
29
30    def get_list(self) -> list:
31        """Returns a list of available recordings"""
32
33        return self._request(get_recordings).data
34
35    def delete(self, recording_id: str):
36        """Deletes recording with given id"""
37
38        return self._request(delete_recording, recording_id=recording_id)

Allows for managing recordings

RecordingApi( server_address: str = 'localhost:5002', server_api_token: str = 'development', secure: bool = False)
13    def __init__(
14        self,
15        server_address: str = "localhost:5002",
16        server_api_token: str = "development",
17        secure: bool = False,
18    ):
19        """
20        Create RecordingApi instance, providing the jellyfish address and api token.
21        Set secure to `True` for `https` and `False` for `http` connection (default).
22        """
23
24        super().__init__(
25            server_address=server_address,
26            server_api_token=server_api_token,
27            secure=secure,
28        )

Create RecordingApi instance, providing the jellyfish address and api token. Set secure to True for https and False for http connection (default).

def get_list(self) -> list:
30    def get_list(self) -> list:
31        """Returns a list of available recordings"""
32
33        return self._request(get_recordings).data

Returns a list of available recordings

def delete(self, recording_id: str):
35    def delete(self, recording_id: str):
36        """Deletes recording with given id"""
37
38        return self._request(delete_recording, recording_id=recording_id)

Deletes recording with given id

Inherited Members
jellyfish.api._base_api.BaseApi
client
class Notifier:
 24class Notifier:
 25    """
 26    Allows for receiving WebSocket messages from Jellyfish.
 27    """
 28
 29    def __init__(
 30        self,
 31        server_address: str = "localhost:5002",
 32        server_api_token: str = "development",
 33        secure: bool = False,
 34    ):
 35        """
 36        Create Notifier instance, providing the jellyfish address and api token.
 37        Set secure to `True` for `wss` and `False` for `ws` connection (default).
 38        """
 39
 40        protocol = "wss" if secure else "ws"
 41        self._server_address = f"{protocol}://{server_address}/socket/server/websocket"
 42        self._server_api_token = server_api_token
 43        self._websocket = None
 44        self._ready = False
 45
 46        self._ready_event: asyncio.Event = None
 47
 48        self._notification_handler: Callable = None
 49        self._metrics_handler: Callable = None
 50
 51    def on_server_notification(self, handler: Callable[[Any], None]):
 52        """
 53        Decorator used for defining handler for ServerNotifications
 54        i.e. all messages other than `ServerMessageMetricsReport`.
 55        """
 56        self._notification_handler = handler
 57        return handler
 58
 59    def on_metrics(self, handler: Callable[[ServerMessageMetricsReport], None]):
 60        """
 61        Decorator used for defining handler for `ServerMessageMetricsReport`.
 62        """
 63        self._metrics_handler = handler
 64        return handler
 65
 66    async def connect(self):
 67        """
 68        A coroutine which connects Notifier to Jellyfish and listens for all incoming
 69        messages from the Jellyfish.
 70
 71        It runs until the connection isn't closed.
 72
 73        The incoming messages are handled by the functions defined using the
 74        `on_server_notification` and `on_metrics` decorators.
 75
 76        The handlers have to be defined before calling `connect`,
 77        otherwise the messages won't be received.
 78        """
 79        async with client.connect(self._server_address) as websocket:
 80            try:
 81                self._websocket = websocket
 82                await self._authenticate()
 83
 84                if self._notification_handler:
 85                    await self._subscribe_event(
 86                        event=ServerMessageEventType.EVENT_TYPE_SERVER_NOTIFICATION
 87                    )
 88
 89                if self._metrics_handler:
 90                    await self._subscribe_event(
 91                        event=ServerMessageEventType.EVENT_TYPE_METRICS
 92                    )
 93
 94                self._ready = True
 95                if self._ready_event:
 96                    self._ready_event.set()
 97
 98                await self._receive_loop()
 99            finally:
100                self._websocket = None
101
102    async def wait_ready(self) -> True:
103        """
104        Waits until the notifier is connected and authenticated to Jellyfish.
105
106        If already connected, returns `True` immediately.
107        """
108        if self._ready:
109            return True
110
111        if self._ready_event is None:
112            self._ready_event = asyncio.Event()
113
114        await self._ready_event.wait()
115
116    async def _authenticate(self):
117        msg = ServerMessage(
118            auth_request=ServerMessageAuthRequest(token=self._server_api_token)
119        )
120        await self._websocket.send(bytes(msg))
121
122        try:
123            message = await self._websocket.recv()
124        except ConnectionClosed as exception:
125            if "invalid token" in str(exception):
126                raise RuntimeError("Invalid server_api_token") from exception
127            raise
128
129        message = ServerMessage().parse(message)
130
131        _type, message = betterproto.which_one_of(message, "content")
132        assert isinstance(message, ServerMessageAuthenticated)
133
134    async def _receive_loop(self):
135        while True:
136            message = await self._websocket.recv()
137            message = ServerMessage().parse(message)
138            _which, message = betterproto.which_one_of(message, "content")
139
140            if isinstance(message, ServerMessageMetricsReport):
141                self._metrics_handler(message)
142            else:
143                self._notification_handler(message)
144
145    async def _subscribe_event(self, event: ServerMessageEventType):
146        request = ServerMessage(subscribe_request=ServerMessageSubscribeRequest(event))
147
148        await self._websocket.send(bytes(request))
149        message = await self._websocket.recv()
150        message = ServerMessage().parse(message)
151        _which, message = betterproto.which_one_of(message, "content")
152        assert isinstance(message, ServerMessageSubscribeResponse)

Allows for receiving WebSocket messages from Jellyfish.

Notifier( server_address: str = 'localhost:5002', server_api_token: str = 'development', secure: bool = False)
29    def __init__(
30        self,
31        server_address: str = "localhost:5002",
32        server_api_token: str = "development",
33        secure: bool = False,
34    ):
35        """
36        Create Notifier instance, providing the jellyfish address and api token.
37        Set secure to `True` for `wss` and `False` for `ws` connection (default).
38        """
39
40        protocol = "wss" if secure else "ws"
41        self._server_address = f"{protocol}://{server_address}/socket/server/websocket"
42        self._server_api_token = server_api_token
43        self._websocket = None
44        self._ready = False
45
46        self._ready_event: asyncio.Event = None
47
48        self._notification_handler: Callable = None
49        self._metrics_handler: Callable = None

Create Notifier instance, providing the jellyfish address and api token. Set secure to True for wss and False for ws connection (default).

def on_server_notification(self, handler: Callable[[Any], NoneType]):
51    def on_server_notification(self, handler: Callable[[Any], None]):
52        """
53        Decorator used for defining handler for ServerNotifications
54        i.e. all messages other than `ServerMessageMetricsReport`.
55        """
56        self._notification_handler = handler
57        return handler

Decorator used for defining handler for ServerNotifications i.e. all messages other than ServerMessageMetricsReport.

def on_metrics( self, handler: Callable[[jellyfish.events._protos.jellyfish.ServerMessageMetricsReport], NoneType]):
59    def on_metrics(self, handler: Callable[[ServerMessageMetricsReport], None]):
60        """
61        Decorator used for defining handler for `ServerMessageMetricsReport`.
62        """
63        self._metrics_handler = handler
64        return handler

Decorator used for defining handler for ServerMessageMetricsReport.

async def connect(self):
 66    async def connect(self):
 67        """
 68        A coroutine which connects Notifier to Jellyfish and listens for all incoming
 69        messages from the Jellyfish.
 70
 71        It runs until the connection isn't closed.
 72
 73        The incoming messages are handled by the functions defined using the
 74        `on_server_notification` and `on_metrics` decorators.
 75
 76        The handlers have to be defined before calling `connect`,
 77        otherwise the messages won't be received.
 78        """
 79        async with client.connect(self._server_address) as websocket:
 80            try:
 81                self._websocket = websocket
 82                await self._authenticate()
 83
 84                if self._notification_handler:
 85                    await self._subscribe_event(
 86                        event=ServerMessageEventType.EVENT_TYPE_SERVER_NOTIFICATION
 87                    )
 88
 89                if self._metrics_handler:
 90                    await self._subscribe_event(
 91                        event=ServerMessageEventType.EVENT_TYPE_METRICS
 92                    )
 93
 94                self._ready = True
 95                if self._ready_event:
 96                    self._ready_event.set()
 97
 98                await self._receive_loop()
 99            finally:
100                self._websocket = None

A coroutine which connects Notifier to Jellyfish and listens for all incoming messages from the Jellyfish.

It runs until the connection isn't closed.

The incoming messages are handled by the functions defined using the on_server_notification and on_metrics decorators.

The handlers have to be defined before calling connect, otherwise the messages won't be received.

async def wait_ready(self) -> True:
102    async def wait_ready(self) -> True:
103        """
104        Waits until the notifier is connected and authenticated to Jellyfish.
105
106        If already connected, returns `True` immediately.
107        """
108        if self._ready:
109            return True
110
111        if self._ready_event is None:
112            self._ready_event = asyncio.Event()
113
114        await self._ready_event.wait()

Waits until the notifier is connected and authenticated to Jellyfish.

If already connected, returns True immediately.

def receive_json(json: Dict) -> betterproto.Message:
14def receive_json(json: Dict) -> betterproto.Message:
15    """
16    Transform received json notification to adequate notification instance.
17
18    The available notifications are listed in `jellyfish.events` module.
19    """
20    msg = json["notification"]
21    msg = bytes(msg, "utf-8")
22    message = ServerMessage().parse(msg)
23    _which, message = betterproto.which_one_of(message, "content")
24    return message

Transform received json notification to adequate notification instance.

The available notifications are listed in jellyfish.events module.

class Room:
 18@_attrs_define
 19class Room:
 20    """Description of the room state"""
 21
 22    components: List[Union["ComponentFile", "ComponentHLS", "ComponentRTSP"]]
 23    """List of all components"""
 24    config: "RoomConfig"
 25    """Room configuration"""
 26    id: str
 27    """Room ID"""
 28    peers: List["Peer"]
 29    """List of all peers"""
 30    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
 31    """@private"""
 32
 33    def to_dict(self) -> Dict[str, Any]:
 34        """@private"""
 35        from ..models.component_hls import ComponentHLS
 36        from ..models.component_rtsp import ComponentRTSP
 37
 38        components = []
 39        for components_item_data in self.components:
 40            components_item: Dict[str, Any]
 41
 42            if isinstance(components_item_data, ComponentHLS):
 43                components_item = components_item_data.to_dict()
 44
 45            elif isinstance(components_item_data, ComponentRTSP):
 46                components_item = components_item_data.to_dict()
 47
 48            else:
 49                components_item = components_item_data.to_dict()
 50
 51            components.append(components_item)
 52
 53        config = self.config.to_dict()
 54
 55        id = self.id
 56        peers = []
 57        for peers_item_data in self.peers:
 58            peers_item = peers_item_data.to_dict()
 59
 60            peers.append(peers_item)
 61
 62        field_dict: Dict[str, Any] = {}
 63        field_dict.update(self.additional_properties)
 64        field_dict.update(
 65            {
 66                "components": components,
 67                "config": config,
 68                "id": id,
 69                "peers": peers,
 70            }
 71        )
 72
 73        return field_dict
 74
 75    @classmethod
 76    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
 77        """@private"""
 78        from ..models.component_file import ComponentFile
 79        from ..models.component_hls import ComponentHLS
 80        from ..models.component_rtsp import ComponentRTSP
 81        from ..models.peer import Peer
 82        from ..models.room_config import RoomConfig
 83
 84        d = src_dict.copy()
 85        components = []
 86        _components = d.pop("components")
 87        for components_item_data in _components:
 88
 89            def _parse_components_item(
 90                data: object,
 91            ) -> Union["ComponentFile", "ComponentHLS", "ComponentRTSP"]:
 92                try:
 93                    if not isinstance(data, dict):
 94                        raise TypeError()
 95                    componentsschemas_component_type_0 = ComponentHLS.from_dict(data)
 96
 97                    return componentsschemas_component_type_0
 98                except:  # noqa: E722
 99                    pass
100                try:
101                    if not isinstance(data, dict):
102                        raise TypeError()
103                    componentsschemas_component_type_1 = ComponentRTSP.from_dict(data)
104
105                    return componentsschemas_component_type_1
106                except:  # noqa: E722
107                    pass
108                if not isinstance(data, dict):
109                    raise TypeError()
110                componentsschemas_component_type_2 = ComponentFile.from_dict(data)
111
112                return componentsschemas_component_type_2
113
114            components_item = _parse_components_item(components_item_data)
115
116            components.append(components_item)
117
118        config = RoomConfig.from_dict(d.pop("config"))
119
120        id = d.pop("id")
121
122        peers = []
123        _peers = d.pop("peers")
124        for peers_item_data in _peers:
125            peers_item = Peer.from_dict(peers_item_data)
126
127            peers.append(peers_item)
128
129        room = cls(
130            components=components,
131            config=config,
132            id=id,
133            peers=peers,
134        )
135
136        room.additional_properties = d
137        return room
138
139    @property
140    def additional_keys(self) -> List[str]:
141        """@private"""
142        return list(self.additional_properties.keys())
143
144    def __getitem__(self, key: str) -> Any:
145        return self.additional_properties[key]
146
147    def __setitem__(self, key: str, value: Any) -> None:
148        self.additional_properties[key] = value
149
150    def __delitem__(self, key: str) -> None:
151        del self.additional_properties[key]
152
153    def __contains__(self, key: str) -> bool:
154        return key in self.additional_properties

Description of the room state

Room( components: List[Union[ComponentFile, ComponentHLS, ComponentRTSP]], config: RoomConfig, id: str, peers: List[Peer])
2def __init__(self, components, config, id, peers):
3    self.components = components
4    self.config = config
5    self.id = id
6    self.peers = peers
7    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class Room.

components: List[Union[ComponentFile, ComponentHLS, ComponentRTSP]]

List of all components

config: RoomConfig

Room configuration

id: str

Room ID

peers: List[Peer]

List of all peers

class RoomConfig:
13@_attrs_define
14class RoomConfig:
15    """Room configuration"""
16
17    max_peers: Union[Unset, None, int] = UNSET
18    """Maximum amount of peers allowed into the room"""
19    room_id: Union[Unset, None, str] = UNSET
20    """Custom id used for identifying room within Jellyfish. Must be unique across all rooms. If not provided, random UUID is generated."""
21    video_codec: Union[Unset, None, RoomConfigVideoCodec] = UNSET
22    """Enforces video codec for each peer in the room"""
23    webhook_url: Union[Unset, None, str] = UNSET
24    """URL where Jellyfish notifications will be sent"""
25    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
26    """@private"""
27
28    def to_dict(self) -> Dict[str, Any]:
29        """@private"""
30        max_peers = self.max_peers
31        room_id = self.room_id
32        video_codec: Union[Unset, None, str] = UNSET
33        if not isinstance(self.video_codec, Unset):
34            video_codec = self.video_codec.value if self.video_codec else None
35
36        webhook_url = self.webhook_url
37
38        field_dict: Dict[str, Any] = {}
39        field_dict.update(self.additional_properties)
40        field_dict.update({})
41        if max_peers is not UNSET:
42            field_dict["maxPeers"] = max_peers
43        if room_id is not UNSET:
44            field_dict["roomId"] = room_id
45        if video_codec is not UNSET:
46            field_dict["videoCodec"] = video_codec
47        if webhook_url is not UNSET:
48            field_dict["webhookUrl"] = webhook_url
49
50        return field_dict
51
52    @classmethod
53    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
54        """@private"""
55        d = src_dict.copy()
56        max_peers = d.pop("maxPeers", UNSET)
57
58        room_id = d.pop("roomId", UNSET)
59
60        _video_codec = d.pop("videoCodec", UNSET)
61        video_codec: Union[Unset, None, RoomConfigVideoCodec]
62        if _video_codec is None:
63            video_codec = None
64        elif isinstance(_video_codec, Unset):
65            video_codec = UNSET
66        else:
67            video_codec = RoomConfigVideoCodec(_video_codec)
68
69        webhook_url = d.pop("webhookUrl", UNSET)
70
71        room_config = cls(
72            max_peers=max_peers,
73            room_id=room_id,
74            video_codec=video_codec,
75            webhook_url=webhook_url,
76        )
77
78        room_config.additional_properties = d
79        return room_config
80
81    @property
82    def additional_keys(self) -> List[str]:
83        """@private"""
84        return list(self.additional_properties.keys())
85
86    def __getitem__(self, key: str) -> Any:
87        return self.additional_properties[key]
88
89    def __setitem__(self, key: str, value: Any) -> None:
90        self.additional_properties[key] = value
91
92    def __delitem__(self, key: str) -> None:
93        del self.additional_properties[key]
94
95    def __contains__(self, key: str) -> bool:
96        return key in self.additional_properties

Room configuration

RoomConfig( max_peers: Union[jellyfish._openapi_client.types.Unset, NoneType, int] = <jellyfish._openapi_client.types.Unset object>, room_id: Union[jellyfish._openapi_client.types.Unset, NoneType, str] = <jellyfish._openapi_client.types.Unset object>, video_codec: Union[jellyfish._openapi_client.types.Unset, NoneType, RoomConfigVideoCodec] = <jellyfish._openapi_client.types.Unset object>, webhook_url: Union[jellyfish._openapi_client.types.Unset, NoneType, str] = <jellyfish._openapi_client.types.Unset object>)
2def __init__(self, max_peers=attr_dict['max_peers'].default, room_id=attr_dict['room_id'].default, video_codec=attr_dict['video_codec'].default, webhook_url=attr_dict['webhook_url'].default):
3    self.max_peers = max_peers
4    self.room_id = room_id
5    self.video_codec = video_codec
6    self.webhook_url = webhook_url
7    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class RoomConfig.

max_peers: Union[jellyfish._openapi_client.types.Unset, NoneType, int]

Maximum amount of peers allowed into the room

room_id: Union[jellyfish._openapi_client.types.Unset, NoneType, str]

Custom id used for identifying room within Jellyfish. Must be unique across all rooms. If not provided, random UUID is generated.

video_codec: Union[jellyfish._openapi_client.types.Unset, NoneType, RoomConfigVideoCodec]

Enforces video codec for each peer in the room

webhook_url: Union[jellyfish._openapi_client.types.Unset, NoneType, str]

URL where Jellyfish notifications will be sent

class RoomConfigVideoCodec(builtins.str, enum.Enum):
 5class RoomConfigVideoCodec(str, Enum):
 6    """Enforces video codec for each peer in the room"""
 7
 8    H264 = "h264"
 9    VP8 = "vp8"
10
11    def __str__(self) -> str:
12        return str(self.value)

Enforces video codec for each peer in the room

H264 = <RoomConfigVideoCodec.H264: 'h264'>
VP8 = <RoomConfigVideoCodec.VP8: 'vp8'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Peer:
12@_attrs_define
13class Peer:
14    """Describes peer status"""
15
16    id: str
17    """Assigned peer id"""
18    status: PeerStatus
19    """Informs about the peer status"""
20    type: str
21    """Peer type"""
22    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
23    """@private"""
24
25    def to_dict(self) -> Dict[str, Any]:
26        """@private"""
27        id = self.id
28        status = self.status.value
29
30        type = self.type
31
32        field_dict: Dict[str, Any] = {}
33        field_dict.update(self.additional_properties)
34        field_dict.update(
35            {
36                "id": id,
37                "status": status,
38                "type": type,
39            }
40        )
41
42        return field_dict
43
44    @classmethod
45    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
46        """@private"""
47        d = src_dict.copy()
48        id = d.pop("id")
49
50        status = PeerStatus(d.pop("status"))
51
52        type = d.pop("type")
53
54        peer = cls(
55            id=id,
56            status=status,
57            type=type,
58        )
59
60        peer.additional_properties = d
61        return peer
62
63    @property
64    def additional_keys(self) -> List[str]:
65        """@private"""
66        return list(self.additional_properties.keys())
67
68    def __getitem__(self, key: str) -> Any:
69        return self.additional_properties[key]
70
71    def __setitem__(self, key: str, value: Any) -> None:
72        self.additional_properties[key] = value
73
74    def __delitem__(self, key: str) -> None:
75        del self.additional_properties[key]
76
77    def __contains__(self, key: str) -> bool:
78        return key in self.additional_properties

Describes peer status

Peer( id: str, status: PeerStatus, type: str)
2def __init__(self, id, status, type):
3    self.id = id
4    self.status = status
5    self.type = type
6    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class Peer.

id: str

Assigned peer id

status: PeerStatus

Informs about the peer status

type: str

Peer type

class PeerOptionsWebRTC:
12@_attrs_define
13class PeerOptionsWebRTC:
14    """Options specific to the WebRTC peer"""
15
16    enable_simulcast: Union[Unset, bool] = True
17    """Enables the peer to use simulcast"""
18    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
19    """@private"""
20
21    def to_dict(self) -> Dict[str, Any]:
22        """@private"""
23        enable_simulcast = self.enable_simulcast
24
25        field_dict: Dict[str, Any] = {}
26        field_dict.update(self.additional_properties)
27        field_dict.update({})
28        if enable_simulcast is not UNSET:
29            field_dict["enableSimulcast"] = enable_simulcast
30
31        return field_dict
32
33    @classmethod
34    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
35        """@private"""
36        d = src_dict.copy()
37        enable_simulcast = d.pop("enableSimulcast", UNSET)
38
39        peer_options_web_rtc = cls(
40            enable_simulcast=enable_simulcast,
41        )
42
43        peer_options_web_rtc.additional_properties = d
44        return peer_options_web_rtc
45
46    @property
47    def additional_keys(self) -> List[str]:
48        """@private"""
49        return list(self.additional_properties.keys())
50
51    def __getitem__(self, key: str) -> Any:
52        return self.additional_properties[key]
53
54    def __setitem__(self, key: str, value: Any) -> None:
55        self.additional_properties[key] = value
56
57    def __delitem__(self, key: str) -> None:
58        del self.additional_properties[key]
59
60    def __contains__(self, key: str) -> bool:
61        return key in self.additional_properties

Options specific to the WebRTC peer

PeerOptionsWebRTC( enable_simulcast: Union[jellyfish._openapi_client.types.Unset, bool] = True)
2def __init__(self, enable_simulcast=attr_dict['enable_simulcast'].default):
3    self.enable_simulcast = enable_simulcast
4    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class PeerOptionsWebRTC.

enable_simulcast: Union[jellyfish._openapi_client.types.Unset, bool]

Enables the peer to use simulcast

class PeerStatus(builtins.str, enum.Enum):
 5class PeerStatus(str, Enum):
 6    """Informs about the peer status"""
 7
 8    CONNECTED = "connected"
 9    DISCONNECTED = "disconnected"
10
11    def __str__(self) -> str:
12        return str(self.value)

Informs about the peer status

CONNECTED = <PeerStatus.CONNECTED: 'connected'>
DISCONNECTED = <PeerStatus.DISCONNECTED: 'disconnected'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class ComponentHLS:
14@_attrs_define
15class ComponentHLS:
16    """Describes the HLS component"""
17
18    id: str
19    """Assigned component ID"""
20    properties: "ComponentPropertiesHLS"
21    """Properties specific to the HLS component"""
22    type: str
23    """Component type"""
24    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
25    """@private"""
26
27    def to_dict(self) -> Dict[str, Any]:
28        """@private"""
29        id = self.id
30        properties = self.properties.to_dict()
31
32        type = self.type
33
34        field_dict: Dict[str, Any] = {}
35        field_dict.update(self.additional_properties)
36        field_dict.update(
37            {
38                "id": id,
39                "properties": properties,
40                "type": type,
41            }
42        )
43
44        return field_dict
45
46    @classmethod
47    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
48        """@private"""
49        from ..models.component_properties_hls import ComponentPropertiesHLS
50
51        d = src_dict.copy()
52        id = d.pop("id")
53
54        properties = ComponentPropertiesHLS.from_dict(d.pop("properties"))
55
56        type = d.pop("type")
57
58        component_hls = cls(
59            id=id,
60            properties=properties,
61            type=type,
62        )
63
64        component_hls.additional_properties = d
65        return component_hls
66
67    @property
68    def additional_keys(self) -> List[str]:
69        """@private"""
70        return list(self.additional_properties.keys())
71
72    def __getitem__(self, key: str) -> Any:
73        return self.additional_properties[key]
74
75    def __setitem__(self, key: str, value: Any) -> None:
76        self.additional_properties[key] = value
77
78    def __delitem__(self, key: str) -> None:
79        del self.additional_properties[key]
80
81    def __contains__(self, key: str) -> bool:
82        return key in self.additional_properties

Describes the HLS component

ComponentHLS( id: str, properties: ComponentPropertiesHLS, type: str)
2def __init__(self, id, properties, type):
3    self.id = id
4    self.properties = properties
5    self.type = type
6    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentHLS.

id: str

Assigned component ID

Properties specific to the HLS component

type: str

Component type

class ComponentOptionsHLS:
 19@_attrs_define
 20class ComponentOptionsHLS:
 21    """Options specific to the HLS component"""
 22
 23    low_latency: Union[Unset, bool] = False
 24    """Whether the component should use LL-HLS"""
 25    persistent: Union[Unset, bool] = False
 26    """Whether the video is stored after end of stream"""
 27    s3: Union[Unset, None, "S3Credentials"] = UNSET
 28    """An AWS S3 credential that will be used to send HLS stream. The stream will only be uploaded if credentials are provided"""
 29    subscribe_mode: Union[
 30        Unset, ComponentOptionsHLSSubscribeMode
 31    ] = ComponentOptionsHLSSubscribeMode.AUTO
 32    """Whether the HLS component should subscribe to tracks automatically or manually."""
 33    target_window_duration: Union[Unset, None, int] = UNSET
 34    """Duration of stream available for viewer"""
 35    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
 36    """@private"""
 37
 38    def to_dict(self) -> Dict[str, Any]:
 39        """@private"""
 40        low_latency = self.low_latency
 41        persistent = self.persistent
 42        s3: Union[Unset, None, Dict[str, Any]] = UNSET
 43        if not isinstance(self.s3, Unset):
 44            s3 = self.s3.to_dict() if self.s3 else None
 45
 46        subscribe_mode: Union[Unset, str] = UNSET
 47        if not isinstance(self.subscribe_mode, Unset):
 48            subscribe_mode = self.subscribe_mode.value
 49
 50        target_window_duration = self.target_window_duration
 51
 52        field_dict: Dict[str, Any] = {}
 53        field_dict.update(self.additional_properties)
 54        field_dict.update({})
 55        if low_latency is not UNSET:
 56            field_dict["lowLatency"] = low_latency
 57        if persistent is not UNSET:
 58            field_dict["persistent"] = persistent
 59        if s3 is not UNSET:
 60            field_dict["s3"] = s3
 61        if subscribe_mode is not UNSET:
 62            field_dict["subscribeMode"] = subscribe_mode
 63        if target_window_duration is not UNSET:
 64            field_dict["targetWindowDuration"] = target_window_duration
 65
 66        return field_dict
 67
 68    @classmethod
 69    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
 70        """@private"""
 71        from ..models.s3_credentials import S3Credentials
 72
 73        d = src_dict.copy()
 74        low_latency = d.pop("lowLatency", UNSET)
 75
 76        persistent = d.pop("persistent", UNSET)
 77
 78        _s3 = d.pop("s3", UNSET)
 79        s3: Union[Unset, None, S3Credentials]
 80        if _s3 is None:
 81            s3 = None
 82        elif isinstance(_s3, Unset):
 83            s3 = UNSET
 84        else:
 85            s3 = S3Credentials.from_dict(_s3)
 86
 87        _subscribe_mode = d.pop("subscribeMode", UNSET)
 88        subscribe_mode: Union[Unset, ComponentOptionsHLSSubscribeMode]
 89        if isinstance(_subscribe_mode, Unset):
 90            subscribe_mode = UNSET
 91        else:
 92            subscribe_mode = ComponentOptionsHLSSubscribeMode(_subscribe_mode)
 93
 94        target_window_duration = d.pop("targetWindowDuration", UNSET)
 95
 96        component_options_hls = cls(
 97            low_latency=low_latency,
 98            persistent=persistent,
 99            s3=s3,
100            subscribe_mode=subscribe_mode,
101            target_window_duration=target_window_duration,
102        )
103
104        component_options_hls.additional_properties = d
105        return component_options_hls
106
107    @property
108    def additional_keys(self) -> List[str]:
109        """@private"""
110        return list(self.additional_properties.keys())
111
112    def __getitem__(self, key: str) -> Any:
113        return self.additional_properties[key]
114
115    def __setitem__(self, key: str, value: Any) -> None:
116        self.additional_properties[key] = value
117
118    def __delitem__(self, key: str) -> None:
119        del self.additional_properties[key]
120
121    def __contains__(self, key: str) -> bool:
122        return key in self.additional_properties

Options specific to the HLS component

ComponentOptionsHLS( low_latency: Union[jellyfish._openapi_client.types.Unset, bool] = False, persistent: Union[jellyfish._openapi_client.types.Unset, bool] = False, s3: Union[jellyfish._openapi_client.types.Unset, NoneType, jellyfish._openapi_client.models.s3_credentials.S3Credentials] = <jellyfish._openapi_client.types.Unset object>, subscribe_mode: Union[jellyfish._openapi_client.types.Unset, ComponentOptionsHLSSubscribeMode] = <ComponentOptionsHLSSubscribeMode.AUTO: 'auto'>, target_window_duration: Union[jellyfish._openapi_client.types.Unset, NoneType, int] = <jellyfish._openapi_client.types.Unset object>)
2def __init__(self, low_latency=attr_dict['low_latency'].default, persistent=attr_dict['persistent'].default, s3=attr_dict['s3'].default, subscribe_mode=attr_dict['subscribe_mode'].default, target_window_duration=attr_dict['target_window_duration'].default):
3    self.low_latency = low_latency
4    self.persistent = persistent
5    self.s3 = s3
6    self.subscribe_mode = subscribe_mode
7    self.target_window_duration = target_window_duration
8    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentOptionsHLS.

low_latency: Union[jellyfish._openapi_client.types.Unset, bool]

Whether the component should use LL-HLS

persistent: Union[jellyfish._openapi_client.types.Unset, bool]

Whether the video is stored after end of stream

s3: Union[jellyfish._openapi_client.types.Unset, NoneType, jellyfish._openapi_client.models.s3_credentials.S3Credentials]

An AWS S3 credential that will be used to send HLS stream. The stream will only be uploaded if credentials are provided

subscribe_mode: Union[jellyfish._openapi_client.types.Unset, ComponentOptionsHLSSubscribeMode]

Whether the HLS component should subscribe to tracks automatically or manually.

target_window_duration: Union[jellyfish._openapi_client.types.Unset, NoneType, int]

Duration of stream available for viewer

class ComponentOptionsHLSSubscribeMode(builtins.str, enum.Enum):
 5class ComponentOptionsHLSSubscribeMode(str, Enum):
 6    """Whether the HLS component should subscribe to tracks automatically or manually."""
 7
 8    AUTO = "auto"
 9    MANUAL = "manual"
10
11    def __str__(self) -> str:
12        return str(self.value)

Whether the HLS component should subscribe to tracks automatically or manually.

Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class ComponentPropertiesHLS:
14@_attrs_define
15class ComponentPropertiesHLS:
16    """Properties specific to the HLS component"""
17
18    low_latency: bool
19    """Whether the component uses LL-HLS"""
20    persistent: bool
21    """Whether the video is stored after end of stream"""
22    playable: bool
23    """Whether the generated HLS playlist is playable"""
24    subscribe_mode: ComponentPropertiesHLSSubscribeMode
25    """Whether the HLS component should subscribe to tracks automatically or manually"""
26    target_window_duration: Optional[int]
27    """Duration of stream available for viewer"""
28    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
29    """@private"""
30
31    def to_dict(self) -> Dict[str, Any]:
32        """@private"""
33        low_latency = self.low_latency
34        persistent = self.persistent
35        playable = self.playable
36        subscribe_mode = self.subscribe_mode.value
37
38        target_window_duration = self.target_window_duration
39
40        field_dict: Dict[str, Any] = {}
41        field_dict.update(self.additional_properties)
42        field_dict.update(
43            {
44                "lowLatency": low_latency,
45                "persistent": persistent,
46                "playable": playable,
47                "subscribeMode": subscribe_mode,
48                "targetWindowDuration": target_window_duration,
49            }
50        )
51
52        return field_dict
53
54    @classmethod
55    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
56        """@private"""
57        d = src_dict.copy()
58        low_latency = d.pop("lowLatency")
59
60        persistent = d.pop("persistent")
61
62        playable = d.pop("playable")
63
64        subscribe_mode = ComponentPropertiesHLSSubscribeMode(d.pop("subscribeMode"))
65
66        target_window_duration = d.pop("targetWindowDuration")
67
68        component_properties_hls = cls(
69            low_latency=low_latency,
70            persistent=persistent,
71            playable=playable,
72            subscribe_mode=subscribe_mode,
73            target_window_duration=target_window_duration,
74        )
75
76        component_properties_hls.additional_properties = d
77        return component_properties_hls
78
79    @property
80    def additional_keys(self) -> List[str]:
81        """@private"""
82        return list(self.additional_properties.keys())
83
84    def __getitem__(self, key: str) -> Any:
85        return self.additional_properties[key]
86
87    def __setitem__(self, key: str, value: Any) -> None:
88        self.additional_properties[key] = value
89
90    def __delitem__(self, key: str) -> None:
91        del self.additional_properties[key]
92
93    def __contains__(self, key: str) -> bool:
94        return key in self.additional_properties

Properties specific to the HLS component

ComponentPropertiesHLS( low_latency: bool, persistent: bool, playable: bool, subscribe_mode: ComponentPropertiesHLSSubscribeMode, target_window_duration: Optional[int])
2def __init__(self, low_latency, persistent, playable, subscribe_mode, target_window_duration):
3    self.low_latency = low_latency
4    self.persistent = persistent
5    self.playable = playable
6    self.subscribe_mode = subscribe_mode
7    self.target_window_duration = target_window_duration
8    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentPropertiesHLS.

low_latency: bool

Whether the component uses LL-HLS

persistent: bool

Whether the video is stored after end of stream

playable: bool

Whether the generated HLS playlist is playable

Whether the HLS component should subscribe to tracks automatically or manually

target_window_duration: Optional[int]

Duration of stream available for viewer

class ComponentPropertiesHLSSubscribeMode(builtins.str, enum.Enum):
 5class ComponentPropertiesHLSSubscribeMode(str, Enum):
 6    """Whether the HLS component should subscribe to tracks automatically or manually"""
 7
 8    AUTO = "auto"
 9    MANUAL = "manual"
10
11    def __str__(self) -> str:
12        return str(self.value)

Whether the HLS component should subscribe to tracks automatically or manually

Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class ComponentRTSP:
14@_attrs_define
15class ComponentRTSP:
16    """Describes the RTSP component"""
17
18    id: str
19    """Assigned component ID"""
20    properties: "ComponentPropertiesRTSP"
21    """Properties specific to the RTSP component"""
22    type: str
23    """Component type"""
24    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
25    """@private"""
26
27    def to_dict(self) -> Dict[str, Any]:
28        """@private"""
29        id = self.id
30        properties = self.properties.to_dict()
31
32        type = self.type
33
34        field_dict: Dict[str, Any] = {}
35        field_dict.update(self.additional_properties)
36        field_dict.update(
37            {
38                "id": id,
39                "properties": properties,
40                "type": type,
41            }
42        )
43
44        return field_dict
45
46    @classmethod
47    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
48        """@private"""
49        from ..models.component_properties_rtsp import ComponentPropertiesRTSP
50
51        d = src_dict.copy()
52        id = d.pop("id")
53
54        properties = ComponentPropertiesRTSP.from_dict(d.pop("properties"))
55
56        type = d.pop("type")
57
58        component_rtsp = cls(
59            id=id,
60            properties=properties,
61            type=type,
62        )
63
64        component_rtsp.additional_properties = d
65        return component_rtsp
66
67    @property
68    def additional_keys(self) -> List[str]:
69        """@private"""
70        return list(self.additional_properties.keys())
71
72    def __getitem__(self, key: str) -> Any:
73        return self.additional_properties[key]
74
75    def __setitem__(self, key: str, value: Any) -> None:
76        self.additional_properties[key] = value
77
78    def __delitem__(self, key: str) -> None:
79        del self.additional_properties[key]
80
81    def __contains__(self, key: str) -> bool:
82        return key in self.additional_properties

Describes the RTSP component

ComponentRTSP( id: str, properties: ComponentPropertiesRTSP, type: str)
2def __init__(self, id, properties, type):
3    self.id = id
4    self.properties = properties
5    self.type = type
6    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentRTSP.

id: str

Assigned component ID

Properties specific to the RTSP component

type: str

Component type

class ComponentOptionsRTSP:
12@_attrs_define
13class ComponentOptionsRTSP:
14    """Options specific to the RTSP component"""
15
16    source_uri: str
17    """URI of RTSP source stream"""
18    keep_alive_interval: Union[Unset, int] = 15000
19    """Interval (in ms) in which keep-alive RTSP messages will be sent to the remote stream source"""
20    pierce_nat: Union[Unset, bool] = True
21    """Whether to attempt to create client-side NAT binding by sending an empty datagram from client to source, after the completion of RTSP setup"""
22    reconnect_delay: Union[Unset, int] = 15000
23    """Delay (in ms) between successive reconnect attempts"""
24    rtp_port: Union[Unset, int] = 20000
25    """Local port RTP stream will be received at"""
26    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
27    """@private"""
28
29    def to_dict(self) -> Dict[str, Any]:
30        """@private"""
31        source_uri = self.source_uri
32        keep_alive_interval = self.keep_alive_interval
33        pierce_nat = self.pierce_nat
34        reconnect_delay = self.reconnect_delay
35        rtp_port = self.rtp_port
36
37        field_dict: Dict[str, Any] = {}
38        field_dict.update(self.additional_properties)
39        field_dict.update(
40            {
41                "sourceUri": source_uri,
42            }
43        )
44        if keep_alive_interval is not UNSET:
45            field_dict["keepAliveInterval"] = keep_alive_interval
46        if pierce_nat is not UNSET:
47            field_dict["pierceNat"] = pierce_nat
48        if reconnect_delay is not UNSET:
49            field_dict["reconnectDelay"] = reconnect_delay
50        if rtp_port is not UNSET:
51            field_dict["rtpPort"] = rtp_port
52
53        return field_dict
54
55    @classmethod
56    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
57        """@private"""
58        d = src_dict.copy()
59        source_uri = d.pop("sourceUri")
60
61        keep_alive_interval = d.pop("keepAliveInterval", UNSET)
62
63        pierce_nat = d.pop("pierceNat", UNSET)
64
65        reconnect_delay = d.pop("reconnectDelay", UNSET)
66
67        rtp_port = d.pop("rtpPort", UNSET)
68
69        component_options_rtsp = cls(
70            source_uri=source_uri,
71            keep_alive_interval=keep_alive_interval,
72            pierce_nat=pierce_nat,
73            reconnect_delay=reconnect_delay,
74            rtp_port=rtp_port,
75        )
76
77        component_options_rtsp.additional_properties = d
78        return component_options_rtsp
79
80    @property
81    def additional_keys(self) -> List[str]:
82        """@private"""
83        return list(self.additional_properties.keys())
84
85    def __getitem__(self, key: str) -> Any:
86        return self.additional_properties[key]
87
88    def __setitem__(self, key: str, value: Any) -> None:
89        self.additional_properties[key] = value
90
91    def __delitem__(self, key: str) -> None:
92        del self.additional_properties[key]
93
94    def __contains__(self, key: str) -> bool:
95        return key in self.additional_properties

Options specific to the RTSP component

ComponentOptionsRTSP( source_uri: str, keep_alive_interval: Union[jellyfish._openapi_client.types.Unset, int] = 15000, pierce_nat: Union[jellyfish._openapi_client.types.Unset, bool] = True, reconnect_delay: Union[jellyfish._openapi_client.types.Unset, int] = 15000, rtp_port: Union[jellyfish._openapi_client.types.Unset, int] = 20000)
2def __init__(self, source_uri, keep_alive_interval=attr_dict['keep_alive_interval'].default, pierce_nat=attr_dict['pierce_nat'].default, reconnect_delay=attr_dict['reconnect_delay'].default, rtp_port=attr_dict['rtp_port'].default):
3    self.source_uri = source_uri
4    self.keep_alive_interval = keep_alive_interval
5    self.pierce_nat = pierce_nat
6    self.reconnect_delay = reconnect_delay
7    self.rtp_port = rtp_port
8    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentOptionsRTSP.

source_uri: str

URI of RTSP source stream

keep_alive_interval: Union[jellyfish._openapi_client.types.Unset, int]

Interval (in ms) in which keep-alive RTSP messages will be sent to the remote stream source

pierce_nat: Union[jellyfish._openapi_client.types.Unset, bool]

Whether to attempt to create client-side NAT binding by sending an empty datagram from client to source, after the completion of RTSP setup

reconnect_delay: Union[jellyfish._openapi_client.types.Unset, int]

Delay (in ms) between successive reconnect attempts

rtp_port: Union[jellyfish._openapi_client.types.Unset, int]

Local port RTP stream will be received at

class ComponentPropertiesRTSP:
12@_attrs_define
13class ComponentPropertiesRTSP:
14    """Properties specific to the RTSP component"""
15
16    source_uri: str
17    """URI of RTSP source stream"""
18    keep_alive_interval: Union[Unset, int] = UNSET
19    """Interval (in ms) in which keep-alive RTSP messages will be sent to the remote stream source"""
20    pierce_nat: Union[Unset, bool] = UNSET
21    """Whether to attempt to create client-side NAT binding by sending an empty datagram from client to source, after the completion of RTSP setup"""
22    reconnect_delay: Union[Unset, int] = UNSET
23    """Delay (in ms) between successive reconnect attempts"""
24    rtp_port: Union[Unset, int] = UNSET
25    """Local port RTP stream will be received at"""
26    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
27    """@private"""
28
29    def to_dict(self) -> Dict[str, Any]:
30        """@private"""
31        source_uri = self.source_uri
32        keep_alive_interval = self.keep_alive_interval
33        pierce_nat = self.pierce_nat
34        reconnect_delay = self.reconnect_delay
35        rtp_port = self.rtp_port
36
37        field_dict: Dict[str, Any] = {}
38        field_dict.update(self.additional_properties)
39        field_dict.update(
40            {
41                "sourceUri": source_uri,
42            }
43        )
44        if keep_alive_interval is not UNSET:
45            field_dict["keepAliveInterval"] = keep_alive_interval
46        if pierce_nat is not UNSET:
47            field_dict["pierceNat"] = pierce_nat
48        if reconnect_delay is not UNSET:
49            field_dict["reconnectDelay"] = reconnect_delay
50        if rtp_port is not UNSET:
51            field_dict["rtpPort"] = rtp_port
52
53        return field_dict
54
55    @classmethod
56    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
57        """@private"""
58        d = src_dict.copy()
59        source_uri = d.pop("sourceUri")
60
61        keep_alive_interval = d.pop("keepAliveInterval", UNSET)
62
63        pierce_nat = d.pop("pierceNat", UNSET)
64
65        reconnect_delay = d.pop("reconnectDelay", UNSET)
66
67        rtp_port = d.pop("rtpPort", UNSET)
68
69        component_properties_rtsp = cls(
70            source_uri=source_uri,
71            keep_alive_interval=keep_alive_interval,
72            pierce_nat=pierce_nat,
73            reconnect_delay=reconnect_delay,
74            rtp_port=rtp_port,
75        )
76
77        component_properties_rtsp.additional_properties = d
78        return component_properties_rtsp
79
80    @property
81    def additional_keys(self) -> List[str]:
82        """@private"""
83        return list(self.additional_properties.keys())
84
85    def __getitem__(self, key: str) -> Any:
86        return self.additional_properties[key]
87
88    def __setitem__(self, key: str, value: Any) -> None:
89        self.additional_properties[key] = value
90
91    def __delitem__(self, key: str) -> None:
92        del self.additional_properties[key]
93
94    def __contains__(self, key: str) -> bool:
95        return key in self.additional_properties

Properties specific to the RTSP component

ComponentPropertiesRTSP( source_uri: str, keep_alive_interval: Union[jellyfish._openapi_client.types.Unset, int] = <jellyfish._openapi_client.types.Unset object>, pierce_nat: Union[jellyfish._openapi_client.types.Unset, bool] = <jellyfish._openapi_client.types.Unset object>, reconnect_delay: Union[jellyfish._openapi_client.types.Unset, int] = <jellyfish._openapi_client.types.Unset object>, rtp_port: Union[jellyfish._openapi_client.types.Unset, int] = <jellyfish._openapi_client.types.Unset object>)
2def __init__(self, source_uri, keep_alive_interval=attr_dict['keep_alive_interval'].default, pierce_nat=attr_dict['pierce_nat'].default, reconnect_delay=attr_dict['reconnect_delay'].default, rtp_port=attr_dict['rtp_port'].default):
3    self.source_uri = source_uri
4    self.keep_alive_interval = keep_alive_interval
5    self.pierce_nat = pierce_nat
6    self.reconnect_delay = reconnect_delay
7    self.rtp_port = rtp_port
8    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentPropertiesRTSP.

source_uri: str

URI of RTSP source stream

keep_alive_interval: Union[jellyfish._openapi_client.types.Unset, int]

Interval (in ms) in which keep-alive RTSP messages will be sent to the remote stream source

pierce_nat: Union[jellyfish._openapi_client.types.Unset, bool]

Whether to attempt to create client-side NAT binding by sending an empty datagram from client to source, after the completion of RTSP setup

reconnect_delay: Union[jellyfish._openapi_client.types.Unset, int]

Delay (in ms) between successive reconnect attempts

rtp_port: Union[jellyfish._openapi_client.types.Unset, int]

Local port RTP stream will be received at

class ComponentFile:
16@_attrs_define
17class ComponentFile:
18    """Describes the File component"""
19
20    id: str
21    """Assigned component ID"""
22    type: str
23    """Component type"""
24    properties: Union[Unset, "ComponentPropertiesFile"] = UNSET
25    """Properties specific to the File component"""
26    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
27    """@private"""
28
29    def to_dict(self) -> Dict[str, Any]:
30        """@private"""
31        id = self.id
32        type = self.type
33        properties: Union[Unset, Dict[str, Any]] = UNSET
34        if not isinstance(self.properties, Unset):
35            properties = self.properties.to_dict()
36
37        field_dict: Dict[str, Any] = {}
38        field_dict.update(self.additional_properties)
39        field_dict.update(
40            {
41                "id": id,
42                "type": type,
43            }
44        )
45        if properties is not UNSET:
46            field_dict["properties"] = properties
47
48        return field_dict
49
50    @classmethod
51    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
52        """@private"""
53        from ..models.component_properties_file import ComponentPropertiesFile
54
55        d = src_dict.copy()
56        id = d.pop("id")
57
58        type = d.pop("type")
59
60        _properties = d.pop("properties", UNSET)
61        properties: Union[Unset, ComponentPropertiesFile]
62        if isinstance(_properties, Unset):
63            properties = UNSET
64        else:
65            properties = ComponentPropertiesFile.from_dict(_properties)
66
67        component_file = cls(
68            id=id,
69            type=type,
70            properties=properties,
71        )
72
73        component_file.additional_properties = d
74        return component_file
75
76    @property
77    def additional_keys(self) -> List[str]:
78        """@private"""
79        return list(self.additional_properties.keys())
80
81    def __getitem__(self, key: str) -> Any:
82        return self.additional_properties[key]
83
84    def __setitem__(self, key: str, value: Any) -> None:
85        self.additional_properties[key] = value
86
87    def __delitem__(self, key: str) -> None:
88        del self.additional_properties[key]
89
90    def __contains__(self, key: str) -> bool:
91        return key in self.additional_properties

Describes the File component

ComponentFile( id: str, type: str, properties: Union[jellyfish._openapi_client.types.Unset, ComponentPropertiesFile] = <jellyfish._openapi_client.types.Unset object>)
2def __init__(self, id, type, properties=attr_dict['properties'].default):
3    self.id = id
4    self.type = type
5    self.properties = properties
6    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentFile.

id: str

Assigned component ID

type: str

Component type

properties: Union[jellyfish._openapi_client.types.Unset, ComponentPropertiesFile]

Properties specific to the File component

class ComponentOptionsFile:
10@_attrs_define
11class ComponentOptionsFile:
12    """Options specific to the File component"""
13
14    file_path: str
15    """Path to track file. Must be either OPUS encapsulated in Ogg or raw h264"""
16    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
17    """@private"""
18
19    def to_dict(self) -> Dict[str, Any]:
20        """@private"""
21        file_path = self.file_path
22
23        field_dict: Dict[str, Any] = {}
24        field_dict.update(self.additional_properties)
25        field_dict.update(
26            {
27                "filePath": file_path,
28            }
29        )
30
31        return field_dict
32
33    @classmethod
34    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
35        """@private"""
36        d = src_dict.copy()
37        file_path = d.pop("filePath")
38
39        component_options_file = cls(
40            file_path=file_path,
41        )
42
43        component_options_file.additional_properties = d
44        return component_options_file
45
46    @property
47    def additional_keys(self) -> List[str]:
48        """@private"""
49        return list(self.additional_properties.keys())
50
51    def __getitem__(self, key: str) -> Any:
52        return self.additional_properties[key]
53
54    def __setitem__(self, key: str, value: Any) -> None:
55        self.additional_properties[key] = value
56
57    def __delitem__(self, key: str) -> None:
58        del self.additional_properties[key]
59
60    def __contains__(self, key: str) -> bool:
61        return key in self.additional_properties

Options specific to the File component

ComponentOptionsFile(file_path: str)
2def __init__(self, file_path):
3    self.file_path = file_path
4    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentOptionsFile.

file_path: str

Path to track file. Must be either OPUS encapsulated in Ogg or raw h264

class ComponentPropertiesFile:
10@_attrs_define
11class ComponentPropertiesFile:
12    """Properties specific to the File component"""
13
14    file_path: str
15    """Path to track file. Must be either OPUS encapsulated in Ogg or raw h264"""
16    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
17    """@private"""
18
19    def to_dict(self) -> Dict[str, Any]:
20        """@private"""
21        file_path = self.file_path
22
23        field_dict: Dict[str, Any] = {}
24        field_dict.update(self.additional_properties)
25        field_dict.update(
26            {
27                "filePath": file_path,
28            }
29        )
30
31        return field_dict
32
33    @classmethod
34    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
35        """@private"""
36        d = src_dict.copy()
37        file_path = d.pop("filePath")
38
39        component_properties_file = cls(
40            file_path=file_path,
41        )
42
43        component_properties_file.additional_properties = d
44        return component_properties_file
45
46    @property
47    def additional_keys(self) -> List[str]:
48        """@private"""
49        return list(self.additional_properties.keys())
50
51    def __getitem__(self, key: str) -> Any:
52        return self.additional_properties[key]
53
54    def __setitem__(self, key: str, value: Any) -> None:
55        self.additional_properties[key] = value
56
57    def __delitem__(self, key: str) -> None:
58        del self.additional_properties[key]
59
60    def __contains__(self, key: str) -> bool:
61        return key in self.additional_properties

Properties specific to the File component

ComponentPropertiesFile(file_path: str)
2def __init__(self, file_path):
3    self.file_path = file_path
4    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class ComponentPropertiesFile.

file_path: str

Path to track file. Must be either OPUS encapsulated in Ogg or raw h264