tauri_plugin_medialibrary/
models.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::{
4 collections::HashMap,
5 fmt::Display,
6 fs::{self},
7 path::Path,
8};
9use tauri::plugin::PermissionState;
10
11#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
12pub enum MediaLibrarySource {
13 #[cfg(not(target_os = "android"))]
14 PictureDir,
15 #[cfg(target_os = "android")]
16 ExternalStorage,
17 #[cfg(target_os = "android")]
18 VolumeExternalPrimary,
19}
20
21#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
22pub enum SortColumn {
23 #[default]
24 DateAdded,
25 DateModified,
26 #[cfg(target_os = "android")]
27 DateTaken,
28}
29
30#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
31pub enum SortDirection {
32 #[default]
33 Ascending,
34 Descending,
35}
36
37#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)]
38#[serde(rename_all = "camelCase")]
39pub enum MetaDataField {
40 DateAdded,
41 DateModified,
42 DateTaken,
43 FileCreated,
44 FileModified,
45 FileSize,
46 FileName,
47 FileExtension,
48 FileReadOnly,
49}
50
51impl Display for MediaLibrarySource {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 #[cfg(not(target_os = "android"))]
55 MediaLibrarySource::PictureDir => write!(f, "PictureDir"),
56 #[cfg(target_os = "android")]
57 MediaLibrarySource::ExternalStorage => write!(f, "ExternalStorage"),
58 #[cfg(target_os = "android")]
59 MediaLibrarySource::VolumeExternalPrimary => write!(f, "VolumeExternalPrimary"),
60 }
61 }
62}
63
64impl Default for MediaLibrarySource {
65 fn default() -> Self {
66 #[cfg(target_os = "android")]
67 return MediaLibrarySource::ExternalStorage;
68
69 #[cfg(not(target_os = "android"))]
70 return MediaLibrarySource::PictureDir;
71 }
72}
73
74#[derive(Debug, Clone, Default, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct GetLibraryContentRequest {
77 pub limit: usize,
78 pub offset: usize,
79 pub source: MediaLibrarySource,
80 pub sort_column: Option<SortColumn>,
81 pub sort_direction: Option<SortDirection>,
82 pub include_file_metadata: Option<bool>,
83}
84
85#[derive(Debug, Clone, Default, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct GetImageRequest {
88 pub uri: String,
89}
90
91impl From<String> for GetImageRequest {
92 fn from(uri: String) -> Self {
93 GetImageRequest { uri }
94 }
95}
96
97#[derive(Debug, Clone, Default, Serialize, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct DeleteImageRequest {
100 pub uri: String,
101}
102
103impl From<String> for DeleteImageRequest {
104 fn from(uri: String) -> Self {
105 DeleteImageRequest { uri }
106 }
107}
108
109#[derive(Debug, Clone, Default, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct RequestPermissionsArgs {
112 pub source: MediaLibrarySource,
113}
114
115#[derive(Debug, Clone, Default, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct ImageInfo {
118 pub path: String,
119 pub content_uri: String,
120 pub mime_type: String,
121 pub meta_data: Option<HashMap<MetaDataField, String>>,
122}
123
124#[derive(Debug, Clone, Default, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct GetImagesResult {
127 pub items: Vec<ImageInfo>,
128}
129
130#[derive(Deserialize, Default, Debug, Serialize, Clone)]
131#[serde(rename_all = "camelCase")]
132pub struct PermissionResponse {
133 pub post_notification: PermissionState,
134}
135
136#[derive(Debug, Clone, Default, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct GetThumbnailResponse {
139 pub content: String,
140}
141
142impl PermissionResponse {
143 pub fn granted() -> Self {
144 Self {
145 post_notification: PermissionState::Granted,
146 }
147 }
148}
149
150impl GetImagesResult {
151 pub fn with_file_metadata(self, include_file_metadata: bool) -> Self {
152 if include_file_metadata {
153 let items = self
154 .items
155 .into_iter()
156 .map(|item| item.with_file_metadata())
157 .collect();
158
159 return Self { items };
160 }
161 self
162 }
163}
164
165impl ImageInfo {
166 pub fn with_file_metadata(self) -> Self {
167 if let Ok(metadata) = fs::metadata(&self.path) {
168 let mut meta_data = self.meta_data.unwrap_or_default();
169 meta_data.insert(MetaDataField::FileSize, metadata.len().to_string());
170
171 if let Ok(created) = metadata.created() {
172 let datetime: DateTime<Utc> = created.into();
173 let iso_string = datetime.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
174 meta_data.insert(MetaDataField::FileCreated, iso_string);
175 }
176
177 if let Ok(modified) = metadata.modified() {
178 let datetime: DateTime<Utc> = modified.into();
179 let iso_string = datetime.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
180 meta_data.insert(MetaDataField::FileModified, iso_string);
181 }
182
183 meta_data.insert(
184 MetaDataField::FileReadOnly,
185 metadata.permissions().readonly().to_string(),
186 );
187
188 if let Some(file_name) = Path::new(&self.path).file_name() {
189 if let Some(name_str) = file_name.to_str() {
190 meta_data.insert(MetaDataField::FileName, name_str.to_string());
191 }
192 }
193
194 if let Some(extension) = Path::new(&self.path).extension() {
195 if let Some(ext_str) = extension.to_str() {
196 meta_data.insert(MetaDataField::FileExtension, ext_str.to_string());
197 }
198 }
199
200 return Self {
201 meta_data: Some(meta_data),
202 ..self
203 };
204 }
205
206 self
207 }
208}