1use crate::dvrip::DVRIPCam;
2use crate::error::Result;
3use crate::protocol::{receive_data, receive_packet_header};
4use async_trait::async_trait;
5use chrono::{DateTime, Local};
6use serde_json::{Value, json};
7use std::path::Path;
8use tokio::{fs::File, io::AsyncWriteExt};
9
10#[async_trait]
11pub trait FileManagement: Send + Sync {
12 async fn list_local_files(
14 &mut self,
15 start_time: DateTime<Local>,
16 end_time: DateTime<Local>,
17 file_type: &str,
18 channel: u8,
19 ) -> Result<Vec<Value>>;
20
21 async fn download_file(
23 &mut self,
24 start_time: DateTime<Local>,
25 end_time: DateTime<Local>,
26 filename: &str,
27 target_path: &str,
28 ) -> Result<()>;
29}
30
31#[async_trait]
32impl FileManagement for DVRIPCam {
33 async fn list_local_files(
34 &mut self,
35 start_time: DateTime<Local>,
36 end_time: DateTime<Local>,
37 file_type: &str,
38 channel: u8,
39 ) -> Result<Vec<Value>> {
40 let start_str = start_time.format("%Y-%m-%d %H:%M:%S").to_string();
41 let end_str = end_time.format("%Y-%m-%d %H:%M:%S").to_string();
42
43 let data = json!({
44 "Name": "OPFileQuery",
45 "OPFileQuery": {
46 "BeginTime": start_str,
47 "Channel": channel,
48 "DriverTypeMask": "0x0000FFFF",
49 "EndTime": end_str,
50 "Event": "*",
51 "StreamType": "0x00000000",
52 "Type": file_type,
53 },
54 });
55
56 let mut reply = self
57 .send_command(1440, data, true)
58 .await?
59 .ok_or_else(|| crate::error::DVRIPError::ProtocolError("Empty response".to_string()))?;
60
61 let mut result = Vec::new();
62
63 if let Some(ret) = reply.get("Ret").and_then(|r| r.as_u64())
64 && ret != 100
65 {
66 return Ok(vec![]);
67 }
68
69 if let Some(files) = reply.get_mut("OPFileQuery").and_then(|f| f.as_array_mut()) {
70 result.append(files);
71 }
72
73 while let Some(files) = reply.get("OPFileQuery").and_then(|f| f.as_array()) {
76 if files.len() == 64 {
77 if let Some(last_file) = files.last() {
78 if let Some(new_start) = last_file.get("BeginTime").and_then(|t| t.as_str()) {
79 let data = json!({
80 "Name": "OPFileQuery",
81 "OPFileQuery": {
82 "BeginTime": new_start,
83 "Channel": channel,
84 "DriverTypeMask": "0x0000FFFF",
85 "EndTime": end_str,
86 "Event": "*",
87 "StreamType": "0x00000000",
88 "Type": file_type,
89 },
90 });
91
92 reply = self.send_command(1440, data, true).await?.ok_or_else(|| {
93 crate::error::DVRIPError::ProtocolError("Resposta vazia".to_string())
94 })?;
95
96 if let Some(new_files) = reply.get("OPFileQuery").and_then(|f| f.as_array())
97 {
98 if new_files.is_empty() {
99 break;
100 }
101 result.extend(new_files.clone());
102 } else {
103 break;
104 }
105 } else {
106 break;
107 }
108 } else {
109 break;
110 }
111 } else {
112 break;
113 }
114 }
115
116 Ok(result)
117 }
118
119 async fn download_file(
120 &mut self,
121 start_time: DateTime<Local>,
122 end_time: DateTime<Local>,
123 filename: &str,
124 target_path: &str,
125 ) -> Result<()> {
126 if let Some(parent) = Path::new(target_path).parent() {
128 tokio::fs::create_dir_all(parent).await?;
129 }
130
131 let start_str = start_time.format("%Y-%m-%d %H:%M:%S").to_string();
132 let end_str = end_time.format("%Y-%m-%d %H:%M:%S").to_string();
133
134 let claim_data = json!({
136 "Name": "OPPlayBack",
137 "OPPlayBack": {
138 "Action": "Claim",
139 "Parameter": {
140 "PlayMode": "ByName",
141 "FileName": filename,
142 "StreamType": 0,
143 "Value": 0,
144 "TransMode": "TCP",
145 },
146 "StartTime": start_str,
147 "EndTime": end_str,
148 },
149 });
150
151 self.send_command(1424, claim_data, true).await?;
152
153 let download_start_data = json!({
155 "Name": "OPPlayBack",
156 "OPPlayBack": {
157 "Action": "DownloadStart",
158 "Parameter": {
159 "PlayMode": "ByName",
160 "FileName": filename,
161 "StreamType": 0,
162 "Value": 0,
163 "TransMode": "TCP",
164 },
165 "StartTime": start_str,
166 "EndTime": end_str,
167 },
168 });
169
170 self.send_command(1420, download_start_data, false).await?;
171
172 {
174 let mut stream_guard = self.stream.lock().await;
175 if let Some(s) = stream_guard.as_mut() {
176 let (mut reader, _) = s.split();
177
178 let header = receive_packet_header(&mut reader).await?;
180
181 let mut file_data =
183 receive_data(&mut reader, header.data_len as usize, self.timeout).await?;
184
185 loop {
187 let next_header = receive_packet_header(&mut reader).await?;
188 if next_header.data_len == 0 {
189 break;
190 }
191 let chunk =
192 receive_data(&mut reader, next_header.data_len as usize, self.timeout)
193 .await?;
194 file_data.extend_from_slice(&chunk);
195 }
196 let mut file = File::create(target_path).await?;
198 file.write_all(&file_data).await?;
199 file.sync_all().await?;
200 }
201 }
202
203 let download_stop_data = json!({
205 "Name": "OPPlayBack",
206 "OPPlayBack": {
207 "Action": "DownloadStop",
208 "Parameter": {
209 "FileName": filename,
210 "PlayMode": "ByName",
211 "StreamType": 0,
212 "TransMode": "TCP",
213 "Channel": 0,
214 "Value": 0,
215 },
216 "StartTime": start_str,
217 "EndTime": end_str,
218 },
219 });
220
221 self.send_command(1420, download_stop_data, false).await?;
222
223 Ok(())
224 }
225}