Fishjam Python Server SDK

Ad Ad Ad

Fishjam Python Server SDK

Python server SDK for the Fishjam.

Read the docs here

Installation

pip install fishjam-server-sdk

Usage

The SDK exports two main classes for interacting with Fishjam server: FishjamClient and FishjamNotifier.

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

FishjamClient

Create a FishjamClient instance, providing the fishjam server address and api token

from fishjam import FishjamClient

fishjam_client = FishjamClient(fishjam_id="<fishjam_id>", management_token="<management_token>")

You can use it to interact with Fishjam to manage rooms and peers

# Create a room
options = RoomOptions(video_codec="h264", webhook_url="http://localhost:5000/webhook")
room = fishjam_client.create_room(options=options)

# 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
peer, token = fishjam_client.create_peer(room.id)

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

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

FishjamNotifier

FishjamNotifier allows for receiving real-time updates from the Fishjam Server.

You can read more about notifications in the Fishjam Docs.

Create FishjamNotifier instance

from fishjam import FishjamNotifier

fishjam_notifier = FishjamNotifier(fishjam_id='<fishjam_id>', management_token='<management_token>')

Then define a handler for incoming messages

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

After that you can start the notifier

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

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

    # Create a room to trigger a server notification
    fishjam_client = FishjamClient()
    fishjam_client.create_room()

    await notifier_task

asyncio.run(test_notifier())

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

License

Licensed under the Apache License, Version 2.0

Fishjam is created by Software Mansion

Since 2012 Software Mansion is a software agency with experience in building web and mobile apps. We are Core React Native Contributors and experts in dealing with all kinds of React Native issues. We can help you build your next dream product – Hire us.

Software Mansion

 1""".. include:: ../README.md"""
 2
 3# pylint: disable=locally-disabled, no-name-in-module, import-error
 4
 5# Exceptions and Server Messages
 6
 7# API
 8# pylint: disable=locally-disabled, no-name-in-module, import-error
 9
10# Exceptions and Server Messages
11from fishjam import agent, errors, events, integrations, peer, room, version
12from fishjam._openapi_client.models import PeerMetadata
13
14# API
15from fishjam._webhook_notifier import (
16    decode_server_notifications,
17    receive_binary,
18    verify_webhook_signature,
19)
20from fishjam._ws_notifier import FishjamNotifier
21from fishjam.api._fishjam_client import (
22    AgentOptions,
23    AgentOutputOptions,
24    FishjamClient,
25    MoqAccess,
26    Peer,
27    PeerOptions,
28    PeerOptionsVapi,
29    Room,
30    RoomOptions,
31)
32from fishjam.errors import InvalidFishjamCredentialsError, MissingFishjamIdError
33
34__version__ = version.__version__
35
36__all__ = [
37    "FishjamClient",
38    "FishjamNotifier",
39    "decode_server_notifications",
40    "receive_binary",
41    "verify_webhook_signature",
42    "PeerMetadata",
43    "PeerOptions",
44    "PeerOptionsVapi",
45    "RoomOptions",
46    "AgentOptions",
47    "AgentOutputOptions",
48    "Room",
49    "Peer",
50    "MoqAccess",
51    "MissingFishjamIdError",
52    "InvalidFishjamCredentialsError",
53    "events",
54    "errors",
55    "room",
56    "peer",
57    "agent",
58    "integrations",
59]
60
61
62__docformat__ = "restructuredtext"
class FishjamClient(fishjam.api._client.Client):
164class FishjamClient(Client):
165    """Allows for managing rooms."""
166
167    def __init__(
168        self,
169        fishjam_id: str,
170        management_token: str,
171    ):
172        """Create a FishjamClient instance.
173
174        Does not contact the Fishjam backend — use :meth:`create_and_verify`
175        or :meth:`check_credentials` to verify credentials live.
176
177        Args:
178            fishjam_id: The unique identifier for the Fishjam instance.
179            management_token: The token used for authenticating management operations.
180        """
181        super().__init__(fishjam_id=fishjam_id, management_token=management_token)
182
183    @classmethod
184    def create_and_verify(
185        cls, *, fishjam_id: str, management_token: str
186    ) -> "FishjamClient":
187        """Construct a FishjamClient and verify its credentials against the backend.
188
189        Args:
190            fishjam_id: The unique identifier for the Fishjam instance.
191            management_token: The token used for authenticating management operations.
192
193        Returns:
194            FishjamClient: A client whose credentials have been verified.
195
196        Raises:
197            InvalidFishjamCredentialsError: If the token is rejected.
198        """
199        client = cls(fishjam_id=fishjam_id, management_token=management_token)
200        client.check_credentials()
201        return client
202
203    def check_credentials(self) -> None:
204        """Verify the management token via a single ``/validate`` call.
205
206        Raises:
207            InvalidFishjamCredentialsError: If the token is rejected.
208        """
209        response = credentials_validate_credentials.sync_detailed(client=self.client)
210        self._handle_deprecation_header(response.headers)
211
212        if response.status_code == HTTPStatus.NOT_FOUND:
213            raise InvalidFishjamCredentialsError("Invalid Fishjam credentials")
214
215    def create_peer(
216        self,
217        room_id: str,
218        options: PeerOptions | None = None,
219    ) -> tuple[Peer, str]:
220        """Creates a peer in the room.
221
222        Args:
223            room_id: The ID of the room where the peer will be created.
224            options: Configuration options for the peer. Defaults to None.
225
226        Returns:
227            A tuple containing:
228                - Peer: The created peer object.
229                - str: The peer token needed to authenticate to Fishjam.
230        """
231        options = options or PeerOptions()
232
233        peer_metadata = self.__parse_peer_metadata(options.metadata)
234        peer_options = PeerOptionsWebRTC(
235            metadata=peer_metadata,
236            subscribe_mode=SubscribeMode(options.subscribe_mode),
237        )
238        body = PeerConfigWebRTC(type_=PeerConfigWebRTCType.WEBRTC, options=peer_options)
239
240        resp = cast(
241            PeerDetailsResponse,
242            self._request(room_add_peer, room_id=room_id, body=body),
243        )
244
245        return (resp.data.peer, resp.data.token)
246
247    def create_agent(self, room_id: str, options: AgentOptions | None = None):
248        """Creates an agent in the room.
249
250        Args:
251            room_id: The ID of the room where the agent will be created.
252            options: Configuration options for the agent. Defaults to None.
253
254        Returns:
255            Agent: The created agent instance initialized with peer ID, room ID, token,
256                and Fishjam URL.
257        """
258        options = options or AgentOptions()
259        body = PeerConfigAgent(
260            type_=PeerConfigAgentType.AGENT,
261            options=PeerOptionsAgent(
262                output=AgentOutput(
263                    audio_format=AudioFormat(options.output.audio_format),
264                    audio_sample_rate=AudioSampleRate(options.output.audio_sample_rate),
265                ),
266                subscribe_mode=SubscribeMode(options.subscribe_mode),
267            ),
268        )
269
270        resp = cast(
271            PeerDetailsResponse,
272            self._request(room_add_peer, room_id=room_id, body=body),
273        )
274
275        socket_base_url = self._peer_socket_base_url(resp.data.peer_websocket_url)
276        return Agent(resp.data.peer.id, room_id, resp.data.token, socket_base_url)
277
278    def _peer_socket_base_url(self, peer_websocket_url: str | Unset) -> str:
279        if isinstance(peer_websocket_url, Unset) or not peer_websocket_url:
280            return self._fishjam_url
281
282        url = peer_websocket_url
283        if "://" not in url:
284            url = f"https://{url}"
285        for suffix in ("/socket/peer/websocket", "/socket/agent/websocket"):
286            url = url.removesuffix(suffix)
287        return url
288
289    def create_vapi_agent(
290        self,
291        room_id: str,
292        options: PeerOptionsVapi,
293    ) -> Peer:
294        """Creates a vapi agent in the room.
295
296        Args:
297            room_id: The ID of the room where the vapi agent will be created.
298            options: Configuration options for the vapi peer.
299
300        Returns:
301            - Peer: The created peer object.
302        """
303        body = PeerConfigVAPI(type_=PeerConfigVAPIType.VAPI, options=options)
304
305        resp = cast(
306            PeerDetailsResponse,
307            self._request(room_add_peer, room_id=room_id, body=body),
308        )
309
310        return resp.data.peer
311
312    def create_room(self, options: RoomOptions | None = None) -> Room:
313        """Creates a new room.
314
315        Args:
316            options: Configuration options for the room. Defaults to None.
317
318        Returns:
319            Room: The created Room object.
320        """
321        options = options or RoomOptions()
322
323        if options.video_codec is None:
324            codec = UNSET
325        else:
326            codec = VideoCodec(options.video_codec)
327
328        config = RoomConfig(
329            max_peers=options.max_peers,
330            video_codec=codec,
331            webhook_url=options.webhook_url,
332            room_type=RoomType(options.room_type),
333            public=options.public,
334            batch_webhook_notifications=options.batch_webhook_notifications,
335        )
336
337        room = cast(
338            RoomCreateDetailsResponse, self._request(room_create_room, body=config)
339        ).data.room
340
341        return Room(config=room.config, id=room.id, peers=room.peers)
342
343    def get_all_rooms(self) -> list[Room]:
344        """Returns list of all rooms.
345
346        Returns:
347            list[Room]: A list of all available Room objects.
348        """
349        rooms = cast(RoomsListingResponse, self._request(room_get_all_rooms)).data
350
351        return [
352            Room(config=room.config, id=room.id, peers=room.peers) for room in rooms
353        ]
354
355    def get_room(self, room_id: str) -> Room:
356        """Returns room with the given id.
357
358        Args:
359            room_id: The ID of the room to retrieve.
360
361        Returns:
362            Room: The Room object corresponding to the given ID.
363        """
364        room = cast(
365            RoomDetailsResponse, self._request(room_get_room, room_id=room_id)
366        ).data
367
368        return Room(config=room.config, id=room.id, peers=room.peers)
369
370    def delete_peer(self, room_id: str, peer_id: str) -> None:
371        """Deletes a peer from a room.
372
373        Args:
374            room_id: The ID of the room the peer belongs to.
375            peer_id: The ID of the peer to delete.
376        """
377        return self._request(room_delete_peer, id=peer_id, room_id=room_id)
378
379    def delete_room(self, room_id: str) -> None:
380        """Deletes a room.
381
382        Args:
383            room_id: The ID of the room to delete.
384        """
385        return self._request(room_delete_room, room_id=room_id)
386
387    def refresh_peer_token(self, room_id: str, peer_id: str) -> str:
388        """Refreshes a peer token.
389
390        Args:
391            room_id: The ID of the room.
392            peer_id: The ID of the peer whose token needs refreshing.
393
394        Returns:
395            str: The new peer token.
396        """
397        response = cast(
398            PeerRefreshTokenResponse,
399            self._request(room_refresh_token, id=peer_id, room_id=room_id),
400        )
401
402        return response.data.token
403
404    def create_livestream_viewer_token(self, room_id: str) -> str:
405        """Generates a viewer token for livestream rooms.
406
407        Args:
408            room_id: The ID of the livestream room.
409
410        Returns:
411            str: The generated viewer token.
412        """
413        response = cast(
414            ViewerToken, self._request(viewer_generate_viewer_token, room_id=room_id)
415        )
416
417        return response.token
418
419    def create_livestream_streamer_token(self, room_id: str) -> str:
420        """Generates a streamer token for livestream rooms.
421
422        Args:
423            room_id: The ID of the livestream room.
424
425        Returns:
426            str: The generated streamer token.
427        """
428        response = cast(
429            StreamerToken,
430            self._request(streamer_generate_streamer_token, room_id=room_id),
431        )
432
433        return response.token
434
435    def create_moq_access(
436        self,
437        publish_path: str | None = None,
438        subscribe_path: str | None = None,
439    ) -> MoqAccess:
440        """Generates MoQ relay connection details.
441
442        Args:
443            publish_path: Path the access grants publish access to.
444            subscribe_path: Path the access grants subscribe access to.
445
446        Returns:
447            MoqAccess: The relay connection details, containing the
448            ``connection_url`` (with the JWT embedded as a ``?jwt=`` query
449            parameter) and the ``token`` itself.
450        """
451        config = MoqAccessConfig(
452            publish_path=publish_path, subscribe_path=subscribe_path
453        )
454        response = cast(
455            MoqAccess,
456            self._request(moq_create_access, body=config),
457        )
458
459        return response
460
461    def subscribe_peer(self, room_id: str, peer_id: str, target_peer_id: str):
462        """Subscribes a peer to all tracks of another peer.
463
464        Args:
465            room_id: The ID of the room.
466            peer_id: The ID of the subscribing peer.
467            target_peer_id: The ID of the peer to subscribe to.
468        """
469        self._request(
470            room_subscribe_peer,
471            room_id=room_id,
472            id=peer_id,
473            peer_id=target_peer_id,
474        )
475
476    def subscribe_tracks(self, room_id: str, peer_id: str, track_ids: list[str]):
477        """Subscribes a peer to specific tracks of another peer.
478
479        Args:
480            room_id: The ID of the room.
481            peer_id: The ID of the subscribing peer.
482            track_ids: A list of track IDs to subscribe to.
483        """
484        self._request(
485            room_subscribe_tracks,
486            room_id=room_id,
487            id=peer_id,
488            body=SubscribeTracksBody(track_ids=track_ids),
489        )
490
491    def __parse_peer_metadata(self, metadata: dict | None) -> WebRTCMetadata:
492        peer_metadata = WebRTCMetadata()
493
494        if not metadata:
495            return peer_metadata
496
497        for key, value in metadata.items():
498            peer_metadata.additional_properties[key] = value
499
500        return peer_metadata

Allows for managing rooms.

FishjamClient(fishjam_id: str, management_token: str)
167    def __init__(
168        self,
169        fishjam_id: str,
170        management_token: str,
171    ):
172        """Create a FishjamClient instance.
173
174        Does not contact the Fishjam backend — use :meth:`create_and_verify`
175        or :meth:`check_credentials` to verify credentials live.
176
177        Args:
178            fishjam_id: The unique identifier for the Fishjam instance.
179            management_token: The token used for authenticating management operations.
180        """
181        super().__init__(fishjam_id=fishjam_id, management_token=management_token)

Create a FishjamClient instance.

Does not contact the Fishjam backend — use create_and_verify() or check_credentials() to verify credentials live.

Args:

  • fishjam_id: The unique identifier for the Fishjam instance.
  • management_token: The token used for authenticating management operations.
@classmethod
def create_and_verify(cls, *, fishjam_id: str, management_token: str) -> 'FishjamClient':
183    @classmethod
184    def create_and_verify(
185        cls, *, fishjam_id: str, management_token: str
186    ) -> "FishjamClient":
187        """Construct a FishjamClient and verify its credentials against the backend.
188
189        Args:
190            fishjam_id: The unique identifier for the Fishjam instance.
191            management_token: The token used for authenticating management operations.
192
193        Returns:
194            FishjamClient: A client whose credentials have been verified.
195
196        Raises:
197            InvalidFishjamCredentialsError: If the token is rejected.
198        """
199        client = cls(fishjam_id=fishjam_id, management_token=management_token)
200        client.check_credentials()
201        return client

Construct a FishjamClient and verify its credentials against the backend.

Args:

  • fishjam_id: The unique identifier for the Fishjam instance.
  • management_token: The token used for authenticating management operations.

Returns:

  • FishjamClient: A client whose credentials have been verified.

Raises:

  • InvalidFishjamCredentialsError: If the token is rejected.
def check_credentials(self) -> None:
203    def check_credentials(self) -> None:
204        """Verify the management token via a single ``/validate`` call.
205
206        Raises:
207            InvalidFishjamCredentialsError: If the token is rejected.
208        """
209        response = credentials_validate_credentials.sync_detailed(client=self.client)
210        self._handle_deprecation_header(response.headers)
211
212        if response.status_code == HTTPStatus.NOT_FOUND:
213            raise InvalidFishjamCredentialsError("Invalid Fishjam credentials")

Verify the management token via a single /validate call.

Raises:

  • InvalidFishjamCredentialsError: If the token is rejected.
def create_peer( self, room_id: str, options: PeerOptions | None = None) -> tuple[Peer, str]:
215    def create_peer(
216        self,
217        room_id: str,
218        options: PeerOptions | None = None,
219    ) -> tuple[Peer, str]:
220        """Creates a peer in the room.
221
222        Args:
223            room_id: The ID of the room where the peer will be created.
224            options: Configuration options for the peer. Defaults to None.
225
226        Returns:
227            A tuple containing:
228                - Peer: The created peer object.
229                - str: The peer token needed to authenticate to Fishjam.
230        """
231        options = options or PeerOptions()
232
233        peer_metadata = self.__parse_peer_metadata(options.metadata)
234        peer_options = PeerOptionsWebRTC(
235            metadata=peer_metadata,
236            subscribe_mode=SubscribeMode(options.subscribe_mode),
237        )
238        body = PeerConfigWebRTC(type_=PeerConfigWebRTCType.WEBRTC, options=peer_options)
239
240        resp = cast(
241            PeerDetailsResponse,
242            self._request(room_add_peer, room_id=room_id, body=body),
243        )
244
245        return (resp.data.peer, resp.data.token)

Creates a peer in the room.

Args:

  • room_id: The ID of the room where the peer will be created.
  • options: Configuration options for the peer. Defaults to None.

Returns:

  • A tuple containing:
    • Peer: The created peer object.
    • str: The peer token needed to authenticate to Fishjam.
def create_agent( self, room_id: str, options: AgentOptions | None = None):
247    def create_agent(self, room_id: str, options: AgentOptions | None = None):
248        """Creates an agent in the room.
249
250        Args:
251            room_id: The ID of the room where the agent will be created.
252            options: Configuration options for the agent. Defaults to None.
253
254        Returns:
255            Agent: The created agent instance initialized with peer ID, room ID, token,
256                and Fishjam URL.
257        """
258        options = options or AgentOptions()
259        body = PeerConfigAgent(
260            type_=PeerConfigAgentType.AGENT,
261            options=PeerOptionsAgent(
262                output=AgentOutput(
263                    audio_format=AudioFormat(options.output.audio_format),
264                    audio_sample_rate=AudioSampleRate(options.output.audio_sample_rate),
265                ),
266                subscribe_mode=SubscribeMode(options.subscribe_mode),
267            ),
268        )
269
270        resp = cast(
271            PeerDetailsResponse,
272            self._request(room_add_peer, room_id=room_id, body=body),
273        )
274
275        socket_base_url = self._peer_socket_base_url(resp.data.peer_websocket_url)
276        return Agent(resp.data.peer.id, room_id, resp.data.token, socket_base_url)

Creates an agent in the room.

Args:

  • room_id: The ID of the room where the agent will be created.
  • options: Configuration options for the agent. Defaults to None.

Returns:

  • Agent: The created agent instance initialized with peer ID, room ID, token, and Fishjam URL.
def create_vapi_agent( self, room_id: str, options: PeerOptionsVapi) -> Peer:
289    def create_vapi_agent(
290        self,
291        room_id: str,
292        options: PeerOptionsVapi,
293    ) -> Peer:
294        """Creates a vapi agent in the room.
295
296        Args:
297            room_id: The ID of the room where the vapi agent will be created.
298            options: Configuration options for the vapi peer.
299
300        Returns:
301            - Peer: The created peer object.
302        """
303        body = PeerConfigVAPI(type_=PeerConfigVAPIType.VAPI, options=options)
304
305        resp = cast(
306            PeerDetailsResponse,
307            self._request(room_add_peer, room_id=room_id, body=body),
308        )
309
310        return resp.data.peer

Creates a vapi agent in the room.

Args:

  • room_id: The ID of the room where the vapi agent will be created.
  • options: Configuration options for the vapi peer.

- - Peer: The created peer object.

def create_room( self, options: RoomOptions | None = None) -> Room:
312    def create_room(self, options: RoomOptions | None = None) -> Room:
313        """Creates a new room.
314
315        Args:
316            options: Configuration options for the room. Defaults to None.
317
318        Returns:
319            Room: The created Room object.
320        """
321        options = options or RoomOptions()
322
323        if options.video_codec is None:
324            codec = UNSET
325        else:
326            codec = VideoCodec(options.video_codec)
327
328        config = RoomConfig(
329            max_peers=options.max_peers,
330            video_codec=codec,
331            webhook_url=options.webhook_url,
332            room_type=RoomType(options.room_type),
333            public=options.public,
334            batch_webhook_notifications=options.batch_webhook_notifications,
335        )
336
337        room = cast(
338            RoomCreateDetailsResponse, self._request(room_create_room, body=config)
339        ).data.room
340
341        return Room(config=room.config, id=room.id, peers=room.peers)

Creates a new room.

Args:

  • options: Configuration options for the room. Defaults to None.

Returns:

  • Room: The created Room object.
def get_all_rooms(self) -> list[Room]:
343    def get_all_rooms(self) -> list[Room]:
344        """Returns list of all rooms.
345
346        Returns:
347            list[Room]: A list of all available Room objects.
348        """
349        rooms = cast(RoomsListingResponse, self._request(room_get_all_rooms)).data
350
351        return [
352            Room(config=room.config, id=room.id, peers=room.peers) for room in rooms
353        ]

Returns list of all rooms.

Returns:

  • list[Room]: A list of all available Room objects.
def get_room(self, room_id: str) -> Room:
355    def get_room(self, room_id: str) -> Room:
356        """Returns room with the given id.
357
358        Args:
359            room_id: The ID of the room to retrieve.
360
361        Returns:
362            Room: The Room object corresponding to the given ID.
363        """
364        room = cast(
365            RoomDetailsResponse, self._request(room_get_room, room_id=room_id)
366        ).data
367
368        return Room(config=room.config, id=room.id, peers=room.peers)

Returns room with the given id.

Args:

  • room_id: The ID of the room to retrieve.

Returns:

  • Room: The Room object corresponding to the given ID.
def delete_peer(self, room_id: str, peer_id: str) -> None:
370    def delete_peer(self, room_id: str, peer_id: str) -> None:
371        """Deletes a peer from a room.
372
373        Args:
374            room_id: The ID of the room the peer belongs to.
375            peer_id: The ID of the peer to delete.
376        """
377        return self._request(room_delete_peer, id=peer_id, room_id=room_id)

Deletes a peer from a room.

Args:

  • room_id: The ID of the room the peer belongs to.
  • peer_id: The ID of the peer to delete.
def delete_room(self, room_id: str) -> None:
379    def delete_room(self, room_id: str) -> None:
380        """Deletes a room.
381
382        Args:
383            room_id: The ID of the room to delete.
384        """
385        return self._request(room_delete_room, room_id=room_id)

Deletes a room.

Args:

  • room_id: The ID of the room to delete.
def refresh_peer_token(self, room_id: str, peer_id: str) -> str:
387    def refresh_peer_token(self, room_id: str, peer_id: str) -> str:
388        """Refreshes a peer token.
389
390        Args:
391            room_id: The ID of the room.
392            peer_id: The ID of the peer whose token needs refreshing.
393
394        Returns:
395            str: The new peer token.
396        """
397        response = cast(
398            PeerRefreshTokenResponse,
399            self._request(room_refresh_token, id=peer_id, room_id=room_id),
400        )
401
402        return response.data.token

Refreshes a peer token.

Args:

  • room_id: The ID of the room.
  • peer_id: The ID of the peer whose token needs refreshing.

Returns:

  • str: The new peer token.
def create_livestream_viewer_token(self, room_id: str) -> str:
404    def create_livestream_viewer_token(self, room_id: str) -> str:
405        """Generates a viewer token for livestream rooms.
406
407        Args:
408            room_id: The ID of the livestream room.
409
410        Returns:
411            str: The generated viewer token.
412        """
413        response = cast(
414            ViewerToken, self._request(viewer_generate_viewer_token, room_id=room_id)
415        )
416
417        return response.token

Generates a viewer token for livestream rooms.

Args:

  • room_id: The ID of the livestream room.

Returns:

  • str: The generated viewer token.
def create_livestream_streamer_token(self, room_id: str) -> str:
419    def create_livestream_streamer_token(self, room_id: str) -> str:
420        """Generates a streamer token for livestream rooms.
421
422        Args:
423            room_id: The ID of the livestream room.
424
425        Returns:
426            str: The generated streamer token.
427        """
428        response = cast(
429            StreamerToken,
430            self._request(streamer_generate_streamer_token, room_id=room_id),
431        )
432
433        return response.token

Generates a streamer token for livestream rooms.

Args:

  • room_id: The ID of the livestream room.

Returns:

  • str: The generated streamer token.
def create_moq_access( self, publish_path: str | None = None, subscribe_path: str | None = None) -> MoqAccess:
435    def create_moq_access(
436        self,
437        publish_path: str | None = None,
438        subscribe_path: str | None = None,
439    ) -> MoqAccess:
440        """Generates MoQ relay connection details.
441
442        Args:
443            publish_path: Path the access grants publish access to.
444            subscribe_path: Path the access grants subscribe access to.
445
446        Returns:
447            MoqAccess: The relay connection details, containing the
448            ``connection_url`` (with the JWT embedded as a ``?jwt=`` query
449            parameter) and the ``token`` itself.
450        """
451        config = MoqAccessConfig(
452            publish_path=publish_path, subscribe_path=subscribe_path
453        )
454        response = cast(
455            MoqAccess,
456            self._request(moq_create_access, body=config),
457        )
458
459        return response

Generates MoQ relay connection details.

Args:

  • publish_path: Path the access grants publish access to.
  • subscribe_path: Path the access grants subscribe access to.

Returns:

  • MoqAccess: The relay connection details, containing the
  • connection_url (with the JWT embedded as a ?jwt= query
  • parameter) and the token itself.
def subscribe_peer(self, room_id: str, peer_id: str, target_peer_id: str):
461    def subscribe_peer(self, room_id: str, peer_id: str, target_peer_id: str):
462        """Subscribes a peer to all tracks of another peer.
463
464        Args:
465            room_id: The ID of the room.
466            peer_id: The ID of the subscribing peer.
467            target_peer_id: The ID of the peer to subscribe to.
468        """
469        self._request(
470            room_subscribe_peer,
471            room_id=room_id,
472            id=peer_id,
473            peer_id=target_peer_id,
474        )

Subscribes a peer to all tracks of another peer.

Args:

  • room_id: The ID of the room.
  • peer_id: The ID of the subscribing peer.
  • target_peer_id: The ID of the peer to subscribe to.
def subscribe_tracks(self, room_id: str, peer_id: str, track_ids: list[str]):
476    def subscribe_tracks(self, room_id: str, peer_id: str, track_ids: list[str]):
477        """Subscribes a peer to specific tracks of another peer.
478
479        Args:
480            room_id: The ID of the room.
481            peer_id: The ID of the subscribing peer.
482            track_ids: A list of track IDs to subscribe to.
483        """
484        self._request(
485            room_subscribe_tracks,
486            room_id=room_id,
487            id=peer_id,
488            body=SubscribeTracksBody(track_ids=track_ids),
489        )

Subscribes a peer to specific tracks of another peer.

Args:

  • room_id: The ID of the room.
  • peer_id: The ID of the subscribing peer.
  • track_ids: A list of track IDs to subscribe to.
Inherited Members
fishjam.api._client.Client
client
warnings_shown
class FishjamNotifier:
 33class FishjamNotifier:
 34    """Allows for receiving WebSocket messages from Fishjam."""
 35
 36    def __init__(
 37        self,
 38        fishjam_id: str,
 39        management_token: str,
 40    ):
 41        """Create a FishjamNotifier instance with an ID and management token."""
 42        websocket_url = get_fishjam_url(fishjam_id).replace("http", "ws")
 43        self._fishjam_url = f"{websocket_url}/socket/server/websocket"
 44        self._management_token: str = management_token
 45        self._websocket: client.ClientConnection | None = None
 46        self._ready: bool = False
 47
 48        self._ready_event: asyncio.Event | None = None
 49
 50        self._notification_handler: NotificationHandler | None = None
 51
 52    def on_server_notification(self, handler: NotificationHandler):
 53        """Decorator for defining a handler for Fishjam notifications.
 54
 55        Args:
 56            handler: The function to be registered as the notification handler.
 57
 58        Returns:
 59            NotificationHandler: The original handler function (unmodified).
 60        """
 61        self._notification_handler = handler
 62        return handler
 63
 64    async def connect(self):
 65        """Connects to Fishjam and listens for all incoming messages.
 66
 67        It runs until the connection isn't closed.
 68
 69        The incoming messages are handled by the functions defined using the
 70        `on_server_notification` decorator.
 71
 72        The handler have to be defined before calling `connect`,
 73        otherwise the messages won't be received.
 74        """
 75        async with client.connect(self._fishjam_url) as websocket:
 76            try:
 77                self._websocket = websocket
 78                await self._authenticate()
 79
 80                if self._notification_handler:
 81                    await self._subscribe_event(
 82                        event=ServerMessageEventType.EVENT_TYPE_SERVER_NOTIFICATION
 83                    )
 84
 85                self._ready = True
 86                if self._ready_event:
 87                    self._ready_event.set()
 88
 89                await self._receive_loop()
 90            finally:
 91                self._websocket = None
 92
 93    async def wait_ready(self) -> None:
 94        """Waits until the notifier is connected and authenticated to Fishjam.
 95
 96        If already connected, returns immediately.
 97        """
 98        if self._ready:
 99            return
100
101        if self._ready_event is None:
102            self._ready_event = asyncio.Event()
103
104        await self._ready_event.wait()
105
106    async def _authenticate(self):
107        if not self._websocket:
108            raise RuntimeError("Websocket is not connected")
109
110        msg = ServerMessage(
111            auth_request=ServerMessageAuthRequest(token=self._management_token)
112        )
113        await self._websocket.send(bytes(msg))
114
115        try:
116            message = await self._websocket.recv(decode=False)
117        except ConnectionClosed as exception:
118            if "invalid token" in str(exception):
119                raise RuntimeError("Invalid management token") from exception
120            raise
121
122        message = ServerMessage().parse(message)
123
124        _type, message = betterproto.which_one_of(message, "content")
125        assert isinstance(message, ServerMessageAuthenticated)
126
127    async def _receive_loop(self):
128        if not self._websocket:
129            raise RuntimeError("Websocket is not connected")
130        if not self._notification_handler:
131            raise RuntimeError("Notification handler is not defined")
132
133        while True:
134            message = await self._websocket.recv(decode=False)
135            message = ServerMessage().parse(message)
136            _which, message = betterproto.which_one_of(message, "content")
137
138            if isinstance(message, ALLOWED_NOTIFICATIONS):
139                res = self._notification_handler(message)
140                if inspect.isawaitable(res):
141                    await res
142
143    async def _subscribe_event(self, event: ServerMessageEventType):
144        if not self._websocket:
145            raise RuntimeError("Websocket is not connected")
146
147        request = ServerMessage(subscribe_request=ServerMessageSubscribeRequest(event))
148
149        await self._websocket.send(bytes(request))
150        message = cast(bytes, await self._websocket.recv())
151        message = ServerMessage().parse(message)
152        _which, message = betterproto.which_one_of(message, "content")
153        assert isinstance(message, ServerMessageSubscribeResponse)

Allows for receiving WebSocket messages from Fishjam.

FishjamNotifier(fishjam_id: str, management_token: str)
36    def __init__(
37        self,
38        fishjam_id: str,
39        management_token: str,
40    ):
41        """Create a FishjamNotifier instance with an ID and management token."""
42        websocket_url = get_fishjam_url(fishjam_id).replace("http", "ws")
43        self._fishjam_url = f"{websocket_url}/socket/server/websocket"
44        self._management_token: str = management_token
45        self._websocket: client.ClientConnection | None = None
46        self._ready: bool = False
47
48        self._ready_event: asyncio.Event | None = None
49
50        self._notification_handler: NotificationHandler | None = None

Create a FishjamNotifier instance with an ID and management token.

def on_server_notification( self, handler: Callable[[fishjam.events.ServerMessageRoomCreated | fishjam.events.ServerMessageRoomDeleted | fishjam.events.ServerMessageRoomCrashed | fishjam.events.ServerMessagePeerAdded | fishjam.events.ServerMessagePeerDeleted | fishjam.events.ServerMessagePeerConnected | fishjam.events.ServerMessagePeerDisconnected | fishjam.events.ServerMessagePeerMetadataUpdated | fishjam.events.ServerMessagePeerCrashed | fishjam.events.ServerMessageStreamerConnected | fishjam.events.ServerMessageStreamerDisconnected | fishjam.events.ServerMessageChannelAdded | fishjam.events.ServerMessageChannelRemoved | fishjam.events.ServerMessageViewerConnected | fishjam.events.ServerMessageViewerDisconnected | fishjam.events.ServerMessageTrackAdded | fishjam.events.ServerMessageTrackRemoved | fishjam.events.ServerMessageTrackMetadataUpdated], NoneType] | Callable[[fishjam.events.ServerMessageRoomCreated | fishjam.events.ServerMessageRoomDeleted | fishjam.events.ServerMessageRoomCrashed | fishjam.events.ServerMessagePeerAdded | fishjam.events.ServerMessagePeerDeleted | fishjam.events.ServerMessagePeerConnected | fishjam.events.ServerMessagePeerDisconnected | fishjam.events.ServerMessagePeerMetadataUpdated | fishjam.events.ServerMessagePeerCrashed | fishjam.events.ServerMessageStreamerConnected | fishjam.events.ServerMessageStreamerDisconnected | fishjam.events.ServerMessageChannelAdded | fishjam.events.ServerMessageChannelRemoved | fishjam.events.ServerMessageViewerConnected | fishjam.events.ServerMessageViewerDisconnected | fishjam.events.ServerMessageTrackAdded | fishjam.events.ServerMessageTrackRemoved | fishjam.events.ServerMessageTrackMetadataUpdated], Coroutine[Any, Any, None]]):
52    def on_server_notification(self, handler: NotificationHandler):
53        """Decorator for defining a handler for Fishjam notifications.
54
55        Args:
56            handler: The function to be registered as the notification handler.
57
58        Returns:
59            NotificationHandler: The original handler function (unmodified).
60        """
61        self._notification_handler = handler
62        return handler

Decorator for defining a handler for Fishjam notifications.

Args:

  • handler: The function to be registered as the notification handler.

Returns:

  • NotificationHandler: The original handler function (unmodified).
async def connect(self):
64    async def connect(self):
65        """Connects to Fishjam and listens for all incoming messages.
66
67        It runs until the connection isn't closed.
68
69        The incoming messages are handled by the functions defined using the
70        `on_server_notification` decorator.
71
72        The handler have to be defined before calling `connect`,
73        otherwise the messages won't be received.
74        """
75        async with client.connect(self._fishjam_url) as websocket:
76            try:
77                self._websocket = websocket
78                await self._authenticate()
79
80                if self._notification_handler:
81                    await self._subscribe_event(
82                        event=ServerMessageEventType.EVENT_TYPE_SERVER_NOTIFICATION
83                    )
84
85                self._ready = True
86                if self._ready_event:
87                    self._ready_event.set()
88
89                await self._receive_loop()
90            finally:
91                self._websocket = None

Connects to Fishjam and listens for all incoming messages.

It runs until the connection isn't closed.

The incoming messages are handled by the functions defined using the on_server_notification decorator.

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

async def wait_ready(self) -> None:
 93    async def wait_ready(self) -> None:
 94        """Waits until the notifier is connected and authenticated to Fishjam.
 95
 96        If already connected, returns immediately.
 97        """
 98        if self._ready:
 99            return
100
101        if self._ready_event is None:
102            self._ready_event = asyncio.Event()
103
104        await self._ready_event.wait()

Waits until the notifier is connected and authenticated to Fishjam.

If already connected, returns immediately.

46def decode_server_notifications(binary: bytes) -> List[AllowedNotification]:
47    """Decode a received protobuf payload into a list of notifications.
48
49    Handles both single notifications and batches transparently: a single
50    notification is returned as a one-element list, a batch is unpacked into
51    its members (in order), and anything unsupported yields an empty list.
52
53    The available notifications are listed in the `fishjam.events` module.
54
55    Args:
56        binary: The raw binary data received from the webhook.
57
58    Returns:
59        list[AllowedNotification]: The decoded notifications, in order. Empty
60            when the payload carries no supported notification.
61    """
62    message = ServerMessage().parse(binary)
63    _which, content = betterproto.which_one_of(message, "content")
64
65    if isinstance(content, ServerMessageNotificationBatch):
66        return _unpack_batch(content)
67
68    if isinstance(content, ALLOWED_NOTIFICATIONS):
69        return [content]
70
71    return []

Decode a received protobuf payload into a list of notifications.

Handles both single notifications and batches transparently: a single notification is returned as a one-element list, a batch is unpacked into its members (in order), and anything unsupported yields an empty list.

The available notifications are listed in the fishjam.events module.

Args:

  • binary: The raw binary data received from the webhook.

Returns:

  • list[AllowedNotification]: The decoded notifications, in order. Empty when the payload carries no supported notification.
def receive_binary( binary: bytes) -> fishjam.events.ServerMessageRoomCreated | fishjam.events.ServerMessageRoomDeleted | fishjam.events.ServerMessageRoomCrashed | fishjam.events.ServerMessagePeerAdded | fishjam.events.ServerMessagePeerDeleted | fishjam.events.ServerMessagePeerConnected | fishjam.events.ServerMessagePeerDisconnected | fishjam.events.ServerMessagePeerMetadataUpdated | fishjam.events.ServerMessagePeerCrashed | fishjam.events.ServerMessageStreamerConnected | fishjam.events.ServerMessageStreamerDisconnected | fishjam.events.ServerMessageChannelAdded | fishjam.events.ServerMessageChannelRemoved | fishjam.events.ServerMessageViewerConnected | fishjam.events.ServerMessageViewerDisconnected | fishjam.events.ServerMessageTrackAdded | fishjam.events.ServerMessageTrackRemoved | fishjam.events.ServerMessageTrackMetadataUpdated | List[fishjam.events.ServerMessageRoomCreated | fishjam.events.ServerMessageRoomDeleted | fishjam.events.ServerMessageRoomCrashed | fishjam.events.ServerMessagePeerAdded | fishjam.events.ServerMessagePeerDeleted | fishjam.events.ServerMessagePeerConnected | fishjam.events.ServerMessagePeerDisconnected | fishjam.events.ServerMessagePeerMetadataUpdated | fishjam.events.ServerMessagePeerCrashed | fishjam.events.ServerMessageStreamerConnected | fishjam.events.ServerMessageStreamerDisconnected | fishjam.events.ServerMessageChannelAdded | fishjam.events.ServerMessageChannelRemoved | fishjam.events.ServerMessageViewerConnected | fishjam.events.ServerMessageViewerDisconnected | fishjam.events.ServerMessageTrackAdded | fishjam.events.ServerMessageTrackRemoved | fishjam.events.ServerMessageTrackMetadataUpdated] | None:
 94def receive_binary(
 95    binary: bytes,
 96) -> Union[AllowedNotification, List[AllowedNotification], None]:
 97    """Transforms a received protobuf notification into a notification instance.
 98
 99    .. deprecated::
100        Use `decode_server_notifications` instead, which always returns a list
101        and handles batched payloads with a single, consistent return type.
102
103    The available notifications are listed in `fishjam.events` module.
104
105    Args:
106        binary: The raw binary data received from the webhook.
107
108    Returns:
109        AllowedNotification: A single notification when the payload carries one.
110        list[AllowedNotification]: The unpacked notifications, in order, when the
111            payload is a batch (webhook batching enabled).
112        None: When the payload is not a supported notification.
113    """
114    warnings.warn(
115        "receive_binary is deprecated; use decode_server_notifications instead.",
116        DeprecationWarning,
117        stacklevel=2,
118    )
119
120    message = ServerMessage().parse(binary)
121    _which, content = betterproto.which_one_of(message, "content")
122
123    if isinstance(content, ServerMessageNotificationBatch):
124        return _unpack_batch(content)
125
126    if isinstance(content, ALLOWED_NOTIFICATIONS):
127        return content
128
129    return None

Transforms a received protobuf notification into a notification instance.

Deprecated since version .

  • Use decode_server_notifications instead, which always returns a list
  • and handles batched payloads with a single, consistent return type.

The available notifications are listed in fishjam.events module.

Args:

  • binary: The raw binary data received from the webhook.

Returns:

  • AllowedNotification: A single notification when the payload carries one.
  • list[AllowedNotification]: The unpacked notifications, in order, when the payload is a batch (webhook batching enabled).
  • None: When the payload is not a supported notification.
def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
74def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
75    """Verify the `x-fishjam-signature-256` header of a raw webhook body.
76
77    Accepts the `sha256=<hex>` format sent by Fishjam (the prefix is
78    optional) and compares in constant time. Call this with the raw request
79    body before passing it to `decode_server_notifications`.
80
81    Args:
82        body: The raw binary body of the webhook request.
83        signature: The value of the `x-fishjam-signature-256` header.
84        secret: The webhook secret configured in Fishjam.
85
86    Returns:
87        bool: True when the signature matches the body, False otherwise.
88    """
89    expected = hmac.new(secret.encode(), body, "sha256").hexdigest()
90    provided = signature.strip().removeprefix("sha256=")
91    return hmac.compare_digest(provided, expected)

Verify the x-fishjam-signature-256 header of a raw webhook body.

Accepts the sha256=<hex> format sent by Fishjam (the prefix is optional) and compares in constant time. Call this with the raw request body before passing it to decode_server_notifications.

Args:

  • body: The raw binary body of the webhook request.
  • signature: The value of the x-fishjam-signature-256 header.
  • secret: The webhook secret configured in Fishjam.

Returns:

  • bool: True when the signature matches the body, False otherwise.
class PeerMetadata:
13@_attrs_define
14class PeerMetadata:
15    """Custom metadata set by the peer
16
17    Example:
18        {'name': 'FishjamUser'}
19
20    """
21
22    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
23
24    def to_dict(self) -> dict[str, Any]:
25        field_dict: dict[str, Any] = {}
26        field_dict.update(self.additional_properties)
27
28        return field_dict
29
30    @classmethod
31    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
32        d = dict(src_dict)
33        peer_metadata = cls()
34
35        peer_metadata.additional_properties = d
36        return peer_metadata
37
38    @property
39    def additional_keys(self) -> list[str]:
40        return list(self.additional_properties.keys())
41
42    def __getitem__(self, key: str) -> Any:
43        return self.additional_properties[key]
44
45    def __setitem__(self, key: str, value: Any) -> None:
46        self.additional_properties[key] = value
47
48    def __delitem__(self, key: str) -> None:
49        del self.additional_properties[key]
50
51    def __contains__(self, key: str) -> bool:
52        return key in self.additional_properties

Custom metadata set by the peer

Example:

  • {'name': 'FishjamUser'}
PeerMetadata()
23def __init__(self, ):
24    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class PeerMetadata.

additional_properties: 'dict[str, Any]'
def to_dict(self) -> 'dict[str, Any]':
24    def to_dict(self) -> dict[str, Any]:
25        field_dict: dict[str, Any] = {}
26        field_dict.update(self.additional_properties)
27
28        return field_dict
@classmethod
def from_dict(cls: 'type[T]', src_dict: 'Mapping[str, Any]') -> 'T':
30    @classmethod
31    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
32        d = dict(src_dict)
33        peer_metadata = cls()
34
35        peer_metadata.additional_properties = d
36        return peer_metadata
additional_keys: 'list[str]'
38    @property
39    def additional_keys(self) -> list[str]:
40        return list(self.additional_properties.keys())
@dataclass
class PeerOptions:
122@dataclass
123class PeerOptions:
124    """Options specific to a WebRTC Peer.
125
126    Attributes:
127        metadata: Peer metadata.
128        subscribe_mode: Configuration of peer's subscribing policy.
129    """
130
131    metadata: dict[str, Any] | None = None
132    """Peer metadata"""
133    subscribe_mode: Literal["auto", "manual"] = "auto"
134    """Configuration of peer's subscribing policy"""

Options specific to a WebRTC Peer.

Attributes:

  • metadata: Peer metadata.
  • subscribe_mode: Configuration of peer's subscribing policy.
PeerOptions( metadata: dict[str, Any] | None = None, subscribe_mode: Literal['auto', 'manual'] = 'auto')
metadata: dict[str, Any] | None = None

Peer metadata

subscribe_mode: Literal['auto', 'manual'] = 'auto'

Configuration of peer's subscribing policy

class PeerOptionsVapi:
15@_attrs_define
16class PeerOptionsVapi:
17    """Options specific to the VAPI peer
18
19    Attributes:
20        api_key (str): VAPI API key
21        call_id (str): VAPI call ID
22        subscribe_mode (SubscribeMode | Unset): Configuration of peer's subscribing policy
23    """
24
25    api_key: str
26    call_id: str
27    subscribe_mode: SubscribeMode | Unset = UNSET
28
29    def to_dict(self) -> dict[str, Any]:
30        api_key = self.api_key
31
32        call_id = self.call_id
33
34        subscribe_mode: str | Unset = UNSET
35        if not isinstance(self.subscribe_mode, Unset):
36            subscribe_mode = self.subscribe_mode.value
37
38        field_dict: dict[str, Any] = {}
39
40        field_dict.update({
41            "apiKey": api_key,
42            "callId": call_id,
43        })
44        if subscribe_mode is not UNSET:
45            field_dict["subscribeMode"] = subscribe_mode
46
47        return field_dict
48
49    @classmethod
50    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
51        d = dict(src_dict)
52        api_key = d.pop("apiKey")
53
54        call_id = d.pop("callId")
55
56        _subscribe_mode = d.pop("subscribeMode", UNSET)
57        subscribe_mode: SubscribeMode | Unset
58        if isinstance(_subscribe_mode, Unset):
59            subscribe_mode = UNSET
60        else:
61            subscribe_mode = SubscribeMode(_subscribe_mode)
62
63        peer_options_vapi = cls(
64            api_key=api_key,
65            call_id=call_id,
66            subscribe_mode=subscribe_mode,
67        )
68
69        return peer_options_vapi

Options specific to the VAPI peer

Attributes:

  • api_key (str): VAPI API key
  • call_id (str): VAPI call ID
  • subscribe_mode (SubscribeMode | Unset): Configuration of peer's subscribing policy
PeerOptionsVapi( api_key: 'str', call_id: 'str', subscribe_mode: 'SubscribeMode | Unset' = <fishjam._openapi_client.types.Unset object>)
25def __init__(self, api_key, call_id, subscribe_mode=attr_dict['subscribe_mode'].default):
26    self.api_key = api_key
27    self.call_id = call_id
28    self.subscribe_mode = subscribe_mode

Method generated by attrs for class PeerOptionsVapi.

api_key: 'str'
call_id: 'str'
subscribe_mode: 'SubscribeMode | Unset'
def to_dict(self) -> 'dict[str, Any]':
29    def to_dict(self) -> dict[str, Any]:
30        api_key = self.api_key
31
32        call_id = self.call_id
33
34        subscribe_mode: str | Unset = UNSET
35        if not isinstance(self.subscribe_mode, Unset):
36            subscribe_mode = self.subscribe_mode.value
37
38        field_dict: dict[str, Any] = {}
39
40        field_dict.update({
41            "apiKey": api_key,
42            "callId": call_id,
43        })
44        if subscribe_mode is not UNSET:
45            field_dict["subscribeMode"] = subscribe_mode
46
47        return field_dict
@classmethod
def from_dict(cls: 'type[T]', src_dict: 'Mapping[str, Any]') -> 'T':
49    @classmethod
50    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
51        d = dict(src_dict)
52        api_key = d.pop("apiKey")
53
54        call_id = d.pop("callId")
55
56        _subscribe_mode = d.pop("subscribeMode", UNSET)
57        subscribe_mode: SubscribeMode | Unset
58        if isinstance(_subscribe_mode, Unset):
59            subscribe_mode = UNSET
60        else:
61            subscribe_mode = SubscribeMode(_subscribe_mode)
62
63        peer_options_vapi = cls(
64            api_key=api_key,
65            call_id=call_id,
66            subscribe_mode=subscribe_mode,
67        )
68
69        return peer_options_vapi
@dataclass
class RoomOptions:
 85@dataclass
 86class RoomOptions:
 87    """Description of a room options.
 88
 89    Attributes:
 90        max_peers: Maximum amount of peers allowed into the room.
 91        video_codec: Enforces video codec for each peer in the room.
 92        webhook_url: URL where Fishjam notifications will be sent.
 93        room_type: The use-case of the room. If not provided, this defaults
 94            to conference.
 95        public: True if livestream viewers can omit specifying a token.
 96        batch_webhook_notifications: If true, webhook notifications for this room
 97            are coalesced into a single NotificationBatch per HTTP send instead
 98            of one request per notification.
 99    """
100
101    max_peers: int | None = None
102    """Maximum amount of peers allowed into the room"""
103    video_codec: Literal["h264", "vp8"] | None = None
104    """Enforces video codec for each peer in the room"""
105    webhook_url: str | None = None
106    """URL where Fishjam notifications will be sent"""
107    room_type: Literal[
108        "conference",
109        "audio_only",
110        "livestream",
111        "full_feature",
112        "broadcaster",
113        "audio_only_livestream",
114    ] = "conference"
115    """The use-case of the room. If not provided, this defaults to conference."""
116    public: bool = False
117    """True if livestream viewers can omit specifying a token."""
118    batch_webhook_notifications: bool = False
119    """Coalesce webhook notifications into a single NotificationBatch per send."""

Description of a room options.

Attributes:

  • max_peers: Maximum amount of peers allowed into the room.
  • video_codec: Enforces video codec for each peer in the room.
  • webhook_url: URL where Fishjam notifications will be sent.
  • room_type: The use-case of the room. If not provided, this defaults to conference.
  • public: True if livestream viewers can omit specifying a token.
  • batch_webhook_notifications: If true, webhook notifications for this room are coalesced into a single NotificationBatch per HTTP send instead of one request per notification.
RoomOptions( max_peers: int | None = None, video_codec: Literal['h264', 'vp8'] | None = None, webhook_url: str | None = None, room_type: Literal['conference', 'audio_only', 'livestream', 'full_feature', 'broadcaster', 'audio_only_livestream'] = 'conference', public: bool = False, batch_webhook_notifications: bool = False)
max_peers: int | None = None

Maximum amount of peers allowed into the room

video_codec: Literal['h264', 'vp8'] | None = None

Enforces video codec for each peer in the room

webhook_url: str | None = None

URL where Fishjam notifications will be sent

room_type: Literal['conference', 'audio_only', 'livestream', 'full_feature', 'broadcaster', 'audio_only_livestream'] = 'conference'

The use-case of the room. If not provided, this defaults to conference.

public: bool = False

True if livestream viewers can omit specifying a token.

batch_webhook_notifications: bool = False

Coalesce webhook notifications into a single NotificationBatch per send.

@dataclass
class AgentOptions:
150@dataclass
151class AgentOptions:
152    """Options specific to an Agent Peer.
153
154    Attributes:
155        output: Configuration for the agent's output options.
156        subscribe_mode: Configuration of peer's subscribing policy.
157    """
158
159    output: AgentOutputOptions = field(default_factory=AgentOutputOptions)
160
161    subscribe_mode: Literal["auto", "manual"] = "auto"

Options specific to an Agent Peer.

Attributes:

  • output: Configuration for the agent's output options.
  • subscribe_mode: Configuration of peer's subscribing policy.
AgentOptions( output: AgentOutputOptions = <factory>, subscribe_mode: Literal['auto', 'manual'] = 'auto')
subscribe_mode: Literal['auto', 'manual'] = 'auto'
@dataclass
class AgentOutputOptions:
137@dataclass
138class AgentOutputOptions:
139    """Options of the desired format of audio tracks going from Fishjam to the agent.
140
141    Attributes:
142        audio_format: The format of the audio stream (e.g., 'pcm16').
143        audio_sample_rate: The sample rate of the audio stream.
144    """
145
146    audio_format: Literal["pcm16"] = "pcm16"
147    audio_sample_rate: Literal[16000, 24000] = 16000

Options of the desired format of audio tracks going from Fishjam to the agent.

Attributes:

  • audio_format: The format of the audio stream (e.g., 'pcm16').
  • audio_sample_rate: The sample rate of the audio stream.
AgentOutputOptions( audio_format: Literal['pcm16'] = 'pcm16', audio_sample_rate: Literal[16000, 24000] = 16000)
audio_format: Literal['pcm16'] = 'pcm16'
audio_sample_rate: Literal[16000, 24000] = 16000
@dataclass
class Room:
67@dataclass
68class Room:
69    """Description of the room state.
70
71    Attributes:
72        config: Room configuration.
73        id: Room ID.
74        peers: List of all peers.
75    """
76
77    config: RoomConfig
78    """Room configuration"""
79    id: str
80    """Room ID"""
81    peers: list[Peer]
82    """List of all peers"""

Description of the room state.

Attributes:

  • config: Room configuration.
  • id: Room ID.
  • peers: List of all peers.
Room( config: fishjam._openapi_client.models.room_config.RoomConfig, id: str, peers: list[Peer])
config: fishjam._openapi_client.models.room_config.RoomConfig

Room configuration

id: str

Room ID

peers: list[Peer]

List of all peers

class Peer:
 23@_attrs_define
 24class Peer:
 25    """Describes peer status
 26
 27    Attributes:
 28        id (str): Assigned peer id Example: 4a1c1164-5fb7-425d-89d7-24cdb8fff1cf.
 29        metadata (None | PeerMetadata): Custom metadata set by the peer Example: {'name': 'FishjamUser'}.
 30        status (PeerStatus): Informs about the peer status Example: disconnected.
 31        subscribe_mode (SubscribeMode): Configuration of peer's subscribing policy
 32        subscriptions (Subscriptions): Describes peer's subscriptions in manual mode
 33        tracks (list[Track]): List of all peer's tracks
 34        type_ (PeerType): Peer type Example: webrtc.
 35    """
 36
 37    id: str
 38    metadata: None | PeerMetadata
 39    status: PeerStatus
 40    subscribe_mode: SubscribeMode
 41    subscriptions: Subscriptions
 42    tracks: list[Track]
 43    type_: PeerType
 44    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
 45
 46    def to_dict(self) -> dict[str, Any]:
 47        from ..models.peer_metadata import PeerMetadata
 48
 49        id = self.id
 50
 51        metadata: dict[str, Any] | None
 52        if isinstance(self.metadata, PeerMetadata):
 53            metadata = self.metadata.to_dict()
 54        else:
 55            metadata = self.metadata
 56
 57        status = self.status.value
 58
 59        subscribe_mode = self.subscribe_mode.value
 60
 61        subscriptions = self.subscriptions.to_dict()
 62
 63        tracks = []
 64        for tracks_item_data in self.tracks:
 65            tracks_item = tracks_item_data.to_dict()
 66            tracks.append(tracks_item)
 67
 68        type_ = self.type_.value
 69
 70        field_dict: dict[str, Any] = {}
 71        field_dict.update(self.additional_properties)
 72        field_dict.update({
 73            "id": id,
 74            "metadata": metadata,
 75            "status": status,
 76            "subscribeMode": subscribe_mode,
 77            "subscriptions": subscriptions,
 78            "tracks": tracks,
 79            "type": type_,
 80        })
 81
 82        return field_dict
 83
 84    @classmethod
 85    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 86        from ..models.peer_metadata import PeerMetadata
 87        from ..models.subscriptions import Subscriptions
 88        from ..models.track import Track
 89
 90        d = dict(src_dict)
 91        id = d.pop("id")
 92
 93        def _parse_metadata(data: object) -> None | PeerMetadata:
 94            if data is None:
 95                return data
 96            try:
 97                if not isinstance(data, dict):
 98                    raise TypeError()
 99                componentsschemas_peer_metadata_type_0 = PeerMetadata.from_dict(data)
100
101                return componentsschemas_peer_metadata_type_0
102            except (TypeError, ValueError, AttributeError, KeyError):
103                pass
104            return cast(None | PeerMetadata, data)
105
106        metadata = _parse_metadata(d.pop("metadata"))
107
108        status = PeerStatus(d.pop("status"))
109
110        subscribe_mode = SubscribeMode(d.pop("subscribeMode"))
111
112        subscriptions = Subscriptions.from_dict(d.pop("subscriptions"))
113
114        tracks = []
115        _tracks = d.pop("tracks")
116        for tracks_item_data in _tracks:
117            tracks_item = Track.from_dict(tracks_item_data)
118
119            tracks.append(tracks_item)
120
121        type_ = PeerType(d.pop("type"))
122
123        peer = cls(
124            id=id,
125            metadata=metadata,
126            status=status,
127            subscribe_mode=subscribe_mode,
128            subscriptions=subscriptions,
129            tracks=tracks,
130            type_=type_,
131        )
132
133        peer.additional_properties = d
134        return peer
135
136    @property
137    def additional_keys(self) -> list[str]:
138        return list(self.additional_properties.keys())
139
140    def __getitem__(self, key: str) -> Any:
141        return self.additional_properties[key]
142
143    def __setitem__(self, key: str, value: Any) -> None:
144        self.additional_properties[key] = value
145
146    def __delitem__(self, key: str) -> None:
147        del self.additional_properties[key]
148
149    def __contains__(self, key: str) -> bool:
150        return key in self.additional_properties

Describes peer status

Attributes:

  • id (str): Assigned peer id Example: 4a1c1164-5fb7-425d-89d7-24cdb8fff1cf.
  • metadata (None | PeerMetadata): Custom metadata set by the peer Example: {'name': 'FishjamUser'}.
  • status (PeerStatus): Informs about the peer status Example: disconnected.
  • subscribe_mode (SubscribeMode): Configuration of peer's subscribing policy
  • subscriptions (Subscriptions): Describes peer's subscriptions in manual mode
  • tracks (list[Track]): List of all peer's tracks
  • type_ (PeerType): Peer type Example: webrtc.
Peer( id: 'str', metadata: 'None | PeerMetadata', status: 'PeerStatus', subscribe_mode: 'SubscribeMode', subscriptions: 'Subscriptions', tracks: 'list[Track]', type_: 'PeerType')
30def __init__(self, id, metadata, status, subscribe_mode, subscriptions, tracks, type_):
31    self.id = id
32    self.metadata = metadata
33    self.status = status
34    self.subscribe_mode = subscribe_mode
35    self.subscriptions = subscriptions
36    self.tracks = tracks
37    self.type_ = type_
38    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class Peer.

id: 'str'
metadata: 'None | PeerMetadata'
status: 'PeerStatus'
subscribe_mode: 'SubscribeMode'
subscriptions: 'Subscriptions'
tracks: 'list[Track]'
type_: 'PeerType'
additional_properties: 'dict[str, Any]'
def to_dict(self) -> 'dict[str, Any]':
46    def to_dict(self) -> dict[str, Any]:
47        from ..models.peer_metadata import PeerMetadata
48
49        id = self.id
50
51        metadata: dict[str, Any] | None
52        if isinstance(self.metadata, PeerMetadata):
53            metadata = self.metadata.to_dict()
54        else:
55            metadata = self.metadata
56
57        status = self.status.value
58
59        subscribe_mode = self.subscribe_mode.value
60
61        subscriptions = self.subscriptions.to_dict()
62
63        tracks = []
64        for tracks_item_data in self.tracks:
65            tracks_item = tracks_item_data.to_dict()
66            tracks.append(tracks_item)
67
68        type_ = self.type_.value
69
70        field_dict: dict[str, Any] = {}
71        field_dict.update(self.additional_properties)
72        field_dict.update({
73            "id": id,
74            "metadata": metadata,
75            "status": status,
76            "subscribeMode": subscribe_mode,
77            "subscriptions": subscriptions,
78            "tracks": tracks,
79            "type": type_,
80        })
81
82        return field_dict
@classmethod
def from_dict(cls: 'type[T]', src_dict: 'Mapping[str, Any]') -> 'T':
 84    @classmethod
 85    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
 86        from ..models.peer_metadata import PeerMetadata
 87        from ..models.subscriptions import Subscriptions
 88        from ..models.track import Track
 89
 90        d = dict(src_dict)
 91        id = d.pop("id")
 92
 93        def _parse_metadata(data: object) -> None | PeerMetadata:
 94            if data is None:
 95                return data
 96            try:
 97                if not isinstance(data, dict):
 98                    raise TypeError()
 99                componentsschemas_peer_metadata_type_0 = PeerMetadata.from_dict(data)
100
101                return componentsschemas_peer_metadata_type_0
102            except (TypeError, ValueError, AttributeError, KeyError):
103                pass
104            return cast(None | PeerMetadata, data)
105
106        metadata = _parse_metadata(d.pop("metadata"))
107
108        status = PeerStatus(d.pop("status"))
109
110        subscribe_mode = SubscribeMode(d.pop("subscribeMode"))
111
112        subscriptions = Subscriptions.from_dict(d.pop("subscriptions"))
113
114        tracks = []
115        _tracks = d.pop("tracks")
116        for tracks_item_data in _tracks:
117            tracks_item = Track.from_dict(tracks_item_data)
118
119            tracks.append(tracks_item)
120
121        type_ = PeerType(d.pop("type"))
122
123        peer = cls(
124            id=id,
125            metadata=metadata,
126            status=status,
127            subscribe_mode=subscribe_mode,
128            subscriptions=subscriptions,
129            tracks=tracks,
130            type_=type_,
131        )
132
133        peer.additional_properties = d
134        return peer
additional_keys: 'list[str]'
136    @property
137    def additional_keys(self) -> list[str]:
138        return list(self.additional_properties.keys())
class MoqAccess:
13@_attrs_define
14class MoqAccess:
15    """Connection details for a MoQ relay client
16
17    Attributes:
18        connection_url (str): Relay connection URL with the JWT embedded as a `?jwt=` query parameter. Pass directly to
19            a MoQ client SDK. Example: https://relay.fishjam.io/abc123?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....
20        token (str): JWT authorizing the MoQ relay connection, also embedded in `connection_url` Example: eyJhbGciOiJIUz
21            I1NiIsInR5cCI6IkpXVCJ9.eyJyb290IjoiZmlzaGphbSIsInB1dCI6WyJteS1zdHJlYW0iXSwiZ2V0IjpbXSwiaWF0IjoxNzEzMzYwMDAwLCJle
22            HAiOjE3MTMzNjM2MDB9.abc123.
23    """
24
25    connection_url: str
26    token: str
27    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
28
29    def to_dict(self) -> dict[str, Any]:
30        connection_url = self.connection_url
31
32        token = self.token
33
34        field_dict: dict[str, Any] = {}
35        field_dict.update(self.additional_properties)
36        field_dict.update({
37            "connection_url": connection_url,
38            "token": token,
39        })
40
41        return field_dict
42
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        d = dict(src_dict)
46        connection_url = d.pop("connection_url")
47
48        token = d.pop("token")
49
50        moq_access = cls(
51            connection_url=connection_url,
52            token=token,
53        )
54
55        moq_access.additional_properties = d
56        return moq_access
57
58    @property
59    def additional_keys(self) -> list[str]:
60        return list(self.additional_properties.keys())
61
62    def __getitem__(self, key: str) -> Any:
63        return self.additional_properties[key]
64
65    def __setitem__(self, key: str, value: Any) -> None:
66        self.additional_properties[key] = value
67
68    def __delitem__(self, key: str) -> None:
69        del self.additional_properties[key]
70
71    def __contains__(self, key: str) -> bool:
72        return key in self.additional_properties

Connection details for a MoQ relay client

Attributes:

  • connection_url (str): Relay connection URL with the JWT embedded as a ?jwt= query parameter. Pass directly to a MoQ client SDK. Example: https://relay.fishjam.io/abc123?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....
  • token (str): JWT authorizing the MoQ relay connection, also embedded in connection_url Example: eyJhbGciOiJIUz I1NiIsInR5cCI6IkpXVCJ9.eyJyb290IjoiZmlzaGphbSIsInB1dCI6WyJteS1zdHJlYW0iXSwiZ2V0IjpbXSwiaWF0IjoxNzEzMzYwMDAwLCJle HAiOjE3MTMzNjM2MDB9.abc123.
MoqAccess(connection_url: 'str', token: 'str')
25def __init__(self, connection_url, token):
26    self.connection_url = connection_url
27    self.token = token
28    self.additional_properties = __attr_factory_additional_properties()

Method generated by attrs for class MoqAccess.

connection_url: 'str'
token: 'str'
additional_properties: 'dict[str, Any]'
def to_dict(self) -> 'dict[str, Any]':
29    def to_dict(self) -> dict[str, Any]:
30        connection_url = self.connection_url
31
32        token = self.token
33
34        field_dict: dict[str, Any] = {}
35        field_dict.update(self.additional_properties)
36        field_dict.update({
37            "connection_url": connection_url,
38            "token": token,
39        })
40
41        return field_dict
@classmethod
def from_dict(cls: 'type[T]', src_dict: 'Mapping[str, Any]') -> 'T':
43    @classmethod
44    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
45        d = dict(src_dict)
46        connection_url = d.pop("connection_url")
47
48        token = d.pop("token")
49
50        moq_access = cls(
51            connection_url=connection_url,
52            token=token,
53        )
54
55        moq_access.additional_properties = d
56        return moq_access
additional_keys: 'list[str]'
58    @property
59    def additional_keys(self) -> list[str]:
60        return list(self.additional_properties.keys())
class MissingFishjamIdError(builtins.ValueError):
 8class MissingFishjamIdError(ValueError):
 9    def __init__(self) -> None:
10        super().__init__("Fishjam ID is required")

Inappropriate argument value (of correct type).

Inherited Members
builtins.BaseException
with_traceback
add_note
args
class InvalidFishjamCredentialsError(fishjam.errors.HTTPError):
80class InvalidFishjamCredentialsError(HTTPError):
81    def __init__(self, errors):
82        """@private"""
83        super().__init__(errors)
Inherited Members
builtins.BaseException
with_traceback
add_note
args