// Lustro modeli z FastAPI app/api/schemas.py + admin endpoints. export interface ExternalRef { source: string; external_id: string; url?: string | null; last_seen?: string | null; } export interface StudioOut { id: string; name: string; slug: string; network?: string | null; } export interface PerformerOut { id: string; canonical_name: string; slug: string; gender?: string | null; as_alias?: string | null; } export interface TagOut { id: string; name: string; slug: string; } export interface PlaybackSource { id: string; origin: string; page_url: string; embed_url?: string | null; stream_url?: string | null; quality?: string | null; duration_sec?: number | null; thumbnail_url?: string | null; animated_thumbnail_url?: string | null; } export interface StreamLink { // proxy URL (fallback gdy direct fails). Backend re-fetchuje content z VPS IP + // streamuje do mobile. stream_url?: string | null; // hoster embed URL — mobile otwiera w WebView gdy `type=hoster`. embed_url?: string | null; // raw CDN URL + headers do bezpośredniego fetchu z urządzenia (preferred — 0 // bandwidth przez VPS). Mobile próbuje to PIERWSZE; fallback na `stream_url` na error. direct_url?: string | null; headers?: Record | null; quality?: string | null; type?: string | null; } export interface ResolveOut { source: PlaybackSource; best?: StreamLink | null; links: StreamLink[]; } export interface TagCount { id: string; name: string; slug: string; scene_count: number; } export interface TagListOut { items: TagCount[]; total: number; page: number; per_page: number; } export interface PerformerCount { id: string; canonical_name: string; slug: string; gender?: string | null; scene_count: number; } export interface PerformerListOut { items: PerformerCount[]; total: number; page: number; per_page: number; } export interface StudioCount { id: string; name: string; slug: string; network?: string | null; scene_count: number; } export interface StudioListOut { items: StudioCount[]; total: number; page: number; per_page: number; } export interface SourceOut { origin: string; sitetag: string; display_name: string; scene_count: number; last_scraped_at: string | null; } export interface SourceListOut { items: SourceOut[]; total: number; } export type ScenesSort = 'created_at' | 'release_date' | 'title' | 'studio'; export interface ScenesListParams { q?: string; studio_slugs?: string[]; tags?: string[]; performer_ids?: string[]; has_playback?: boolean; has_animated_thumbnail?: boolean; min_duration_sec?: number; max_duration_sec?: number; released_within_days?: number; min_quality_p?: number; include_stubs?: boolean; origin?: string; sort?: ScenesSort; page?: number; per_page?: number; } export interface SceneOut { id: string; title: string; slug?: string | null; release_date?: string | null; duration_sec?: number | null; description?: string | null; code?: string | null; director?: string | null; studio?: StudioOut | null; performers: PerformerOut[]; tags: TagOut[]; external_refs: ExternalRef[]; playback_sources: PlaybackSource[]; // Kiedy scena trafiła do bazy (ingest). Używane do oznaczenia "NEW" — gdy // `created_at > favoriteSeenSince` (param przekazany z FavoritesScreen). created_at?: string | null; // Watched indicator + favorite state. Backend dolicza z scene_play_progress + favorite_scenes. last_played_at?: string | null; finished?: boolean; position_sec?: number; is_favorite?: boolean; } export interface SceneListOut { items: SceneOut[]; total: number; page: number; per_page: number; } export interface MovieChapterOut { chapter_index: number; title?: string | null; start_sec?: number | null; end_sec?: number | null; scene_id?: string | null; } export interface MovieOut { id: string; title: string; slug?: string | null; release_year?: number | null; release_date?: string | null; duration_sec?: number | null; description?: string | null; director?: string | null; country?: string | null; rating?: number | null; poster_url?: string | null; backdrop_url?: string | null; studio?: StudioOut | null; performers: PerformerOut[]; tags: TagOut[]; chapters: MovieChapterOut[]; external_refs: ExternalRef[]; playback_sources: PlaybackSource[]; created_at?: string | null; is_favorite?: boolean; } export interface MovieListOut { items: MovieOut[]; total: number; page: number; per_page: number; } export interface FavoriteMovieOut { movie_id: string; title: string; slug: string | null; poster_url: string | null; release_year: number | null; studio_name: string | null; last_seen_at: string; created_at: string; } export interface FavoriteMovieListOut { items: FavoriteMovieOut[]; total: number; } export interface FavoriteMovieAddOut { movie_id: string; created: boolean; } export type MoviesSort = 'created_at' | 'release_year' | 'release_date' | 'title' | 'rating'; export interface MoviesListParams { q?: string; studio_slugs?: string[]; tags?: string[]; performer_ids?: string[]; year_from?: number; year_to?: number; has_playback?: boolean; sort?: MoviesSort; page?: number; per_page?: number; } export interface MergeCandidateSummary { id: string; kind: string; left_id: string; right_id: string; score: number; status: 'pending' | 'auto_merged' | 'merged' | 'rejected'; left_title?: string | null; right_title?: string | null; left_thumbnail_url?: string | null; left_animated_thumbnail_url?: string | null; right_thumbnail_url?: string | null; right_animated_thumbnail_url?: string | null; } export interface MergeCandidateListOut { items: MergeCandidateSummary[]; total: number; page: number; per_page: number; } export interface MergeCandidateDetail { id: string; kind: string; score: number; status: string; reasons: Record; left: SceneOut | null; right: SceneOut | null; } export interface ResolveResult { id: string; status: string; keep_id?: string | null; drop_id?: string | null; } export type ResolveAction = 'merge' | 'reject'; export type KeepSide = 'left' | 'right'; export interface FavoriteOut { performer_id: string; canonical_name: string; slug: string | null; scene_count: number; new_count: number; last_seen_at: string; created_at: string; } export interface FavoriteListOut { items: FavoriteOut[]; total: number; new_total: number; } export interface FavoriteAddOut { performer_id: string; created: boolean; } export interface FavoriteStudioOut { studio_id: string; name: string; slug: string; network: string | null; scene_count: number; new_count: number; last_seen_at: string; created_at: string; } export interface FavoriteStudioListOut { items: FavoriteStudioOut[]; total: number; new_total: number; } export interface FavoriteStudioAddOut { studio_id: string; created: boolean; } export interface BlacklistEntry { id: string; name: string; slug?: string | null; } export interface BlacklistOut { performers: BlacklistEntry[]; studios: BlacklistEntry[]; tags: BlacklistEntry[]; } export type BlacklistKind = 'performer' | 'studio' | 'tag'; export interface ProgressOut { scene_id: string; position_sec: number; duration_sec: number | null; finished: boolean; last_played_at: string; } export interface WatchEntry { scene: SceneOut; position_sec: number; duration_sec: number | null; finished: boolean; last_played_at: string; } export interface WatchListOut { items: WatchEntry[]; }