from __future__ import annotations import logging from typing import Dict, Optional from flask import url_for from models import Display, EventDisplayMap, Video, db from sync import ActiveEventState, server_time_ms, set_trigger_event logger = logging.getLogger(__name__) def _build_assignments(event_id: int) -> Dict[str, str]: maps = ( db.session.query(EventDisplayMap) .filter_by(event_id=event_id) .join(Display, EventDisplayMap.display_id == Display.id) .join(Video, EventDisplayMap.video_id == Video.id) .all() ) assignments: Dict[str, str] = {} for m in maps: assignments[m.display.public_id] = url_for("main.media", filename=m.video.filename) return assignments def broadcast_trigger_state(state: ActiveEventState) -> None: # Lazy import to avoid circular import (sockets -> event_manager -> sockets) from sockets import socketio for public_id, video_url in state.assignments.items(): socketio.emit( "event_start", { "event_id": state.event_id, "event_name": state.name, "video_url": video_url, "start_time_ms": state.start_time_ms, "server_time_ms": server_time_ms(), }, room=f"display:{public_id}", ) def activate_trigger_event(event_id: int, start_ms: float, end_ms: float, name: str) -> Optional[ActiveEventState]: """Activate a triggered event, store it in-memory, and broadcast it to displays.""" assignments = _build_assignments(event_id) if not assignments: return None state = ActiveEventState( event_id=event_id, name=name, start_time_ms=start_ms, end_time_ms=end_ms, assignments=assignments, ) set_trigger_event(state) broadcast_trigger_state(state) logger.info("Activated trigger event %s (%s)", event_id, name) return state