import logging from enum import Enum from typing import Optional from uuid import uuid4 from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from pydantic import BaseModel from sqlalchemy.orm import Session from app.auth.dependencies import get_current_user from app.iptv.stream_manager import StreamManager from app.models.auth import CognitoUser from app.utils.database import get_db_session router = APIRouter(prefix="/playlist", tags=["playlist"]) logger = logging.getLogger(__name__) # In-memory store for validation processes validation_processes: dict[str, dict] = {} class ProcessStatus(str, Enum): PENDING = "pending" IN_PROGRESS = "in_progress" COMPLETED = "completed" FAILED = "failed" class StreamValidationRequest(BaseModel): """Request model for stream validation endpoint""" channel_id: Optional[str] = None class ValidatedStream(BaseModel): """Model for a validated working stream""" channel_id: str stream_url: str class ValidationProcessResponse(BaseModel): """Response model for validation process initiation""" process_id: str status: ProcessStatus message: str class ValidationResultResponse(BaseModel): """Response model for validation results""" process_id: str status: ProcessStatus working_streams: Optional[list[ValidatedStream]] = None error: Optional[str] = None def run_stream_validation(process_id: str, channel_id: Optional[str], db: Session): """Background task to validate streams""" try: validation_processes[process_id]["status"] = ProcessStatus.IN_PROGRESS manager = StreamManager(db) if channel_id: stream_url = manager.validate_and_select_stream(channel_id) if stream_url: validation_processes[process_id]["result"] = { "working_streams": [ ValidatedStream(channel_id=channel_id, stream_url=stream_url) ] } else: validation_processes[process_id]["error"] = ( f"No working streams found for channel {channel_id}" ) else: # TODO: Implement validation for all channels validation_processes[process_id]["error"] = ( "Validation of all channels not yet implemented" ) validation_processes[process_id]["status"] = ProcessStatus.COMPLETED except Exception as e: logger.error(f"Error validating streams: {str(e)}") validation_processes[process_id]["status"] = ProcessStatus.FAILED validation_processes[process_id]["error"] = str(e) @router.post( "/validate-streams", summary="Start stream validation process", response_model=ValidationProcessResponse, status_code=status.HTTP_202_ACCEPTED, responses={202: {"description": "Validation process started successfully"}}, ) async def start_stream_validation( request: StreamValidationRequest, background_tasks: BackgroundTasks, user: CognitoUser = Depends(get_current_user), db: Session = Depends(get_db_session), ): """ Start asynchronous validation of streams. - Returns immediately with a process ID - Use GET /validate-streams/{process_id} to check status """ process_id = str(uuid4()) validation_processes[process_id] = { "status": ProcessStatus.PENDING, "channel_id": request.channel_id, } background_tasks.add_task(run_stream_validation, process_id, request.channel_id, db) return { "process_id": process_id, "status": ProcessStatus.PENDING, "message": "Validation process started", } @router.get( "/validate-streams/{process_id}", summary="Check validation process status", response_model=ValidationResultResponse, responses={ 200: {"description": "Process status and results"}, 404: {"description": "Process not found"}, }, ) async def get_validation_status( process_id: str, user: CognitoUser = Depends(get_current_user) ): """ Check status of a stream validation process. Returns current status and results if completed. """ if process_id not in validation_processes: raise HTTPException(status_code=404, detail="Process not found") process = validation_processes[process_id] response = {"process_id": process_id, "status": process["status"]} if process["status"] == ProcessStatus.COMPLETED: if "error" in process: response["error"] = process["error"] else: response["working_streams"] = process["result"]["working_streams"] elif process["status"] == ProcessStatus.FAILED: response["error"] = process["error"] return response