59 lines
2.6 KiB
Python
59 lines
2.6 KiB
Python
"""Тонкая обёртка над LiveKit `EgressService.start_track_egress` — единственная точка для
|
||
мокирования в тестах (по образцу `workers/livekit_client.py::delete_livekit_room`).
|
||
|
||
Запускает Track Egress для одного аудиотрека: пишет исходный opus без
|
||
транскодирования в `.ogg` на общий volume `recordings_dir`. Финализация
|
||
результата (итоговый `location`/ошибка) приходит асинхронно через webhook
|
||
`egress_ended` — здесь только сам запуск и `egress_id`/`started_at` из ответа.
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import UTC, datetime
|
||
|
||
from livekit import api
|
||
|
||
from core.config import get_settings
|
||
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class EgressStartResult:
|
||
"""Результат запуска Track Egress: идентификатор задания и время старта (UTC)."""
|
||
|
||
egress_id: str
|
||
started_at: datetime
|
||
|
||
|
||
async def start_track_egress(room_name: str, track_sid: str, filepath: str) -> EgressStartResult:
|
||
"""Запустить запись одного аудиотрека комнаты в файл `filepath` на общем volume.
|
||
|
||
`DirectFileOutput` без указания облачного хранилища (s3/gcp/azure) пишет
|
||
файл напрямую на диск egress-контейнера — тот же volume `recordings_dir`,
|
||
что и у воркера транскрибации (см. `deploy/docker-compose.yml`).
|
||
"""
|
||
settings = get_settings()
|
||
lkapi = api.LiveKitAPI(
|
||
settings.livekit_url,
|
||
api_key=settings.livekit_api_key,
|
||
api_secret=settings.livekit_api_secret,
|
||
)
|
||
try:
|
||
info = await lkapi.egress.start_track_egress(
|
||
api.TrackEgressRequest(
|
||
room_name=room_name,
|
||
track_id=track_sid,
|
||
file=api.DirectFileOutput(filepath=filepath),
|
||
)
|
||
)
|
||
finally:
|
||
await lkapi.aclose()
|
||
|
||
# `EgressInfo.started_at` — unix-наносекунды (см. документацию livekit/egress,
|
||
# `pkg/config/manifest.go`); при отсутствии (ещё не проставлен на момент
|
||
# ответа STARTING) считаем стартом текущий момент.
|
||
started_at = (
|
||
datetime.fromtimestamp(info.started_at / 1_000_000_000, tz=UTC)
|
||
if info.started_at
|
||
else datetime.now(UTC)
|
||
)
|
||
return EgressStartResult(egress_id=info.egress_id, started_at=started_at)
|