From 5f2f61edfe6a7a54de87a66db5ae9fd289876a30 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Tue, 14 Apr 2026 10:01:22 -0400 Subject: [PATCH] feat: add per-port SCTE35 inject endpoint --- backend/app/api/routes.py | 67 ++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/backend/app/api/routes.py b/backend/app/api/routes.py index 6996cee..bcd672d 100644 --- a/backend/app/api/routes.py +++ b/backend/app/api/routes.py @@ -23,6 +23,7 @@ class SCTE35InjectionRequest(BaseModel): webhook_url: Optional[str] = Field(None, description="Optional webhook URL") out_of_network: bool = Field(True, description="Out of network indicator") splice_immediate: bool = Field(False, description="Splice immediate flag") + srt_destination_url: Optional[str] = Field(None, description="Target specific SRT destination (None = all)") router = APIRouter(prefix="/api") @@ -206,15 +207,10 @@ async def inject_scte35_marker( manager: SCTE35Manager = Depends(get_scte35_manager), ) -> SCTE35Marker: """ - Inject a SCTE35 ad break marker. + Inject a SCTE35 ad break marker globally (all ports). Args: - request: SCTE35InjectionRequest with: - - event_id: int (unique event identifier) - - duration_seconds: float (duration in seconds) - - webhook_url: str | None (optional callback URL) - - out_of_network: bool (whether out-of-network ad) - - splice_immediate: bool (whether splice immediately) + request: SCTE35InjectionRequest Returns: SCTE35Marker with timestamp and metadata @@ -229,9 +225,11 @@ async def inject_scte35_marker( webhook_url=request.webhook_url, out_of_network=request.out_of_network, splice_immediate=request.splice_immediate, + port_index=None, + srt_destination_url=request.srt_destination_url, ) - logger.info(f"Injected SCTE35 marker with event_id {request.event_id}") + logger.info(f"Injected global SCTE35 marker event_id={request.event_id}") return marker except ValueError as e: @@ -242,6 +240,44 @@ async def inject_scte35_marker( raise HTTPException(status_code=500, detail="Internal server error") +@router.post("/ports/{port_index}/scte35/inject", response_model=SCTE35Marker) +async def inject_scte35_marker_for_port( + port_index: int, + request: SCTE35InjectionRequest, + manager: SCTE35Manager = Depends(get_scte35_manager), +) -> SCTE35Marker: + """ + Inject a SCTE35 ad break marker on a specific port's SRT output(s). + + Args: + port_index: 0-based port index + request: SCTE35InjectionRequest + + Returns: + SCTE35Marker with timestamp, port_index, and metadata + """ + try: + marker = await manager.inject_marker( + event_id=request.event_id, + duration_seconds=request.duration_seconds, + webhook_url=request.webhook_url, + out_of_network=request.out_of_network, + splice_immediate=request.splice_immediate, + port_index=port_index, + srt_destination_url=request.srt_destination_url, + ) + + logger.info(f"Injected SCTE35 marker event_id={request.event_id} on port {port_index}") + return marker + + except ValueError as e: + logger.warning(f"Invalid SCTE35 parameters: {e}") + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(f"Error injecting SCTE35 marker on port {port_index}: {e}") + raise HTTPException(status_code=500, detail="Internal server error") + + @router.get("/scte35/history", response_model=list[SCTE35Marker]) async def get_scte35_history( manager: SCTE35Manager = Depends(get_scte35_manager), @@ -257,3 +293,18 @@ async def get_scte35_history( except Exception as e: logger.error(f"Error retrieving SCTE35 history: {e}") raise HTTPException(status_code=500, detail="Internal server error") + + +@router.get("/ports/{port_index}/scte35/history", response_model=list[SCTE35Marker]) +async def get_scte35_history_for_port( + port_index: int, + manager: SCTE35Manager = Depends(get_scte35_manager), +) -> list[SCTE35Marker]: + """ + Get SCTE35 marker history for a specific port. + """ + try: + return [m for m in manager.get_marker_history() if m.port_index == port_index] + except Exception as e: + logger.error(f"Error retrieving SCTE35 history for port {port_index}: {e}") + raise HTTPException(status_code=500, detail="Internal server error")