Skip to main content

dvrip_rs/commands/
file_management.rs

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    /// List local files on the device
13    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    /// Download a file from the device
22    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        // OPFileQuery only returns the first 64 items
74        // We need to keep querying until we get all
75        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        // Create directory if it doesn't exist
127        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        // Claim
135        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        // DownloadStart
154        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        // Receber dados do arquivo
173        {
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                // Ler header
179                let header = receive_packet_header(&mut reader).await?;
180
181                // Ler primeiro chunk
182                let mut file_data =
183                    receive_data(&mut reader, header.data_len as usize, self.timeout).await?;
184
185                // Continue reading chunks until receiving one with data_len == 0
186                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                // Escrever arquivo
197                let mut file = File::create(target_path).await?;
198                file.write_all(&file_data).await?;
199                file.sync_all().await?;
200            }
201        }
202
203        // DownloadStop
204        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}