diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 8c6229f..2b9ba71 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -18,16 +18,10 @@ uv run ./compile_proto.sh ## Testing -You can test the SDK by running +You can test the SDK by running. Remember to pass the testing credentials in .env file. ```console -uv run ci_test -``` - -In local development you can use - -```console -uv run local_test +uv run test ``` ## Format & Lint diff --git a/fishjam/__init__.py b/fishjam/__init__.py index ed2ca4b..15195a4 100644 --- a/fishjam/__init__.py +++ b/fishjam/__init__.py @@ -20,6 +20,7 @@ FishjamClient, Peer, PeerOptions, + PeerOptionsVapi, Room, RoomOptions, ) @@ -32,6 +33,7 @@ "receive_binary", "PeerMetadata", "PeerOptions", + "PeerOptionsVapi", "RoomOptions", "AgentOptions", "AgentOutputOptions", diff --git a/fishjam/_openapi_client/api/room/add_peer.py b/fishjam/_openapi_client/api/room/add_peer.py index 96b33dc..b7c9507 100644 --- a/fishjam/_openapi_client/api/room/add_peer.py +++ b/fishjam/_openapi_client/api/room/add_peer.py @@ -48,6 +48,10 @@ def _parse_response( response_401 = Error.from_dict(response.json()) return response_401 + if response.status_code == 402: + response_402 = Error.from_dict(response.json()) + + return response_402 if response.status_code == 404: response_404 = Error.from_dict(response.json()) diff --git a/fishjam/_openapi_client/api/room/create_room.py b/fishjam/_openapi_client/api/room/create_room.py index 5b26c44..9109c3f 100644 --- a/fishjam/_openapi_client/api/room/create_room.py +++ b/fishjam/_openapi_client/api/room/create_room.py @@ -45,6 +45,10 @@ def _parse_response( response_401 = Error.from_dict(response.json()) return response_401 + if response.status_code == 402: + response_402 = Error.from_dict(response.json()) + + return response_402 if response.status_code == 503: response_503 = Error.from_dict(response.json()) diff --git a/fishjam/_openapi_client/api/stream/create_stream.py b/fishjam/_openapi_client/api/stream/create_stream.py index 6dc8015..0626da7 100644 --- a/fishjam/_openapi_client/api/stream/create_stream.py +++ b/fishjam/_openapi_client/api/stream/create_stream.py @@ -6,8 +6,8 @@ from ... import errors from ...client import AuthenticatedClient, Client from ...models.error import Error -from ...models.stream import Stream from ...models.stream_config import StreamConfig +from ...models.stream_details_response import StreamDetailsResponse from ...types import Response @@ -32,9 +32,9 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Error, Stream]]: +) -> Optional[Union[Error, StreamDetailsResponse]]: if response.status_code == 201: - response_201 = Stream.from_dict(response.json()) + response_201 = StreamDetailsResponse.from_dict(response.json()) return response_201 if response.status_code == 400: @@ -57,7 +57,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Error, Stream]]: +) -> Response[Union[Error, StreamDetailsResponse]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -70,7 +70,7 @@ def sync_detailed( *, client: AuthenticatedClient, body: StreamConfig, -) -> Response[Union[Error, Stream]]: +) -> Response[Union[Error, StreamDetailsResponse]]: """Creates stream Args: @@ -81,7 +81,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Stream]] + Response[Union[Error, StreamDetailsResponse]] """ kwargs = _get_kwargs( @@ -99,7 +99,7 @@ def sync( *, client: AuthenticatedClient, body: StreamConfig, -) -> Optional[Union[Error, Stream]]: +) -> Optional[Union[Error, StreamDetailsResponse]]: """Creates stream Args: @@ -110,7 +110,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Stream] + Union[Error, StreamDetailsResponse] """ return sync_detailed( @@ -123,7 +123,7 @@ async def asyncio_detailed( *, client: AuthenticatedClient, body: StreamConfig, -) -> Response[Union[Error, Stream]]: +) -> Response[Union[Error, StreamDetailsResponse]]: """Creates stream Args: @@ -134,7 +134,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Stream]] + Response[Union[Error, StreamDetailsResponse]] """ kwargs = _get_kwargs( @@ -150,7 +150,7 @@ async def asyncio( *, client: AuthenticatedClient, body: StreamConfig, -) -> Optional[Union[Error, Stream]]: +) -> Optional[Union[Error, StreamDetailsResponse]]: """Creates stream Args: @@ -161,7 +161,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Stream] + Union[Error, StreamDetailsResponse] """ return ( diff --git a/fishjam/_openapi_client/api/stream/get_stream.py b/fishjam/_openapi_client/api/stream/get_stream.py index 92244b5..296d9ce 100644 --- a/fishjam/_openapi_client/api/stream/get_stream.py +++ b/fishjam/_openapi_client/api/stream/get_stream.py @@ -6,7 +6,7 @@ from ... import errors from ...client import AuthenticatedClient, Client from ...models.error import Error -from ...models.stream import Stream +from ...models.stream_details_response import StreamDetailsResponse from ...types import Response @@ -25,9 +25,9 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Error, Stream]]: +) -> Optional[Union[Error, StreamDetailsResponse]]: if response.status_code == 200: - response_200 = Stream.from_dict(response.json()) + response_200 = StreamDetailsResponse.from_dict(response.json()) return response_200 if response.status_code == 401: @@ -50,7 +50,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Error, Stream]]: +) -> Response[Union[Error, StreamDetailsResponse]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -63,7 +63,7 @@ def sync_detailed( stream_id: str, *, client: AuthenticatedClient, -) -> Response[Union[Error, Stream]]: +) -> Response[Union[Error, StreamDetailsResponse]]: """Shows information about the stream Args: @@ -74,7 +74,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Stream]] + Response[Union[Error, StreamDetailsResponse]] """ kwargs = _get_kwargs( @@ -92,7 +92,7 @@ def sync( stream_id: str, *, client: AuthenticatedClient, -) -> Optional[Union[Error, Stream]]: +) -> Optional[Union[Error, StreamDetailsResponse]]: """Shows information about the stream Args: @@ -103,7 +103,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Stream] + Union[Error, StreamDetailsResponse] """ return sync_detailed( @@ -116,7 +116,7 @@ async def asyncio_detailed( stream_id: str, *, client: AuthenticatedClient, -) -> Response[Union[Error, Stream]]: +) -> Response[Union[Error, StreamDetailsResponse]]: """Shows information about the stream Args: @@ -127,7 +127,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Stream]] + Response[Union[Error, StreamDetailsResponse]] """ kwargs = _get_kwargs( @@ -143,7 +143,7 @@ async def asyncio( stream_id: str, *, client: AuthenticatedClient, -) -> Optional[Union[Error, Stream]]: +) -> Optional[Union[Error, StreamDetailsResponse]]: """Shows information about the stream Args: @@ -154,7 +154,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Stream] + Union[Error, StreamDetailsResponse] """ return ( diff --git a/fishjam/_openapi_client/api/streamer/create_streamer.py b/fishjam/_openapi_client/api/streamer/create_streamer.py index a0be94a..4eff6b2 100644 --- a/fishjam/_openapi_client/api/streamer/create_streamer.py +++ b/fishjam/_openapi_client/api/streamer/create_streamer.py @@ -6,7 +6,7 @@ from ... import errors from ...client import AuthenticatedClient, Client from ...models.error import Error -from ...models.streamer import Streamer +from ...models.streamer_details_response import StreamerDetailsResponse from ...types import Response @@ -25,9 +25,9 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Error, Streamer]]: +) -> Optional[Union[Error, StreamerDetailsResponse]]: if response.status_code == 201: - response_201 = Streamer.from_dict(response.json()) + response_201 = StreamerDetailsResponse.from_dict(response.json()) return response_201 if response.status_code == 401: @@ -50,7 +50,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Error, Streamer]]: +) -> Response[Union[Error, StreamerDetailsResponse]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -63,7 +63,7 @@ def sync_detailed( stream_id: str, *, client: AuthenticatedClient, -) -> Response[Union[Error, Streamer]]: +) -> Response[Union[Error, StreamerDetailsResponse]]: """Creates streamer Args: @@ -74,7 +74,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Streamer]] + Response[Union[Error, StreamerDetailsResponse]] """ kwargs = _get_kwargs( @@ -92,7 +92,7 @@ def sync( stream_id: str, *, client: AuthenticatedClient, -) -> Optional[Union[Error, Streamer]]: +) -> Optional[Union[Error, StreamerDetailsResponse]]: """Creates streamer Args: @@ -103,7 +103,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Streamer] + Union[Error, StreamerDetailsResponse] """ return sync_detailed( @@ -116,7 +116,7 @@ async def asyncio_detailed( stream_id: str, *, client: AuthenticatedClient, -) -> Response[Union[Error, Streamer]]: +) -> Response[Union[Error, StreamerDetailsResponse]]: """Creates streamer Args: @@ -127,7 +127,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Streamer]] + Response[Union[Error, StreamerDetailsResponse]] """ kwargs = _get_kwargs( @@ -143,7 +143,7 @@ async def asyncio( stream_id: str, *, client: AuthenticatedClient, -) -> Optional[Union[Error, Streamer]]: +) -> Optional[Union[Error, StreamerDetailsResponse]]: """Creates streamer Args: @@ -154,7 +154,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Streamer] + Union[Error, StreamerDetailsResponse] """ return ( diff --git a/fishjam/_openapi_client/api/track_forwarding/__init__.py b/fishjam/_openapi_client/api/track_forwarding/__init__.py new file mode 100644 index 0000000..2d7c0b2 --- /dev/null +++ b/fishjam/_openapi_client/api/track_forwarding/__init__.py @@ -0,0 +1 @@ +"""Contains endpoint functions for accessing the API""" diff --git a/fishjam/_openapi_client/api/track_forwarding/create_track_forwarding.py b/fishjam/_openapi_client/api/track_forwarding/create_track_forwarding.py new file mode 100644 index 0000000..1c7c303 --- /dev/null +++ b/fishjam/_openapi_client/api/track_forwarding/create_track_forwarding.py @@ -0,0 +1,185 @@ +from http import HTTPStatus +from typing import Any, Optional, Union, cast + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.error import Error +from ...models.track_forwarding import TrackForwarding +from ...types import Response + + +def _get_kwargs( + room_id: str, + *, + body: TrackForwarding, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + + _kwargs: dict[str, Any] = { + "method": "post", + "url": "/room/{room_id}/track_forwardings".format( + room_id=room_id, + ), + } + + _kwargs["json"] = body.to_dict() + + headers["Content-Type"] = "application/json" + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Union[Any, Error]]: + if response.status_code == 201: + response_201 = cast(Any, None) + return response_201 + if response.status_code == 400: + response_400 = Error.from_dict(response.json()) + + return response_400 + if response.status_code == 401: + response_401 = Error.from_dict(response.json()) + + return response_401 + if response.status_code == 404: + response_404 = Error.from_dict(response.json()) + + return response_404 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Union[Any, Error]]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + room_id: str, + *, + client: AuthenticatedClient, + body: TrackForwarding, +) -> Response[Union[Any, Error]]: + """Creates a track forwarding in a room + + Args: + room_id (str): + body (TrackForwarding): Track forwardings for a room + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Union[Any, Error]] + """ + + kwargs = _get_kwargs( + room_id=room_id, + body=body, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + room_id: str, + *, + client: AuthenticatedClient, + body: TrackForwarding, +) -> Optional[Union[Any, Error]]: + """Creates a track forwarding in a room + + Args: + room_id (str): + body (TrackForwarding): Track forwardings for a room + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Union[Any, Error] + """ + + return sync_detailed( + room_id=room_id, + client=client, + body=body, + ).parsed + + +async def asyncio_detailed( + room_id: str, + *, + client: AuthenticatedClient, + body: TrackForwarding, +) -> Response[Union[Any, Error]]: + """Creates a track forwarding in a room + + Args: + room_id (str): + body (TrackForwarding): Track forwardings for a room + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Union[Any, Error]] + """ + + kwargs = _get_kwargs( + room_id=room_id, + body=body, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + room_id: str, + *, + client: AuthenticatedClient, + body: TrackForwarding, +) -> Optional[Union[Any, Error]]: + """Creates a track forwarding in a room + + Args: + room_id (str): + body (TrackForwarding): Track forwardings for a room + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Union[Any, Error] + """ + + return ( + await asyncio_detailed( + room_id=room_id, + client=client, + body=body, + ) + ).parsed diff --git a/fishjam/_openapi_client/api/viewer/create_viewer.py b/fishjam/_openapi_client/api/viewer/create_viewer.py index d19484e..8689054 100644 --- a/fishjam/_openapi_client/api/viewer/create_viewer.py +++ b/fishjam/_openapi_client/api/viewer/create_viewer.py @@ -6,7 +6,7 @@ from ... import errors from ...client import AuthenticatedClient, Client from ...models.error import Error -from ...models.viewer import Viewer +from ...models.viewer_details_response import ViewerDetailsResponse from ...types import Response @@ -25,9 +25,9 @@ def _get_kwargs( def _parse_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Optional[Union[Error, Viewer]]: +) -> Optional[Union[Error, ViewerDetailsResponse]]: if response.status_code == 201: - response_201 = Viewer.from_dict(response.json()) + response_201 = ViewerDetailsResponse.from_dict(response.json()) return response_201 if response.status_code == 401: @@ -50,7 +50,7 @@ def _parse_response( def _build_response( *, client: Union[AuthenticatedClient, Client], response: httpx.Response -) -> Response[Union[Error, Viewer]]: +) -> Response[Union[Error, ViewerDetailsResponse]]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -63,7 +63,7 @@ def sync_detailed( stream_id: str, *, client: AuthenticatedClient, -) -> Response[Union[Error, Viewer]]: +) -> Response[Union[Error, ViewerDetailsResponse]]: """Creates viewer Args: @@ -74,7 +74,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Viewer]] + Response[Union[Error, ViewerDetailsResponse]] """ kwargs = _get_kwargs( @@ -92,7 +92,7 @@ def sync( stream_id: str, *, client: AuthenticatedClient, -) -> Optional[Union[Error, Viewer]]: +) -> Optional[Union[Error, ViewerDetailsResponse]]: """Creates viewer Args: @@ -103,7 +103,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Viewer] + Union[Error, ViewerDetailsResponse] """ return sync_detailed( @@ -116,7 +116,7 @@ async def asyncio_detailed( stream_id: str, *, client: AuthenticatedClient, -) -> Response[Union[Error, Viewer]]: +) -> Response[Union[Error, ViewerDetailsResponse]]: """Creates viewer Args: @@ -127,7 +127,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[Union[Error, Viewer]] + Response[Union[Error, ViewerDetailsResponse]] """ kwargs = _get_kwargs( @@ -143,7 +143,7 @@ async def asyncio( stream_id: str, *, client: AuthenticatedClient, -) -> Optional[Union[Error, Viewer]]: +) -> Optional[Union[Error, ViewerDetailsResponse]]: """Creates viewer Args: @@ -154,7 +154,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Union[Error, Viewer] + Union[Error, ViewerDetailsResponse] """ return ( diff --git a/fishjam/_openapi_client/models/__init__.py b/fishjam/_openapi_client/models/__init__.py index 2ee6c44..b4636bf 100644 --- a/fishjam/_openapi_client/models/__init__.py +++ b/fishjam/_openapi_client/models/__init__.py @@ -3,6 +3,7 @@ from .agent_output import AgentOutput from .audio_format import AudioFormat from .audio_sample_rate import AudioSampleRate +from .composition_info import CompositionInfo from .error import Error from .peer import Peer from .peer_config import PeerConfig @@ -10,6 +11,7 @@ from .peer_details_response_data import PeerDetailsResponseData from .peer_metadata import PeerMetadata from .peer_options_agent import PeerOptionsAgent +from .peer_options_vapi import PeerOptionsVapi from .peer_options_web_rtc import PeerOptionsWebRTC from .peer_refresh_token_response import PeerRefreshTokenResponse from .peer_refresh_token_response_data import PeerRefreshTokenResponseData @@ -24,7 +26,9 @@ from .rooms_listing_response import RoomsListingResponse from .stream import Stream from .stream_config import StreamConfig +from .stream_details_response import StreamDetailsResponse from .streamer import Streamer +from .streamer_details_response import StreamerDetailsResponse from .streamer_status import StreamerStatus from .streamer_token import StreamerToken from .streams_listing_response import StreamsListingResponse @@ -32,11 +36,13 @@ from .subscribe_tracks_body import SubscribeTracksBody from .subscriptions import Subscriptions from .track import Track +from .track_forwarding import TrackForwarding +from .track_forwarding_info import TrackForwardingInfo from .track_metadata import TrackMetadata from .track_type import TrackType from .video_codec import VideoCodec from .viewer import Viewer -from .viewer_status import ViewerStatus +from .viewer_details_response import ViewerDetailsResponse from .viewer_token import ViewerToken from .web_rtc_metadata import WebRTCMetadata @@ -44,6 +50,7 @@ "AgentOutput", "AudioFormat", "AudioSampleRate", + "CompositionInfo", "Error", "Peer", "PeerConfig", @@ -51,6 +58,7 @@ "PeerDetailsResponseData", "PeerMetadata", "PeerOptionsAgent", + "PeerOptionsVapi", "PeerOptionsWebRTC", "PeerRefreshTokenResponse", "PeerRefreshTokenResponseData", @@ -65,7 +73,9 @@ "RoomType", "Stream", "StreamConfig", + "StreamDetailsResponse", "Streamer", + "StreamerDetailsResponse", "StreamerStatus", "StreamerToken", "StreamsListingResponse", @@ -73,11 +83,13 @@ "SubscribeTracksBody", "Subscriptions", "Track", + "TrackForwarding", + "TrackForwardingInfo", "TrackMetadata", "TrackType", "VideoCodec", "Viewer", - "ViewerStatus", + "ViewerDetailsResponse", "ViewerToken", "WebRTCMetadata", ) diff --git a/fishjam/_openapi_client/models/composition_info.py b/fishjam/_openapi_client/models/composition_info.py new file mode 100644 index 0000000..bb43317 --- /dev/null +++ b/fishjam/_openapi_client/models/composition_info.py @@ -0,0 +1,84 @@ +from collections.abc import Mapping +from typing import ( + TYPE_CHECKING, + Any, + TypeVar, +) + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.track_forwarding_info import TrackForwardingInfo + + +T = TypeVar("T", bound="CompositionInfo") + + +@_attrs_define +class CompositionInfo: + """Composition and track forwarding state for the room + + Attributes: + composition_url (str): URL of the active composition Example: https://rtc.fishjam.io/api/composition/12asdfxcf. + forwardings (list['TrackForwardingInfo']): List of active track forwardings + """ + + composition_url: str + forwardings: list["TrackForwardingInfo"] + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + composition_url = self.composition_url + + forwardings = [] + for forwardings_item_data in self.forwardings: + forwardings_item = forwardings_item_data.to_dict() + forwardings.append(forwardings_item) + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "compositionUrl": composition_url, + "forwardings": forwardings, + }) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.track_forwarding_info import TrackForwardingInfo + + d = dict(src_dict) + composition_url = d.pop("compositionUrl") + + forwardings = [] + _forwardings = d.pop("forwardings") + for forwardings_item_data in _forwardings: + forwardings_item = TrackForwardingInfo.from_dict(forwardings_item_data) + + forwardings.append(forwardings_item) + + composition_info = cls( + composition_url=composition_url, + forwardings=forwardings, + ) + + composition_info.additional_properties = d + return composition_info + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/peer_config.py b/fishjam/_openapi_client/models/peer_config.py index 9639d0a..413b53c 100644 --- a/fishjam/_openapi_client/models/peer_config.py +++ b/fishjam/_openapi_client/models/peer_config.py @@ -12,6 +12,7 @@ if TYPE_CHECKING: from ..models.peer_options_agent import PeerOptionsAgent + from ..models.peer_options_vapi import PeerOptionsVapi from ..models.peer_options_web_rtc import PeerOptionsWebRTC @@ -23,19 +24,22 @@ class PeerConfig: """Peer configuration Attributes: - options (Union['PeerOptionsAgent', 'PeerOptionsWebRTC']): Peer-specific options + options (Union['PeerOptionsAgent', 'PeerOptionsVapi', 'PeerOptionsWebRTC']): Peer-specific options type_ (PeerType): Peer type Example: webrtc. """ - options: Union["PeerOptionsAgent", "PeerOptionsWebRTC"] + options: Union["PeerOptionsAgent", "PeerOptionsVapi", "PeerOptionsWebRTC"] type_: PeerType def to_dict(self) -> dict[str, Any]: + from ..models.peer_options_agent import PeerOptionsAgent from ..models.peer_options_web_rtc import PeerOptionsWebRTC options: dict[str, Any] if isinstance(self.options, PeerOptionsWebRTC): options = self.options.to_dict() + elif isinstance(self.options, PeerOptionsAgent): + options = self.options.to_dict() else: options = self.options.to_dict() @@ -53,13 +57,14 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.peer_options_agent import PeerOptionsAgent + from ..models.peer_options_vapi import PeerOptionsVapi from ..models.peer_options_web_rtc import PeerOptionsWebRTC d = dict(src_dict) def _parse_options( data: object, - ) -> Union["PeerOptionsAgent", "PeerOptionsWebRTC"]: + ) -> Union["PeerOptionsAgent", "PeerOptionsVapi", "PeerOptionsWebRTC"]: try: if not isinstance(data, dict): raise TypeError() @@ -70,11 +75,19 @@ def _parse_options( return componentsschemas_peer_options_type_0 except: # noqa: E722 pass + try: + if not isinstance(data, dict): + raise TypeError() + componentsschemas_peer_options_type_1 = PeerOptionsAgent.from_dict(data) + + return componentsschemas_peer_options_type_1 + except: # noqa: E722 + pass if not isinstance(data, dict): raise TypeError() - componentsschemas_peer_options_type_1 = PeerOptionsAgent.from_dict(data) + componentsschemas_peer_options_type_2 = PeerOptionsVapi.from_dict(data) - return componentsschemas_peer_options_type_1 + return componentsschemas_peer_options_type_2 options = _parse_options(d.pop("options")) diff --git a/fishjam/_openapi_client/models/peer_options_vapi.py b/fishjam/_openapi_client/models/peer_options_vapi.py new file mode 100644 index 0000000..078e473 --- /dev/null +++ b/fishjam/_openapi_client/models/peer_options_vapi.py @@ -0,0 +1,70 @@ +from collections.abc import Mapping +from typing import ( + Any, + TypeVar, + Union, +) + +from attrs import define as _attrs_define + +from ..models.subscribe_mode import SubscribeMode +from ..types import UNSET, Unset + +T = TypeVar("T", bound="PeerOptionsVapi") + + +@_attrs_define +class PeerOptionsVapi: + """Options specific to the VAPI peer + + Attributes: + api_key (str): VAPI API key + call_id (str): VAPI call ID + subscribe_mode (Union[Unset, SubscribeMode]): Configuration of peer's subscribing policy + """ + + api_key: str + call_id: str + subscribe_mode: Union[Unset, SubscribeMode] = UNSET + + def to_dict(self) -> dict[str, Any]: + api_key = self.api_key + + call_id = self.call_id + + subscribe_mode: Union[Unset, str] = UNSET + if not isinstance(self.subscribe_mode, Unset): + subscribe_mode = self.subscribe_mode.value + + field_dict: dict[str, Any] = {} + + field_dict.update({ + "apiKey": api_key, + "callId": call_id, + }) + if subscribe_mode is not UNSET: + field_dict["subscribeMode"] = subscribe_mode + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + api_key = d.pop("apiKey") + + call_id = d.pop("callId") + + _subscribe_mode = d.pop("subscribeMode", UNSET) + subscribe_mode: Union[Unset, SubscribeMode] + if isinstance(_subscribe_mode, Unset): + subscribe_mode = UNSET + else: + subscribe_mode = SubscribeMode(_subscribe_mode) + + peer_options_vapi = cls( + api_key=api_key, + call_id=call_id, + subscribe_mode=subscribe_mode, + ) + + return peer_options_vapi diff --git a/fishjam/_openapi_client/models/peer_type.py b/fishjam/_openapi_client/models/peer_type.py index a3fa920..482ccc1 100644 --- a/fishjam/_openapi_client/models/peer_type.py +++ b/fishjam/_openapi_client/models/peer_type.py @@ -5,6 +5,7 @@ class PeerType(str, Enum): """Peer type""" AGENT = "agent" + VAPI = "vapi" WEBRTC = "webrtc" def __str__(self) -> str: diff --git a/fishjam/_openapi_client/models/room.py b/fishjam/_openapi_client/models/room.py index e2c8fbf..79259ab 100644 --- a/fishjam/_openapi_client/models/room.py +++ b/fishjam/_openapi_client/models/room.py @@ -3,12 +3,17 @@ TYPE_CHECKING, Any, TypeVar, + Union, + cast, ) from attrs import define as _attrs_define from attrs import field as _attrs_field +from ..types import UNSET, Unset + if TYPE_CHECKING: + from ..models.composition_info import CompositionInfo from ..models.peer import Peer from ..models.room_config import RoomConfig @@ -24,14 +29,18 @@ class Room: config (RoomConfig): Room configuration id (str): Room ID Example: room-1. peers (list['Peer']): List of all peers + composition_info (Union['CompositionInfo', None, Unset]): Composition and track forwarding state for the room """ config: "RoomConfig" id: str peers: list["Peer"] + composition_info: Union["CompositionInfo", None, Unset] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.composition_info import CompositionInfo + config = self.config.to_dict() id = self.id @@ -41,6 +50,14 @@ def to_dict(self) -> dict[str, Any]: peers_item = peers_item_data.to_dict() peers.append(peers_item) + composition_info: Union[None, Unset, dict[str, Any]] + if isinstance(self.composition_info, Unset): + composition_info = UNSET + elif isinstance(self.composition_info, CompositionInfo): + composition_info = self.composition_info.to_dict() + else: + composition_info = self.composition_info + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({ @@ -48,11 +65,14 @@ def to_dict(self) -> dict[str, Any]: "id": id, "peers": peers, }) + if composition_info is not UNSET: + field_dict["compositionInfo"] = composition_info return field_dict @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.composition_info import CompositionInfo from ..models.peer import Peer from ..models.room_config import RoomConfig @@ -68,10 +88,32 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: peers.append(peers_item) + def _parse_composition_info( + data: object, + ) -> Union["CompositionInfo", None, Unset]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + componentsschemas_composition_info_type_0 = CompositionInfo.from_dict( + data + ) + + return componentsschemas_composition_info_type_0 + except: # noqa: E722 + pass + return cast(Union["CompositionInfo", None, Unset], data) + + composition_info = _parse_composition_info(d.pop("compositionInfo", UNSET)) + room = cls( config=config, id=id, peers=peers, + composition_info=composition_info, ) room.additional_properties = d diff --git a/fishjam/_openapi_client/models/room_create_details_response_data.py b/fishjam/_openapi_client/models/room_create_details_response_data.py index 8ac7dc9..bcab900 100644 --- a/fishjam/_openapi_client/models/room_create_details_response_data.py +++ b/fishjam/_openapi_client/models/room_create_details_response_data.py @@ -19,24 +19,18 @@ class RoomCreateDetailsResponseData: """ Attributes: - fishjam_address (str): Fishjam instance address where the room was created. This might be different than the - address of Fishjam where the request was sent only when running a cluster of Fishjams. Example: fishjam1:5003. room (Room): Description of the room state """ - fishjam_address: str room: "Room" additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: - fishjam_address = self.fishjam_address - room = self.room.to_dict() field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({ - "fishjam_address": fishjam_address, "room": room, }) @@ -47,12 +41,9 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.room import Room d = dict(src_dict) - fishjam_address = d.pop("fishjam_address") - room = Room.from_dict(d.pop("room")) room_create_details_response_data = cls( - fishjam_address=fishjam_address, room=room, ) diff --git a/fishjam/_openapi_client/models/stream.py b/fishjam/_openapi_client/models/stream.py index 0704770..4f85dd3 100644 --- a/fishjam/_openapi_client/models/stream.py +++ b/fishjam/_openapi_client/models/stream.py @@ -24,7 +24,6 @@ class Stream: """Describes stream status Attributes: - connected_viewers (int): Number of connected viewers id (str): Assigned stream id public (bool): streamers (list['Streamer']): List of all streamers @@ -32,7 +31,6 @@ class Stream: audio_only (Union[Unset, bool]): True if stream is restricted to audio only """ - connected_viewers: int id: str public: bool streamers: list["Streamer"] @@ -41,8 +39,6 @@ class Stream: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: - connected_viewers = self.connected_viewers - id = self.id public = self.public @@ -62,7 +58,6 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({ - "connectedViewers": connected_viewers, "id": id, "public": public, "streamers": streamers, @@ -79,8 +74,6 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.viewer import Viewer d = dict(src_dict) - connected_viewers = d.pop("connectedViewers") - id = d.pop("id") public = d.pop("public") @@ -102,7 +95,6 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: audio_only = d.pop("audioOnly", UNSET) stream = cls( - connected_viewers=connected_viewers, id=id, public=public, streamers=streamers, diff --git a/fishjam/_openapi_client/models/stream_details_response.py b/fishjam/_openapi_client/models/stream_details_response.py new file mode 100644 index 0000000..030eb9f --- /dev/null +++ b/fishjam/_openapi_client/models/stream_details_response.py @@ -0,0 +1,68 @@ +from collections.abc import Mapping +from typing import ( + TYPE_CHECKING, + Any, + TypeVar, +) + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.stream import Stream + + +T = TypeVar("T", bound="StreamDetailsResponse") + + +@_attrs_define +class StreamDetailsResponse: + """Response containing stream details + + Attributes: + data (Stream): Describes stream status + """ + + data: "Stream" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + data = self.data.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "data": data, + }) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.stream import Stream + + d = dict(src_dict) + data = Stream.from_dict(d.pop("data")) + + stream_details_response = cls( + data=data, + ) + + stream_details_response.additional_properties = d + return stream_details_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/streamer.py b/fishjam/_openapi_client/models/streamer.py index f234c00..61f7662 100644 --- a/fishjam/_openapi_client/models/streamer.py +++ b/fishjam/_openapi_client/models/streamer.py @@ -1,19 +1,11 @@ from collections.abc import Mapping -from typing import ( - TYPE_CHECKING, - Any, - TypeVar, -) +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field from ..models.streamer_status import StreamerStatus -if TYPE_CHECKING: - from ..models.streamer_token import StreamerToken - - T = TypeVar("T", bound="Streamer") @@ -24,12 +16,12 @@ class Streamer: Attributes: id (str): Assigned streamer id status (StreamerStatus): - token (StreamerToken): Token for authorizing broadcaster streamer connection + token (str): Example: 5cdac726-57a3-4ecb-b1d5-72a3d62ec242. """ id: str status: StreamerStatus - token: "StreamerToken" + token: str additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: @@ -37,7 +29,7 @@ def to_dict(self) -> dict[str, Any]: status = self.status.value - token = self.token.to_dict() + token = self.token field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -51,14 +43,12 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.streamer_token import StreamerToken - d = dict(src_dict) id = d.pop("id") status = StreamerStatus(d.pop("status")) - token = StreamerToken.from_dict(d.pop("token")) + token = d.pop("token") streamer = cls( id=id, diff --git a/fishjam/_openapi_client/models/streamer_details_response.py b/fishjam/_openapi_client/models/streamer_details_response.py new file mode 100644 index 0000000..cc9bc9c --- /dev/null +++ b/fishjam/_openapi_client/models/streamer_details_response.py @@ -0,0 +1,68 @@ +from collections.abc import Mapping +from typing import ( + TYPE_CHECKING, + Any, + TypeVar, +) + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.streamer import Streamer + + +T = TypeVar("T", bound="StreamerDetailsResponse") + + +@_attrs_define +class StreamerDetailsResponse: + """Response containing streamer details + + Attributes: + data (Streamer): Describes streamer status + """ + + data: "Streamer" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + data = self.data.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "data": data, + }) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.streamer import Streamer + + d = dict(src_dict) + data = Streamer.from_dict(d.pop("data")) + + streamer_details_response = cls( + data=data, + ) + + streamer_details_response.additional_properties = d + return streamer_details_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/track_forwarding.py b/fishjam/_openapi_client/models/track_forwarding.py new file mode 100644 index 0000000..ebbe3c4 --- /dev/null +++ b/fishjam/_openapi_client/models/track_forwarding.py @@ -0,0 +1,55 @@ +from collections.abc import Mapping +from typing import ( + Any, + TypeVar, + Union, +) + +from attrs import define as _attrs_define + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="TrackForwarding") + + +@_attrs_define +class TrackForwarding: + """Track forwardings for a room + + Attributes: + composition_url (str): URL for the composition + selector (Union[Unset, str]): Selects tracks that should be forwarded, currently only "all" is supported + Default: 'all'. + """ + + composition_url: str + selector: Union[Unset, str] = "all" + + def to_dict(self) -> dict[str, Any]: + composition_url = self.composition_url + + selector = self.selector + + field_dict: dict[str, Any] = {} + + field_dict.update({ + "compositionURL": composition_url, + }) + if selector is not UNSET: + field_dict["selector"] = selector + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + composition_url = d.pop("compositionURL") + + selector = d.pop("selector", UNSET) + + track_forwarding = cls( + composition_url=composition_url, + selector=selector, + ) + + return track_forwarding diff --git a/fishjam/_openapi_client/models/track_forwarding_info.py b/fishjam/_openapi_client/models/track_forwarding_info.py new file mode 100644 index 0000000..7a166ff --- /dev/null +++ b/fishjam/_openapi_client/models/track_forwarding_info.py @@ -0,0 +1,113 @@ +from collections.abc import Mapping +from typing import ( + Any, + TypeVar, + Union, + cast, +) + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +T = TypeVar("T", bound="TrackForwardingInfo") + + +@_attrs_define +class TrackForwardingInfo: + """Information about a track forwarding for a specific peer + + Attributes: + input_id (str): Input ID used by the composition Example: input-1. + peer_id (str): Peer ID Example: peer-1. + audio_track_id (Union[None, Unset, str]): ID of the forwarded audio track Example: track-audio-1. + video_track_id (Union[None, Unset, str]): ID of the forwarded video track Example: track-video-1. + """ + + input_id: str + peer_id: str + audio_track_id: Union[None, Unset, str] = UNSET + video_track_id: Union[None, Unset, str] = UNSET + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + input_id = self.input_id + + peer_id = self.peer_id + + audio_track_id: Union[None, Unset, str] + if isinstance(self.audio_track_id, Unset): + audio_track_id = UNSET + else: + audio_track_id = self.audio_track_id + + video_track_id: Union[None, Unset, str] + if isinstance(self.video_track_id, Unset): + video_track_id = UNSET + else: + video_track_id = self.video_track_id + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "inputId": input_id, + "peerId": peer_id, + }) + if audio_track_id is not UNSET: + field_dict["audioTrackId"] = audio_track_id + if video_track_id is not UNSET: + field_dict["videoTrackId"] = video_track_id + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + input_id = d.pop("inputId") + + peer_id = d.pop("peerId") + + def _parse_audio_track_id(data: object) -> Union[None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, str], data) + + audio_track_id = _parse_audio_track_id(d.pop("audioTrackId", UNSET)) + + def _parse_video_track_id(data: object) -> Union[None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, str], data) + + video_track_id = _parse_video_track_id(d.pop("videoTrackId", UNSET)) + + track_forwarding_info = cls( + input_id=input_id, + peer_id=peer_id, + audio_track_id=audio_track_id, + video_track_id=video_track_id, + ) + + track_forwarding_info.additional_properties = d + return track_forwarding_info + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/viewer.py b/fishjam/_openapi_client/models/viewer.py index b34aeaa..a787c5b 100644 --- a/fishjam/_openapi_client/models/viewer.py +++ b/fishjam/_openapi_client/models/viewer.py @@ -1,19 +1,9 @@ from collections.abc import Mapping -from typing import ( - TYPE_CHECKING, - Any, - TypeVar, -) +from typing import Any, TypeVar from attrs import define as _attrs_define from attrs import field as _attrs_field -from ..models.viewer_status import ViewerStatus - -if TYPE_CHECKING: - from ..models.viewer_token import ViewerToken - - T = TypeVar("T", bound="Viewer") @@ -23,27 +13,22 @@ class Viewer: Attributes: id (str): Assigned viewer id - status (ViewerStatus): - token (ViewerToken): Token for authorizing broadcaster viewer connection + token (str): Example: 5cdac726-57a3-4ecb-b1d5-72a3d62ec242. """ id: str - status: ViewerStatus - token: "ViewerToken" + token: str additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: id = self.id - status = self.status.value - - token = self.token.to_dict() + token = self.token field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update({ "id": id, - "status": status, "token": token, }) @@ -51,18 +36,13 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.viewer_token import ViewerToken - d = dict(src_dict) id = d.pop("id") - status = ViewerStatus(d.pop("status")) - - token = ViewerToken.from_dict(d.pop("token")) + token = d.pop("token") viewer = cls( id=id, - status=status, token=token, ) diff --git a/fishjam/_openapi_client/models/viewer_details_response.py b/fishjam/_openapi_client/models/viewer_details_response.py new file mode 100644 index 0000000..b9fc1ca --- /dev/null +++ b/fishjam/_openapi_client/models/viewer_details_response.py @@ -0,0 +1,68 @@ +from collections.abc import Mapping +from typing import ( + TYPE_CHECKING, + Any, + TypeVar, +) + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.viewer import Viewer + + +T = TypeVar("T", bound="ViewerDetailsResponse") + + +@_attrs_define +class ViewerDetailsResponse: + """Response containing viewer details + + Attributes: + data (Viewer): Describes viewer status + """ + + data: "Viewer" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + data = self.data.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "data": data, + }) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.viewer import Viewer + + d = dict(src_dict) + data = Viewer.from_dict(d.pop("data")) + + viewer_details_response = cls( + data=data, + ) + + viewer_details_response.additional_properties = d + return viewer_details_response + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/viewer_status.py b/fishjam/_openapi_client/models/viewer_status.py deleted file mode 100644 index 59fe9e2..0000000 --- a/fishjam/_openapi_client/models/viewer_status.py +++ /dev/null @@ -1,11 +0,0 @@ -from enum import Enum - - -class ViewerStatus(str, Enum): - """None""" - - CONNECTED = "connected" - DISCONNECTED = "disconnected" - - def __str__(self) -> str: - return str(self.value) diff --git a/fishjam/api/_fishjam_client.py b/fishjam/api/_fishjam_client.py index 3dc5d29..fa61f66 100644 --- a/fishjam/api/_fishjam_client.py +++ b/fishjam/api/_fishjam_client.py @@ -26,6 +26,7 @@ PeerConfig, PeerDetailsResponse, PeerOptionsAgent, + PeerOptionsVapi, PeerOptionsWebRTC, PeerRefreshTokenResponse, PeerType, @@ -126,7 +127,7 @@ class AgentOutputOptions: @dataclass class AgentOptions: - """Options specific to a WebRTC Peer. + """Options specific to an Agent Peer. Attributes: output: Configuration for the agent's output options. @@ -216,6 +217,29 @@ def create_agent(self, room_id: str, options: AgentOptions | None = None): return Agent(resp.data.peer.id, room_id, resp.data.token, self._fishjam_url) + def create_vapi_agent( + self, + room_id: str, + options: PeerOptionsVapi, + ) -> 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. + + Returns: + - Peer: The created peer object. + """ + body = PeerConfig(type_=PeerType.VAPI, options=options) + + resp = cast( + PeerDetailsResponse, + self._request(room_add_peer, room_id=room_id, body=body), + ) + + return resp.data.peer + def create_room(self, options: RoomOptions | None = None) -> Room: """Creates a new room. diff --git a/fishjam/events/_protos/fishjam/__init__.py b/fishjam/events/_protos/fishjam/__init__.py index b10d0a9..9836cd4 100644 --- a/fishjam/events/_protos/fishjam/__init__.py +++ b/fishjam/events/_protos/fishjam/__init__.py @@ -4,6 +4,7 @@ # This file has been @generated import warnings from dataclasses import dataclass +from typing import Optional import betterproto @@ -14,6 +15,7 @@ class ServerMessagePeerType(betterproto.Enum): PEER_TYPE_UNSPECIFIED = 0 PEER_TYPE_WEBRTC = 1 PEER_TYPE_AGENT = 2 + PEER_TYPE_VAPI = 3 class ServerMessageEventType(betterproto.Enum): @@ -23,6 +25,12 @@ class ServerMessageEventType(betterproto.Enum): EVENT_TYPE_SERVER_NOTIFICATION = 1 +class ServerMessageVadNotificationStatus(betterproto.Enum): + STATUS_UNSPECIFIED = 0 + STATUS_SILENCE = 1 + STATUS_SPEECH = 2 + + @dataclass(eq=False, repr=False) class AgentRequest(betterproto.Message): """Defines any type of message passed from agent peer to Fishjam""" @@ -143,21 +151,6 @@ class AgentResponseTrackImage(betterproto.Message): class ServerMessage(betterproto.Message): """Defines any type of message passed between FJ and server peer""" - room_crashed: "ServerMessageRoomCrashed" = betterproto.message_field( - 1, group="content" - ) - peer_connected: "ServerMessagePeerConnected" = betterproto.message_field( - 2, group="content" - ) - peer_disconnected: "ServerMessagePeerDisconnected" = betterproto.message_field( - 3, group="content" - ) - peer_crashed: "ServerMessagePeerCrashed" = betterproto.message_field( - 4, group="content" - ) - component_crashed: "ServerMessageComponentCrashed" = betterproto.message_field( - 5, group="content" - ) authenticated: "ServerMessageAuthenticated" = betterproto.message_field( 6, group="content" ) @@ -176,14 +169,17 @@ class ServerMessage(betterproto.Message): room_deleted: "ServerMessageRoomDeleted" = betterproto.message_field( 11, group="content" ) - hls_playable: "ServerMessageHlsPlayable" = betterproto.message_field( - 13, group="content" + room_crashed: "ServerMessageRoomCrashed" = betterproto.message_field( + 1, group="content" ) - hls_uploaded: "ServerMessageHlsUploaded" = betterproto.message_field( - 14, group="content" + peer_connected: "ServerMessagePeerConnected" = betterproto.message_field( + 2, group="content" ) - hls_upload_crashed: "ServerMessageHlsUploadCrashed" = betterproto.message_field( - 15, group="content" + peer_disconnected: "ServerMessagePeerDisconnected" = betterproto.message_field( + 3, group="content" + ) + peer_crashed: "ServerMessagePeerCrashed" = betterproto.message_field( + 4, group="content" ) peer_metadata_updated: "ServerMessagePeerMetadataUpdated" = ( betterproto.message_field(16, group="content") @@ -203,11 +199,20 @@ class ServerMessage(betterproto.Message): peer_deleted: "ServerMessagePeerDeleted" = betterproto.message_field( 21, group="content" ) - stream_connected: "ServerMessageStreamConnected" = betterproto.message_field( - 22, group="content" + channel_added: "ServerMessageChannelAdded" = betterproto.message_field( + 28, group="content" ) - stream_disconnected: "ServerMessageStreamDisconnected" = betterproto.message_field( - 23, group="content" + channel_removed: "ServerMessageChannelRemoved" = betterproto.message_field( + 29, group="content" + ) + track_forwarding: "ServerMessageTrackForwarding" = betterproto.message_field( + 30, group="content" + ) + track_forwarding_removed: "ServerMessageTrackForwardingRemoved" = ( + betterproto.message_field(31, group="content") + ) + vad_notification: "ServerMessageVadNotification" = betterproto.message_field( + 32, group="content" ) viewer_connected: "ServerMessageViewerConnected" = betterproto.message_field( 24, group="content" @@ -221,11 +226,23 @@ class ServerMessage(betterproto.Message): streamer_disconnected: "ServerMessageStreamerDisconnected" = ( betterproto.message_field(27, group="content") ) - channel_added: "ServerMessageChannelAdded" = betterproto.message_field( - 28, group="content" + stream_connected: "ServerMessageStreamConnected" = betterproto.message_field( + 22, group="content" ) - channel_removed: "ServerMessageChannelRemoved" = betterproto.message_field( - 29, group="content" + stream_disconnected: "ServerMessageStreamDisconnected" = betterproto.message_field( + 23, group="content" + ) + hls_playable: "ServerMessageHlsPlayable" = betterproto.message_field( + 13, group="content" + ) + hls_uploaded: "ServerMessageHlsUploaded" = betterproto.message_field( + 14, group="content" + ) + hls_upload_crashed: "ServerMessageHlsUploadCrashed" = betterproto.message_field( + 15, group="content" + ) + component_crashed: "ServerMessageComponentCrashed" = betterproto.message_field( + 5, group="content" ) def __post_init__(self) -> None: @@ -238,6 +255,22 @@ def __post_init__(self) -> None: warnings.warn( "ServerMessage.stream_disconnected is deprecated", DeprecationWarning ) + if self.is_set("hls_playable"): + warnings.warn( + "ServerMessage.hls_playable is deprecated", DeprecationWarning + ) + if self.is_set("hls_uploaded"): + warnings.warn( + "ServerMessage.hls_uploaded is deprecated", DeprecationWarning + ) + if self.is_set("hls_upload_crashed"): + warnings.warn( + "ServerMessage.hls_upload_crashed is deprecated", DeprecationWarning + ) + if self.is_set("component_crashed"): + warnings.warn( + "ServerMessage.component_crashed is deprecated", DeprecationWarning + ) @dataclass(eq=False, repr=False) @@ -427,6 +460,45 @@ class ServerMessageChannelRemoved(betterproto.Message): channel_id: str = betterproto.string_field(4) +@dataclass(eq=False, repr=False) +class ServerMessageTrackForwarding(betterproto.Message): + """ + Sent when there is an upsert to track forwardings from Fishjam to + Composition + """ + + room_id: str = betterproto.string_field(1) + peer_id: str = betterproto.string_field(2) + composition_url: str = betterproto.string_field(3) + input_id: str = betterproto.string_field(4) + audio_track: Optional["notifications.Track"] = betterproto.message_field( + 5, optional=True, group="_audio_track" + ) + video_track: Optional["notifications.Track"] = betterproto.message_field( + 6, optional=True, group="_video_track" + ) + + +@dataclass(eq=False, repr=False) +class ServerMessageTrackForwardingRemoved(betterproto.Message): + """Sent when track forwarding is removed""" + + room_id: str = betterproto.string_field(1) + peer_id: str = betterproto.string_field(2) + composition_url: str = betterproto.string_field(3) + input_id: str = betterproto.string_field(4) + + +@dataclass(eq=False, repr=False) +class ServerMessageVadNotification(betterproto.Message): + """Notification sent when voice activity changes on a track""" + + room_id: str = betterproto.string_field(1) + peer_id: str = betterproto.string_field(2) + track_id: str = betterproto.string_field(3) + status: "ServerMessageVadNotificationStatus" = betterproto.enum_field(4) + + @dataclass(eq=False, repr=False) class ServerMessageStreamConnected(betterproto.Message): """Notification sent when streamer successfully connects""" diff --git a/protos b/protos index 9d807b5..244345c 160000 --- a/protos +++ b/protos @@ -1 +1 @@ -Subproject commit 9d807b55279de385136f82b12f5df75d73104514 +Subproject commit 244345c478e8cec97b5603268e16eb831a54e0df diff --git a/pyproject.toml b/pyproject.toml index 814f075..1c72c31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,8 +22,7 @@ Homepage = "https://github.com/fishjam-cloud/python-server-sdk" Documentation = "https://fishjam-cloud.github.io/python-server-sdk/fishjam" [project.scripts] -ci_test = "scripts:run_tests" -local_test = "scripts:run_local_test" +test = "scripts:run_tests" format = "scripts:run_formatter" format_check = "scripts:run_format_check" lint = "scripts:run_linter" @@ -53,6 +52,7 @@ test = [ "pytest-asyncio>=0.21.1,<0.22", "requests>=2.31.0,<3", "flask>=3.0.3,<4", + "google-genai>=1.43.0" ] [tool.uv] diff --git a/scripts.py b/scripts.py index 12dc4f7..8a23a98 100644 --- a/scripts.py +++ b/scripts.py @@ -26,17 +26,7 @@ def check_exit_code(command): def run_tests(): - check_exit_code("docker rm -f fishjam") - check_exit_code("docker compose -f docker-compose-test.yaml pull") - check_exit_code( - "docker compose -f docker-compose-test.yaml up --build --remove-orphans test \ - --exit-code-from test" - ) - check_exit_code("docker compose -f docker-compose-test.yaml down") - - -def run_local_test(): - check_exit_code('uv run pytest -m "not file_component_sources" -vv') + check_exit_code("uv run --env-file .env pytest") def run_formatter(): diff --git a/tests/test_room_api.py b/tests/test_room_api.py index 3407e1e..36e9e77 100644 --- a/tests/test_room_api.py +++ b/tests/test_room_api.py @@ -7,6 +7,7 @@ FishjamClient, Peer, PeerOptions, + PeerOptionsVapi, Room, RoomOptions, ) @@ -151,7 +152,7 @@ def test_valid(self, room_api: FishjamClient): assert room.id != r.id def test_invalid_id(self, room_api: FishjamClient): - with pytest.raises(BadRequestError): + with pytest.raises(NotFoundError): room_api.delete_room("invalid_id") def test_id_not_found(self, room_api: FishjamClient): @@ -251,6 +252,15 @@ def test_peer_limit_reached(self, room_api: FishjamClient): room_api.create_peer(room.id) +class TestCreateVapiAgent: + def test_create_vapi_agent(self, room_api: FishjamClient): + room = room_api.create_room() + options = PeerOptionsVapi(api_key="test_api_key", call_id="test-call-id") + peer = room_api.create_vapi_agent(room.id, options=options) + + assert peer.type_ == PeerType.VAPI + + class TestDeletePeer: def test_valid(self, room_api: FishjamClient): room = room_api.create_room() diff --git a/uv.lock b/uv.lock index 3b22411..f0b74f5 100644 --- a/uv.lock +++ b/uv.lock @@ -356,6 +356,7 @@ dev = [ ] test = [ { name = "flask" }, + { name = "google-genai" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "requests" }, @@ -389,6 +390,7 @@ dev = [ ] test = [ { name = "flask", specifier = ">=3.0.3,<4" }, + { name = "google-genai", specifier = ">=1.43.0" }, { name = "pytest", specifier = ">=7.4.2,<8" }, { name = "pytest-asyncio", specifier = ">=0.21.1,<0.22" }, { name = "requests", specifier = ">=2.31.0,<3" },