use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MediaItemSummary { pub id: i64, pub kind: String, pub title: String, pub year: Option, pub monitored: bool, pub episode_count: i64, pub missing_count: i64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EpisodeSummary { pub id: i64, pub season_number: i64, pub episode_number: i64, pub title: Option, pub air_date: Option, pub monitored: bool, pub has_file: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MediaItemDetail { pub id: i64, pub kind: String, pub title: String, pub year: Option, pub monitored: bool, pub root_folder: String, pub episodes: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReleaseSummary { pub id: i64, pub media_title: String, pub raw_title: String, pub score: Option, pub status: String, pub grabbed_at: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReviewQueueEntry { pub id: i64, pub raw_release_title: String, pub candidate_media_title: Option, pub confidence: f64, pub status: String, pub created_at: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StalledGrab { pub release_id: i64, pub media_item_id: i64, pub media_title: String, pub raw_title: String, pub grabbed_at: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MaxedSearchTarget { pub media_item_id: i64, pub media_title: String, pub search_count: i64, pub last_searched_at: Option, } /// The daily "why don't I have this yet" answer — grabs that have been /// sitting without importing longer than expected, how deep the review /// queue has backed up, and search targets that have been failing every /// attempt for so long their backoff has hit its ceiling. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StuckReport { pub stalled_grabs: Vec, pub review_queue_depth: i64, pub maxed_out_search_targets: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SearchResult { pub external_id: String, pub title: String, pub year: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AddSeriesRequest { pub tvdb_id: String, pub title: String, pub year: Option, pub aliases: Vec, pub root_folder: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AddSeriesResponse { pub media_item_id: i64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AddMovieRequest { pub tmdb_id: String, pub title: String, pub year: Option, pub root_folder: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AddMovieResponse { pub media_item_id: i64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SearchNowResult { pub targets: usize, pub searched: usize, pub grabbed: usize, pub errors: usize, pub source_exhausted: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CalendarEntry { pub media_item_id: i64, pub media_title: String, pub season_number: i64, pub episode_number: i64, pub title: Option, pub air_date: String, pub monitored: bool, pub has_file: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HealthDetail { pub status: String, pub last_grab_cycle: Option, pub last_import_cycle: Option, pub last_search_cycle: Option, pub last_upgrade_cycle: Option, pub last_transcode_cycle: Option, pub search_halted: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CycleInfo { pub at: chrono::DateTime, pub ok: bool, pub detail: String, } /// One file flagged by a `media_file_probe` category — reused across every /// flag list in `LibraryHealthReport` so a client can render them all with /// the same widget. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FlaggedFile { pub episode_file_id: i64, pub media_title: String, pub episode_label: Option, pub path: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DuplicateGroup { pub media_title: String, pub episode_label: Option, pub paths: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CodecCount { pub codec: String, pub count: i64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LibrarySummary { pub total_files: i64, pub total_size_bytes: i64, pub probed_files: i64, pub by_video_codec: Vec, pub sd_count: i64, pub hd_720p_count: i64, pub full_hd_1080p_count: i64, pub uhd_4k_count: i64, pub pct_with_subtitles: f64, } /// One search result scored (or gate-rejected) for manual review — the /// same candidate a search cycle would evaluate automatically, surfaced /// before a grab decision is made instead of after. `link`/`guid`/ /// `source_id` are carried through so a chosen candidate can be grabbed /// directly without re-searching. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReleaseCandidate { pub raw_title: String, pub link: String, pub guid: String, pub source_id: i64, pub source_name: String, pub seeders: Option, pub leechers: Option, pub size_bytes: Option, /// `None` when gate-rejected — `rejected_reason` explains why. pub score: Option, pub rejected_reason: Option, pub resolution: Option, pub is_repack: bool, pub is_season_pack: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GrabCandidateRequest { pub raw_title: String, pub link: String, pub guid: String, pub source_id: i64, } /// Every scoring axis `QualityProfile::Weights` has, mirrored 1:1 for the /// wire — used both to show the currently-effective (defaults-plus-stored- /// override) values and to submit a full replacement set when the user /// edits one. #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct WeightsDto { pub seeder: f32, pub resolution_tier: f32, pub source_tier: f32, pub codec_tier: f32, pub bit_depth: f32, pub container: f32, pub group_allowlist: f32, pub repack: f32, pub hdr: f32, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QualityProfileSummary { pub id: i64, pub name: String, pub kind: String, /// Always the fully-resolved values (built-in defaults with any stored /// override already applied) — never a partial/sparse object, so the /// TUI always has something concrete to display and re-submit. pub weights: WeightsDto, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UpdateQualityProfileWeightsRequest { pub weights: WeightsDto, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LibraryHealthReport { pub corrupt_files: Vec, pub under_quality_files: Vec, pub no_subtitle_files: Vec, pub no_english_audio_files: Vec, pub non_english_default_audio_files: Vec, pub duplicate_groups: Vec, pub summary: LibrarySummary, }