diff --git a/Cargo.toml b/Cargo.toml index 2879b15..6e5d4de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ repository = "https://github.com/trivernis/hydrus-api-rs" serde = {version = "^1.0", features = ["derive"]} reqwest = {version = "0.11.4", features = ["json"]} log = "0.4.14" +mime = "0.3.16" [dev-dependencies] env_logger = "0.8.4" diff --git a/src/endpoints/common.rs b/src/endpoints/common.rs index ac9fb65..6160ee6 100644 --- a/src/endpoints/common.rs +++ b/src/endpoints/common.rs @@ -22,7 +22,7 @@ pub struct FileMetadataInfo { pub height: Option, pub duration: Option, pub has_audio: Option, - pub num_frames: Option, + pub num_frames: Option, pub num_words: Option, pub is_inbox: bool, pub is_local: bool, diff --git a/src/error.rs b/src/error.rs index c62f74e..4bc8549 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,7 @@ pub enum Error { ImportVetoed(String), ImportFailed(String), FileNotFound(FileIdentifier), + InvalidMime(String), } impl fmt::Display for Error { @@ -25,6 +26,7 @@ impl fmt::Display for Error { Self::ImportFailed(msg) => write!(f, "File import failed: {}", msg), Self::ImportVetoed(msg) => write!(f, "File import vetoed: {}", msg), Self::FileNotFound(id) => write!(f, "File {:?} not found", id), + Self::InvalidMime(mime) => write!(f, "Failed to parse invalid mime {}", mime), } } } diff --git a/src/models/hydrus_file.rs b/src/models/hydrus_file.rs index 51abc5a..6c8267f 100644 --- a/src/models/hydrus_file.rs +++ b/src/models/hydrus_file.rs @@ -1,10 +1,11 @@ use crate::endpoints::adding_tags::{AddTagsRequestBuilder, TagAction}; use crate::endpoints::common::{FileIdentifier, FileMetadataInfo, FileRecord}; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::service::ServiceName; use crate::tag::Tag; use crate::utils::tag_list_to_string_list; use crate::Client; +use mime::Mime; use std::collections::HashMap; #[derive(Clone, Debug, PartialOrd, PartialEq)] @@ -77,25 +78,6 @@ impl HydrusFile { Ok(()) } - /// Returns the metadata for the given file - /// if there's already known metadata about the file it uses that - async fn metadata(&mut self) -> Result<&FileMetadataInfo> { - if self.metadata.is_none() { - let metadata = self - .client - .get_file_metadata_by_identifier(self.id.clone()) - .await?; - self.status = if metadata.is_trashed { - FileStatus::Deleted - } else { - FileStatus::InDatabase - }; - self.metadata = Some(metadata); - } - - Ok(self.metadata.as_ref().unwrap()) - } - /// Returns the hash of the file /// if the file identifier is an id it calls hydrus to resolve the file pub async fn hash(&mut self) -> Result { @@ -108,6 +90,84 @@ impl HydrusFile { } } + /// Returns the file size in bytes + pub async fn size(&mut self) -> Result> { + let metadata = self.metadata().await?; + + Ok(metadata.size.clone()) + } + + /// Returns the mime of the file + pub async fn mime(&mut self) -> Result { + let metadata = self.metadata().await?; + let mime = metadata + .mime + .as_str() + .parse() + .map_err(|_| Error::InvalidMime(metadata.mime.clone()))?; + + Ok(mime) + } + + /// Return the file extension + pub async fn ext(&mut self) -> Result { + let metadata = self.metadata().await?; + + Ok(metadata.ext.clone()) + } + + /// Returns the dimensions of the file in pixels + pub async fn dimensions(&mut self) -> Result> { + let metadata = self.metadata().await?; + if let (Some(width), Some(height)) = (&metadata.width, &metadata.height) { + Ok(Some((*width, *height))) + } else { + Ok(None) + } + } + + /// Returns the duration of the file in seconds if it's a video + pub async fn duration(&mut self) -> Result> { + let metadata = self.metadata().await?; + + Ok(metadata.duration.clone()) + } + + /// Returns the number of frames of the file if it's a video + pub async fn num_frames(&mut self) -> Result> { + let metadata = self.metadata().await?; + + Ok(metadata.num_frames.clone()) + } + + /// Returns if the file has audio + pub async fn has_audio(&mut self) -> Result { + let metadata = self.metadata().await?; + + Ok(metadata.has_audio.unwrap_or(false)) + } + + /// Returns if the file is currently in the inbox + pub async fn in_inbox(&mut self) -> Result { + let metadata = self.metadata().await?; + + Ok(metadata.is_inbox) + } + + /// Returns if the file is stored locally + pub async fn stored_locally(&mut self) -> Result { + let metadata = self.metadata().await?; + + Ok(metadata.is_local) + } + + /// Returns if the file has been moved to the trash + pub async fn moved_to_trashed(&mut self) -> Result { + let metadata = self.metadata().await?; + + Ok(metadata.is_trashed) + } + /// Associates the file with a list of urls pub async fn associate_urls(&mut self, urls: Vec) -> Result<()> { let hash = self.hash().await?; @@ -182,4 +242,23 @@ impl HydrusFile { pub async fn retrieve(&self) -> Result { self.client.get_file(self.id.clone()).await } + + /// Returns the metadata for the given file + /// if there's already known metadata about the file it uses that + async fn metadata(&mut self) -> Result<&FileMetadataInfo> { + if self.metadata.is_none() { + let metadata = self + .client + .get_file_metadata_by_identifier(self.id.clone()) + .await?; + self.status = if metadata.is_trashed { + FileStatus::Deleted + } else { + FileStatus::InDatabase + }; + self.metadata = Some(metadata); + } + + Ok(self.metadata.as_ref().unwrap()) + } } diff --git a/tests/wrapper/test_files.rs b/tests/wrapper/test_files.rs index 1fccbc4..2304681 100644 --- a/tests/wrapper/test_files.rs +++ b/tests/wrapper/test_files.rs @@ -80,3 +80,11 @@ async fn it_retrieves_content() { assert!(file.bytes.len() > 0) // assuming it exists } + +#[tokio::test] +async fn it_retrieves_metadata() { + let mut file = get_file().await; + assert!(file.dimensions().await.unwrap().is_some()); + assert!(file.stored_locally().await.unwrap()); + assert!(file.duration().await.unwrap().is_none()); +}