From 144b95dcfd8848694300c4f8433011066b175275 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 9 Nov 2021 17:53:24 +0100 Subject: [PATCH] Add command to retrieve files for a list of paths Signed-off-by: trivernis --- mediarepo-api/Cargo.toml | 5 +- .../src/tauri_plugin/commands/file.rs | 86 ++++++++++++++++++- mediarepo-api/src/tauri_plugin/mod.rs | 4 +- mediarepo-api/src/tauri_plugin/utils.rs | 12 +++ mediarepo-api/src/types/files.rs | 9 ++ 5 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 mediarepo-api/src/tauri_plugin/utils.rs diff --git a/mediarepo-api/Cargo.toml b/mediarepo-api/Cargo.toml index 07998d7..89ac073 100644 --- a/mediarepo-api/Cargo.toml +++ b/mediarepo-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mediarepo-api" -version = "0.5.1" +version = "0.5.2" edition = "2018" license = "gpl-3" @@ -14,6 +14,7 @@ rmp-ipc = {version = "0.9.0", optional=true} parking_lot = {version="0.11.2", optional=true} serde_json = {version="1.0.68", optional=true} directories = {version="4.0.1", optional=true} +mime_guess = {version = "2.0.3", optional=true} serde_piecewise_default = "0.2.0" [dependencies.serde] @@ -40,5 +41,5 @@ version = "0.5.8" optional = true [features] -tauri-plugin = ["client-api","tauri", "rmp-ipc", "parking_lot", "serde_json", "tokio", "toml", "directories"] +tauri-plugin = ["client-api","tauri", "rmp-ipc", "parking_lot", "serde_json", "tokio", "toml", "directories", "mime_guess"] client-api = ["rmp-ipc", "async-trait", "tokio"] \ No newline at end of file diff --git a/mediarepo-api/src/tauri_plugin/commands/file.rs b/mediarepo-api/src/tauri_plugin/commands/file.rs index 08ac34f..8ea6827 100644 --- a/mediarepo-api/src/tauri_plugin/commands/file.rs +++ b/mediarepo-api/src/tauri_plugin/commands/file.rs @@ -1,7 +1,13 @@ use crate::tauri_plugin::commands::{add_once_buffer, ApiAccess, BufferAccess}; use crate::tauri_plugin::error::PluginResult; -use crate::types::files::{FileMetadataResponse, SortKey, TagQuery, ThumbnailMetadataResponse}; +use crate::tauri_plugin::utils::system_time_to_naive_date_time; +use crate::types::files::{ + FileMetadataResponse, FileOSMetadata, SortKey, TagQuery, ThumbnailMetadataResponse, +}; use crate::types::identifier::FileIdentifier; +use std::path::PathBuf; +use tokio::fs; +use tokio::fs::DirEntry; #[tauri::command] pub async fn get_all_files(api_state: ApiAccess<'_>) -> PluginResult> { @@ -108,3 +114,81 @@ pub async fn update_file_name( Ok(metadata) } + +#[tauri::command] +pub async fn resolve_paths_to_files(paths: Vec) -> PluginResult> { + let mut files = Vec::new(); + + for path in paths { + let path = PathBuf::from(path); + if path.exists() { + files.append(&mut resolve_path_to_files(path).await?); + } + } + + Ok(files) +} + +/// Resolves a path into several file metadata objects +async fn resolve_path_to_files(path: PathBuf) -> PluginResult> { + let mut files = Vec::new(); + + if path.is_dir() { + let mut read_dir = fs::read_dir(path).await?; + + while let Some(entry) = read_dir.next_entry().await? { + let subdir_entries = resolve_subdir(entry).await?; + for entry in subdir_entries { + let metadata = retrieve_file_information(entry.path()).await?; + files.push(metadata); + } + } + } else { + let metadata = retrieve_file_information(path).await?; + files.push(metadata); + } + + Ok(files) +} + +/// Iteratively resolves a directory into its sub components +async fn resolve_subdir(entry: DirEntry) -> PluginResult> { + let mut entries = vec![entry]; + + for i in 0..entries.len() { + let entry = &entries[i]; + + if entry.path().is_dir() { + let mut read_dir = fs::read_dir(entry.path()).await?; + while let Some(entry) = read_dir.next_entry().await? { + entries.push(entry); + } + } + } + + Ok(entries) +} + +/// Retrieves information about a path that MUST be a file and returns +/// metadata for it +async fn retrieve_file_information(path: PathBuf) -> PluginResult { + let mime = mime_guess::from_path(&path) + .first() + .ok_or_else(|| format!("Could not guess mime for file {:?}", path))?; + let metadata = fs::metadata(&path).await?; + let creation_time = metadata.created()?; + let change_time = metadata.modified()?; + let name = path + .file_name() + .ok_or_else(|| "Could not retrieve file name")?; + + let os_metadata = FileOSMetadata { + path: path.to_string_lossy().to_string(), + name: name.to_string_lossy().to_string(), + mime_type: mime.to_string(), + creation_time: system_time_to_naive_date_time(creation_time), + change_time: system_time_to_naive_date_time(change_time), + }; + + Ok(os_metadata) +} diff --git a/mediarepo-api/src/tauri_plugin/mod.rs b/mediarepo-api/src/tauri_plugin/mod.rs index 2a3ff0a..004d6f4 100644 --- a/mediarepo-api/src/tauri_plugin/mod.rs +++ b/mediarepo-api/src/tauri_plugin/mod.rs @@ -12,6 +12,7 @@ pub mod custom_schemes; pub mod error; mod settings; mod state; +mod utils; use commands::*; @@ -52,7 +53,8 @@ impl MediarepoPlugin { remove_repository, change_file_tags, create_tags, - update_file_name + update_file_name, + resolve_paths_to_files ]), } } diff --git a/mediarepo-api/src/tauri_plugin/utils.rs b/mediarepo-api/src/tauri_plugin/utils.rs new file mode 100644 index 0000000..d3a4035 --- /dev/null +++ b/mediarepo-api/src/tauri_plugin/utils.rs @@ -0,0 +1,12 @@ +use chrono::NaiveDateTime; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Converts a system time timestamp to a NaiveDateTime object +pub fn system_time_to_naive_date_time(system_time: SystemTime) -> NaiveDateTime { + let epoch_duration = system_time.duration_since(UNIX_EPOCH).unwrap(); + + NaiveDateTime::from_timestamp( + epoch_duration.as_secs() as i64, + epoch_duration.subsec_nanos(), + ) +} diff --git a/mediarepo-api/src/types/files.rs b/mediarepo-api/src/types/files.rs index 83609f8..3f03694 100644 --- a/mediarepo-api/src/types/files.rs +++ b/mediarepo-api/src/types/files.rs @@ -85,6 +85,15 @@ pub struct FileMetadataResponse { pub import_time: NaiveDateTime, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct FileOSMetadata { + pub path: String, + pub name: String, + pub mime_type: String, + pub creation_time: NaiveDateTime, + pub change_time: NaiveDateTime, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ThumbnailMetadataResponse { pub id: i64,