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