diff --git a/jellyfish/_openapi_client/__init__.py b/jellyfish/_openapi_client/__init__.py index d521219..f450a36 100644 --- a/jellyfish/_openapi_client/__init__.py +++ b/jellyfish/_openapi_client/__init__.py @@ -15,7 +15,7 @@ __version__ = "1.0.0" # import apis into sdk package -from jellyfish._openapi_client.api.default_api import DefaultApi +from jellyfish._openapi_client.api.hls_api import HlsApi from jellyfish._openapi_client.api.room_api import RoomApi # import ApiClient @@ -40,6 +40,9 @@ from jellyfish._openapi_client.models.component_metadata_hls import ComponentMetadataHLS from jellyfish._openapi_client.models.component_options import ComponentOptions from jellyfish._openapi_client.models.component_options_hls import ComponentOptionsHLS +from jellyfish._openapi_client.models.component_options_hlss3 import ( + ComponentOptionsHLSS3, +) from jellyfish._openapi_client.models.component_options_rtsp import ComponentOptionsRTSP from jellyfish._openapi_client.models.component_rtsp import ComponentRTSP from jellyfish._openapi_client.models.error import Error @@ -62,3 +65,5 @@ ) from jellyfish._openapi_client.models.room_details_response import RoomDetailsResponse from jellyfish._openapi_client.models.rooms_listing_response import RoomsListingResponse +from jellyfish._openapi_client.models.s3_credentials import S3Credentials +from jellyfish._openapi_client.models.subscription_config import SubscriptionConfig diff --git a/jellyfish/_openapi_client/api/__init__.py b/jellyfish/_openapi_client/api/__init__.py index 5820a27..480f08c 100644 --- a/jellyfish/_openapi_client/api/__init__.py +++ b/jellyfish/_openapi_client/api/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa # import apis into api package -from jellyfish._openapi_client.api.default_api import DefaultApi +from jellyfish._openapi_client.api.hls_api import HlsApi from jellyfish._openapi_client.api.room_api import RoomApi diff --git a/jellyfish/_openapi_client/api/default_api.py b/jellyfish/_openapi_client/api/hls_api.py similarity index 54% rename from jellyfish/_openapi_client/api/default_api.py rename to jellyfish/_openapi_client/api/hls_api.py index 772c03e..db66759 100644 --- a/jellyfish/_openapi_client/api/default_api.py +++ b/jellyfish/_openapi_client/api/hls_api.py @@ -22,6 +22,7 @@ from typing import Optional from jellyfish._openapi_client.models.hls_skip import HlsSkip +from jellyfish._openapi_client.models.subscription_config import SubscriptionConfig from jellyfish._openapi_client.api_client import ApiClient from jellyfish._openapi_client.api_response import ApiResponse @@ -31,7 +32,7 @@ ) -class DefaultApi(object): +class HlsApi(object): """NOTE: This class is auto generated by OpenAPI Generator Ref: https://openapi-generator.tech @@ -44,7 +45,7 @@ def __init__(self, api_client=None): self.api_client = api_client @validate_arguments - def jellyfish_web_hls_controller_index( + def get_hls_content( self, room_id: Annotated[StrictStr, Field(..., description="Room id")], filename: Annotated[StrictStr, Field(..., description="Name of the file")], @@ -64,12 +65,12 @@ def jellyfish_web_hls_controller_index( ] = None, **kwargs ) -> str: # noqa: E501 - """Send file # noqa: E501 + """Retrieve HLS Content # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.jellyfish_web_hls_controller_index(room_id, filename, range, hls_msn, hls_part, hls_skip, async_req=True) + >>> thread = api.get_hls_content(room_id, filename, range, hls_msn, hls_part, hls_skip, async_req=True) >>> result = thread.get() :param room_id: Room id (required) @@ -98,14 +99,14 @@ def jellyfish_web_hls_controller_index( kwargs["_return_http_data_only"] = True if "_preload_content" in kwargs: raise ValueError( - "Error! Please call the jellyfish_web_hls_controller_index_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" + "Error! Please call the get_hls_content_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" ) - return self.jellyfish_web_hls_controller_index_with_http_info( + return self.get_hls_content_with_http_info( room_id, filename, range, hls_msn, hls_part, hls_skip, **kwargs ) # noqa: E501 @validate_arguments - def jellyfish_web_hls_controller_index_with_http_info( + def get_hls_content_with_http_info( self, room_id: Annotated[StrictStr, Field(..., description="Room id")], filename: Annotated[StrictStr, Field(..., description="Name of the file")], @@ -125,12 +126,12 @@ def jellyfish_web_hls_controller_index_with_http_info( ] = None, **kwargs ) -> ApiResponse: # noqa: E501 - """Send file # noqa: E501 + """Retrieve HLS Content # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.jellyfish_web_hls_controller_index_with_http_info(room_id, filename, range, hls_msn, hls_part, hls_skip, async_req=True) + >>> thread = api.get_hls_content_with_http_info(room_id, filename, range, hls_msn, hls_part, hls_skip, async_req=True) >>> result = thread.get() :param room_id: Room id (required) @@ -197,7 +198,7 @@ def jellyfish_web_hls_controller_index_with_http_info( if _key not in _all_params: raise ApiTypeError( "Got an unexpected keyword argument '%s'" - " to method jellyfish_web_hls_controller_index" % _key + " to method get_hls_content" % _key ) _params[_key] = _val del _params["kwargs"] @@ -264,3 +265,171 @@ def jellyfish_web_hls_controller_index_with_http_info( collection_formats=_collection_formats, _request_auth=_params.get("_request_auth"), ) + + @validate_arguments + def subscribe_tracks( + self, + room_id: Annotated[StrictStr, Field(..., description="Room ID")], + subscription_config: Annotated[ + Optional[SubscriptionConfig], Field(description="Subscribe configuration") + ] = None, + **kwargs + ) -> None: # noqa: E501 + """Subscribe hls component for tracks # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.subscribe_tracks(room_id, subscription_config, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param subscription_config: Subscribe configuration + :type subscription_config: SubscriptionConfig + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + kwargs["_return_http_data_only"] = True + if "_preload_content" in kwargs: + raise ValueError( + "Error! Please call the subscribe_tracks_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" + ) + return self.subscribe_tracks_with_http_info( + room_id, subscription_config, **kwargs + ) # noqa: E501 + + @validate_arguments + def subscribe_tracks_with_http_info( + self, + room_id: Annotated[StrictStr, Field(..., description="Room ID")], + subscription_config: Annotated[ + Optional[SubscriptionConfig], Field(description="Subscribe configuration") + ] = None, + **kwargs + ) -> ApiResponse: # noqa: E501 + """Subscribe hls component for tracks # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + + >>> thread = api.subscribe_tracks_with_http_info(room_id, subscription_config, async_req=True) + >>> result = thread.get() + + :param room_id: Room ID (required) + :type room_id: str + :param subscription_config: Subscribe configuration + :type subscription_config: SubscriptionConfig + :param async_req: Whether to execute the request asynchronously. + :type async_req: bool, optional + :param _preload_content: if False, the ApiResponse.data will + be set to none and raw_data will store the + HTTP response body without reading/decoding. + Default is True. + :type _preload_content: bool, optional + :param _return_http_data_only: response data instead of ApiResponse + object with status code, headers, etc + :type _return_http_data_only: bool, optional + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the authentication + in the spec for a single request. + :type _request_auth: dict, optional + :type _content_type: string, optional: force content-type for the request + :return: Returns the result object. + If the method is called asynchronously, + returns the request thread. + :rtype: None + """ + + _params = locals() + + _all_params = ["room_id", "subscription_config"] + _all_params.extend( + [ + "async_req", + "_return_http_data_only", + "_preload_content", + "_request_timeout", + "_request_auth", + "_content_type", + "_headers", + ] + ) + + # validate the arguments + for _key, _val in _params["kwargs"].items(): + if _key not in _all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method subscribe_tracks" % _key + ) + _params[_key] = _val + del _params["kwargs"] + + _collection_formats = {} + + # process the path parameters + _path_params = {} + if _params["room_id"]: + _path_params["room_id"] = _params["room_id"] + + # process the query parameters + _query_params = [] + # process the header parameters + _header_params = dict(_params.get("_headers", {})) + # process the form parameters + _form_params = [] + _files = {} + # process the body parameter + _body_params = None + if _params["subscription_config"] is not None: + _body_params = _params["subscription_config"] + + # set the HTTP header `Accept` + _header_params["Accept"] = self.api_client.select_header_accept( + ["application/json"] + ) # noqa: E501 + + # set the HTTP header `Content-Type` + _content_types_list = _params.get( + "_content_type", + self.api_client.select_header_content_type(["application/json"]), + ) + if _content_types_list: + _header_params["Content-Type"] = _content_types_list + + # authentication setting + _auth_settings = ["authorization"] # noqa: E501 + + _response_types_map = {} + + return self.api_client.call_api( + "/hls/{room_id}/subscribe", + "POST", + _path_params, + _query_params, + _header_params, + body=_body_params, + post_params=_form_params, + files=_files, + response_types_map=_response_types_map, + auth_settings=_auth_settings, + async_req=_params.get("async_req"), + _return_http_data_only=_params.get("_return_http_data_only"), # noqa: E501 + _preload_content=_params.get("_preload_content", True), + _request_timeout=_params.get("_request_timeout"), + collection_formats=_collection_formats, + _request_auth=_params.get("_request_auth"), + ) diff --git a/jellyfish/_openapi_client/models/__init__.py b/jellyfish/_openapi_client/models/__init__.py index a7774d9..91a7a66 100644 --- a/jellyfish/_openapi_client/models/__init__.py +++ b/jellyfish/_openapi_client/models/__init__.py @@ -22,6 +22,9 @@ from jellyfish._openapi_client.models.component_metadata_hls import ComponentMetadataHLS from jellyfish._openapi_client.models.component_options import ComponentOptions from jellyfish._openapi_client.models.component_options_hls import ComponentOptionsHLS +from jellyfish._openapi_client.models.component_options_hlss3 import ( + ComponentOptionsHLSS3, +) from jellyfish._openapi_client.models.component_options_rtsp import ComponentOptionsRTSP from jellyfish._openapi_client.models.component_rtsp import ComponentRTSP from jellyfish._openapi_client.models.error import Error @@ -44,3 +47,5 @@ ) from jellyfish._openapi_client.models.room_details_response import RoomDetailsResponse from jellyfish._openapi_client.models.rooms_listing_response import RoomsListingResponse +from jellyfish._openapi_client.models.s3_credentials import S3Credentials +from jellyfish._openapi_client.models.subscription_config import SubscriptionConfig diff --git a/jellyfish/_openapi_client/models/component_metadata_hls.py b/jellyfish/_openapi_client/models/component_metadata_hls.py index 6ecdbe5..68f0936 100644 --- a/jellyfish/_openapi_client/models/component_metadata_hls.py +++ b/jellyfish/_openapi_client/models/component_metadata_hls.py @@ -17,7 +17,7 @@ from typing import Optional -from pydantic import BaseModel, Field, StrictBool, StrictInt +from pydantic import BaseModel, Field, StrictBool, StrictInt, StrictStr, validator class ComponentMetadataHLS(BaseModel): @@ -34,12 +34,30 @@ class ComponentMetadataHLS(BaseModel): playable: StrictBool = Field( ..., description="Whether the generated HLS playlist is playable" ) + subscribe_mode: StrictStr = Field( + ..., + alias="subscribeMode", + description="Whether the HLS component should subscribe to tracks automatically or manually", + ) target_window_duration: Optional[StrictInt] = Field( ..., alias="targetWindowDuration", description="Duration of stream available for viewer", ) - __properties = ["lowLatency", "persistent", "playable", "targetWindowDuration"] + __properties = [ + "lowLatency", + "persistent", + "playable", + "subscribeMode", + "targetWindowDuration", + ] + + @validator("subscribe_mode") + def subscribe_mode_validate_enum(cls, value): + """Validates the enum""" + if value not in ("auto", "manual"): + raise ValueError("must be one of enum values ('auto', 'manual')") + return value class Config: """Pydantic configuration""" @@ -87,6 +105,7 @@ def from_dict(cls, obj: dict) -> ComponentMetadataHLS: "low_latency": obj.get("lowLatency"), "persistent": obj.get("persistent"), "playable": obj.get("playable"), + "subscribe_mode": obj.get("subscribeMode"), "target_window_duration": obj.get("targetWindowDuration"), } ) diff --git a/jellyfish/_openapi_client/models/component_options_hls.py b/jellyfish/_openapi_client/models/component_options_hls.py index 102b891..3f31589 100644 --- a/jellyfish/_openapi_client/models/component_options_hls.py +++ b/jellyfish/_openapi_client/models/component_options_hls.py @@ -17,7 +17,10 @@ from typing import Optional -from pydantic import BaseModel, Field, StrictBool, StrictInt +from pydantic import BaseModel, Field, StrictBool, StrictInt, StrictStr, validator +from jellyfish._openapi_client.models.component_options_hlss3 import ( + ComponentOptionsHLSS3, +) class ComponentOptionsHLS(BaseModel): @@ -31,12 +34,34 @@ class ComponentOptionsHLS(BaseModel): persistent: Optional[StrictBool] = Field( False, description="Whether the video is stored after end of stream" ) + s3: Optional[ComponentOptionsHLSS3] = None + subscribe_mode: Optional[StrictStr] = Field( + "auto", + alias="subscribeMode", + description="Whether the HLS component should subscribe to tracks automatically or manually.", + ) target_window_duration: Optional[StrictInt] = Field( None, alias="targetWindowDuration", description="Duration of stream available for viewer", ) - __properties = ["lowLatency", "persistent", "targetWindowDuration"] + __properties = [ + "lowLatency", + "persistent", + "s3", + "subscribeMode", + "targetWindowDuration", + ] + + @validator("subscribe_mode") + def subscribe_mode_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in ("auto", "manual"): + raise ValueError("must be one of enum values ('auto', 'manual')") + return value class Config: """Pydantic configuration""" @@ -60,6 +85,14 @@ def from_json(cls, json_str: str) -> ComponentOptionsHLS: def to_dict(self): """Returns the dictionary representation of the model using alias""" _dict = self.dict(by_alias=True, exclude={}, exclude_none=True) + # override the default output from pydantic by calling `to_dict()` of s3 + if self.s3: + _dict["s3"] = self.s3.to_dict() + # set to None if s3 (nullable) is None + # and __fields_set__ contains the field + if self.s3 is None and "s3" in self.__fields_set__: + _dict["s3"] = None + # set to None if target_window_duration (nullable) is None # and __fields_set__ contains the field if ( @@ -87,6 +120,12 @@ def from_dict(cls, obj: dict) -> ComponentOptionsHLS: "persistent": obj.get("persistent") if obj.get("persistent") is not None else False, + "s3": ComponentOptionsHLSS3.from_dict(obj.get("s3")) + if obj.get("s3") is not None + else None, + "subscribe_mode": obj.get("subscribeMode") + if obj.get("subscribeMode") is not None + else "auto", "target_window_duration": obj.get("targetWindowDuration"), } ) diff --git a/jellyfish/_openapi_client/models/component_options_hlss3.py b/jellyfish/_openapi_client/models/component_options_hlss3.py new file mode 100644 index 0000000..cb146dd --- /dev/null +++ b/jellyfish/_openapi_client/models/component_options_hlss3.py @@ -0,0 +1,149 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +from inspect import getfullargspec +import json +import pprint +import re # noqa: F401 + +from typing import Any, List, Optional +from pydantic import BaseModel, Field, StrictStr, ValidationError, validator +from jellyfish._openapi_client.models.s3_credentials import S3Credentials +from typing import Union, Any, List, TYPE_CHECKING +from pydantic import StrictStr, Field + +COMPONENTOPTIONSHLSS3_ONE_OF_SCHEMAS = ["S3Credentials"] + + +class ComponentOptionsHLSS3(BaseModel): + """ + Credentials to AWS S3 bucket. + """ + + # data type: S3Credentials + oneof_schema_1_validator: Optional[S3Credentials] = None + if TYPE_CHECKING: + actual_instance: Union[S3Credentials] + else: + actual_instance: Any + one_of_schemas: List[str] = Field(COMPONENTOPTIONSHLSS3_ONE_OF_SCHEMAS, const=True) + + class Config: + validate_assignment = True + + def __init__(self, *args, **kwargs): + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + if v is None: + return v + + instance = ComponentOptionsHLSS3.construct() + error_messages = [] + match = 0 + # validate data type: S3Credentials + if not isinstance(v, S3Credentials): + error_messages.append( + f"Error! Input type `{type(v)}` is not `S3Credentials`" + ) + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in ComponentOptionsHLSS3 with oneOf schemas: S3Credentials. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in ComponentOptionsHLSS3 with oneOf schemas: S3Credentials. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> ComponentOptionsHLSS3: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> ComponentOptionsHLSS3: + """Returns the object represented by the json string""" + instance = ComponentOptionsHLSS3.construct() + if json_str is None: + return instance + + error_messages = [] + match = 0 + + # deserialize data into S3Credentials + try: + instance.actual_instance = S3Credentials.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into ComponentOptionsHLSS3 with oneOf schemas: S3Credentials. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into ComponentOptionsHLSS3 with oneOf schemas: S3Credentials. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.dict()) diff --git a/jellyfish/_openapi_client/models/s3_credentials.py b/jellyfish/_openapi_client/models/s3_credentials.py new file mode 100644 index 0000000..55ff77a --- /dev/null +++ b/jellyfish/_openapi_client/models/s3_credentials.py @@ -0,0 +1,86 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from pydantic import BaseModel, Field, StrictStr + + +class S3Credentials(BaseModel): + """ + An AWS S3 credential that will be used to send HLS stream. The stream will only be uploaded if credentials are provided + """ + + access_key_id: StrictStr = Field( + ..., + alias="accessKeyId", + description="An AWS access key identifier, linked to your AWS account.", + ) + bucket: StrictStr = Field( + ..., description="The name of the S3 bucket where your data will be stored." + ) + region: StrictStr = Field( + ..., description="The AWS region where your bucket is located." + ) + secret_access_key: StrictStr = Field( + ..., + alias="secretAccessKey", + description="The secret key that is linked to the Access Key ID.", + ) + __properties = ["accessKeyId", "bucket", "region", "secretAccessKey"] + + class Config: + """Pydantic configuration""" + + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> S3Credentials: + """Create an instance of S3Credentials from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, exclude={}, exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> S3Credentials: + """Create an instance of S3Credentials from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return S3Credentials.parse_obj(obj) + + _obj = S3Credentials.parse_obj( + { + "access_key_id": obj.get("accessKeyId"), + "bucket": obj.get("bucket"), + "region": obj.get("region"), + "secret_access_key": obj.get("secretAccessKey"), + } + ) + return _obj diff --git a/jellyfish/_openapi_client/models/subscription_config.py b/jellyfish/_openapi_client/models/subscription_config.py new file mode 100644 index 0000000..8c2fe76 --- /dev/null +++ b/jellyfish/_openapi_client/models/subscription_config.py @@ -0,0 +1,67 @@ +# coding: utf-8 + +""" + Python API wrapper for Jellyfish Media Server + + The version of the OpenAPI document: 0.2.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + + +from typing import List, Optional +from pydantic import BaseModel, Field, StrictStr, conlist + + +class SubscriptionConfig(BaseModel): + """ + Subscription config + """ + + tracks: Optional[conlist(StrictStr)] = Field( + None, description="List of tracks that hls endpoint will subscribe for" + ) + __properties = ["tracks"] + + class Config: + """Pydantic configuration""" + + allow_population_by_field_name = True + validate_assignment = True + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.dict(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> SubscriptionConfig: + """Create an instance of SubscriptionConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self): + """Returns the dictionary representation of the model using alias""" + _dict = self.dict(by_alias=True, exclude={}, exclude_none=True) + return _dict + + @classmethod + def from_dict(cls, obj: dict) -> SubscriptionConfig: + """Create an instance of SubscriptionConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return SubscriptionConfig.parse_obj(obj) + + _obj = SubscriptionConfig.parse_obj({"tracks": obj.get("tracks")}) + return _obj diff --git a/jellyfish/_room_api.py b/jellyfish/_room_api.py index 5957c18..81691bd 100644 --- a/jellyfish/_room_api.py +++ b/jellyfish/_room_api.py @@ -42,6 +42,7 @@ def __init__( self._api_client = jellyfish_api.ApiClient(self._configuration) self._room_api = jellyfish_api.RoomApi(self._api_client) + self._hls_api = jellyfish_api.HlsApi(self._api_client) def create_room( self, @@ -126,3 +127,8 @@ def delete_component(self, room_id: str, component_id: str) -> None: """Deletes component""" return self._room_api.delete_component(room_id, component_id) + + def hls_subscribe(self, room_id: str, tracks: list): + """subscribes hls component for tracks""" + + return self._hls_api.subscribe_tracks(room_id, {"tracks": tracks}) diff --git a/tests/test_room_api.py b/tests/test_room_api.py index fda6b3f..14142d0 100644 --- a/tests/test_room_api.py +++ b/tests/test_room_api.py @@ -11,7 +11,7 @@ from jellyfish import ValidationError -from jellyfish import UnauthorizedException, NotFoundException +from jellyfish import UnauthorizedException, NotFoundException, BadRequestException HOST = "jellyfish" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" @@ -173,6 +173,21 @@ def test_invalid_component(self, room_api: RoomApi): room_api.delete_component(room.id, "invalid_id") +class TestHLSSubscribe: + def test_valid_subscription(self, room_api: RoomApi): + _, room = room_api.create_room(video_codec=CODEC_H264) + _ = room_api.add_component( + room.id, options=ComponentOptionsHLS(subscribe_mode="manual") + ) + assert room_api.hls_subscribe(room.id, ["track-id"]) is None + + def test_invalid_subscription(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(BadRequestException): + room_api.hls_subscribe(room.id, ["track-id"]) + + class TestAddPeer: def _assert_peer_created(self, room_api, peer, room_id): assert peer.status == "disconnected"