Started (incomplete) implementation of stream verification scheduler and endpoints
All checks were successful
AWS Deploy on Push / build (push) Successful in 5m18s
All checks were successful
AWS Deploy on Push / build (push) Successful in 5m18s
This commit is contained in:
@@ -1,15 +1,156 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
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] = {}
|
||||
|
||||
|
||||
@router.get("/protected", summary="Protected endpoint for authenticated users")
|
||||
async def protected_route(user: CognitoUser = Depends(get_current_user)):
|
||||
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),
|
||||
):
|
||||
"""
|
||||
Protected endpoint that requires authentication for all users.
|
||||
If the user is authenticated, returns success message.
|
||||
Start asynchronous validation of streams.
|
||||
|
||||
- Returns immediately with a process ID
|
||||
- Use GET /validate-streams/{process_id} to check status
|
||||
"""
|
||||
return {"message": f"Hello {user.username}, you have access to support resources!"}
|
||||
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
|
||||
|
||||
57
app/routers/scheduler.py
Normal file
57
app/routers/scheduler.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.auth.dependencies import get_current_user, require_roles
|
||||
from app.iptv.scheduler import StreamScheduler
|
||||
from app.models.auth import CognitoUser
|
||||
from app.utils.database import get_db
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/scheduler",
|
||||
tags=["scheduler"],
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
|
||||
async def get_scheduler(request: Request) -> StreamScheduler:
|
||||
"""Get the scheduler instance from the app state."""
|
||||
if not hasattr(request.app.state.scheduler, "scheduler"):
|
||||
raise HTTPException(status_code=500, detail="Scheduler not initialized")
|
||||
return request.app.state.scheduler
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
@require_roles("admin")
|
||||
def scheduler_health(
|
||||
scheduler: StreamScheduler = Depends(get_scheduler),
|
||||
user: CognitoUser = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Check scheduler health status (admin only)."""
|
||||
try:
|
||||
job = scheduler.scheduler.get_job("daily_stream_validation")
|
||||
next_run = str(job.next_run_time) if job and job.next_run_time else None
|
||||
|
||||
return {
|
||||
"status": "running" if scheduler.scheduler.running else "stopped",
|
||||
"next_run": next_run,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"Failed to check scheduler health: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/trigger")
|
||||
@require_roles("admin")
|
||||
def trigger_validation(
|
||||
scheduler: StreamScheduler = Depends(get_scheduler),
|
||||
user: CognitoUser = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Manually trigger stream validation (admin only)."""
|
||||
scheduler.trigger_manual_validation()
|
||||
return JSONResponse(
|
||||
status_code=202, content={"message": "Stream validation triggered"}
|
||||
)
|
||||
Reference in New Issue
Block a user