rs_plugin_common_interfaces/domain/
media.rs1use std::str::FromStr;
2
3use crate::url::RsLink;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use strum_macros::EnumString;
7
8use crate::domain::backup::BackupFile;
9
10
11
12pub const DEFAULT_MIME: &str = "application/octet-stream";
13
14#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
15#[serde(rename_all = "camelCase")]
16pub struct FileEpisode {
17 pub id: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub season: Option<u32>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub episode: Option<u32>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub episode_to: Option<u32>,
24}
25
26impl FromStr for FileEpisode {
27 type Err = String;
28
29 fn from_str(s: &str) -> Result<Self, Self::Err> {
30 let splitted: Vec<&str> = s.split("|").collect();
31 if splitted.len() == 3 {
32 Ok(FileEpisode {
33 id: splitted[0].to_string(),
34 season: splitted[1].parse::<u32>().ok().and_then(|i| {
35 if i == 0 {
36 None
37 } else {
38 Some(i)
39 }
40 }),
41 episode: splitted[2].parse::<u32>().ok().and_then(|i| {
42 if i == 0 {
43 None
44 } else {
45 Some(i)
46 }
47 }),
48 episode_to: None,
49 })
50 } else if splitted.len() == 2 {
51 Ok(FileEpisode {
52 id: splitted[0].to_string(),
53 season: splitted[1].parse::<u32>().ok().and_then(|i| {
54 if i == 0 {
55 None
56 } else {
57 Some(i)
58 }
59 }),
60 episode: None,
61 episode_to: None,
62 })
63 } else {
64 Ok(FileEpisode {
65 id: splitted[0].to_string(),
66 season: None,
67 episode: None,
68 episode_to: None,
69 })
70 }
71 }
72}
73
74#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
75pub struct MediaItemReference {
76 pub id: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub conf: Option<u16>,
79}
80
81impl FromStr for MediaItemReference {
82 type Err = String;
83
84 fn from_str(s: &str) -> Result<Self, Self::Err> {
85 let splitted: Vec<&str> = s.split('|').collect();
86 if splitted.len() == 2 {
87 Ok(MediaItemReference {
88 id: splitted[0].to_string(),
89 conf: splitted[1].parse::<u16>().ok().and_then(|e| {
90 if e == 100 {
91 None
92 } else {
93 Some(e)
94 }
95 }),
96 })
97 } else {
98 Ok(MediaItemReference {
99 id: splitted[0].to_string(),
100 conf: None,
101 })
102 }
103 }
104}
105
106#[derive(
107 Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display, EnumString, Default,
108)]
109#[strum(serialize_all = "camelCase")]
110#[serde(rename_all = "camelCase")]
111pub enum FileType {
112 Directory,
113 Photo,
114 Video,
115 Archive,
116 Album,
117 #[default]
118 Other,
119}
120
121#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
122#[serde(rename_all = "camelCase")]
123pub struct Media {
124 pub id: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub source: Option<String>,
127 pub name: String,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub description: Option<String>,
130
131 #[serde(rename = "type")]
132 pub kind: FileType,
133 pub mimetype: String,
134 pub size: Option<u64>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub params: Option<Value>,
138
139 pub added: Option<i64>,
140 pub modified: Option<i64>,
141 pub created: Option<i64>,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub rating: Option<f32>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub avg_rating: Option<f32>,
147
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub md5: Option<String>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub width: Option<usize>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub height: Option<usize>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub phash: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub thumbhash: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub focal: Option<u64>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub iso: Option<u64>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub color_space: Option<String>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub icc: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub mp: Option<u32>,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub sspeed: Option<String>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub f_number: Option<f64>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub orientation: Option<usize>,
175
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub duration: Option<usize>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub acodecs: Option<Vec<String>>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub achan: Option<Vec<usize>>,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub vcodecs: Option<Vec<String>>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub fps: Option<f64>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub bitrate: Option<u64>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub long: Option<f64>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub lat: Option<f64>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub model: Option<String>,
195
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub pages: Option<usize>,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub progress: Option<usize>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub tags: Option<Vec<MediaItemReference>>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub series: Option<Vec<FileEpisode>>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub people: Option<Vec<MediaItemReference>>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub faces: Option<Vec<FaceEmbedding>>,
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub backups: Option<Vec<BackupFile>>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub thumb: Option<String>,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub thumbv: Option<usize>,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub thumbsize: Option<u64>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 pub iv: Option<String>,
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub origin: Option<RsLink>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub movie: Option<String>,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub book: Option<String>,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub lang: Option<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub uploader: Option<String>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub uploadkey: Option<String>,
231
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub original_hash: Option<String>,
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub original_id: Option<String>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub face_recognition_error: Option<String>,
239}
240
241impl Media {
242 pub fn max_date(&self) -> i64 {
243 *[
244 self.created.unwrap_or(0),
245 self.added.unwrap_or(0),
246 self.modified.unwrap_or(0),
247 ]
248 .iter()
249 .max()
250 .unwrap_or(&0)
251 }
252
253 pub fn bytes_size(&self) -> Option<u64> {
254 if self.iv.is_none() {
255 self.size
256 } else {
257 if let Some(file_size) = self.size {
265 Some(file_size + 16 + 4 + 4 + 32 + 256 + self.thumbsize.unwrap_or(0) + 0)
266 } else {
267 None
268 }
269 }
270 }
271}
272
273#[derive(Debug, Serialize, Deserialize, Clone, Default)]
274#[serde(rename_all = "camelCase")]
275pub struct RsGpsPosition {
276 pub lat: f64,
277 pub long: f64,
278}
279
280
281#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
282#[serde(rename_all = "camelCase")]
283pub struct FaceEmbedding {
284 pub id: String,
285 pub embedding: Vec<f32>,
286 pub media_ref: Option<String>,
287 pub bbox: Option<FaceBBox>,
288 pub confidence: Option<f32>,
289 pub pose: Option<(f32, f32, f32)>,
290 pub person_id: Option<String>,
291}
292
293#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
294#[serde(rename_all = "camelCase")]
295pub struct FaceBBox {
296 pub x1: f32,
297 pub y1: f32,
298 pub x2: f32,
299 pub y2: f32,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub video_s: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
303 pub video_percent: Option<u32>, }