1745 wystapien w 291 plikach (backend, scripts, mobile). Dominowal jeden ksztalt, ' — ' laczace zdania, wiec poszedl przecinek; myslnik otwierajacy mysl zdjety; zakresy 0–5 i A–Z na dywiz. Podzial komentarz/kod robil skaner stanow, nie regex, zeby nie ruszyc myslnika stojacego w stringu albo w wyrazeniu regularnym. Potwierdzone maszynowo: po wycieciu komentarzy z wersji HEAD i z dysku obie sa identyczne we wszystkich 291 plikach, czyli poza komentarzami nie zmienilo sie nic. 233 pliki .py parsuja sie bez bledu, tsc ma te same 11 bledow w tych samych 3 plikach co przed zmiana. Dwie rzeczy wymagaly poprawki po automacie: wyrownane listy definicji, gdzie myslnik byl separatorem kolumny (37 miejsc, kolumna przywrocona), i trzy przeniesienia mysli do nowej linii, gdzie zostawal wiszacy przecinek na poczatku (poprawione recznie). Bundla nie republikuje: Hermes i tak nie niesie komentarzy, wiec dla uzytkownika to zero zmiany. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
2 KiB
Python
53 lines
2 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
|
|
|
|
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()
|