"""Playback telemetry — POST /playback-events (fire-and-forget z apki). Apka po każdej próbie odtwarzania woła to z origin sceny + wynikiem (success/error). Sygnał zasila health/szybkość w rankingu źródeł (run_source_stats agreguje okno 7d). Best-effort: apka NIE czeka i ignoruje błąd — telemetria nie może psuć playbacku. """ from __future__ import annotations import uuid from typing import Annotated, Literal from fastapi import APIRouter, Depends from pydantic import BaseModel, Field from sqlalchemy.orm import Session from app.api.device import get_device_id from app.auth import require_api_key from app.db import get_session from app.models.playback_event import PlaybackEvent router = APIRouter(prefix="/playback-events", tags=["telemetry"], dependencies=[Depends(require_api_key)]) class PlaybackEventIn(BaseModel): # 'tube:' — zgodne z playback_sources.origin. Bez tego ping bezużyteczny. origin: str = Field(min_length=1, max_length=64) status: Literal["success", "error"] scene_id: uuid.UUID | None = None error_kind: str | None = Field(default=None, max_length=64) ttff_ms: int | None = Field(default=None, ge=0, le=600_000) @router.post("", status_code=204) def post_playback_event( body: PlaybackEventIn, session: Annotated[Session, Depends(get_session)], device_id: Annotated[str, Depends(get_device_id)], ) -> None: session.add( PlaybackEvent( origin=body.origin, scene_id=body.scene_id, status=body.status, error_kind=body.error_kind, ttff_ms=body.ttff_ms if body.status == "success" else None, device_id=device_id, ) ) session.commit()