diff --git a/examples/README.md b/examples/room_manager/README.md similarity index 91% rename from examples/README.md rename to examples/room_manager/README.md index 601a2e8..1d0cc39 100644 --- a/examples/README.md +++ b/examples/room_manager/README.md @@ -6,7 +6,7 @@ All available options are defined in [arguments.py](room_manager/arguments.py). ```console uv sync --all-packages -uv run room_manager # Room Manager has to be started in the project root directory +uv run main.py # Room Manager has to be started in the project root directory ``` ## How does it work? diff --git a/examples/room_manager/main.py b/examples/room_manager/main.py index 4801feb..00eb814 100755 --- a/examples/room_manager/main.py +++ b/examples/room_manager/main.py @@ -1,11 +1,10 @@ import logging +from arguments import parse_arguments from flask import Flask from flask_cors import CORS - -from .arguments import parse_arguments -from .room_service import RoomService -from .routes import setup_routes +from room_service import RoomService +from routes import setup_routes app = Flask(__name__) CORS(app) diff --git a/examples/room_manager/routes.py b/examples/room_manager/routes.py index ce06645..3270a8d 100644 --- a/examples/room_manager/routes.py +++ b/examples/room_manager/routes.py @@ -1,12 +1,11 @@ from dataclasses import asdict from flask import Flask, abort, jsonify, request +from room_service import RoomService from fishjam import receive_binary from fishjam.room import RoomConfigRoomType -from .room_service import RoomService - def setup_routes(app: Flask, room_service: RoomService): @app.route("/health", methods=["GET"]) diff --git a/examples/transcription/README.md b/examples/transcription/README.md index cd00429..79f6a81 100644 --- a/examples/transcription/README.md +++ b/examples/transcription/README.md @@ -31,6 +31,6 @@ GEMINI_API_KEY= \ uv run fastapi dev ``` -Now, you can create peer tokens by going to . +Now, you can create peer tokens by going to . You can then use the [minimal-react](https://github.com/fishjam-cloud/web-client-sdk/tree/main/examples/react-client) demo app to connect as these peers and see your transcriptions live in the console! diff --git a/examples/transcription/transcription/config.py b/examples/transcription/transcription/config.py index 4d78034..63d29a1 100644 --- a/examples/transcription/transcription/config.py +++ b/examples/transcription/transcription/config.py @@ -2,7 +2,7 @@ from google.genai.types import AudioTranscriptionConfig, LiveConnectConfig, Modality -FISHJAM_ID = os.environ["FISHJAM_ID"] +FISHJAM_ID = os.getenv("FISHJAM_ID", "") FISHJAM_TOKEN = os.environ["FISHJAM_MANAGEMENT_TOKEN"] FISHJAM_URL = os.getenv("FISHJAM_URL") TRANSCRIPTION_MODEL = "gemini-live-2.5-flash-preview" diff --git a/fishjam/__init__.py b/fishjam/__init__.py index 2733948..b5a817a 100644 --- a/fishjam/__init__.py +++ b/fishjam/__init__.py @@ -11,6 +11,7 @@ # Exceptions and Server Messages from fishjam import agent, errors, events, peer, room +from fishjam._openapi_client.models import PeerMetadata # API from fishjam._webhook_notifier import receive_binary @@ -28,6 +29,7 @@ "FishjamClient", "FishjamNotifier", "receive_binary", + "PeerMetadata", "PeerOptions", "SubscribeOptions", "RoomOptions", diff --git a/fishjam/_openapi_client/models/__init__.py b/fishjam/_openapi_client/models/__init__.py index 2f43ff1..0a60927 100644 --- a/fishjam/_openapi_client/models/__init__.py +++ b/fishjam/_openapi_client/models/__init__.py @@ -1,10 +1,11 @@ -"""Contains all the data models used in inputs/outputs""" +""" Contains all the data models used in inputs/outputs """ from .add_peer_body import AddPeerBody from .error import Error from .peer import Peer from .peer_details_response import PeerDetailsResponse from .peer_details_response_data import PeerDetailsResponseData +from .peer_metadata import PeerMetadata from .peer_options_agent import PeerOptionsAgent from .peer_options_web_rtc import PeerOptionsWebRTC from .peer_options_web_rtc_metadata import PeerOptionsWebRTCMetadata @@ -32,6 +33,7 @@ from .subscribe_options_audio_format import SubscribeOptionsAudioFormat from .subscribe_options_audio_sample_rate import SubscribeOptionsAudioSampleRate from .track import Track +from .track_metadata_type_0 import TrackMetadataType0 from .track_type import TrackType from .viewer_token import ViewerToken @@ -41,6 +43,7 @@ "Peer", "PeerDetailsResponse", "PeerDetailsResponseData", + "PeerMetadata", "PeerOptionsAgent", "PeerOptionsWebRTC", "PeerOptionsWebRTCMetadata", @@ -64,6 +67,7 @@ "SubscribeOptionsAudioFormat", "SubscribeOptionsAudioSampleRate", "Track", + "TrackMetadataType0", "TrackType", "ViewerToken", ) diff --git a/fishjam/_openapi_client/models/peer.py b/fishjam/_openapi_client/models/peer.py index 9027fc8..3099774 100644 --- a/fishjam/_openapi_client/models/peer.py +++ b/fishjam/_openapi_client/models/peer.py @@ -14,6 +14,7 @@ from ..models.peer_type import PeerType if TYPE_CHECKING: + from ..models.peer_metadata import PeerMetadata from ..models.subscribe_options import SubscribeOptions from ..models.track import Track @@ -27,7 +28,7 @@ class Peer: Attributes: id (str): Assigned peer id Example: peer-1. - metadata (Any): Custom metadata set by the peer Example: {'name': 'FishjamUser'}. + metadata (Union['PeerMetadata', None]): Custom metadata set by the peer Example: {'name': 'FishjamUser'}. status (PeerStatus): Informs about the peer status Example: disconnected. subscribe (Union['SubscribeOptions', None]): Configuration of server-side subscriptions to the peer's tracks Example: {'audioFormat': 'pcm16'}. @@ -36,7 +37,7 @@ class Peer: """ id: str - metadata: Any + metadata: Union["PeerMetadata", None] status: PeerStatus subscribe: Union["SubscribeOptions", None] tracks: list["Track"] @@ -44,11 +45,16 @@ class Peer: additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.peer_metadata import PeerMetadata from ..models.subscribe_options import SubscribeOptions id = self.id - metadata = self.metadata + metadata: Union[None, dict[str, Any]] + if isinstance(self.metadata, PeerMetadata): + metadata = self.metadata.to_dict() + else: + metadata = self.metadata status = self.status.value @@ -82,13 +88,27 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.peer_metadata import PeerMetadata from ..models.subscribe_options import SubscribeOptions from ..models.track import Track d = dict(src_dict) id = d.pop("id") - metadata = d.pop("metadata") + def _parse_metadata(data: object) -> Union["PeerMetadata", None]: + if data is None: + return data + try: + if not isinstance(data, dict): + raise TypeError() + componentsschemas_peer_metadata_type_0 = PeerMetadata.from_dict(data) + + return componentsschemas_peer_metadata_type_0 + except: # noqa: E722 + pass + return cast(Union["PeerMetadata", None], data) + + metadata = _parse_metadata(d.pop("metadata")) status = PeerStatus(d.pop("status")) diff --git a/fishjam/_openapi_client/models/peer_metadata.py b/fishjam/_openapi_client/models/peer_metadata.py new file mode 100644 index 0000000..e78c90d --- /dev/null +++ b/fishjam/_openapi_client/models/peer_metadata.py @@ -0,0 +1,49 @@ +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="PeerMetadata") + + +@_attrs_define +class PeerMetadata: + """Custom metadata set by the peer + + Example: + {'name': 'FishjamUser'} + + """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + peer_metadata = cls() + + peer_metadata.additional_properties = d + return peer_metadata + + @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.py b/fishjam/_openapi_client/models/track.py index e11ed9e..cfbc46e 100644 --- a/fishjam/_openapi_client/models/track.py +++ b/fishjam/_openapi_client/models/track.py @@ -1,8 +1,10 @@ from collections.abc import Mapping from typing import ( + TYPE_CHECKING, Any, TypeVar, Union, + cast, ) from attrs import define as _attrs_define @@ -11,6 +13,10 @@ from ..models.track_type import TrackType from ..types import UNSET, Unset +if TYPE_CHECKING: + from ..models.track_metadata_type_0 import TrackMetadataType0 + + T = TypeVar("T", bound="Track") @@ -20,19 +26,27 @@ class Track: Attributes: id (Union[Unset, str]): - metadata (Union[Unset, Any]): + metadata (Union['TrackMetadataType0', None, Unset]): Example: {'source': 'camera'}. type_ (Union[Unset, TrackType]): """ id: Union[Unset, str] = UNSET - metadata: Union[Unset, Any] = UNSET + metadata: Union["TrackMetadataType0", None, Unset] = UNSET type_: Union[Unset, TrackType] = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: + from ..models.track_metadata_type_0 import TrackMetadataType0 + id = self.id - metadata = self.metadata + metadata: Union[None, Unset, dict[str, Any]] + if isinstance(self.metadata, Unset): + metadata = UNSET + elif isinstance(self.metadata, TrackMetadataType0): + metadata = self.metadata.to_dict() + else: + metadata = self.metadata type_: Union[Unset, str] = UNSET if not isinstance(self.type_, Unset): @@ -52,10 +66,27 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.track_metadata_type_0 import TrackMetadataType0 + d = dict(src_dict) id = d.pop("id", UNSET) - metadata = d.pop("metadata", UNSET) + def _parse_metadata(data: object) -> Union["TrackMetadataType0", None, Unset]: + if data is None: + return data + if isinstance(data, Unset): + return data + try: + if not isinstance(data, dict): + raise TypeError() + metadata_type_0 = TrackMetadataType0.from_dict(data) + + return metadata_type_0 + except: # noqa: E722 + pass + return cast(Union["TrackMetadataType0", None, Unset], data) + + metadata = _parse_metadata(d.pop("metadata", UNSET)) _type_ = d.pop("type", UNSET) type_: Union[Unset, TrackType] diff --git a/fishjam/_openapi_client/models/track_metadata_type_0.py b/fishjam/_openapi_client/models/track_metadata_type_0.py new file mode 100644 index 0000000..f768ce2 --- /dev/null +++ b/fishjam/_openapi_client/models/track_metadata_type_0.py @@ -0,0 +1,48 @@ +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +T = TypeVar("T", bound="TrackMetadataType0") + + +@_attrs_define +class TrackMetadataType0: + """ + Example: + {'source': 'camera'} + + """ + + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + track_metadata_type_0 = cls() + + track_metadata_type_0.additional_properties = d + return track_metadata_type_0 + + @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/pyproject.toml b/pyproject.toml index 3fb67dd..9bb0385 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fishjam-server-sdk" -version = "0.20.0" +version = "0.21.0" description = "Python server SDK for the Fishjam" authors = [{ name = "Fishjam Team", email = "contact@fishjam.io" }] requires-python = ">=3.11" diff --git a/scripts.py b/scripts.py index 83e0205..61220a0 100644 --- a/scripts.py +++ b/scripts.py @@ -87,7 +87,7 @@ def update_client(): check_exit_code( f"openapi-python-client generate \ - --path {sys.argv[1]} \ + --url {sys.argv[1]} \ --config openapi-python-client-config.yaml \ --meta=none \ --overwrite \ diff --git a/tests/agent/test_agent.py b/tests/agent/test_agent.py index 2ebd833..4d78fa3 100644 --- a/tests/agent/test_agent.py +++ b/tests/agent/test_agent.py @@ -18,7 +18,7 @@ HOST = "fishjam" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" FISHJAM_URL = f"http://{HOST}:5002" FISHJAM_ID = "" -SERVER_API_TOKEN = "development" +SERVER_API_TOKEN = os.getenv("MANAGEMENT_TOKEN", "development") @pytest.fixture diff --git a/tests/test_notifier.py b/tests/test_notifier.py index fbb8370..9cf1091 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -26,7 +26,7 @@ HOST = "fishjam" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" FISHJAM_URL = f"http://{HOST}:5002" FISHJAM_ID = "" -SERVER_API_TOKEN = "development" +SERVER_API_TOKEN = os.getenv("MANAGEMENT_TOKEN", "development") WEBHOOK_ADDRESS = "test" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" WEBHOOK_URL = f"http://{WEBHOOK_ADDRESS}:5000/webhook" queue = Queue() diff --git a/tests/test_room_api.py b/tests/test_room_api.py index 0ed38e4..48ade7f 100644 --- a/tests/test_room_api.py +++ b/tests/test_room_api.py @@ -11,6 +11,7 @@ RoomOptions, ) from fishjam._openapi_client.models import ( + PeerMetadata, PeerStatus, PeerType, RoomConfig, @@ -28,7 +29,7 @@ HOST = "fishjam" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" FISHJAM_URL = f"http://{HOST}:5002" FISHJAM_ID = "" -MANAGEMENT_TOKEN = "development" +MANAGEMENT_TOKEN = os.getenv("MANAGEMENT_TOKEN", "development") MAX_PEERS = 10 CODEC_H264 = "h264" @@ -187,7 +188,7 @@ def _assert_peer_created( type_=PeerType.WEBRTC, status=PeerStatus("disconnected"), tracks=[], - metadata={"peer": {}, "server": server_metadata}, + metadata=PeerMetadata.from_dict({"peer": {}, "server": server_metadata}), subscribe=None, ) diff --git a/uv.lock b/uv.lock index dbef479..fc749cc 100644 --- a/uv.lock +++ b/uv.lock @@ -279,7 +279,7 @@ wheels = [ [[package]] name = "fishjam-server-sdk" -version = "0.20.0" +version = "0.21.0" source = { editable = "." } dependencies = [ { name = "aenum" },