mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 12:19:06 +00:00
* llm instructions * vibe dailyco * vibe dailyco * doc update (vibe) * dont show recording ui on call * stub processor (vibe) * stub processor (vibe) self-review * stub processor (vibe) self-review * chore(main): release 0.14.0 (#670) * Add multitrack pipeline * Mixdown audio tracks * Mixdown with pyav filter graph * Trigger multitrack processing for daily recordings * apply platform from envs in priority: non-dry * Use explicit track keys for processing * Align tracks of a multitrack recording * Generate waveforms for the mixed audio * Emit multriack pipeline events * Fix multitrack pipeline track alignment * dailico docs * Enable multitrack reprocessing * modal temp files uniform names, cleanup. remove llm temporary docs * docs cleanup * dont proceed with raw recordings if any of the downloads fail * dry transcription pipelines * remove is_miltitrack * comments * explicit dailyco room name * docs * remove stub data/method * frontend daily/whereby code self-review (no-mistake) * frontend daily/whereby code self-review (no-mistakes) * frontend daily/whereby code self-review (no-mistakes) * consent cleanup for multitrack (no-mistakes) * llm fun * remove extra comments * fix tests * merge migrations * Store participant names * Get participants by meeting session id * pop back main branch migration * s3 paddington (no-mistakes) * comment * pr comments * pr comments * pr comments * platform / meeting cleanup * Use participant names in summary generation * platform assignment to meeting at controller level * pr comment * room playform properly default none * room playform properly default none * restore migration lost * streaming WIP * extract storage / use common storage / proper env vars for storage * fix mocks tests * remove fall back * streaming for multifile * cenrtal storage abstraction (no-mistakes) * remove dead code / vars * Set participant user id for authenticated users * whereby recording name parsing fix * whereby recording name parsing fix * more file stream * storage dry + tests * remove homemade boto3 streaming and use proper boto * update migration guide * webhook creation script - print uuid --------- Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com> Co-authored-by: Mathieu Virbel <mat@meltingrocks.com> Co-authored-by: Sergey Mankovsky <sergey@monadical.com>
142 lines
4.6 KiB
Python
142 lines
4.6 KiB
Python
import hmac
|
|
import json
|
|
import re
|
|
import time
|
|
from datetime import datetime
|
|
from hashlib import sha256
|
|
from typing import Any, Dict, Optional
|
|
|
|
import httpx
|
|
|
|
from reflector.db.rooms import Room
|
|
from reflector.storage import get_whereby_storage
|
|
|
|
from ..schemas.platform import WHEREBY_PLATFORM, Platform
|
|
from ..utils.string import NonEmptyString
|
|
from .base import (
|
|
MeetingData,
|
|
VideoPlatformClient,
|
|
VideoPlatformConfig,
|
|
)
|
|
from .whereby_utils import whereby_room_name_prefix
|
|
|
|
|
|
class WherebyClient(VideoPlatformClient):
|
|
PLATFORM_NAME: Platform = WHEREBY_PLATFORM
|
|
TIMEOUT = 10 # seconds
|
|
MAX_ELAPSED_TIME = 60 * 1000 # 1 minute in milliseconds
|
|
|
|
def __init__(self, config: VideoPlatformConfig):
|
|
super().__init__(config)
|
|
self.headers = {
|
|
"Content-Type": "application/json; charset=utf-8",
|
|
"Authorization": f"Bearer {config.api_key}",
|
|
}
|
|
|
|
async def create_meeting(
|
|
self, room_name_prefix: NonEmptyString, end_date: datetime, room: Room
|
|
) -> MeetingData:
|
|
data = {
|
|
"isLocked": room.is_locked,
|
|
"roomNamePrefix": whereby_room_name_prefix(room_name_prefix),
|
|
"roomNamePattern": "uuid",
|
|
"roomMode": room.room_mode,
|
|
"endDate": end_date.isoformat(),
|
|
"fields": ["hostRoomUrl"],
|
|
}
|
|
|
|
if room.recording_type == "cloud":
|
|
# Get storage config for passing credentials to Whereby API
|
|
whereby_storage = get_whereby_storage()
|
|
key_id, secret = whereby_storage.key_credentials
|
|
data["recording"] = {
|
|
"type": room.recording_type,
|
|
"destination": {
|
|
"provider": "s3",
|
|
"bucket": whereby_storage.bucket_name,
|
|
"accessKeyId": key_id,
|
|
"accessKeySecret": secret,
|
|
"fileFormat": "mp4",
|
|
},
|
|
"startTrigger": room.recording_trigger,
|
|
}
|
|
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.post(
|
|
f"{self.config.api_url}/meetings",
|
|
headers=self.headers,
|
|
json=data,
|
|
timeout=self.TIMEOUT,
|
|
)
|
|
response.raise_for_status()
|
|
result = response.json()
|
|
|
|
return MeetingData(
|
|
meeting_id=result["meetingId"],
|
|
room_name=result["roomName"],
|
|
room_url=result["roomUrl"],
|
|
host_room_url=result["hostRoomUrl"],
|
|
platform=self.PLATFORM_NAME,
|
|
extra_data=result,
|
|
)
|
|
|
|
async def get_room_sessions(self, room_name: str) -> Dict[str, Any]:
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(
|
|
f"{self.config.api_url}/insights/room-sessions?roomName={room_name}",
|
|
headers=self.headers,
|
|
timeout=self.TIMEOUT,
|
|
)
|
|
response.raise_for_status()
|
|
return response.json().get("results", [])
|
|
|
|
async def delete_room(self, room_name: str) -> bool:
|
|
return True
|
|
|
|
async def upload_logo(self, room_name: str, logo_path: str) -> bool:
|
|
async with httpx.AsyncClient() as client:
|
|
with open(logo_path, "rb") as f:
|
|
response = await client.put(
|
|
f"{self.config.api_url}/rooms/{room_name}/theme/logo",
|
|
headers={
|
|
"Authorization": f"Bearer {self.config.api_key}",
|
|
},
|
|
timeout=self.TIMEOUT,
|
|
files={"image": f},
|
|
)
|
|
response.raise_for_status()
|
|
return True
|
|
|
|
def verify_webhook_signature(
|
|
self, body: bytes, signature: str, timestamp: Optional[str] = None
|
|
) -> bool:
|
|
if not signature:
|
|
return False
|
|
|
|
matches = re.match(r"t=(.*),v1=(.*)", signature)
|
|
if not matches:
|
|
return False
|
|
|
|
ts, sig = matches.groups()
|
|
|
|
current_time = int(time.time() * 1000)
|
|
diff_time = current_time - int(ts) * 1000
|
|
if diff_time >= self.MAX_ELAPSED_TIME:
|
|
return False
|
|
|
|
body_dict = json.loads(body)
|
|
signed_payload = f"{ts}.{json.dumps(body_dict, separators=(',', ':'))}"
|
|
hmac_obj = hmac.new(
|
|
self.config.webhook_secret.encode("utf-8"),
|
|
signed_payload.encode("utf-8"),
|
|
sha256,
|
|
)
|
|
expected_signature = hmac_obj.hexdigest()
|
|
|
|
try:
|
|
return hmac.compare_digest(
|
|
expected_signature.encode("utf-8"), sig.encode("utf-8")
|
|
)
|
|
except Exception:
|
|
return False
|