diff --git a/docs/jitsi.md b/docs/jitsi.md new file mode 100644 index 00000000..f537f964 --- /dev/null +++ b/docs/jitsi.md @@ -0,0 +1,369 @@ +# Jitsi Integration for Reflector + +This document contains research and planning notes for integrating Jitsi Meet as a replacement for Whereby in Reflector. + +## Overview + +Jitsi Meet is an open-source video conferencing solution that can replace Whereby in Reflector, providing: +- Cost reduction (no per-minute charges) +- Direct recording access via Jibri +- Real-time event webhooks +- Full customization and control + +## Current Whereby Integration Analysis + +### Architecture +1. **Room Creation**: User creates a "room" template in Reflector DB with settings +2. **Meeting Creation**: `/rooms/{room_name}/meeting` endpoint calls Whereby API to create meeting +3. **Recording**: Whereby handles recording automatically to S3 bucket +4. **Webhooks**: Whereby sends events for participant tracking + +### Database Structure +```python +# Room = Template/Configuration +class Room: + id, name, user_id + recording_type, recording_trigger # cloud, automatic-2nd-participant + webhook_url, webhook_secret + +# Meeting = Actual Whereby Meeting Instance +class Meeting: + id # Whereby meetingId + room_name # Generated by Whereby + room_url, host_room_url # Whereby URLs + num_clients # Updated via webhooks +``` + +## Jitsi Components + +### Core Architecture +- **Jitsi Meet**: Web frontend (Next.js + React) +- **Prosody**: XMPP server for messaging/rooms +- **Jicofo**: Conference focus (orchestration) +- **JVB**: Videobridge (media routing) +- **Jibri**: Recording service +- **Jigasi**: SIP gateway (optional, for phone dial-in) + +### Exposure Requirements +- **Web service**: 443/80 (frontend) +- **JVB**: 10000/UDP (media streams) - **MUST EXPOSE** +- **Prosody**: 5280 (BOSH/WebSocket) - can proxy via web +- **Jicofo, Jibri, Jigasi**: Internal only + +## Recording with Jibri + +### How Jibri Works +- Each Jibri instance handles **one recording at a time** +- Records mixed audio/video to MP4 format +- Uses Chrome headless + ffmpeg for capture +- Supports finalize scripts for post-processing + +### Jibri Pool for Scaling +- Multiple Jibri instances join "jibribrewery" MUC +- Jicofo distributes recording requests to available instances +- Automatic load balancing and failover + +```yaml +# Multiple Jibri instances +jibri1: + environment: + - JIBRI_INSTANCE_ID=jibri1 + - JIBRI_BREWERY_MUC=jibribrewery + +jibri2: + environment: + - JIBRI_INSTANCE_ID=jibri2 + - JIBRI_BREWERY_MUC=jibribrewery +``` + +### Recording Automation Options +1. **Environment Variables**: `ENABLE_RECORDING=1`, `AUTO_RECORDING=1` +2. **URL Parameters**: `?config.autoRecord=true` +3. **JWT Token**: Include recording permissions in JWT +4. **API Control**: `api.executeCommand('startRecording')` + +### Post-Processing Integration +```bash +#!/bin/bash +# finalize.sh - runs after recording completion +RECORDING_FILE=$1 +MEETING_METADATA=$2 +ROOM_NAME=$3 + +# Copy to Reflector-accessible location +cp "$RECORDING_FILE" /shared/reflector-uploads/ + +# Trigger Reflector processing +curl -X POST "http://reflector-api:8000/v1/transcripts/process" \ + -H "Content-Type: application/json" \ + -d "{ + \"file_path\": \"/shared/reflector-uploads/$(basename $RECORDING_FILE)\", + \"room_name\": \"$ROOM_NAME\", + \"source\": \"jitsi\" + }" +``` + +## React Integration + +### Official React SDK +```bash +npm i @jitsi/react-sdk +``` + +```jsx +import { JitsiMeeting } from '@jitsi/react-sdk' + + { + // Track participant events + }} + onRecordingStatusChanged={(status) => { + // Handle recording events + }} +/> +``` + +## Authentication & Room Control + +### JWT-Based Access Control +```python +def generate_jitsi_jwt(payload): + return jwt.encode({ + "aud": "jitsi", + "iss": "reflector", + "sub": "reflector-user", + "room": payload["room"], + "exp": int(payload["exp"].timestamp()), + "context": { + "user": { + "name": payload["user_name"], + "moderator": payload.get("moderator", False) + }, + "features": { + "recording": payload.get("recording", True) + } + } + }, JITSI_JWT_SECRET) +``` + +### Prevent Anonymous Room Creation +```bash +# Environment configuration +ENABLE_AUTH=1 +ENABLE_GUESTS=0 +AUTH_TYPE=jwt +JWT_APP_ID=reflector +JWT_APP_SECRET=your-secret-key +``` + +## Webhook Integration + +### Real-time Events via Prosody +Custom event-sync module can send webhooks for: +- Participant join/leave +- Recording start/stop +- Room creation/destruction +- Mute/unmute events + +```lua +-- mod_event_sync.lua +module:hook("muc-occupant-joined", function(event) + send_event({ + type = "participant_joined", + room = event.room.jid, + participant = { + nick = event.occupant.nick, + jid = event.occupant.jid, + }, + timestamp = os.time(), + }); +end); +``` + +### Jibri Recording Webhooks +```bash +# Environment variable +JIBRI_WEBHOOK_SUBSCRIBERS=https://your-reflector.com/webhooks/jibri +``` + +## Proposed Reflector Integration + +### Modified Database Schema +```python +class Meeting(BaseModel): + id: str # Our generated meeting ID + room_name: str # Generated: reflector-{room.name}-{timestamp} + room_url: str # https://jitsi.domain/room_name?jwt=token + host_room_url: str # Same but with moderator JWT + # Add Jitsi-specific fields + jitsi_jwt: str # JWT token + jitsi_room_id: str # Internal room identifier + recording_status: str # pending, recording, completed + recording_file_path: Optional[str] +``` + +### API Replacement +```python +# Replace whereby.py with jitsi.py +async def create_meeting(room_name_prefix: str, end_date: datetime, room: Room): + # Generate unique room name + jitsi_room = f"reflector-{room.name}-{int(time.time())}" + + # Generate JWT tokens + user_jwt = generate_jwt(room=jitsi_room, moderator=False, exp=end_date) + host_jwt = generate_jwt(room=jitsi_room, moderator=True, exp=end_date) + + return { + "meetingId": generate_uuid4(), # Our ID + "roomName": jitsi_room, + "roomUrl": f"https://jitsi.domain/{jitsi_room}?jwt={user_jwt}", + "hostRoomUrl": f"https://jitsi.domain/{jitsi_room}?jwt={host_jwt}", + "startDate": datetime.now().isoformat(), + "endDate": end_date.isoformat(), + } +``` + +### Webhook Endpoints +```python +# Replace whereby webhook with jitsi webhooks +@router.post("/jitsi/events") +async def jitsi_events_webhook(event_data: dict): + event_type = event_data.get("event") + room_name = event_data.get("room", "").split("@")[0] + + meeting = await Meeting.get_by_room(room_name) + + if event_type == "muc-occupant-joined": + # Update participant count + meeting.num_clients += 1 + + elif event_type == "jibri-recording-on": + meeting.recording_status = "recording" + + elif event_type == "jibri-recording-off": + meeting.recording_status = "processing" + await process_meeting_recording.delay(meeting.id) + +@router.post("/jibri/recording-complete") +async def recording_complete(data: dict): + # Handle finalize script webhook + room_name = data.get("room_name") + file_path = data.get("file_path") + + meeting = await Meeting.get_by_room(room_name) + meeting.recording_file_path = file_path + meeting.recording_status = "completed" + + # Start Reflector processing + await process_recording_for_transcription(meeting.id, file_path) +``` + +## Deployment with Docker + +### Official docker-jitsi-meet +```bash +# Download official release +wget $(wget -q -O - https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep zip | cut -d\" -f4) + +# Setup +mkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri} +./gen-passwords.sh # Generate secure passwords +docker compose up -d +``` + +### Coolify Integration +```yaml +services: + web: + ports: ["80:80", "443:443"] + jvb: + ports: ["10000:10000/udp"] # Must expose for media + jibri1: + environment: + - JIBRI_INSTANCE_ID=jibri1 + - JIBRI_FINALIZE_RECORDING_SCRIPT_PATH=/config/finalize.sh + jibri2: + environment: + - JIBRI_INSTANCE_ID=jibri2 +``` + +## Benefits vs Whereby + +### Cost & Control +✅ **No per-minute charges** - significant cost savings +✅ **Full recording control** - direct file access +✅ **Custom branding** - complete UI control +✅ **Self-hosted** - no vendor lock-in + +### Technical Advantages +✅ **Real-time events** - immediate webhook notifications +✅ **Rich participant metadata** - detailed tracking +✅ **JWT security** - token-based access with expiration +✅ **Multiple recording formats** - audio-only options +✅ **Scalable architecture** - horizontal Jibri scaling + +### Integration Benefits +✅ **Same API surface** - minimal changes to existing code +✅ **React SDK** - better frontend integration +✅ **Direct processing** - no S3 download delays +✅ **Event-driven architecture** - better real-time capabilities + +## Implementation Plan + +1. **Deploy Jitsi Stack** - Set up docker-jitsi-meet with multiple Jibri instances +2. **Create jitsi.py** - Replace whereby.py with Jitsi API functions +3. **Update Database** - Add Jitsi-specific fields to Meeting model +4. **Webhook Integration** - Replace Whereby webhooks with Jitsi events +5. **Frontend Updates** - Replace Whereby embed with Jitsi React SDK +6. **Testing & Migration** - Gradual rollout with fallback to Whereby + +## Recording Limitations & Considerations + +### Current Limitations +- **Mixed audio only** - Jibri doesn't separate participant tracks natively +- **One recording per Jibri** - requires multiple instances for concurrent recordings +- **Chrome dependency** - Jibri uses headless Chrome for recording + +### Metadata Capabilities +✅ **Participant join/leave timestamps** - via webhooks +✅ **Speaking time tracking** - via audio level events +✅ **Meeting duration** - precise timing +✅ **Room-specific data** - custom metadata in JWT + +### Alternative Recording Methods +- **Local recording** - browser-based, per-participant +- **Custom recording** - lib-jitsi-meet for individual streams +- **Third-party solutions** - Recall.ai, Otter.ai integrations + +## Security Considerations + +### JWT Configuration +- **Room-specific tokens** - limit access to specific rooms +- **Time-based expiration** - automatic cleanup +- **Feature permissions** - control recording, moderation rights +- **User identification** - embed user metadata in tokens + +### Access Control +- **No anonymous rooms** - all rooms require valid JWT +- **API-only creation** - prevent direct room access +- **Webhook verification** - HMAC signature validation + +## Next Steps + +1. **Deploy test Jitsi instance** - validate recording pipeline +2. **Prototype jitsi.py** - create equivalent API functions +3. **Test webhook integration** - ensure event delivery works +4. **Performance testing** - validate multiple concurrent recordings +5. **Migration strategy** - plan gradual transition from Whereby + +--- + +*This document serves as the comprehensive planning and research notes for Jitsi integration in Reflector. It should be updated as implementation progresses and new insights are discovered.* \ No newline at end of file diff --git a/docs/video_platforms.md b/docs/video_platforms.md new file mode 100644 index 00000000..05883c24 --- /dev/null +++ b/docs/video_platforms.md @@ -0,0 +1,474 @@ +# Video Platforms Architecture (PR #529 Analysis) + +This document analyzes the video platforms refactoring implemented in PR #529 for daily.co integration, providing a blueprint for extending support to Jitsi and other video conferencing platforms. + +## Overview + +The video platforms refactoring introduces a clean abstraction layer that allows Reflector to support multiple video conferencing providers (Whereby, Daily.co, etc.) without changing core application logic. This architecture enables: + +- Seamless switching between video platforms +- Platform-specific feature support +- Isolated platform code organization +- Consistent API surface across platforms +- Feature flags for gradual migration + +## Architecture Components + +### 1. **Directory Structure** + +``` +server/reflector/video_platforms/ +├── __init__.py # Public API exports +├── base.py # Abstract base classes +├── factory.py # Platform client factory +├── registry.py # Platform registration system +├── whereby.py # Whereby implementation +├── daily.py # Daily.co implementation +└── mock.py # Testing implementation +``` + +### 2. **Core Abstract Classes** + +#### `VideoPlatformClient` (base.py) +Abstract base class defining the interface all platforms must implement: + +```python +class VideoPlatformClient(ABC): + PLATFORM_NAME: str = "" + + @abstractmethod + async def create_meeting(self, room_name_prefix: str, end_date: datetime, room: Room) -> MeetingData + + @abstractmethod + async def get_room_sessions(self, room_name: str) -> Dict[str, Any] + + @abstractmethod + async def delete_room(self, room_name: str) -> bool + + @abstractmethod + async def upload_logo(self, room_name: str, logo_path: str) -> bool + + @abstractmethod + def verify_webhook_signature(self, body: bytes, signature: str, timestamp: Optional[str] = None) -> bool +``` + +#### `MeetingData` (base.py) +Standardized meeting data structure returned by all platforms: + +```python +class MeetingData(BaseModel): + meeting_id: str + room_name: str + room_url: str + host_room_url: str + platform: str + extra_data: Dict[str, Any] = {} # Platform-specific data +``` + +#### `VideoPlatformConfig` (base.py) +Unified configuration structure for all platforms: + +```python +class VideoPlatformConfig(BaseModel): + 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 +``` + +### 3. **Platform Registration System** + +#### Registry Pattern (registry.py) +- Automatic registration of built-in platforms +- Runtime platform discovery +- Type-safe client instantiation + +```python +# Auto-registration of platforms +_PLATFORMS: Dict[str, Type[VideoPlatformClient]] = {} + +def register_platform(name: str, client_class: Type[VideoPlatformClient]) +def get_platform_client(platform: str, config: VideoPlatformConfig) -> VideoPlatformClient +``` + +#### Factory System (factory.py) +- Configuration management per platform +- Platform selection logic +- Feature flag integration + +```python +def get_platform_for_room(room_id: Optional[str] = None) -> str: + """Determine which platform to use based on feature flags.""" + if not settings.DAILY_MIGRATION_ENABLED: + return "whereby" + + if room_id and room_id in settings.DAILY_MIGRATION_ROOM_IDS: + return "daily" + + return settings.DEFAULT_VIDEO_PLATFORM +``` + +### 4. **Database Schema Changes** + +#### Room Model Updates +Added `platform` field to track which video platform each room uses: + +```python +# Database Schema +platform_column = sqlalchemy.Column( + "platform", + sqlalchemy.String, + nullable=False, + server_default="whereby" +) + +# Pydantic Model +class Room(BaseModel): + platform: Literal["whereby", "daily"] = "whereby" +``` + +#### Meeting Model Updates +Added `platform` field to meetings for tracking and debugging: + +```python +# Database Schema +platform_column = sqlalchemy.Column( + "platform", + sqlalchemy.String, + nullable=False, + server_default="whereby" +) + +# Pydantic Model +class Meeting(BaseModel): + platform: Literal["whereby", "daily"] = "whereby" +``` + +**Key Decision**: No platform-specific fields were added to models. Instead, the `extra_data` field in `MeetingData` handles platform-specific information, following the user's rule of using generic `provider_data` as JSON if needed. + +### 5. **Settings Configuration** + +#### Feature Flags +```python +# Migration control +DAILY_MIGRATION_ENABLED: bool = True +DAILY_MIGRATION_ROOM_IDS: list[str] = [] +DEFAULT_VIDEO_PLATFORM: str = "daily" + +# Daily.co specific settings +DAILY_API_KEY: str | None = None +DAILY_WEBHOOK_SECRET: str | None = None +DAILY_SUBDOMAIN: str | None = None +AWS_DAILY_S3_BUCKET: str | None = None +AWS_DAILY_S3_REGION: str = "us-west-2" +AWS_DAILY_ROLE_ARN: str | None = None +``` + +#### Configuration Pattern +Each platform gets its own configuration namespace while sharing common patterns: + +```python +def get_platform_config(platform: str) -> VideoPlatformConfig: + if platform == "whereby": + return VideoPlatformConfig( + api_key=settings.WHEREBY_API_KEY or "", + webhook_secret=settings.WHEREBY_WEBHOOK_SECRET or "", + # ... whereby-specific config + ) + elif platform == "daily": + return VideoPlatformConfig( + api_key=settings.DAILY_API_KEY or "", + webhook_secret=settings.DAILY_WEBHOOK_SECRET or "", + # ... daily-specific config + ) +``` + +### 6. **API Integration Updates** + +#### Room Creation (views/rooms.py) +Updated to use platform factory instead of direct Whereby calls: + +```python +@router.post("/rooms/{room_name}/meeting") +async def rooms_create_meeting(room_name: str, user: UserInfo): + # OLD: Direct Whereby integration + # whereby_meeting = await create_meeting("", end_date=end_date, room=room) + + # NEW: Platform abstraction + platform = get_platform_for_room(room.id) + client = create_platform_client(platform) + + meeting_data = await client.create_meeting( + room_name_prefix=room.name, end_date=end_date, room=room + ) + + await client.upload_logo(meeting_data.room_name, "./images/logo.png") +``` + +### 7. **Webhook Handling** + +#### Separate Webhook Endpoints +Each platform gets its own webhook endpoint with platform-specific signature verification: + +```python +# views/daily.py +@router.post("/daily_webhook") +async def daily_webhook(event: DailyWebhookEvent, request: Request): + # Verify Daily.co signature + body = await request.body() + signature = request.headers.get("X-Daily-Signature", "") + + if not verify_daily_webhook_signature(body, signature): + raise HTTPException(status_code=401) + + # Handle platform-specific events + if event.type == "participant.joined": + await _handle_participant_joined(event) +``` + +#### Consistent Event Handling +Despite different event formats, the core business logic remains the same: + +```python +async def _handle_participant_joined(event): + room_name = event.data.get("room", {}).get("name") # Daily.co format + meeting = await meetings_controller.get_by_room_name(room_name) + if meeting: + current_count = getattr(meeting, "num_clients", 0) + await meetings_controller.update_meeting( + meeting.id, num_clients=current_count + 1 + ) +``` + +### 8. **Worker Task Integration** + +#### New Task for Daily.co Recording Processing +Added platform-specific recording processing while maintaining the same pipeline: + +```python +@shared_task +@asynctask +async def process_recording_from_url(recording_url: str, meeting_id: str, recording_id: str): + """Process recording from Direct URL (Daily.co webhook).""" + logger.info("Processing recording from URL for meeting: %s", meeting_id) + # Uses same processing pipeline as Whereby S3 recordings +``` + +**Key Decision**: Worker tasks remain in main worker module but could be moved to platform-specific folders as suggested by the user. + +### 9. **Testing Infrastructure** + +#### Comprehensive Test Suite +- Unit tests for each platform client +- Integration tests for platform switching +- Mock platform for testing without external dependencies +- Webhook signature verification tests + +```python +class TestPlatformIntegration: + """Integration tests for platform switching.""" + + async def test_platform_switching_preserves_interface(self): + """Test that different platforms provide consistent interface.""" + # Test both Mock and Daily platforms return MeetingData objects + # with consistent fields +``` + +## Implementation Patterns for Jitsi Integration + +Based on the daily.co implementation, here's how Jitsi should be integrated: + +### 1. **Jitsi Client Implementation** + +```python +# video_platforms/jitsi.py +class JitsiClient(VideoPlatformClient): + PLATFORM_NAME = "jitsi" + + async def create_meeting(self, room_name_prefix: str, end_date: datetime, room: Room) -> MeetingData: + # Generate unique room name + jitsi_room = f"reflector-{room.name}-{int(time.time())}" + + # Generate JWT tokens + user_jwt = self._generate_jwt(room=jitsi_room, moderator=False, exp=end_date) + host_jwt = self._generate_jwt(room=jitsi_room, moderator=True, exp=end_date) + + return MeetingData( + meeting_id=generate_uuid4(), + room_name=jitsi_room, + room_url=f"https://jitsi.domain/{jitsi_room}?jwt={user_jwt}", + host_room_url=f"https://jitsi.domain/{jitsi_room}?jwt={host_jwt}", + platform=self.PLATFORM_NAME, + extra_data={"user_jwt": user_jwt, "host_jwt": host_jwt} + ) +``` + +### 2. **Settings Integration** + +```python +# settings.py +JITSI_DOMAIN: str = "meet.jit.si" +JITSI_JWT_SECRET: str | None = None +JITSI_WEBHOOK_SECRET: str | None = None +JITSI_API_URL: str | None = None # If using Jitsi API +``` + +### 3. **Factory Registration** + +```python +# registry.py +def _register_builtin_platforms(): + from .jitsi import JitsiClient + register_platform("jitsi", JitsiClient) + +# factory.py +def get_platform_config(platform: str) -> VideoPlatformConfig: + elif platform == "jitsi": + return VideoPlatformConfig( + api_key="", # Jitsi may not need API key + webhook_secret=settings.JITSI_WEBHOOK_SECRET or "", + api_url=settings.JITSI_API_URL, + ) +``` + +### 4. **Webhook Integration** + +```python +# views/jitsi.py +@router.post("/jitsi/events") +async def jitsi_events_webhook(event_data: dict): + # Handle Prosody event-sync webhook format + event_type = event_data.get("event") + room_name = event_data.get("room", "").split("@")[0] + + if event_type == "muc-occupant-joined": + # Same participant handling logic as other platforms +``` + +## Key Benefits of This Architecture + +### 1. **Isolation and Organization** +- Platform-specific code contained in separate modules +- No platform logic leaking into core application +- Easy to add/remove platforms without affecting others + +### 2. **Consistent Interface** +- All platforms implement the same abstract methods +- Standardized `MeetingData` structure +- Uniform error handling and logging + +### 3. **Gradual Migration Support** +- Feature flags for controlled rollouts +- Room-specific platform selection +- Fallback mechanisms for platform failures + +### 4. **Configuration Management** +- Centralized settings per platform +- Consistent naming patterns +- Environment-based configuration + +### 5. **Testing and Quality** +- Mock platform for testing +- Comprehensive test coverage +- Platform-specific test utilities + +## Migration Strategy Applied + +The daily.co implementation demonstrates a careful migration approach: + +### 1. **Backward Compatibility** +- Default platform remains "whereby" +- Existing rooms continue using Whereby unless explicitly migrated +- Same API endpoints and response formats + +### 2. **Feature Flag Control** +```python +# Gradual rollout control +DAILY_MIGRATION_ENABLED: bool = True +DAILY_MIGRATION_ROOM_IDS: list[str] = [] # Specific rooms to migrate +DEFAULT_VIDEO_PLATFORM: str = "daily" # New rooms default +``` + +### 3. **Data Integrity** +- Platform field tracks which service each room/meeting uses +- No data loss during migration +- Platform-specific data preserved in `extra_data` + +### 4. **Monitoring and Rollback** +- Comprehensive logging of platform selection +- Easy rollback by changing feature flags +- Platform-specific error tracking + +## Recommendations for Jitsi Integration + +Based on this analysis and the user's requirements: + +### 1. **Follow the Pattern** +- Create `video_platforms/jitsi/` directory with: + - `client.py` - Main JitsiClient implementation + - `tasks.py` - Jitsi-specific worker tasks + - `__init__.py` - Module exports + +### 2. **Settings Organization** +- Use `JITSI_*` prefix for all Jitsi settings +- Follow the same configuration pattern as Daily.co +- Support both environment variables and config files + +### 3. **Generic Database Fields** +- Avoid platform-specific columns in database +- Use `provider_data` JSON field if platform-specific data needed +- Keep `platform` field as simple string identifier + +### 4. **Worker Task Migration** +According to user requirements, migrate platform-specific tasks: +``` +video_platforms/ +├── whereby/ +│ ├── client.py (moved from whereby.py) +│ └── tasks.py (moved from worker/whereby_tasks.py) +├── daily/ +│ ├── client.py (moved from daily.py) +│ └── tasks.py (moved from worker/daily_tasks.py) +└── jitsi/ + ├── client.py (new JitsiClient) + └── tasks.py (new Jitsi recording tasks) +``` + +### 5. **Webhook Architecture** +- Create `views/jitsi.py` for Jitsi-specific webhooks +- Follow the same signature verification pattern +- Reuse existing participant tracking logic + +## Implementation Checklist for Jitsi + +- [ ] Create `video_platforms/jitsi/` directory structure +- [ ] Implement `JitsiClient` following the abstract interface +- [ ] Add Jitsi settings to configuration +- [ ] Register Jitsi platform in factory/registry +- [ ] Create Jitsi webhook endpoint +- [ ] Implement JWT token generation for room access +- [ ] Add Jitsi recording processing tasks +- [ ] Create comprehensive test suite +- [ ] Update database migrations for platform field +- [ ] Document Jitsi-specific configuration + +## Conclusion + +The video platforms refactoring in PR #529 provides an excellent foundation for adding Jitsi support. The architecture is well-designed with clear separation of concerns, consistent interfaces, and excellent extensibility. The daily.co implementation demonstrates how to add a new platform while maintaining backward compatibility and providing gradual migration capabilities. + +The pattern should be directly applicable to Jitsi integration, with the main differences being: +- JWT-based authentication instead of API keys +- Different webhook event formats +- Jibri recording pipeline integration +- Self-hosted deployment considerations + +This architecture successfully achieves the user's goals of: +1. Settings-based configuration +2. Generic database fields (no provider-specific columns) +3. Platform isolation in separate directories +4. Worker task organization within platform folders \ No newline at end of file diff --git a/server/reflector/video_platforms/registry.py b/server/reflector/video_platforms/registry.py index eee9c656..a080b7bf 100644 --- a/server/reflector/video_platforms/registry.py +++ b/server/reflector/video_platforms/registry.py @@ -30,8 +30,9 @@ def get_available_platforms() -> list[str]: # Auto-register built-in platforms def _register_builtin_platforms(): - # Will be populated as we add platforms - pass + from .jitsi import JitsiClient + + register_platform("jitsi", JitsiClient) _register_builtin_platforms()