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