Skip to main content

tauri_plugin_medialibrary/
models.rs

1use 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}