1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use sqlx::types::Json;
5use sqlx::FromRow;
6
7use crate::camera_url;
8
9#[derive(Debug, Clone, FromRow)]
11pub struct Camera {
12 pub id: String,
13 pub site_id: Option<String>,
14 pub name: String,
15 pub vendor: String,
16 pub model: Option<String>,
17 pub address: Option<String>,
18 pub rtsp_port: i64,
19 pub username: Option<String>,
20 pub password: Option<String>,
21 pub main_stream_url: Option<String>,
22 pub sub_stream_url: Option<String>,
23 pub record_stream: String,
24 pub codec: Option<String>,
25 pub resolution_main: Option<String>,
26 pub resolution_sub: Option<String>,
27 pub fps_main: Option<i64>,
28 pub fps_sub: Option<i64>,
29 pub capabilities: Json<Value>,
30 pub record_enabled: bool,
31 pub segment_seconds: i64,
32 pub retention_hours: i64,
33 pub storage_quota_bytes: Option<i64>,
35 pub record_audio: bool,
37 pub record_mode: String,
39 pub pre_roll_seconds: i64,
41 pub post_roll_seconds: i64,
43 pub mirror_enabled: bool,
46 pub anr_enabled: bool,
48 pub anr_replay_url_template: Option<String>,
51 pub enabled: bool,
52 pub priority: i64,
55 pub created_at: DateTime<Utc>,
56 pub updated_at: DateTime<Utc>,
57}
58
59impl Camera {
60 pub fn should_record(&self) -> bool {
62 self.enabled && self.record_enabled
63 }
64}
65
66#[derive(Debug, Clone, Serialize)]
68pub struct CameraView {
69 pub id: String,
70 pub site_id: Option<String>,
71 pub name: String,
72 pub vendor: String,
73 pub model: Option<String>,
74 pub address: Option<String>,
75 pub rtsp_port: i64,
76 pub username: Option<String>,
77 pub has_password: bool,
78 pub record_stream: String,
79 pub record_url_masked: Option<String>,
81 pub codec: Option<String>,
82 pub resolution_main: Option<String>,
83 pub resolution_sub: Option<String>,
84 pub fps_main: Option<i64>,
85 pub fps_sub: Option<i64>,
86 pub capabilities: Value,
87 pub record_enabled: bool,
88 pub segment_seconds: i64,
89 pub retention_hours: i64,
90 pub storage_quota_bytes: Option<i64>,
91 pub record_audio: bool,
92 pub record_mode: String,
93 pub pre_roll_seconds: i64,
94 pub post_roll_seconds: i64,
95 pub mirror_enabled: bool,
96 pub anr_enabled: bool,
97 pub anr_replay_url_template: Option<String>,
98 pub enabled: bool,
99 pub priority: i64,
100 pub created_at: DateTime<Utc>,
101 pub updated_at: DateTime<Utc>,
102}
103
104impl From<Camera> for CameraView {
105 fn from(c: Camera) -> Self {
106 let record_url_masked = camera_url::record_url(&c).map(|u| camera_url::mask_url(&u));
107 CameraView {
108 id: c.id,
109 site_id: c.site_id,
110 name: c.name,
111 vendor: c.vendor,
112 model: c.model,
113 address: c.address,
114 rtsp_port: c.rtsp_port,
115 username: c.username,
116 has_password: c
117 .password
118 .as_deref()
119 .map(|p| !p.is_empty())
120 .unwrap_or(false),
121 record_stream: c.record_stream,
122 record_url_masked,
123 codec: c.codec,
124 resolution_main: c.resolution_main,
125 resolution_sub: c.resolution_sub,
126 fps_main: c.fps_main,
127 fps_sub: c.fps_sub,
128 capabilities: c.capabilities.0,
129 record_enabled: c.record_enabled,
130 segment_seconds: c.segment_seconds,
131 retention_hours: c.retention_hours,
132 storage_quota_bytes: c.storage_quota_bytes,
133 record_audio: c.record_audio,
134 record_mode: c.record_mode,
135 pre_roll_seconds: c.pre_roll_seconds,
136 post_roll_seconds: c.post_roll_seconds,
137 mirror_enabled: c.mirror_enabled,
138 anr_enabled: c.anr_enabled,
139 anr_replay_url_template: c.anr_replay_url_template,
140 enabled: c.enabled,
141 priority: c.priority,
142 created_at: c.created_at,
143 updated_at: c.updated_at,
144 }
145 }
146}
147
148#[derive(Debug, Deserialize)]
150pub struct CameraCreate {
151 pub id: Option<String>,
152 pub name: String,
153 pub site_id: Option<String>,
154 #[serde(default = "default_vendor")]
155 pub vendor: String,
156 pub model: Option<String>,
157 pub address: Option<String>,
158 pub rtsp_port: Option<i64>,
159 pub username: Option<String>,
160 pub password: Option<String>,
161 pub main_stream_url: Option<String>,
162 pub sub_stream_url: Option<String>,
163 pub record_stream: Option<String>,
164 pub capabilities: Option<Value>,
165 pub record_enabled: Option<bool>,
166 pub segment_seconds: Option<i64>,
167 pub retention_hours: Option<i64>,
168 pub storage_quota_bytes: Option<i64>,
169 pub record_audio: Option<bool>,
170 pub record_mode: Option<String>,
171 pub pre_roll_seconds: Option<i64>,
172 pub post_roll_seconds: Option<i64>,
173 pub mirror_enabled: Option<bool>,
174 pub anr_enabled: Option<bool>,
175 pub anr_replay_url_template: Option<String>,
176 pub enabled: Option<bool>,
177}
178
179fn default_vendor() -> String {
180 "generic".to_string()
181}
182
183#[derive(Debug, Deserialize, Default)]
185pub struct CameraUpdate {
186 pub name: Option<String>,
187 pub site_id: Option<String>,
188 pub vendor: Option<String>,
189 pub model: Option<String>,
190 pub address: Option<String>,
191 pub rtsp_port: Option<i64>,
192 pub username: Option<String>,
193 pub password: Option<String>,
194 pub main_stream_url: Option<String>,
195 pub sub_stream_url: Option<String>,
196 pub record_stream: Option<String>,
197 pub capabilities: Option<Value>,
198 pub record_enabled: Option<bool>,
199 pub segment_seconds: Option<i64>,
200 pub retention_hours: Option<i64>,
201 pub storage_quota_bytes: Option<i64>,
202 pub record_audio: Option<bool>,
203 pub record_mode: Option<String>,
204 pub pre_roll_seconds: Option<i64>,
205 pub post_roll_seconds: Option<i64>,
206 pub mirror_enabled: Option<bool>,
207 pub anr_enabled: Option<bool>,
208 pub anr_replay_url_template: Option<String>,
209 pub enabled: Option<bool>,
210 pub priority: Option<i64>,
211}
212
213#[derive(Debug, Clone, Serialize, FromRow)]
214pub struct Segment {
215 pub id: String,
216 pub camera_id: String,
217 pub path: String,
218 pub start_time: DateTime<Utc>,
219 pub end_time: DateTime<Utc>,
220 pub duration_s: f64,
221 pub codec: Option<String>,
222 pub width: Option<i64>,
223 pub height: Option<i64>,
224 pub size_bytes: i64,
225 pub container: String,
226 pub locked: bool,
228 pub evidence_locked: bool,
231 pub incident_id: Option<String>,
232 pub created_at: DateTime<Utc>,
233}
234
235#[derive(Debug, Clone, Serialize, FromRow)]
239pub struct RecordingGap {
240 pub id: String,
241 pub camera_id: String,
242 pub gap_start: DateTime<Utc>,
243 pub gap_end: DateTime<Utc>,
244 pub gap_seconds: i64,
245 pub fill_state: String,
246 pub fill_attempts: i64,
247 pub last_attempt_at: Option<DateTime<Utc>>,
248 pub filled_at: Option<DateTime<Utc>>,
249 pub created_at: DateTime<Utc>,
250}
251
252#[derive(Debug, Clone, Serialize, FromRow)]
253pub struct CameraStatus {
254 pub camera_id: String,
255 pub state: String,
256 pub last_segment_at: Option<DateTime<Utc>>,
257 pub last_started_at: Option<DateTime<Utc>>,
258 pub reconnect_count: i64,
259 pub segments_written: i64,
260 pub fps_observed: Option<f64>,
261 pub bitrate_kbps: Option<f64>,
262 pub last_error: Option<String>,
263 pub recorder_pid: Option<i64>,
264 pub updated_at: DateTime<Utc>,
265}