upend/src/previews/mod.rs

127 lines
4.1 KiB
Rust

use crate::database::stores::UpStore;
use crate::util::hash::b58_encode;
use crate::util::hash::Hash;
use crate::util::jobs::{JobContainer, JobState};
use anyhow::{anyhow, Result};
use log::{debug, trace};
use std::{
collections::HashMap,
fs::File,
io::Write,
path::{Path, PathBuf},
sync::{Arc, Mutex},
};
use self::audio::AudioPath;
use self::image::ImagePath;
use self::text::TextPath;
use self::video::VideoPath;
pub mod audio;
pub mod image;
pub mod text;
pub mod video;
pub trait Previewable {
fn get_thumbnail(&self) -> Result<Option<Vec<u8>>>;
}
pub struct PreviewStore {
path: PathBuf,
store: Arc<Box<dyn UpStore + Send + Sync>>,
locks: Mutex<HashMap<Hash, Arc<Mutex<PathBuf>>>>,
}
#[cfg(feature = "previews")]
impl PreviewStore {
pub fn new<P: AsRef<Path>>(path: P, store: Arc<Box<dyn UpStore + Send + Sync>>) -> Self {
PreviewStore {
path: PathBuf::from(path.as_ref()),
store,
locks: Mutex::new(HashMap::new()),
}
}
fn get_path(&self, hash: &Hash) -> Arc<Mutex<PathBuf>> {
let mut locks = self.locks.lock().unwrap();
if let Some(path) = locks.get(hash) {
path.clone()
} else {
let thumbpath = self.path.join(b58_encode(hash));
let path = Arc::new(Mutex::new(thumbpath));
locks.insert(hash.clone(), path.clone());
path
}
}
pub fn get<S>(
&self,
hash: Hash,
mime_type: S,
mut job_container: JobContainer,
) -> Result<Option<PathBuf>>
where
S: Into<Option<String>>,
{
debug!("Preview for {hash:?} requested...");
let path_mutex = self.get_path(&hash);
let thumbpath = path_mutex.lock().unwrap();
if thumbpath.exists() {
trace!("Preview for {hash:?} already exists, returning {thumbpath:?}");
Ok(Some(thumbpath.clone()))
} else {
trace!("Calculating preview for {hash:?}...");
let files = self.store.retrieve(&hash)?;
if let Some(file) = files.get(0) {
let file_path = file.get_file_path();
let mut job_handle = job_container.add_job(
None,
&format!("Creating preview for {:?}", file_path.file_name().unwrap()),
)?;
let mime_type = mime_type.into();
let mime_type: Option<String> = if mime_type.is_some() {
mime_type
} else {
tree_magic_mini::from_filepath(file_path).map(|m| m.into())
};
let preview = match mime_type {
Some(tm) if tm.starts_with("text") => TextPath(file_path).get_thumbnail(),
Some(tm) if tm.starts_with("video") || tm == "application/x-matroska" => {
VideoPath(file_path).get_thumbnail()
}
Some(tm) if tm.starts_with("audio") || tm == "application/x-riff" => {
AudioPath(file_path).get_thumbnail()
}
Some(tm) if tm.starts_with("image") => ImagePath(file_path).get_thumbnail(),
Some(unknown) => Err(anyhow!("No capability for {:?} thumbnails.", unknown)),
_ => Err(anyhow!("Unknown file type, or file doesn't exist.")),
};
match preview {
Ok(preview) => {
trace!("Got preview for {hash:?}.");
let _ = job_handle.update_state(JobState::Done);
if let Some(data) = preview {
std::fs::create_dir_all(&self.path)?;
let mut file = File::create(&*thumbpath)?;
file.write_all(&data)?;
Ok(Some(thumbpath.clone()))
} else {
Ok(None)
}
}
Err(err) => Err(err),
}
} else {
Err(anyhow!("Object not found, or is not a file."))
}
}
}
}