Po przejsciu porndoe na resolver po stronie telefonu 35 urzadzen dalej dostawalo wylacznie 403 i nie dalo sie odroznic, czy pada nowa droga, zapas przez WebView, czy stara paczka. Apka wysyla teraz via: phone, backend, hoster, webview, proxy, movie; '-re' po ponownym rozwiazaniu w tym samym ekranie, '<droga>>wv' gdy adres wyciagnal WebView jako zapas. NULL = paczka sprzed tej zmiany. Migracja 0053 (kolumna nullable, sama zmiana metadanych). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.5 KiB
Python
62 lines
2.5 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)
|
|
# Skad przyszedl adres w odtwarzaczu (patrz PlaybackEvent.via). Opcjonalne:
|
|
# starsze paczki go nie wysylaja.
|
|
via: str | None = Field(default=None, max_length=16)
|
|
|
|
|
|
@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,
|
|
via=body.via,
|
|
)
|
|
)
|
|
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)
|