Skip to content

Commit

Permalink
Add file component
Browse files Browse the repository at this point in the history
  • Loading branch information
roznawsk committed Dec 27, 2023
1 parent 25d3d16 commit 042eef9
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
- run:
name: Lint
command: poetry run lint
- run:
name: Check format
command: poetry run format_check
- persist_to_workspace:
root: ~/project
paths:
Expand Down
6 changes: 6 additions & 0 deletions jellyfish/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@

# Models
from jellyfish._openapi_client.models import (
ComponentFile,
ComponentHLS,
ComponentOptionsFile,
ComponentOptionsHLS,
ComponentOptionsHLSSubscribeMode,
ComponentOptionsRTSP,
ComponentPropertiesFile,
ComponentPropertiesHLS,
ComponentPropertiesHLSSubscribeMode,
ComponentPropertiesRTSP,
Expand Down Expand Up @@ -50,6 +53,9 @@
"ComponentRTSP",
"ComponentOptionsRTSP",
"ComponentPropertiesRTSP",
"ComponentFile",
"ComponentOptionsFile",
"ComponentPropertiesFile",
"events",
"errors",
]
Expand Down
2 changes: 2 additions & 0 deletions jellyfish/_openapi_client/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .component_options_hls import ComponentOptionsHLS
from .component_options_hls_subscribe_mode import ComponentOptionsHLSSubscribeMode
from .component_options_rtsp import ComponentOptionsRTSP
from .component_properties_file import ComponentPropertiesFile
from .component_properties_hls import ComponentPropertiesHLS
from .component_properties_hls_subscribe_mode import ComponentPropertiesHLSSubscribeMode
from .component_properties_rtsp import ComponentPropertiesRTSP
Expand Down Expand Up @@ -40,6 +41,7 @@
"ComponentOptionsHLS",
"ComponentOptionsHLSSubscribeMode",
"ComponentOptionsRTSP",
"ComponentPropertiesFile",
"ComponentPropertiesHLS",
"ComponentPropertiesHLSSubscribeMode",
"ComponentPropertiesRTSP",
Expand Down
25 changes: 24 additions & 1 deletion jellyfish/_openapi_client/models/component_file.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from typing import Any, Dict, List, Type, TypeVar
from typing import TYPE_CHECKING, Any, Dict, List, Type, TypeVar, Union

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

if TYPE_CHECKING:
from ..models.component_properties_file import ComponentPropertiesFile


T = TypeVar("T", bound="ComponentFile")


Expand All @@ -14,13 +20,18 @@ class ComponentFile:
"""Assigned component ID"""
type: str
"""Component type"""
properties: Union[Unset, "ComponentPropertiesFile"] = UNSET
"""Properties specific to the File component"""
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
"""@private"""

def to_dict(self) -> Dict[str, Any]:
"""@private"""
id = self.id
type = self.type
properties: Union[Unset, Dict[str, Any]] = UNSET
if not isinstance(self.properties, Unset):
properties = self.properties.to_dict()

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
Expand All @@ -30,20 +41,32 @@ def to_dict(self) -> Dict[str, Any]:
"type": type,
}
)
if properties is not UNSET:
field_dict["properties"] = properties

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
"""@private"""
from ..models.component_properties_file import ComponentPropertiesFile

d = src_dict.copy()
id = d.pop("id")

type = d.pop("type")

_properties = d.pop("properties", UNSET)
properties: Union[Unset, ComponentPropertiesFile]
if isinstance(_properties, Unset):
properties = UNSET
else:
properties = ComponentPropertiesFile.from_dict(_properties)

component_file = cls(
id=id,
type=type,
properties=properties,
)

component_file.additional_properties = d
Expand Down
60 changes: 60 additions & 0 deletions jellyfish/_openapi_client/models/component_properties_file.py
Original file line number Diff line number Diff line change
@@ -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="ComponentPropertiesFile")


@_attrs_define
class ComponentPropertiesFile:
"""Properties specific to the File component"""

file_path: str
"""Path to track file. Must be either OPUS encapsulated in Ogg or raw h264"""
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
"""@private"""

def to_dict(self) -> Dict[str, Any]:
"""@private"""
file_path = self.file_path

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"filePath": file_path,
}
)

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
"""@private"""
d = src_dict.copy()
file_path = d.pop("filePath")

component_properties_file = cls(
file_path=file_path,
)

component_properties_file.additional_properties = d
return component_properties_file

@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
51 changes: 48 additions & 3 deletions jellyfish/_openapi_client/models/component_properties_rtsp.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,77 @@
from typing import Any, Dict, List, Type, TypeVar
from typing import Any, Dict, List, Type, TypeVar, Union

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

T = TypeVar("T", bound="ComponentPropertiesRTSP")


@_attrs_define
class ComponentPropertiesRTSP:
"""Properties specific to the RTSP component"""

source_uri: str
"""URI of RTSP source stream"""
keep_alive_interval: Union[Unset, int] = UNSET
"""Interval (in ms) in which keep-alive RTSP messages will be sent to the remote stream source"""
pierce_nat: Union[Unset, bool] = UNSET
"""Whether to attempt to create client-side NAT binding by sending an empty datagram from client to source, after the completion of RTSP setup"""
reconnect_delay: Union[Unset, int] = UNSET
"""Delay (in ms) between successive reconnect attempts"""
rtp_port: Union[Unset, int] = UNSET
"""Local port RTP stream will be received at"""
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)
"""@private"""

def to_dict(self) -> Dict[str, Any]:
"""@private"""
source_uri = self.source_uri
keep_alive_interval = self.keep_alive_interval
pierce_nat = self.pierce_nat
reconnect_delay = self.reconnect_delay
rtp_port = self.rtp_port

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update({})
field_dict.update(
{
"sourceUri": source_uri,
}
)
if keep_alive_interval is not UNSET:
field_dict["keepAliveInterval"] = keep_alive_interval
if pierce_nat is not UNSET:
field_dict["pierceNat"] = pierce_nat
if reconnect_delay is not UNSET:
field_dict["reconnectDelay"] = reconnect_delay
if rtp_port is not UNSET:
field_dict["rtpPort"] = rtp_port

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
"""@private"""
d = src_dict.copy()
component_properties_rtsp = cls()
source_uri = d.pop("sourceUri")

keep_alive_interval = d.pop("keepAliveInterval", UNSET)

pierce_nat = d.pop("pierceNat", UNSET)

reconnect_delay = d.pop("reconnectDelay", UNSET)

rtp_port = d.pop("rtpPort", UNSET)

component_properties_rtsp = cls(
source_uri=source_uri,
keep_alive_interval=keep_alive_interval,
pierce_nat=pierce_nat,
reconnect_delay=reconnect_delay,
rtp_port=rtp_port,
)

component_properties_rtsp.additional_properties = d
return component_properties_rtsp
Expand Down
15 changes: 11 additions & 4 deletions jellyfish/api/_room_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from jellyfish._openapi_client.models import (
AddComponentJsonBody,
AddPeerJsonBody,
ComponentFile,
ComponentHLS,
ComponentOptionsFile,
ComponentOptionsHLS,
ComponentOptionsRTSP,
ComponentRTSP,
Expand Down Expand Up @@ -119,17 +121,22 @@ def delete_peer(self, room_id: str, peer_id: str) -> None:
return self._request(room_delete_peer, id=peer_id, room_id=room_id)

def add_component(
self, room_id: str, options: Union[ComponentOptionsHLS, ComponentOptionsRTSP]
) -> Union[ComponentHLS, ComponentRTSP]:
self,
room_id: str,
options: Union[ComponentOptionsFile, ComponentOptionsHLS, ComponentOptionsRTSP],
) -> Union[ComponentFile, ComponentHLS, ComponentRTSP]:
"""Creates component in the room"""

if isinstance(options, ComponentOptionsHLS):
if isinstance(options, ComponentOptionsFile):
component_type = "file"
elif isinstance(options, ComponentOptionsHLS):
component_type = "hls"
elif isinstance(options, ComponentOptionsRTSP):
component_type = "rtsp"
else:
raise ValueError(
"options must be either ComponentOptionsHLS or ComponentOptionsRTSP"
"options must be ComponentFile, ComponentOptionsHLS"
"or ComponentOptionsRTSP"
)

json_body = AddComponentJsonBody(type=component_type, options=options)
Expand Down
4 changes: 4 additions & 0 deletions poetry_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def run_formatter():
check_exit_code("ruff format .")


def run_format_check():
check_exit_code("ruff format . --check")


def run_linter():
check_exit_code("ruff check .")

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
ci_test = "poetry_scripts:run_tests"
format = "poetry_scripts:run_formatter"
format_check = "poetry_scripts:run_format_check"
lint = "poetry_scripts:run_linter"
fix_lint = "poetry_scripts:run_linter_fix"
generate_docs = "poetry_scripts:generate_docs"
Expand Down
37 changes: 33 additions & 4 deletions tests/test_room_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
import pytest

from jellyfish import (
ComponentFile,
ComponentHLS,
ComponentOptionsFile,
ComponentOptionsHLS,
ComponentOptionsHLSSubscribeMode,
ComponentOptionsRTSP,
ComponentPropertiesFile,
ComponentPropertiesHLS,
ComponentPropertiesHLSSubscribeMode,
ComponentPropertiesRTSP,
Expand Down Expand Up @@ -41,6 +44,7 @@
RTSP_OPTIONS = ComponentOptionsRTSP(
source_uri="rtsp://ef36c6dff23ecc5bbe311cc880d95dc8.se:2137/does/not/matter"
)
FILE_OPTIONS = ComponentOptionsFile(file_path="video.h264")


class TestAuthentication:
Expand Down Expand Up @@ -192,7 +196,7 @@ class TestAddComponent:
def test_with_options_hls(self, room_api: RoomApi):
_, room = room_api.create_room(video_codec=CODEC_H264)

room_api.add_component(room.id, options=HLS_OPTIONS)
response = room_api.add_component(room.id, options=HLS_OPTIONS)

component = room_api.get_room(room.id).components[0]

Expand All @@ -204,23 +208,48 @@ def test_with_options_hls(self, room_api: RoomApi):
target_window_duration=None,
)
properties.additional_properties = {"s3": None}

component_hls = ComponentHLS(id=component.id, type="hls", properties=properties)

assert response == component_hls
assert component == component_hls

def test_with_options_rtsp(self, room_api: RoomApi):
_, room = room_api.create_room(video_codec=CODEC_H264)

room_api.add_component(room.id, options=RTSP_OPTIONS)
response = room_api.add_component(room.id, options=RTSP_OPTIONS)
component = room_api.get_room(room.id).components[0]

component_rtsp = ComponentRTSP(
id=component.id, type="rtsp", properties=ComponentPropertiesRTSP()
id=component.id,
type="rtsp",
properties=ComponentPropertiesRTSP(
source_uri=RTSP_OPTIONS.source_uri,
keep_alive_interval=RTSP_OPTIONS.keep_alive_interval,
reconnect_delay=RTSP_OPTIONS.reconnect_delay,
rtp_port=RTSP_OPTIONS.rtp_port,
pierce_nat=RTSP_OPTIONS.pierce_nat,
),
)

assert response == component_rtsp
assert component == component_rtsp

def test_with_options_file(self, room_api: RoomApi):
_, room = room_api.create_room()

response = room_api.add_component(room.id, options=FILE_OPTIONS)

component = room_api.get_room(room.id).components[0]

component_file = ComponentFile(
id=component.id,
type="file",
properties=ComponentPropertiesFile(file_path=FILE_OPTIONS.file_path),
)

assert response == component_file
assert component == component_file

def test_invalid_type(self, room_api: RoomApi):
_, room = room_api.create_room(video_codec=CODEC_H264)

Expand Down

0 comments on commit 042eef9

Please sign in to comment.