"""Тонкая обёртка над 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)