Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: api improvements #77

Merged
merged 26 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions livekit-api/livekit/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"""

# flake8: noqa
from ._proto import livekit_room_pb2 as proto_room
theomonnom marked this conversation as resolved.
Show resolved Hide resolved
from .version import __version__
from .access_token import VideoGrants, AccessToken
from .room_service import RoomService

8 changes: 5 additions & 3 deletions livekit-api/livekit/api/_service.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@

from typing import Dict

from abc import ABC
from ._twirp_client import TwirpClient
from .access_token import AccessToken, VideoGrants

AUTHORIZATION = "authorization"


class Service:
class Service(ABC):
def __init__(self, host: str, api_key: str, api_secret: str):
self._client = TwirpClient(host, "livekit")
self.api_key = api_key
Expand All @@ -20,3 +19,6 @@ def _auth_header(self, grants: VideoGrants) -> Dict[str, str]:
headers = {}
headers[AUTHORIZATION] = "Bearer {}".format(token)
return headers

async def aclose(self):
await self._client.aclose()
8 changes: 4 additions & 4 deletions livekit-api/livekit/api/_twirp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Dict, Optional, Type, TypeVar
from typing import Dict, Type, TypeVar

import aiohttp
from google.protobuf.message import Message
Expand Down Expand Up @@ -47,7 +47,7 @@ class TwirpErrorCode:
DATA_LOSS = "dataloss"


T = TypeVar('T', bound=Message, type=None)
T = TypeVar('T', bound=Message)


class TwirpClient:
Expand All @@ -63,7 +63,7 @@ async def request(
method: str,
data: Message,
headers: Dict[str, str],
response_class: Type[T] = None
response_class: Type[T],
) -> T:
url = f"{self.host}/{self.prefix}/{self.pkg}.{service}/{method}"
headers["Content-Type"] = "application/protobuf"
Expand All @@ -79,5 +79,5 @@ async def request(
error_data = await resp.json()
raise TwirpError(error_data["code"], error_data["msg"])

async def close(self):
async def aclose(self):
await self.session.close()
21 changes: 17 additions & 4 deletions livekit-api/livekit/api/access_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class VideoGrants:
# TrackSource types that a participant may publish.
# When set, it supercedes CanPublish. Only sources explicitly set here can be
# published
can_publish_sources: list[str] = [] # keys keep track of each source
can_publish_sources: list[str] = dataclasses.field(default_factory=list)

# by default, a participant is not allowed to update its own metadata
can_update_own_metadata: bool = False
Expand Down Expand Up @@ -99,14 +99,27 @@ def with_sha256(self, sha256: str) -> 'AccessToken':
return self

def to_jwt(self) -> str:
claims = {

def camel_case_dict(data) -> dict:
return {
"".join(
word if i == 0 else word.title() for i, word in enumerate(key.split("_"))
): value
for key, value in data
if value is not None
}

claims = dataclasses.asdict(self.claims)
claims.update({
'sub': self.identity,
"iss": self.api_key,
"nbf": calendar.timegm(datetime.datetime.utcnow().utctimetuple()),
"exp": calendar.timegm(
(datetime.datetime.utcnow() + self.ttl).utctimetuple()
),
}
"video": dataclasses.asdict(
self.claims.video, dict_factory=camel_case_dict
),
})

claims.update(dataclasses.asdict(self.claims))
return jwt.encode(claims, self.api_secret, algorithm='HS256')
7 changes: 3 additions & 4 deletions livekit-api/livekit/api/room_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from ._proto import livekit_models_pb2 as proto_models
from ._proto import livekit_room_pb2 as proto_room
from ._service import Service
Expand Down Expand Up @@ -53,13 +52,13 @@ async def get_participant(self, get: proto_room.RoomParticipantIdentity) \
return await self._client.request(SVC, "GetParticipant", get,
self._auth_header(
VideoGrants(room_admin=True,
jroom=get.room)),
room=get.room)),
proto_models.ParticipantInfo)

async def remove_participant(self, remove: proto_room.RoomParticipantIdentity) \
-> None:
-> proto_room.RemoveParticipantResponse:
return await self._client.request(SVC, "remove_participant", remove,
self._auth_header(
VideoGrants(room_admin=True,
room=remove.room)),
None)
proto_room.RemoveParticipantResponse)
40 changes: 40 additions & 0 deletions livekit-rtc/livekit/rtc/_event_emitter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Callable, Dict, Set, Optional, Generic, TypeVar

T = TypeVar('T')

class EventEmitter(Generic[T]):
def __init__(self):
self._events: Dict[T, Set[Callable]] = dict()

def emit(self, event: T, *args, **kwargs) -> None:
if event in self._events:
for callback in self._events[event]:
callback(*args, **kwargs)

def once(self, event: T, callback: Optional[Callable]) -> Callable:
if callback is not None:
def once_callback(*args, **kwargs):
self.off(event, once_callback)
callback(*args, **kwargs)
return self.on(event, once_callback)
else:
def decorator(callback: Callable) -> Callable:
self.once(event, callback)
return callback
return decorator

def on(self, event: T, callback: Optional[Callable]) -> Callable:
if callback is not None:
if event not in self._events:
self._events[event] = set()
self._events[event].add(callback)
return callback
else:
def decorator(callback: Callable) -> Callable:
self.on(event, callback)
return callback
return decorator

def off(self, event: T, callback: Callable) -> None:
if event in self._events:
self._events[event].remove(callback)
5 changes: 4 additions & 1 deletion livekit-rtc/livekit/rtc/_ffi_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,13 @@ def __init__(self, handle: int) -> None:
self.handle = handle

def __del__(self):
self.dispose()

def dispose(self) -> None:
if self.handle != INVALID_HANDLE:
assert ffi_lib.livekit_ffi_drop_handle(
ctypes.c_uint64(self.handle))

self.handle = INVALID_HANDLE

T = TypeVar('T')

Expand Down
Loading
Loading