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