Skip to content

Commit 3f5e82b

Browse files
authored
Toggleable ll-hls (#6)
* Update openapispec * asdf * Update action * revert * Update readme * Remove debug
1 parent c926e73 commit 3f5e82b

15 files changed

Lines changed: 473 additions & 152 deletions

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: 2.1
22

33
orbs:
4-
python: circleci/python@2.0.3
4+
python: circleci/python@2.1.1
55

66
executors:
77
machine_executor_amd64:

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,27 @@ First create a `RoomApi` instance, providing the jellyfish server address and ap
2020
```python
2121
from jellyfish import RoomApi
2222

23-
room_api = RoomApi(server_address='localhost:5002', server_api_token='development')
23+
room_api = RoomApi(server_address='http://localhost:5002', server_api_token='development')
2424
```
2525

2626
You can use it to interact with Jellyfish managing rooms, peers and components
2727

2828
```python
2929
# Create a room
30-
jellyfish_address, room = room_api.create_room()
31-
# 'localhost:5002', Room(components=[], id='f7cc2eac-f699-4609-ac8f-92f1ad6bea0c', peers=[])
30+
jellyfish_address, room = room_api.create_room(video_codec='h264')
31+
# 'localhost:5002', Room(components=[], config=RoomConfig(max_peers=None, video_codec='h264'), id='5a099a31-0eb2-4c28-84af-a1ec55c228af', peers=[]))
3232

3333
# Add peer to the room
3434
from jellyfish import PeerOptionsWebRTC
3535

36-
peer_token, peer_webrtc = room_api.add_peer(room.id, peer_type='webrtc', options=PeerOptionsWebRTC())
36+
peer_token, peer_webrtc = room_api.add_peer(room.id, options=PeerOptionsWebRTC())
3737
# 'AgDYfrCSigFiAA', Peer(id='2869fb5', status=<PeerStatus.DISCONNECTED: 'disconnected'>, type='webrtc')
3838

3939
# Add component to the room
40-
component_hls = room_api.add_component(room.id, component_type='hls')
41-
# Component(id='4c028a86', metadata=ComponentMetadata(playable=False), type='hls')
40+
from jellyfish import ComponentOptionsHLS
41+
42+
component_hls = room_api.add_component(room.id, options=ComponentOptionsHLS())
43+
# Component(actual_instance=ComponentHLS(id='c0dfab50-cafd-438d-985e-7b8f97ae55e3', metadata=ComponentMetadataHLS(low_latency=False, playable=False), type='hls'))
4244
```
4345

4446
## Copyright and License
@@ -47,4 +49,4 @@ Copyright 2023, [Software Mansion](https://swmansion.com/?utm_source=git&utm_med
4749

4850
[![Software Mansion](https://logo.swmansion.com/logo?color=white&variant=desktop&width=200&tag=membrane-github)](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=jellyfish)
4951

50-
Licensed under the [Apache License, Version 2.0](LICENSE)
52+
Licensed under the [Apache License, Version 2.0](LICENSE)

generate_client.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ openapi-generator-cli generate \
99
-o $TMP_DIR \
1010
-t templates \
1111
--package-name jellyfish.$PACKAGE_NAME \
12-
--global-property apis,models,modelTests=false,apiTests=false,modelDocs=false,apiDocs=false,supportingFiles &&
12+
--global-property apis,models,modelTests=false,apiTests=false,modelDocs=false,apiDocs=false,supportingFiles \
13+
--additional-properties useOneOfDiscriminatorLookup=true &&
1314
rm -rf jellyfish/$PACKAGE_NAME
1415
mv $TMP_DIR/jellyfish/$PACKAGE_NAME jellyfish/$PACKAGE_NAME
1516
rm -rf $TMP_DIR

jellyfish/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
from pydantic.error_wrappers import ValidationError
88

99
from jellyfish._openapi_client import (
10-
Room, RoomConfig, Peer, Component, ComponentOptions, ComponentOptionsRTSP, PeerOptionsWebRTC)
10+
Room, RoomConfig, Peer, Component, ComponentHLS, ComponentRTSP, ComponentOptions,
11+
ComponentOptionsRTSP, ComponentOptionsHLS, PeerOptionsWebRTC)
1112

1213
from jellyfish._openapi_client.exceptions import (
1314
UnauthorizedException, NotFoundException, BadRequestException)
1415

1516
from jellyfish._room_api import RoomApi
1617

17-
__all__ = ['RoomApi', 'Room', 'Peer', 'Component', 'RoomConfig', 'ComponentOptions',
18-
'ComponentOptionsRTSP', 'PeerOptionsWebRTC', 'UnauthorizedException',
19-
'NotFoundException', 'BadRequestException']
18+
__all__ = ['RoomApi', 'Room', 'Peer', 'Component', 'ComponentHLS', 'ComponentRTSP',
19+
'ComponentOptionsHLS', 'RoomConfig', 'ComponentOptions', 'ComponentOptionsRTSP',
20+
'PeerOptionsWebRTC', 'UnauthorizedException', 'NotFoundException', 'BadRequestException']
2021

2122
__docformat__ = "restructuredtext"

jellyfish/_openapi_client/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@
3434
from jellyfish._openapi_client.models.add_peer_request import AddPeerRequest
3535
from jellyfish._openapi_client.models.component import Component
3636
from jellyfish._openapi_client.models.component_details_response import ComponentDetailsResponse
37-
from jellyfish._openapi_client.models.component_metadata import ComponentMetadata
37+
from jellyfish._openapi_client.models.component_hls import ComponentHLS
38+
from jellyfish._openapi_client.models.component_metadata_hls import ComponentMetadataHLS
3839
from jellyfish._openapi_client.models.component_options import ComponentOptions
40+
from jellyfish._openapi_client.models.component_options_hls import ComponentOptionsHLS
3941
from jellyfish._openapi_client.models.component_options_rtsp import ComponentOptionsRTSP
42+
from jellyfish._openapi_client.models.component_rtsp import ComponentRTSP
4043
from jellyfish._openapi_client.models.error import Error
4144
from jellyfish._openapi_client.models.hls_skip import HlsSkip
4245
from jellyfish._openapi_client.models.peer import Peer

jellyfish/_openapi_client/models/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
from jellyfish._openapi_client.models.add_peer_request import AddPeerRequest
1717
from jellyfish._openapi_client.models.component import Component
1818
from jellyfish._openapi_client.models.component_details_response import ComponentDetailsResponse
19-
from jellyfish._openapi_client.models.component_metadata import ComponentMetadata
19+
from jellyfish._openapi_client.models.component_hls import ComponentHLS
20+
from jellyfish._openapi_client.models.component_metadata_hls import ComponentMetadataHLS
2021
from jellyfish._openapi_client.models.component_options import ComponentOptions
22+
from jellyfish._openapi_client.models.component_options_hls import ComponentOptionsHLS
2123
from jellyfish._openapi_client.models.component_options_rtsp import ComponentOptionsRTSP
24+
from jellyfish._openapi_client.models.component_rtsp import ComponentRTSP
2225
from jellyfish._openapi_client.models.error import Error
2326
from jellyfish._openapi_client.models.hls_skip import HlsSkip
2427
from jellyfish._openapi_client.models.peer import Peer

jellyfish/_openapi_client/models/add_component_request.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ def to_dict(self):
5555
# override the default output from pydantic by calling `to_dict()` of options
5656
if self.options:
5757
_dict['options'] = self.options.to_dict()
58-
# set to None if options (nullable) is None
59-
# and __fields_set__ contains the field
60-
if self.options is None and "options" in self.__fields_set__:
61-
_dict['options'] = None
62-
6358
return _dict
6459

6560
@classmethod

jellyfish/_openapi_client/models/component.py

Lines changed: 131 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,67 +11,157 @@
1111

1212

1313
from __future__ import annotations
14+
from inspect import getfullargspec
15+
import json
1416
import pprint
1517
import re # noqa: F401
16-
import json
17-
1818

19+
from typing import Any, List, Optional
20+
from pydantic import BaseModel, Field, StrictStr, ValidationError, validator
21+
from jellyfish._openapi_client.models.component_hls import ComponentHLS
22+
from jellyfish._openapi_client.models.component_rtsp import ComponentRTSP
23+
from typing import Union, Any, List, TYPE_CHECKING
24+
from pydantic import StrictStr, Field
1925

20-
from pydantic import BaseModel, Field, StrictStr
21-
from jellyfish._openapi_client.models.component_metadata import ComponentMetadata
26+
COMPONENT_ONE_OF_SCHEMAS = ["ComponentHLS", "ComponentRTSP"]
2227

2328
class Component(BaseModel):
2429
"""
2530
Describes component
2631
"""
27-
id: StrictStr = Field(..., description="Assigned component id")
28-
metadata: ComponentMetadata = Field(...)
29-
type: StrictStr = Field(..., description="Component type")
30-
__properties = ["id", "metadata", "type"]
32+
# data type: ComponentHLS
33+
oneof_schema_1_validator: Optional[ComponentHLS] = None
34+
# data type: ComponentRTSP
35+
oneof_schema_2_validator: Optional[ComponentRTSP] = None
36+
if TYPE_CHECKING:
37+
actual_instance: Union[ComponentHLS, ComponentRTSP]
38+
else:
39+
actual_instance: Any
40+
one_of_schemas: List[str] = Field(COMPONENT_ONE_OF_SCHEMAS, const=True)
3141

3242
class Config:
33-
"""Pydantic configuration"""
34-
allow_population_by_field_name = True
3543
validate_assignment = True
3644

37-
def to_str(self) -> str:
38-
"""Returns the string representation of the model using alias"""
39-
return pprint.pformat(self.dict(by_alias=True))
45+
discriminator_value_class_map = {
46+
}
47+
48+
def __init__(self, *args, **kwargs):
49+
if args:
50+
if len(args) > 1:
51+
raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`")
52+
if kwargs:
53+
raise ValueError("If a position argument is used, keyword arguments cannot be used.")
54+
super().__init__(actual_instance=args[0])
55+
else:
56+
super().__init__(**kwargs)
57+
58+
@validator('actual_instance')
59+
def actual_instance_must_validate_oneof(cls, v):
60+
instance = Component.construct()
61+
error_messages = []
62+
match = 0
63+
# validate data type: ComponentHLS
64+
if not isinstance(v, ComponentHLS):
65+
error_messages.append(f"Error! Input type `{type(v)}` is not `ComponentHLS`")
66+
else:
67+
match += 1
68+
# validate data type: ComponentRTSP
69+
if not isinstance(v, ComponentRTSP):
70+
error_messages.append(f"Error! Input type `{type(v)}` is not `ComponentRTSP`")
71+
else:
72+
match += 1
73+
if match > 1:
74+
# more than 1 match
75+
raise ValueError("Multiple matches found when setting `actual_instance` in Component with oneOf schemas: ComponentHLS, ComponentRTSP. Details: " + ", ".join(error_messages))
76+
elif match == 0:
77+
# no match
78+
raise ValueError("No match found when setting `actual_instance` in Component with oneOf schemas: ComponentHLS, ComponentRTSP. Details: " + ", ".join(error_messages))
79+
else:
80+
return v
4081

41-
def to_json(self) -> str:
42-
"""Returns the JSON representation of the model using alias"""
43-
return json.dumps(self.to_dict())
82+
@classmethod
83+
def from_dict(cls, obj: dict) -> Component:
84+
return cls.from_json(json.dumps(obj))
4485

4586
@classmethod
4687
def from_json(cls, json_str: str) -> Component:
47-
"""Create an instance of Component from a JSON string"""
48-
return cls.from_dict(json.loads(json_str))
49-
50-
def to_dict(self):
51-
"""Returns the dictionary representation of the model using alias"""
52-
_dict = self.dict(by_alias=True,
53-
exclude={
54-
},
55-
exclude_none=True)
56-
# override the default output from pydantic by calling `to_dict()` of metadata
57-
if self.metadata:
58-
_dict['metadata'] = self.metadata.to_dict()
59-
return _dict
88+
"""Returns the object represented by the json string"""
89+
instance = Component.construct()
90+
error_messages = []
91+
match = 0
92+
93+
# use oneOf discriminator to lookup the data type
94+
_data_type = json.loads(json_str).get("type")
95+
if not _data_type:
96+
raise ValueError("Failed to lookup data type from the field `type` in the input.")
97+
98+
# check if data type is `ComponentHLS`
99+
if _data_type == "ComponentHLS":
100+
instance.actual_instance = ComponentHLS.from_json(json_str)
101+
return instance
102+
103+
# check if data type is `ComponentRTSP`
104+
if _data_type == "ComponentRTSP":
105+
instance.actual_instance = ComponentRTSP.from_json(json_str)
106+
return instance
107+
108+
# check if data type is `ComponentHLS`
109+
if _data_type == "hls":
110+
instance.actual_instance = ComponentHLS.from_json(json_str)
111+
return instance
112+
113+
# check if data type is `ComponentRTSP`
114+
if _data_type == "rtsp":
115+
instance.actual_instance = ComponentRTSP.from_json(json_str)
116+
return instance
117+
118+
# deserialize data into ComponentHLS
119+
try:
120+
instance.actual_instance = ComponentHLS.from_json(json_str)
121+
match += 1
122+
except (ValidationError, ValueError) as e:
123+
error_messages.append(str(e))
124+
# deserialize data into ComponentRTSP
125+
try:
126+
instance.actual_instance = ComponentRTSP.from_json(json_str)
127+
match += 1
128+
except (ValidationError, ValueError) as e:
129+
error_messages.append(str(e))
130+
131+
if match > 1:
132+
# more than 1 match
133+
raise ValueError("Multiple matches found when deserializing the JSON string into Component with oneOf schemas: ComponentHLS, ComponentRTSP. Details: " + ", ".join(error_messages))
134+
elif match == 0:
135+
# no match
136+
raise ValueError("No match found when deserializing the JSON string into Component with oneOf schemas: ComponentHLS, ComponentRTSP. Details: " + ", ".join(error_messages))
137+
else:
138+
return instance
60139

61-
@classmethod
62-
def from_dict(cls, obj: dict) -> Component:
63-
"""Create an instance of Component from a dict"""
64-
if obj is None:
140+
def to_json(self) -> str:
141+
"""Returns the JSON representation of the actual instance"""
142+
if self.actual_instance is None:
143+
return "null"
144+
145+
to_json = getattr(self.actual_instance, "to_json", None)
146+
if callable(to_json):
147+
return self.actual_instance.to_json()
148+
else:
149+
return json.dumps(self.actual_instance)
150+
151+
def to_dict(self) -> dict:
152+
"""Returns the dict representation of the actual instance"""
153+
if self.actual_instance is None:
65154
return None
66155

67-
if not isinstance(obj, dict):
68-
return Component.parse_obj(obj)
156+
to_dict = getattr(self.actual_instance, "to_dict", None)
157+
if callable(to_dict):
158+
return self.actual_instance.to_dict()
159+
else:
160+
# primitive type
161+
return self.actual_instance
69162

70-
_obj = Component.parse_obj({
71-
"id": obj.get("id"),
72-
"metadata": ComponentMetadata.from_dict(obj.get("metadata")) if obj.get("metadata") is not None else None,
73-
"type": obj.get("type")
74-
})
75-
return _obj
163+
def to_str(self) -> str:
164+
"""Returns the string representation of the actual instance"""
165+
return pprint.pformat(self.dict())
76166

77167

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# coding: utf-8
2+
3+
"""
4+
Python API wrapper for Jellyfish Media Server
5+
6+
The version of the OpenAPI document: 0.2.0
7+
Generated by OpenAPI Generator (https://openapi-generator.tech)
8+
9+
Do not edit the class manually.
10+
""" # noqa: E501
11+
12+
13+
from __future__ import annotations
14+
import pprint
15+
import re # noqa: F401
16+
import json
17+
18+
19+
20+
from pydantic import BaseModel, Field, StrictStr
21+
from jellyfish._openapi_client.models.component_metadata_hls import ComponentMetadataHLS
22+
23+
class ComponentHLS(BaseModel):
24+
"""
25+
Describes HLS component
26+
"""
27+
id: StrictStr = Field(..., description="Assigned component ID")
28+
metadata: ComponentMetadataHLS = Field(...)
29+
type: StrictStr = Field(..., description="Component type")
30+
__properties = ["id", "metadata", "type"]
31+
32+
class Config:
33+
"""Pydantic configuration"""
34+
allow_population_by_field_name = True
35+
validate_assignment = True
36+
37+
def to_str(self) -> str:
38+
"""Returns the string representation of the model using alias"""
39+
return pprint.pformat(self.dict(by_alias=True))
40+
41+
def to_json(self) -> str:
42+
"""Returns the JSON representation of the model using alias"""
43+
return json.dumps(self.to_dict())
44+
45+
@classmethod
46+
def from_json(cls, json_str: str) -> ComponentHLS:
47+
"""Create an instance of ComponentHLS from a JSON string"""
48+
return cls.from_dict(json.loads(json_str))
49+
50+
def to_dict(self):
51+
"""Returns the dictionary representation of the model using alias"""
52+
_dict = self.dict(by_alias=True,
53+
exclude={
54+
},
55+
exclude_none=True)
56+
# override the default output from pydantic by calling `to_dict()` of metadata
57+
if self.metadata:
58+
_dict['metadata'] = self.metadata.to_dict()
59+
return _dict
60+
61+
@classmethod
62+
def from_dict(cls, obj: dict) -> ComponentHLS:
63+
"""Create an instance of ComponentHLS from a dict"""
64+
if obj is None:
65+
return None
66+
67+
if not isinstance(obj, dict):
68+
return ComponentHLS.parse_obj(obj)
69+
70+
_obj = ComponentHLS.parse_obj({
71+
"id": obj.get("id"),
72+
"metadata": ComponentMetadataHLS.from_dict(obj.get("metadata")) if obj.get("metadata") is not None else None,
73+
"type": obj.get("type")
74+
})
75+
return _obj
76+
77+

0 commit comments

Comments
 (0)