mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-17 05:57:48 +00:00
7fd4d20ea7
Update python dependencies. Fix linting due to ruff bump. Co-Authored-By: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
199 lines
6.1 KiB
Python
199 lines
6.1 KiB
Python
"""Participants management service for LiveKit rooms."""
|
|
|
|
# pylint: disable=too-many-arguments,no-name-in-module,too-many-positional-arguments
|
|
# ruff: noqa:PLR0913
|
|
|
|
import json
|
|
import uuid
|
|
from logging import getLogger
|
|
from typing import Dict, Optional
|
|
|
|
from asgiref.sync import async_to_sync
|
|
from livekit.api import (
|
|
MuteRoomTrackRequest,
|
|
RoomParticipantIdentity,
|
|
TwirpError,
|
|
UpdateParticipantRequest,
|
|
)
|
|
from livekit.protocol.models import ParticipantInfo
|
|
|
|
from core import utils
|
|
|
|
from .lobby import LobbyService
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
class ParticipantsManagementException(Exception):
|
|
"""Exception raised when a participant management operations fail."""
|
|
|
|
|
|
class ParticipantNotFoundException(ParticipantsManagementException):
|
|
"""Raised when the target participant does not exist in the room."""
|
|
|
|
|
|
class ParticipantsManagement:
|
|
"""Service for managing participants."""
|
|
|
|
@async_to_sync
|
|
async def mute(self, room_name: str, identity: str, track_sid: str):
|
|
"""Mute a specific audio or video track for a participant in a room."""
|
|
|
|
lkapi = utils.create_livekit_client()
|
|
|
|
try:
|
|
await lkapi.room.mute_published_track(
|
|
MuteRoomTrackRequest(
|
|
room=room_name,
|
|
identity=identity,
|
|
track_sid=track_sid,
|
|
muted=True,
|
|
)
|
|
)
|
|
|
|
except TwirpError as e:
|
|
if e.code == "not_found":
|
|
logger.warning(
|
|
"Participant %s not found in room %s, skipping muting",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantNotFoundException("Participant does not exist") from e
|
|
|
|
logger.exception(
|
|
"Unexpected error muting participant %s for room %s",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantsManagementException("Could not mute participant") from e
|
|
|
|
finally:
|
|
await lkapi.aclose()
|
|
|
|
@async_to_sync
|
|
async def remove(self, room_name: str, identity: str):
|
|
"""Remove a participant from a room and clear their lobby cache."""
|
|
|
|
try:
|
|
LobbyService().clear_participant_cache(
|
|
room_id=uuid.UUID(room_name), participant_id=identity
|
|
)
|
|
except (ValueError, TypeError) as exc:
|
|
logger.warning(
|
|
"participants_management.remove: room_name '%s' is not a UUID; "
|
|
"skipping lobby cache clear",
|
|
room_name,
|
|
exc_info=exc,
|
|
)
|
|
|
|
lkapi = utils.create_livekit_client()
|
|
|
|
try:
|
|
await lkapi.room.remove_participant(
|
|
RoomParticipantIdentity(room=room_name, identity=identity)
|
|
)
|
|
except TwirpError as e:
|
|
if e.code == "not_found":
|
|
logger.warning(
|
|
"Participant %s not found in room %s, skipping removing",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantNotFoundException("Participant does not exist") from e
|
|
|
|
logger.exception(
|
|
"Unexpected error removing participant %s for room %s",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantsManagementException("Could not remove participant") from e
|
|
|
|
finally:
|
|
await lkapi.aclose()
|
|
|
|
@async_to_sync
|
|
async def update( # noqa: PLR0917
|
|
self,
|
|
room_name: str,
|
|
identity: str,
|
|
metadata: Optional[Dict] = None,
|
|
attributes: Optional[Dict] = None,
|
|
permission: Optional[Dict] = None,
|
|
name: Optional[str] = None,
|
|
):
|
|
"""Update participant properties such as metadata, attributes, permissions, or name."""
|
|
|
|
lkapi = utils.create_livekit_client()
|
|
|
|
try:
|
|
await lkapi.room.update_participant(
|
|
UpdateParticipantRequest(
|
|
room=room_name,
|
|
identity=identity,
|
|
metadata=json.dumps(metadata),
|
|
permission=permission,
|
|
attributes=attributes,
|
|
name=name,
|
|
)
|
|
)
|
|
|
|
except TwirpError as e:
|
|
if e.code == "not_found":
|
|
logger.warning(
|
|
"Participant %s not found in room %s, skipping update",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantNotFoundException("Participant does not exist") from e
|
|
|
|
logger.exception(
|
|
"Unexpected error updating participant %s for room %s",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantsManagementException("Could not update participant") from e
|
|
|
|
finally:
|
|
await lkapi.aclose()
|
|
|
|
@async_to_sync
|
|
async def check_if_in_meeting(self, room_name: str, identity: str) -> bool:
|
|
"""Check whether `identity` is currently a participant in `room_name`.
|
|
|
|
Raises ParticipantsManagementException for unexpected LiveKit errors
|
|
so callers can fail closed rather than silently allowing the action.
|
|
"""
|
|
|
|
if not room_name or not identity:
|
|
return False
|
|
|
|
lkapi = utils.create_livekit_client()
|
|
|
|
try:
|
|
participant = await lkapi.room.get_participant(
|
|
RoomParticipantIdentity(
|
|
room=room_name,
|
|
identity=identity,
|
|
)
|
|
)
|
|
except TwirpError as e:
|
|
if e.code == "not_found":
|
|
raise ParticipantNotFoundException("Participant does not exist") from e
|
|
|
|
logger.exception(
|
|
"Unexpected error checking participant %s in room %s",
|
|
identity,
|
|
room_name,
|
|
)
|
|
raise ParticipantsManagementException(
|
|
"Could not verify participant presence"
|
|
) from e
|
|
|
|
finally:
|
|
await lkapi.aclose()
|
|
|
|
return (
|
|
participant is not None
|
|
and participant.state != ParticipantInfo.State.DISCONNECTED
|
|
)
|