diff --git a/README.md b/README.md index 3c7560a..1399f72 100644 --- a/README.md +++ b/README.md @@ -141,8 +141,9 @@ Documentation is generated via openapi-python-client. To update documentation you need to: -- in `poetry_scripts.py` change branch from which openapi.yaml should be downloaded -- run `poetry run update-client` +- Go to https://github.com/fishjam-cloud/fishjam/blob/main/openapi.yaml and open the raw file. +- Copy the URL. +- Run `poetry run update_client ` ## License diff --git a/fishjam/_openapi_client/api/default/notification.py b/fishjam/_openapi_client/api/default/notification.py new file mode 100644 index 0000000..daf0ed1 --- /dev/null +++ b/fishjam/_openapi_client/api/default/notification.py @@ -0,0 +1,131 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union, cast + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.error import Error +from ...types import Response + + +def _get_kwargs() -> Dict[str, Any]: + return { + "method": "post", + "url": "/notifications", + } + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Union[Any, Error]]: + if response.status_code == HTTPStatus.NO_CONTENT: + response_204 = cast(Any, None) + return response_204 + if response.status_code == HTTPStatus.UNAUTHORIZED: + response_401 = Error.from_dict(response.json()) + + return response_401 + if response.status_code == HTTPStatus.NOT_FOUND: + 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( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Union[Any, Error]]: + """Handle notification from broadcaster + + 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() + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[Union[Any, Error]]: + """Handle notification from broadcaster + + 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( + client=client, + ).parsed + + +async def asyncio_detailed( + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Union[Any, Error]]: + """Handle notification from broadcaster + + 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() + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[Union[Any, Error]]: + """Handle notification from broadcaster + + 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( + client=client, + ) + ).parsed diff --git a/fishjam/_openapi_client/api/streamer/__init__.py b/fishjam/_openapi_client/api/streamer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fishjam/_openapi_client/api/streamer/generate_streamer_token.py b/fishjam/_openapi_client/api/streamer/generate_streamer_token.py new file mode 100644 index 0000000..b1f8ba3 --- /dev/null +++ b/fishjam/_openapi_client/api/streamer/generate_streamer_token.py @@ -0,0 +1,167 @@ +from http import HTTPStatus +from typing import Any, Dict, Optional, Union + +import httpx + +from ... import errors +from ...client import AuthenticatedClient, Client +from ...models.error import Error +from ...models.streamer_token import StreamerToken +from ...types import Response + + +def _get_kwargs( + room_id: str, +) -> Dict[str, Any]: + return { + "method": "post", + "url": "/room/{room_id}/streamer".format( + room_id=room_id, + ), + } + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Union[Error, StreamerToken]]: + if response.status_code == HTTPStatus.CREATED: + response_201 = StreamerToken.from_dict(response.json()) + + return response_201 + if response.status_code == HTTPStatus.BAD_REQUEST: + response_400 = Error.from_dict(response.json()) + + return response_400 + if response.status_code == HTTPStatus.UNAUTHORIZED: + response_401 = Error.from_dict(response.json()) + + return response_401 + if response.status_code == HTTPStatus.NOT_FOUND: + response_404 = Error.from_dict(response.json()) + + return response_404 + if response.status_code == HTTPStatus.SERVICE_UNAVAILABLE: + response_503 = Error.from_dict(response.json()) + + return response_503 + 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[Error, StreamerToken]]: + 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: Union[AuthenticatedClient, Client], +) -> Response[Union[Error, StreamerToken]]: + """Generate a token that can be used by a streamer to start streaming + + Args: + room_id (str): + + 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[Error, StreamerToken]] + """ + + kwargs = _get_kwargs( + room_id=room_id, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + room_id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[Union[Error, StreamerToken]]: + """Generate a token that can be used by a streamer to start streaming + + Args: + room_id (str): + + 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[Error, StreamerToken] + """ + + return sync_detailed( + room_id=room_id, + client=client, + ).parsed + + +async def asyncio_detailed( + room_id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Response[Union[Error, StreamerToken]]: + """Generate a token that can be used by a streamer to start streaming + + Args: + room_id (str): + + 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[Error, StreamerToken]] + """ + + kwargs = _get_kwargs( + room_id=room_id, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + room_id: str, + *, + client: Union[AuthenticatedClient, Client], +) -> Optional[Union[Error, StreamerToken]]: + """Generate a token that can be used by a streamer to start streaming + + Args: + room_id (str): + + 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[Error, StreamerToken] + """ + + return ( + await asyncio_detailed( + room_id=room_id, + client=client, + ) + ).parsed diff --git a/fishjam/_openapi_client/api/viewer/generate_token.py b/fishjam/_openapi_client/api/viewer/generate_viewer_token.py similarity index 94% rename from fishjam/_openapi_client/api/viewer/generate_token.py rename to fishjam/_openapi_client/api/viewer/generate_viewer_token.py index d22f1b0..21a35e2 100644 --- a/fishjam/_openapi_client/api/viewer/generate_token.py +++ b/fishjam/_openapi_client/api/viewer/generate_viewer_token.py @@ -66,7 +66,7 @@ def sync_detailed( *, client: Union[AuthenticatedClient, Client], ) -> Response[Union[Error, ViewerToken]]: - """Generate single livestream access token + """Generates token that a viewer can use to watch a livestream Args: room_id (str): @@ -95,7 +95,7 @@ def sync( *, client: Union[AuthenticatedClient, Client], ) -> Optional[Union[Error, ViewerToken]]: - """Generate single livestream access token + """Generates token that a viewer can use to watch a livestream Args: room_id (str): @@ -119,7 +119,7 @@ async def asyncio_detailed( *, client: Union[AuthenticatedClient, Client], ) -> Response[Union[Error, ViewerToken]]: - """Generate single livestream access token + """Generates token that a viewer can use to watch a livestream Args: room_id (str): @@ -146,7 +146,7 @@ async def asyncio( *, client: Union[AuthenticatedClient, Client], ) -> Optional[Union[Error, ViewerToken]]: - """Generate single livestream access token + """Generates token that a viewer can use to watch a livestream Args: room_id (str): diff --git a/fishjam/_openapi_client/models/__init__.py b/fishjam/_openapi_client/models/__init__.py index bbca171..29075cf 100644 --- a/fishjam/_openapi_client/models/__init__.py +++ b/fishjam/_openapi_client/models/__init__.py @@ -63,6 +63,7 @@ from .shutdown_status import ShutdownStatus from .shutdown_status_response import ShutdownStatusResponse from .sip_credentials import SIPCredentials +from .streamer_token import StreamerToken from .subscription_config import SubscriptionConfig from .track import Track from .track_type import TrackType @@ -126,6 +127,7 @@ "ShutdownStatus", "ShutdownStatusResponse", "SIPCredentials", + "StreamerToken", "SubscriptionConfig", "Track", "TrackType", diff --git a/fishjam/_openapi_client/models/streamer_token.py b/fishjam/_openapi_client/models/streamer_token.py new file mode 100644 index 0000000..9a36d44 --- /dev/null +++ b/fishjam/_openapi_client/models/streamer_token.py @@ -0,0 +1,60 @@ +from typing import Any, Dict, List, Type, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="StreamerToken") + + +@_attrs_define +class StreamerToken: + """Token for authorizing broadcaster streamer connection""" + + token: str + """None""" + additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) + """@private""" + + def to_dict(self) -> Dict[str, Any]: + """@private""" + token = self.token + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "token": token, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: + """@private""" + d = src_dict.copy() + token = d.pop("token") + + streamer_token = cls( + token=token, + ) + + streamer_token.additional_properties = d + return streamer_token + + @property + def additional_keys(self) -> List[str]: + """@private""" + 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/api/_fishjam_client.py b/fishjam/api/_fishjam_client.py index e110220..dfa1c4c 100644 --- a/fishjam/api/_fishjam_client.py +++ b/fishjam/api/_fishjam_client.py @@ -12,7 +12,12 @@ from fishjam._openapi_client.api.room import get_all_rooms as room_get_all_rooms from fishjam._openapi_client.api.room import get_room as room_get_room from fishjam._openapi_client.api.room import refresh_token as room_refresh_token -from fishjam._openapi_client.api.viewer import generate_token as viewer_generate_token +from fishjam._openapi_client.api.streamer import ( + generate_streamer_token as streamer_generate_streamer_token, +) +from fishjam._openapi_client.api.viewer import ( + generate_viewer_token as viewer_generate_viewer_token, +) from fishjam._openapi_client.models import ( AddPeerJsonBody, Peer, @@ -24,6 +29,7 @@ RoomCreateDetailsResponse, RoomDetailsResponse, RoomsListingResponse, + StreamerToken, ViewerToken, ) from fishjam._openapi_client.models.peer_options_web_rtc_metadata import ( @@ -176,7 +182,17 @@ def create_livestream_viewer_token(self, room_id: str) -> str: """Generates viewer token for livestream rooms""" response = cast( - ViewerToken, self._request(viewer_generate_token, room_id=room_id) + ViewerToken, self._request(viewer_generate_viewer_token, room_id=room_id) + ) + + return response.token + + def create_livestream_streamer_token(self, room_id: str) -> str: + """Generates streamer token for livestream rooms""" + + response = cast( + StreamerToken, + self._request(streamer_generate_streamer_token, room_id=room_id), ) return response.token diff --git a/fishjam/events/_protos/fishjam/__init__.py b/fishjam/events/_protos/fishjam/__init__.py index 93a85f7..0d7df9f 100644 --- a/fishjam/events/_protos/fishjam/__init__.py +++ b/fishjam/events/_protos/fishjam/__init__.py @@ -100,7 +100,7 @@ class ServerMessage(betterproto.Message): viewer_connected: "ServerMessageViewerConnected" = betterproto.message_field( 24, group="content" ) - viewer_disconnected: "ServerMessageViewerConnected" = betterproto.message_field( + viewer_disconnected: "ServerMessageViewerDisconnected" = betterproto.message_field( 25, group="content" ) diff --git a/protos b/protos index 5cfc90e..ad3032b 160000 --- a/protos +++ b/protos @@ -1 +1 @@ -Subproject commit 5cfc90ed8e1cc2e7e8a5c9f87e89d4351af2f1ab +Subproject commit ad3032ba260ca4e937a18dfca87a3033be2c74d8 diff --git a/tests/test_room_api.py b/tests/test_room_api.py index 2ca6b29..b9b1f22 100644 --- a/tests/test_room_api.py +++ b/tests/test_room_api.py @@ -267,3 +267,17 @@ def test_invalid(self, room_api: FishjamClient): with pytest.raises(BadRequestError): room_api.create_livestream_viewer_token(room.id) + + +class TestCreateLivestreamStreamerToken: + def test_valid(self, room_api: FishjamClient): + room = room_api.create_room(RoomOptions(room_type=LIVESTREAM)) + streamer_token = room_api.create_livestream_streamer_token(room.id) + + assert isinstance(streamer_token, str) + + def test_invalid(self, room_api: FishjamClient): + room = room_api.create_room() + + with pytest.raises(BadRequestError): + room_api.create_livestream_streamer_token(room.id)