use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct KioskBundle { pub kiosk_id: u32, pub kiosk_name: String, pub display: BundleDisplay, pub layouts: Vec, pub cameras: Vec, pub version: String, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BundleDisplay { pub id: u32, pub name: String, pub width_px: u32, pub height_px: u32, pub idle_timeout_seconds: u32, pub sleep_timeout_seconds: u32, pub default_layout_id: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BundleLayout { pub id: u32, pub name: String, pub regions: Vec, pub grid_cols: u32, pub grid_rows: u32, pub priority: String, pub cooling_timeout_seconds: Option, pub preload_camera_ids: Vec, pub is_default: bool, pub resets_idle_timer: bool, pub cells: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BundleRegion { pub name: String, pub row: u32, pub col: u32, #[serde(rename = "rowSpan")] pub row_span: u32, #[serde(rename = "colSpan")] pub col_span: u32, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BundleCell { pub region_name: String, pub content_type: String, pub camera_id: Option, pub stream_selector: Option, pub web_url: Option, pub html_content: Option, pub cooling_timeout_seconds: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BundleCamera { pub id: u32, pub name: String, #[serde(rename = "type")] pub cam_type: String, pub rtsp_url: Option, pub stream_policy: String, pub streams: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct BundleStream { pub id: u32, pub role: String, pub name: String, pub rtsp_uri: String, pub width: Option, pub height: Option, pub encoding: Option, pub framerate: Option, } impl BundleCamera { /// Pick the best stream URI for this camera given a cell's stream_selector. pub fn stream_uri(&self, selector: Option<&str>) -> Option<&str> { let sel = selector.unwrap_or("auto"); match sel { "main" => self.streams.iter().find(|s| s.role == "main"), "sub" => self.streams.iter().find(|s| s.role == "sub"), _ => { // auto: prefer main, fall back to any self.streams.iter().find(|s| s.role == "main") .or_else(|| self.streams.first()) } } .map(|s| s.rtsp_uri.as_str()) .or(self.rtsp_url.as_deref()) } }