tauri_plugin_medialibrary/
models.rs1use 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}