mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
feat: create video platforms architecture with Jitsi directory structure
Create complete video platforms abstraction layer following daily.co branch pattern with Jitsi-specific directory structure. - Add video_platforms base module with abstract classes - Create VideoPlatformClient, MeetingData, VideoPlatformConfig interfaces - Add platform registry system for client management - Create factory pattern for platform client creation - Add Jitsi directory structure with __init__.py, tasks.py, client.py - Configure Jitsi platform in factory with JWT-based authentication 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
17
server/reflector/video_platforms/__init__.py
Normal file
17
server/reflector/video_platforms/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Video Platform Abstraction Layer
|
||||||
|
"""
|
||||||
|
This module provides an abstraction layer for different video conferencing platforms.
|
||||||
|
It allows seamless switching between providers (Whereby, Daily.co, etc.) without
|
||||||
|
changing the core application logic.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .base import MeetingData, VideoPlatformClient, VideoPlatformConfig
|
||||||
|
from .registry import get_platform_client, register_platform
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VideoPlatformClient",
|
||||||
|
"VideoPlatformConfig",
|
||||||
|
"MeetingData",
|
||||||
|
"get_platform_client",
|
||||||
|
"register_platform",
|
||||||
|
]
|
||||||
82
server/reflector/video_platforms/base.py
Normal file
82
server/reflector/video_platforms/base.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from reflector.db.rooms import Room
|
||||||
|
|
||||||
|
|
||||||
|
class MeetingData(BaseModel):
|
||||||
|
"""Standardized meeting data returned by all platforms."""
|
||||||
|
|
||||||
|
meeting_id: str
|
||||||
|
room_name: str
|
||||||
|
room_url: str
|
||||||
|
host_room_url: str
|
||||||
|
platform: str
|
||||||
|
extra_data: Dict[str, Any] = {} # Platform-specific data
|
||||||
|
|
||||||
|
|
||||||
|
class VideoPlatformConfig(BaseModel):
|
||||||
|
"""Configuration for a video platform."""
|
||||||
|
|
||||||
|
api_key: str
|
||||||
|
webhook_secret: str
|
||||||
|
api_url: Optional[str] = None
|
||||||
|
subdomain: Optional[str] = None
|
||||||
|
s3_bucket: Optional[str] = None
|
||||||
|
s3_region: Optional[str] = None
|
||||||
|
aws_role_arn: Optional[str] = None
|
||||||
|
aws_access_key_id: Optional[str] = None
|
||||||
|
aws_access_key_secret: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class VideoPlatformClient(ABC):
|
||||||
|
"""Abstract base class for video platform integrations."""
|
||||||
|
|
||||||
|
PLATFORM_NAME: str = ""
|
||||||
|
|
||||||
|
def __init__(self, config: VideoPlatformConfig):
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def create_meeting(
|
||||||
|
self, room_name_prefix: str, end_date: datetime, room: Room
|
||||||
|
) -> MeetingData:
|
||||||
|
"""Create a new meeting room."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def get_room_sessions(self, room_name: str) -> Dict[str, Any]:
|
||||||
|
"""Get session information for a room."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def delete_room(self, room_name: str) -> bool:
|
||||||
|
"""Delete a room. Returns True if successful."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def upload_logo(self, room_name: str, logo_path: str) -> bool:
|
||||||
|
"""Upload a logo to the room. Returns True if successful."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def verify_webhook_signature(
|
||||||
|
self, body: bytes, signature: str, timestamp: Optional[str] = None
|
||||||
|
) -> bool:
|
||||||
|
"""Verify webhook signature for security."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def format_recording_config(self, room: Room) -> Dict[str, Any]:
|
||||||
|
"""Format recording configuration for the platform.
|
||||||
|
Can be overridden by specific implementations."""
|
||||||
|
if room.recording_type == "cloud" and self.config.s3_bucket:
|
||||||
|
return {
|
||||||
|
"type": room.recording_type,
|
||||||
|
"bucket": self.config.s3_bucket,
|
||||||
|
"region": self.config.s3_region,
|
||||||
|
"trigger": room.recording_trigger,
|
||||||
|
}
|
||||||
|
return {"type": room.recording_type}
|
||||||
40
server/reflector/video_platforms/factory.py
Normal file
40
server/reflector/video_platforms/factory.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
"""Factory for creating video platform clients based on configuration."""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from reflector.settings import settings
|
||||||
|
|
||||||
|
from .base import VideoPlatformClient, VideoPlatformConfig
|
||||||
|
from .registry import get_platform_client
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_config(platform: str) -> VideoPlatformConfig:
|
||||||
|
"""Get configuration for a specific platform."""
|
||||||
|
if platform == "whereby":
|
||||||
|
return VideoPlatformConfig(
|
||||||
|
api_key=settings.WHEREBY_API_KEY or "",
|
||||||
|
webhook_secret=settings.WHEREBY_WEBHOOK_SECRET or "",
|
||||||
|
api_url=settings.WHEREBY_API_URL,
|
||||||
|
aws_access_key_id=settings.AWS_WHEREBY_ACCESS_KEY_ID,
|
||||||
|
aws_access_key_secret=settings.AWS_WHEREBY_ACCESS_KEY_SECRET,
|
||||||
|
)
|
||||||
|
elif platform == "jitsi":
|
||||||
|
return VideoPlatformConfig(
|
||||||
|
api_key="", # Jitsi uses JWT, no API key
|
||||||
|
webhook_secret=settings.JITSI_WEBHOOK_SECRET or "",
|
||||||
|
api_url=f"https://{settings.JITSI_DOMAIN}",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown platform: {platform}")
|
||||||
|
|
||||||
|
|
||||||
|
def create_platform_client(platform: str) -> VideoPlatformClient:
|
||||||
|
"""Create a video platform client instance."""
|
||||||
|
config = get_platform_config(platform)
|
||||||
|
return get_platform_client(platform, config)
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_for_room(room_id: Optional[str] = None) -> str:
|
||||||
|
"""Determine which platform to use for a room based on feature flags."""
|
||||||
|
# For now, default to whereby since we don't have feature flags yet
|
||||||
|
return "whereby"
|
||||||
3
server/reflector/video_platforms/jitsi/__init__.py
Normal file
3
server/reflector/video_platforms/jitsi/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .client import JitsiClient
|
||||||
|
|
||||||
|
__all__ = ["JitsiClient"]
|
||||||
1
server/reflector/video_platforms/jitsi/client.py
Normal file
1
server/reflector/video_platforms/jitsi/client.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# JitsiClient implementation - to be implemented in next task
|
||||||
3
server/reflector/video_platforms/jitsi/tasks.py
Normal file
3
server/reflector/video_platforms/jitsi/tasks.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
"""Jitsi-specific worker tasks."""
|
||||||
|
|
||||||
|
# Placeholder for Jitsi recording tasks
|
||||||
37
server/reflector/video_platforms/registry.py
Normal file
37
server/reflector/video_platforms/registry.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from typing import Dict, Type
|
||||||
|
|
||||||
|
from .base import VideoPlatformClient, VideoPlatformConfig
|
||||||
|
|
||||||
|
# Registry of available video platforms
|
||||||
|
_PLATFORMS: Dict[str, Type[VideoPlatformClient]] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def register_platform(name: str, client_class: Type[VideoPlatformClient]):
|
||||||
|
"""Register a video platform implementation."""
|
||||||
|
_PLATFORMS[name.lower()] = client_class
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_client(
|
||||||
|
platform: str, config: VideoPlatformConfig
|
||||||
|
) -> VideoPlatformClient:
|
||||||
|
"""Get a video platform client instance."""
|
||||||
|
platform_lower = platform.lower()
|
||||||
|
if platform_lower not in _PLATFORMS:
|
||||||
|
raise ValueError(f"Unknown video platform: {platform}")
|
||||||
|
|
||||||
|
client_class = _PLATFORMS[platform_lower]
|
||||||
|
return client_class(config)
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_platforms() -> list[str]:
|
||||||
|
"""Get list of available platform names."""
|
||||||
|
return list(_PLATFORMS.keys())
|
||||||
|
|
||||||
|
|
||||||
|
# Auto-register built-in platforms
|
||||||
|
def _register_builtin_platforms():
|
||||||
|
# Will be populated as we add platforms
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
_register_builtin_platforms()
|
||||||
Reference in New Issue
Block a user