The zload CDN URL (remote_control.php) is a valid faststart progressive mp4 that ExoPlayer plays fine when fresh: 206, no IP/referer binding, ~2h token TTL, verified from a residential IP. Real-device failures (player_error/gone telemetry) come from the static URL going stale: a rotated or dead CDN node, or an expired token. Instead of falling back to the tube page in a WebView (removed, it papered over the real issue), PlayerScreen now re-resolves the scene fresh via the backend on initial-load error for backend-native KVS tubes (watchporn). That yields a live node plus a fresh token and stays on the native direct stream (0 VPS bandwidth). For these origins a CDN 'gone' (404) means stale URL, not deleted, so we re-resolve on gone too; a genuinely deleted post raises HosterDead (410) and falls through to the normal fallback chain. Also: add playback_events.error_detail (raw ExoPlayer message) to pin down the exact failure of tubes we cannot reproduce on the emulator. Keep FLAG_SECURE on release builds only (!__DEV__) so debug builds stay screenshottable for local UI verification. Co-Authored-By: Claude Opus 4.8 <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()
|