Skip to main content

tauri_plugin_medialibrary/
models.rs

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