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 Book,
118 #[default]
119 Other,
120}
121
122#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
123#[serde(rename_all = "camelCase")]
124pub struct Media {
125 pub id: String,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub source: Option<String>,
128 pub name: String,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub description: Option<String>,
131
132 #[serde(rename = "type")]
133 pub kind: FileType,
134 pub mimetype: String,
135 pub size: Option<u64>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub params: Option<Value>,
139
140 pub added: Option<i64>,
141 pub modified: Option<i64>,
142 pub created: Option<i64>,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub rating: Option<f32>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub avg_rating: Option<f32>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub md5: Option<String>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub width: Option<usize>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub height: Option<usize>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub phash: Option<String>,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub thumbhash: Option<String>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub focal: Option<u64>,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub iso: Option<u64>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub color_space: Option<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub icc: Option<String>,
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub mp: Option<u32>,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub sspeed: Option<String>,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub f_number: Option<f64>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub orientation: Option<usize>,
176
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub duration: Option<usize>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub acodecs: Option<Vec<String>>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub achan: Option<Vec<usize>>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub vcodecs: Option<Vec<String>>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub fps: Option<f64>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub bitrate: Option<u64>,
189
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub long: Option<f64>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub lat: Option<f64>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub model: Option<String>,
196
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub pages: Option<usize>,
199
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub progress: Option<usize>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub tags: Option<Vec<MediaItemReference>>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub series: Option<Vec<FileEpisode>>,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub people: Option<Vec<MediaItemReference>>,
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub faces: Option<Vec<FaceEmbedding>>,
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub backups: Option<Vec<BackupFile>>,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub thumb: Option<String>,
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub thumbv: Option<usize>,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub thumbsize: Option<u64>,
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub iv: Option<String>,
220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub origin: Option<RsLink>,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub movie: Option<String>,
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub book: Option<String>,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub lang: Option<String>,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub uploader: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub uploadkey: Option<String>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub original_hash: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub original_id: Option<String>,
237
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub face_recognition_error: Option<String>,
240}
241
242impl Media {
243 pub fn max_date(&self) -> i64 {
244 *[
245 self.created.unwrap_or(0),
246 self.added.unwrap_or(0),
247 self.modified.unwrap_or(0),
248 ]
249 .iter()
250 .max()
251 .unwrap_or(&0)
252 }
253
254 pub fn bytes_size(&self) -> Option<u64> {
255 if self.iv.is_none() {
256 self.size
257 } else {
258 if let Some(file_size) = self.size {
266 Some(file_size + 16 + 4 + 4 + 32 + 256 + self.thumbsize.unwrap_or(0) + 0)
267 } else {
268 None
269 }
270 }
271 }
272}
273
274#[derive(Debug, Serialize, Deserialize, Clone, Default)]
275#[serde(rename_all = "camelCase")]
276pub struct RsGpsPosition {
277 pub lat: f64,
278 pub long: f64,
279}
280
281
282#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
283#[serde(rename_all = "camelCase")]
284pub struct FaceEmbedding {
285 pub id: String,
286 pub embedding: Vec<f32>,
287 pub media_ref: Option<String>,
288 pub bbox: Option<FaceBBox>,
289 pub confidence: Option<f32>,
290 pub pose: Option<(f32, f32, f32)>,
291 pub person_id: Option<String>,
292}
293
294#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
295#[serde(rename_all = "camelCase")]
296pub struct FaceBBox {
297 pub x1: f32,
298 pub y1: f32,
299 pub x2: f32,
300 pub y2: f32,
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub video_s: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
304 pub video_percent: Option<u32>, }