Kierunek A z makiet (miniaturki niosa ekran, chrome cichy) + jeden element z C: jakosc i zrodlo widoczne wprost na kafelku. Backend musial to najpierw WYSTAWIC, bo lista jedzie w trybie `light`, ktory okrawa playback_sources do samej miniatury: - `SceneOut.max_quality_p` (kolumna z migracji 0029, dotad uzywana tylko jako filtr), - slim wpis niesie PRAWDZIWY `origin` zrodla wybranej miniatury zamiast "". Kafelek: badge jakosci w LEWYM dolnym rogu (czas zostaje w prawym, ten sam ciezar wizualny), a w linii meta studio + przygaszona nazwa serwisu. Badge pokazujemy TYLKO gdy jakosc jest znana (6,5% scen), bo ma byc faktem, nie zgadywaniem. Zweryfikowane na emulatorze na PRAWDZIWYCH danych: zeby to bylo mozliwe, zbudowalem build debugowy podpisany kluczem release (przechodzi kontrole X-App-Signature, a jako debug nie ma FLAG_SECURE, wiec da sie zrobic zrzut). Widac "4K" i "1280p" w rogu oraz "ONLY FANS SXYPRN" w linii meta. Tymczasowa zmiana podpisu w build.gradle cofnieta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
150 lines
4.9 KiB
Python
150 lines
4.9 KiB
Python
"""Pydantic schemas eksportowane przez API."""
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import date, datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
|
|
class StudioOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
name: str
|
|
slug: str
|
|
network: str | None = None
|
|
|
|
|
|
class PerformerOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
canonical_name: str
|
|
slug: str
|
|
gender: str | None = None
|
|
as_alias: str | None = None
|
|
|
|
|
|
class TagOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
name: str
|
|
slug: str
|
|
|
|
|
|
class ExternalRefOut(BaseModel):
|
|
source: str
|
|
external_id: str
|
|
url: str | None = None
|
|
last_seen: datetime | None = None
|
|
|
|
|
|
class PlaybackSourceOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
origin: str
|
|
page_url: str
|
|
embed_url: str | None = None
|
|
stream_url: str | None = None
|
|
quality: str | None = None
|
|
duration_sec: int | None = None
|
|
thumbnail_url: str | None = None
|
|
animated_thumbnail_url: str | None = None
|
|
|
|
|
|
class SceneOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
title: str
|
|
slug: str | None = None
|
|
release_date: date | None = None
|
|
duration_sec: int | None = None
|
|
description: str | None = None
|
|
code: str | None = None
|
|
director: str | None = None
|
|
studio: StudioOut | None = None
|
|
performers: list[PerformerOut] = []
|
|
tags: list[TagOut] = []
|
|
external_refs: list[ExternalRefOut] = []
|
|
playback_sources: list[PlaybackSourceOut] = []
|
|
# Wertykał sceny ('jav' / 'animation') albo None dla głównego katalogu. Liczony
|
|
# z originów źródeł, patrz `app/verticals.py`. Mobile MUSI go przekazywać dalej przy
|
|
# nawigacji do tagu/studia/performera: bez `?vertical=` backend wyklucza wszystkie
|
|
# wertykały, więc tag z anime pokazywał sceny spoza anime, a studio animacyjne
|
|
# wyglądało na martwe (zgłoszenia 195ae42e / e9de414c, 2026-08-14).
|
|
vertical: str | None = None
|
|
# Najwyższa ZNANA jakość źródeł (720/1080/2160), NULL gdy żadne jej nie podało.
|
|
# Kafelek pokazuje ją jako badge; patrz migracja 0029_scenes_max_quality.
|
|
max_quality_p: int | None = None
|
|
# Kiedy scena trafiła do bazy (ingest). Używane przez mobile do oznaczenia
|
|
# "NEW" na karcie scen w PerformerScenesScreen / StudioScenesScreen, gdy
|
|
# `created_at > last_seen_at` (favorite) → badge.
|
|
created_at: datetime | None = None
|
|
# True = scena z masowego backfillu katalogu (deep-crawl). Mobile NIE pokazuje na niej
|
|
# NEW badge nawet gdy created_at > last_seen, to stara treść, nie świeży release.
|
|
backfill: bool = False
|
|
# Watched indicator (z `scene_play_progress`): mobile dim'uje kafelek gdy
|
|
# `finished=True`, pokazuje progress bar gdy `position_sec > 0`.
|
|
last_played_at: datetime | None = None
|
|
finished: bool = False
|
|
position_sec: int = 0
|
|
is_favorite: bool = False
|
|
|
|
|
|
class SceneListOut(BaseModel):
|
|
items: list[SceneOut]
|
|
total: int
|
|
page: int
|
|
per_page: int
|
|
# has_more: czy istnieje kolejna strona. Liczone z fetcha per_page+1 (≈darmowe),
|
|
# NIE z `total`, bo dla filtrowanych list `total` jest bounded ("1000+") żeby
|
|
# uniknąć ~5s exhaustive count. Mobile paginuje po has_more, nie po total.
|
|
has_more: bool = False
|
|
# total_capped: True gdy `total` to bounded cap (są >total wyników). UI: "{total}+".
|
|
total_capped: bool = False
|
|
|
|
|
|
class MovieChapterOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
chapter_index: int
|
|
title: str | None = None
|
|
start_sec: int | None = None
|
|
end_sec: int | None = None
|
|
scene_id: uuid.UUID | None = None
|
|
|
|
|
|
class MovieOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
id: uuid.UUID
|
|
title: str
|
|
slug: str | None = None
|
|
release_year: int | None = None
|
|
release_date: date | None = None
|
|
duration_sec: int | None = None
|
|
description: str | None = None
|
|
director: str | None = None
|
|
country: str | None = None
|
|
rating: float | None = None
|
|
poster_url: str | None = None
|
|
backdrop_url: str | None = None
|
|
studio: StudioOut | None = None
|
|
performers: list[PerformerOut] = []
|
|
tags: list[TagOut] = []
|
|
chapters: list[MovieChapterOut] = []
|
|
external_refs: list[ExternalRefOut] = []
|
|
playback_sources: list[PlaybackSourceOut] = []
|
|
# Used by mobile MoviesScreen NEW badge (created_at > client-stored seenSince)
|
|
# and MovieDetail favorite star.
|
|
created_at: datetime | None = None
|
|
is_favorite: bool = False
|
|
# Watched / continue-watching state (mirror SceneOut, bug-report b207ff17
|
|
# 2026-05-26 "przydałoby się oznaczenie filmów już obejrzanych").
|
|
last_played_at: datetime | None = None
|
|
finished: bool = False
|
|
position_sec: int = 0
|
|
|
|
|
|
class MovieListOut(BaseModel):
|
|
items: list[MovieOut]
|
|
total: int
|
|
page: int
|
|
per_page: int
|