diff --git a/examples/mini_tutorial.py b/examples/mini_tutorial.py index e264528..3eb262f 100755 --- a/examples/mini_tutorial.py +++ b/examples/mini_tutorial.py @@ -61,7 +61,7 @@ async def test_notifier(): room_api = RoomApi(server_address=address) # Add HLS component with manual subscribe mode - _hls_component = room_api.add_component( + hls_component = room_api.add_component( room.id, ComponentOptionsHLS(subscribe_mode=ComponentOptionsHLSSubscribeMode.MANUAL), ) @@ -70,7 +70,7 @@ async def test_notifier(): file_component = room_api.add_component(room.id, ComponentOptionsFile("video.h264")) # Subscribe on specific component - room_api.hls_subscribe(room.id, [file_component.id]) + room_api.subscribe(room.id, hls_component.id, [file_component.id]) try: await notifier_task diff --git a/jellyfish/__init__.py b/jellyfish/__init__.py index ada070a..9d2eddd 100644 --- a/jellyfish/__init__.py +++ b/jellyfish/__init__.py @@ -15,12 +15,14 @@ ComponentOptionsHLS, ComponentOptionsHLSSubscribeMode, ComponentOptionsRecording, + ComponentOptionsRecordingSubscribeMode, ComponentOptionsRTSP, ComponentOptionsSIP, ComponentPropertiesFile, ComponentPropertiesHLS, ComponentPropertiesHLSSubscribeMode, ComponentPropertiesRecording, + ComponentPropertiesRecordingSubscribeMode, ComponentPropertiesRTSP, ComponentPropertiesSIP, ComponentPropertiesSIPSIPCredentials, @@ -75,7 +77,9 @@ "SIPCredentials", "ComponentRecording", "ComponentOptionsRecording", + "ComponentOptionsRecordingSubscribeMode", "ComponentPropertiesRecording", + "ComponentPropertiesRecordingSubscribeMode", "S3Credentials", ] __docformat__ = "restructuredtext" diff --git a/jellyfish/_openapi_client/api/hls/subscribe_hls_to.py b/jellyfish/_openapi_client/api/room/subscribe_to.py similarity index 85% rename from jellyfish/_openapi_client/api/hls/subscribe_hls_to.py rename to jellyfish/_openapi_client/api/room/subscribe_to.py index 7836a6c..3216038 100644 --- a/jellyfish/_openapi_client/api/hls/subscribe_hls_to.py +++ b/jellyfish/_openapi_client/api/room/subscribe_to.py @@ -12,6 +12,7 @@ def _get_kwargs( room_id: str, + component_id: str, *, json_body: SubscriptionConfig, ) -> Dict[str, Any]: @@ -19,8 +20,9 @@ def _get_kwargs( return { "method": "post", - "url": "/hls/{room_id}/subscribe".format( + "url": "/room/{room_id}/component/{component_id}/subscribe".format( room_id=room_id, + component_id=component_id, ), "json": json_json_body, } @@ -63,14 +65,16 @@ def _build_response( def sync_detailed( room_id: str, + component_id: str, *, client: AuthenticatedClient, json_body: SubscriptionConfig, ) -> Response[Union[Any, Error]]: - """Subscribe the HLS component to the tracks of peers or components + """Subscribe component to the tracks of peers or components Args: room_id (str): + component_id (str): json_body (SubscriptionConfig): Subscription config Raises: @@ -83,6 +87,7 @@ def sync_detailed( kwargs = _get_kwargs( room_id=room_id, + component_id=component_id, json_body=json_body, ) @@ -95,14 +100,16 @@ def sync_detailed( def sync( room_id: str, + component_id: str, *, client: AuthenticatedClient, json_body: SubscriptionConfig, ) -> Optional[Union[Any, Error]]: - """Subscribe the HLS component to the tracks of peers or components + """Subscribe component to the tracks of peers or components Args: room_id (str): + component_id (str): json_body (SubscriptionConfig): Subscription config Raises: @@ -115,6 +122,7 @@ def sync( return sync_detailed( room_id=room_id, + component_id=component_id, client=client, json_body=json_body, ).parsed @@ -122,14 +130,16 @@ def sync( async def asyncio_detailed( room_id: str, + component_id: str, *, client: AuthenticatedClient, json_body: SubscriptionConfig, ) -> Response[Union[Any, Error]]: - """Subscribe the HLS component to the tracks of peers or components + """Subscribe component to the tracks of peers or components Args: room_id (str): + component_id (str): json_body (SubscriptionConfig): Subscription config Raises: @@ -142,6 +152,7 @@ async def asyncio_detailed( kwargs = _get_kwargs( room_id=room_id, + component_id=component_id, json_body=json_body, ) @@ -152,14 +163,16 @@ async def asyncio_detailed( async def asyncio( room_id: str, + component_id: str, *, client: AuthenticatedClient, json_body: SubscriptionConfig, ) -> Optional[Union[Any, Error]]: - """Subscribe the HLS component to the tracks of peers or components + """Subscribe component to the tracks of peers or components Args: room_id (str): + component_id (str): json_body (SubscriptionConfig): Subscription config Raises: @@ -173,6 +186,7 @@ async def asyncio( return ( await asyncio_detailed( room_id=room_id, + component_id=component_id, client=client, json_body=json_body, ) diff --git a/jellyfish/_openapi_client/models/__init__.py b/jellyfish/_openapi_client/models/__init__.py index 8fabb51..8014cdf 100644 --- a/jellyfish/_openapi_client/models/__init__.py +++ b/jellyfish/_openapi_client/models/__init__.py @@ -9,6 +9,9 @@ from .component_options_hls import ComponentOptionsHLS from .component_options_hls_subscribe_mode import ComponentOptionsHLSSubscribeMode from .component_options_recording import ComponentOptionsRecording +from .component_options_recording_subscribe_mode import ( + ComponentOptionsRecordingSubscribeMode, +) from .component_options_rtsp import ComponentOptionsRTSP from .component_options_sip import ComponentOptionsSIP from .component_options_sipsip_credentials import ComponentOptionsSIPSIPCredentials @@ -16,6 +19,9 @@ from .component_properties_hls import ComponentPropertiesHLS from .component_properties_hls_subscribe_mode import ComponentPropertiesHLSSubscribeMode from .component_properties_recording import ComponentPropertiesRecording +from .component_properties_recording_subscribe_mode import ( + ComponentPropertiesRecordingSubscribeMode, +) from .component_properties_rtsp import ComponentPropertiesRTSP from .component_properties_sip import ComponentPropertiesSIP from .component_properties_sipsip_credentials import ( @@ -59,6 +65,7 @@ "ComponentOptionsHLS", "ComponentOptionsHLSSubscribeMode", "ComponentOptionsRecording", + "ComponentOptionsRecordingSubscribeMode", "ComponentOptionsRTSP", "ComponentOptionsSIP", "ComponentOptionsSIPSIPCredentials", @@ -66,6 +73,7 @@ "ComponentPropertiesHLS", "ComponentPropertiesHLSSubscribeMode", "ComponentPropertiesRecording", + "ComponentPropertiesRecordingSubscribeMode", "ComponentPropertiesRTSP", "ComponentPropertiesSIP", "ComponentPropertiesSIPSIPCredentials", diff --git a/jellyfish/_openapi_client/models/component_options_recording.py b/jellyfish/_openapi_client/models/component_options_recording.py index 7e459e3..ae5b0e2 100644 --- a/jellyfish/_openapi_client/models/component_options_recording.py +++ b/jellyfish/_openapi_client/models/component_options_recording.py @@ -3,6 +3,9 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field +from ..models.component_options_recording_subscribe_mode import ( + ComponentOptionsRecordingSubscribeMode, +) from ..types import UNSET, Unset if TYPE_CHECKING: @@ -18,8 +21,12 @@ class ComponentOptionsRecording: credentials: Union[Unset, None, "S3Credentials"] = UNSET """An AWS S3 credential that will be used to send HLS stream. The stream will only be uploaded if credentials are provided""" - path_prefix: Union[Unset, str] = "" + path_prefix: Union[Unset, None, str] = UNSET """Path prefix under which all recording are stored""" + subscribe_mode: Union[ + Unset, ComponentOptionsRecordingSubscribeMode + ] = ComponentOptionsRecordingSubscribeMode.AUTO + """Whether the Recording component should subscribe to tracks automatically or manually.""" additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) """@private""" @@ -30,6 +37,9 @@ def to_dict(self) -> Dict[str, Any]: credentials = self.credentials.to_dict() if self.credentials else None path_prefix = self.path_prefix + 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(self.additional_properties) @@ -38,6 +48,8 @@ def to_dict(self) -> Dict[str, Any]: field_dict["credentials"] = credentials if path_prefix is not UNSET: field_dict["pathPrefix"] = path_prefix + if subscribe_mode is not UNSET: + field_dict["subscribeMode"] = subscribe_mode return field_dict @@ -58,9 +70,17 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: path_prefix = d.pop("pathPrefix", UNSET) + _subscribe_mode = d.pop("subscribeMode", UNSET) + subscribe_mode: Union[Unset, ComponentOptionsRecordingSubscribeMode] + if isinstance(_subscribe_mode, Unset): + subscribe_mode = UNSET + else: + subscribe_mode = ComponentOptionsRecordingSubscribeMode(_subscribe_mode) + component_options_recording = cls( credentials=credentials, path_prefix=path_prefix, + subscribe_mode=subscribe_mode, ) component_options_recording.additional_properties = d diff --git a/jellyfish/_openapi_client/models/component_options_recording_subscribe_mode.py b/jellyfish/_openapi_client/models/component_options_recording_subscribe_mode.py new file mode 100644 index 0000000..4792705 --- /dev/null +++ b/jellyfish/_openapi_client/models/component_options_recording_subscribe_mode.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class ComponentOptionsRecordingSubscribeMode(str, Enum): + """Whether the Recording component should subscribe to tracks automatically or manually.""" + + AUTO = "auto" + MANUAL = "manual" + + def __str__(self) -> str: + return str(self.value) diff --git a/jellyfish/_openapi_client/models/component_properties_recording.py b/jellyfish/_openapi_client/models/component_properties_recording.py index 31d5434..d9b53e5 100644 --- a/jellyfish/_openapi_client/models/component_properties_recording.py +++ b/jellyfish/_openapi_client/models/component_properties_recording.py @@ -3,6 +3,10 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field +from ..models.component_properties_recording_subscribe_mode import ( + ComponentPropertiesRecordingSubscribeMode, +) + T = TypeVar("T", bound="ComponentPropertiesRecording") @@ -10,20 +14,20 @@ class ComponentPropertiesRecording: """Properties specific to the Recording component""" - path_prefix: str - """Path prefix under which all recording are stored""" + subscribe_mode: ComponentPropertiesRecordingSubscribeMode + """Whether the Recording component should subscribe to tracks automatically or manually""" additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) """@private""" def to_dict(self) -> Dict[str, Any]: """@private""" - path_prefix = self.path_prefix + subscribe_mode = self.subscribe_mode.value field_dict: Dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { - "pathPrefix": path_prefix, + "subscribeMode": subscribe_mode, } ) @@ -33,10 +37,12 @@ def to_dict(self) -> Dict[str, Any]: def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: """@private""" d = src_dict.copy() - path_prefix = d.pop("pathPrefix") + subscribe_mode = ComponentPropertiesRecordingSubscribeMode( + d.pop("subscribeMode") + ) component_properties_recording = cls( - path_prefix=path_prefix, + subscribe_mode=subscribe_mode, ) component_properties_recording.additional_properties = d diff --git a/jellyfish/_openapi_client/models/component_properties_recording_subscribe_mode.py b/jellyfish/_openapi_client/models/component_properties_recording_subscribe_mode.py new file mode 100644 index 0000000..0c44ab2 --- /dev/null +++ b/jellyfish/_openapi_client/models/component_properties_recording_subscribe_mode.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class ComponentPropertiesRecordingSubscribeMode(str, Enum): + """Whether the Recording component should subscribe to tracks automatically or manually""" + + AUTO = "auto" + MANUAL = "manual" + + def __str__(self) -> str: + return str(self.value) diff --git a/jellyfish/_openapi_client/models/health_report.py b/jellyfish/_openapi_client/models/health_report.py index bcdad02..75869f9 100644 --- a/jellyfish/_openapi_client/models/health_report.py +++ b/jellyfish/_openapi_client/models/health_report.py @@ -18,10 +18,14 @@ class HealthReport: distribution: "HealthReportDistribution" """Informs about the status of Jellyfish distribution""" + git_commit: str + """Commit hash of the build""" status: HealthReportStatus """Informs about the status of Jellyfish or a specific service""" uptime: int """Uptime of Jellyfish (in seconds)""" + version: str + """Version of Jellyfish""" additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict) """@private""" @@ -29,17 +33,21 @@ def to_dict(self) -> Dict[str, Any]: """@private""" distribution = self.distribution.to_dict() + git_commit = self.git_commit status = self.status.value uptime = self.uptime + version = self.version field_dict: Dict[str, Any] = {} field_dict.update(self.additional_properties) field_dict.update( { "distribution": distribution, + "gitCommit": git_commit, "status": status, "uptime": uptime, + "version": version, } ) @@ -53,14 +61,20 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T: d = src_dict.copy() distribution = HealthReportDistribution.from_dict(d.pop("distribution")) + git_commit = d.pop("gitCommit") + status = HealthReportStatus(d.pop("status")) uptime = d.pop("uptime") + version = d.pop("version") + health_report = cls( distribution=distribution, + git_commit=git_commit, status=status, uptime=uptime, + version=version, ) health_report.additional_properties = d diff --git a/jellyfish/api/_room_api.py b/jellyfish/api/_room_api.py index 0a624f3..a746cb9 100644 --- a/jellyfish/api/_room_api.py +++ b/jellyfish/api/_room_api.py @@ -4,7 +4,6 @@ from typing import List, Literal, Tuple, Union -from jellyfish._openapi_client.api.hls import subscribe_hls_to as hls_subscribe_hls_to from jellyfish._openapi_client.api.room import add_component as room_add_component from jellyfish._openapi_client.api.room import add_peer as room_add_peer from jellyfish._openapi_client.api.room import create_room as room_create_room @@ -13,6 +12,7 @@ from jellyfish._openapi_client.api.room import delete_room as room_delete_room from jellyfish._openapi_client.api.room import get_all_rooms as room_get_all_rooms from jellyfish._openapi_client.api.room import get_room as room_get_room +from jellyfish._openapi_client.api.room import subscribe_to from jellyfish._openapi_client.api.sip import dial as sip_dial from jellyfish._openapi_client.api.sip import end_call as sip_end_call from jellyfish._openapi_client.models import ( @@ -179,20 +179,21 @@ def delete_component(self, room_id: str, component_id: str) -> None: return self._request(room_delete_component, id=component_id, room_id=room_id) - def hls_subscribe(self, room_id: str, origins: List[str]): + def subscribe(self, room_id: str, component_id: str, origins: List[str]): """ - In order to subscribe to HLS peers/components, - the HLS component should be initialized with the subscribe_mode set to manual. + In order to subscribe the component to peers/components, + the component should be initialized with the subscribe_mode set to manual. This mode proves beneficial when you do not wish to record or stream - all the available streams within a room via HLS. + all the available streams within a room. It allows for selective addition instead – you can manually select specific streams. For instance, you could opt to record only the stream of an event's host. """ return self._request( - hls_subscribe_hls_to, + subscribe_to, room_id=room_id, + component_id=component_id, json_body=SubscriptionConfig(origins=origins), ) diff --git a/tests/test_room_api.py b/tests/test_room_api.py index 18303ca..7aeaece 100644 --- a/tests/test_room_api.py +++ b/tests/test_room_api.py @@ -14,12 +14,14 @@ ComponentOptionsHLS, ComponentOptionsHLSSubscribeMode, ComponentOptionsRecording, + ComponentOptionsRecordingSubscribeMode, ComponentOptionsRTSP, ComponentOptionsSIP, ComponentPropertiesFile, ComponentPropertiesHLS, ComponentPropertiesHLSSubscribeMode, ComponentPropertiesRecording, + ComponentPropertiesRecordingSubscribeMode, ComponentPropertiesRTSP, ComponentPropertiesSIP, ComponentPropertiesSIPSIPCredentials, @@ -98,8 +100,11 @@ secret_access_key="secret", access_key_id="access", ), + subscribe_mode=ComponentOptionsRecordingSubscribeMode.AUTO, +) +RECORDING_PROPERTIES = ComponentPropertiesRecording( + subscribe_mode=ComponentPropertiesRecordingSubscribeMode("auto"), ) -RECORDING_PROPERTIES = ComponentPropertiesRecording(path_prefix="prefix") class TestAuthentication: @@ -330,24 +335,42 @@ def test_invalid_component(self, room_api: RoomApi): class TestHLSSubscribe: def test_valid_subscription(self, room_api: RoomApi): _, room = room_api.create_room(video_codec=CODEC_H264) - _ = room_api.add_component( + hls_component = room_api.add_component( room.id, options=ComponentOptionsHLS( subscribe_mode=ComponentOptionsHLSSubscribeMode("manual") ), ) - assert room_api.hls_subscribe(room.id, ["peer-id"]) is None + recording_component = room_api.add_component( + room.id, + options=ComponentOptionsRecording( + path_prefix="prefix", + credentials=S3Credentials( + bucket="bucket", + region="region", + secret_access_key="secret", + access_key_id="access", + ), + subscribe_mode=ComponentOptionsRecordingSubscribeMode("manual"), + ), + ) + + for component in [hls_component, recording_component]: + assert room_api.subscribe(room.id, component.id, ["peer-id"]) is None def test_invalid_subscription_in_auto_mode(self, room_api: RoomApi): _, room = room_api.create_room(video_codec=CODEC_H264) - _ = room_api.add_component(room.id, options=HLS_OPTIONS) - with pytest.raises(BadRequestError) as exception_info: - room_api.hls_subscribe(room.id, ["component-id"]) + hls_component = room_api.add_component(room.id, options=HLS_OPTIONS) + recording_component = room_api.add_component(room.id, options=RECORDING_OPTIONS) - assert ( - str(exception_info.value) - == "HLS component option `subscribe_mode` is set to :auto" - ) + for component in [hls_component, recording_component]: + with pytest.raises(BadRequestError) as exception_info: + room_api.subscribe(room.id, component.id, ["component-id"]) + + assert ( + str(exception_info.value) + == f"Component {component.id} option `subscribe_mode` is set to :auto" + ) class TestSIPCall: