goon/app/api/playback_events.py
goon-foss 0f128d2e17 feat(playback): zgloszenie 404/410 z telefonu sprawdzane z serwera
Zrodlo znikalo tylko po dlugim przytrzymaniu przez uzytkownika. Teraz
zgloszenie "Response code: 404/410" uruchamia w tle ponowny resolve z serwera;
martwe dopiero, gdy ekstraktor mowi "usuniete" albo plik z serwera tez daje
404/410 (dwie kontrole). Na sucho na 84 parach z 14 dni: 0 martwych, 83 zywe,
czyli 410 z telefonu prawie zawsze znaczy zly adres, nie skasowany plik.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 14:16:14 +02:00

58 lines
2.3 KiB
Python

"""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
from app.playback_gone import is_gone_report, verify_gone_async
router = APIRouter(prefix="/playback-events", tags=["telemetry"], dependencies=[Depends(require_api_key)])
class PlaybackEventIn(BaseModel):
# 'tube:<sitetag>', 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)
# Surowa treść błędu z native playera (ExoPlayer message), diagnostyka realnych
# padów których nie da się odtworzyć na emulatorze (np. watchporn player_error).
error_detail: str | None = Field(default=None, max_length=512)
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,
error_detail=body.error_detail if body.status == "error" else None,
ttff_ms=body.ttff_ms if body.status == "success" else None,
device_id=device_id,
)
)
session.commit()
# Plik zniknal (404/410 w odtwarzaczu): serwer sprawdza zrodlo sam i oznacza martwe
# dopiero po wlasnym potwierdzeniu (app/playback_gone.py).
if is_gone_report(body.status, body.error_kind, body.error_detail):
verify_gone_async(body.scene_id, body.origin)