137 lines
4.4 KiB
Rust
137 lines
4.4 KiB
Rust
|
|
use crate::macros::*;
|
|
use crate::utils::*;
|
|
|
|
use std::fs;
|
|
use std::os::unix::fs::MetadataExt;
|
|
use pathdiff::diff_paths;
|
|
use std::path::{PathBuf};
|
|
use std::fs::{File, Metadata};
|
|
use std::fs::DirEntry;
|
|
use std::io::{BufReader};
|
|
use xdg_mime::{SharedMimeInfo};
|
|
use anyhow::{Context, Result};
|
|
use serde::{de, Deserialize, ser, Serialize};
|
|
use crate::thumbnailer::{Thumbnailers};
|
|
|
|
const DOT_PATH: &str = ".meow_index";
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct ReturnPath {
|
|
pub(crate) name: String,
|
|
pub(crate) file_type: String,
|
|
pub(crate) mtime: i64
|
|
}
|
|
|
|
pub struct Generator {
|
|
pub(crate) mime_db: SharedMimeInfo,
|
|
pub(crate) thumbnailers: Thumbnailers,
|
|
pub(crate) base: PathBuf,
|
|
}
|
|
|
|
impl Generator {
|
|
pub fn new(base: PathBuf) -> Result<Generator> {
|
|
Ok(Generator { mime_db: SharedMimeInfo::new(), thumbnailers: Thumbnailers::load_all()?, base })
|
|
}
|
|
|
|
/// Get the same file location in DOT_PATH directory
|
|
pub fn dot_path(&self, path: &PathBuf) -> PathBuf {
|
|
self.base.join(DOT_PATH).join(diff_paths(&path, &self.base).unwrap())
|
|
}
|
|
|
|
/// Get the cached result
|
|
pub fn get_cached<T>(&self, file: &PathBuf, token: &str, read: impl Fn(&PathBuf) -> Result<T>,
|
|
gen: impl Fn(&PathBuf) -> Result<()>) -> Result<T> {
|
|
let dot = self.dot_path(file).with_extension(token);
|
|
if ! dot.exists() || match (dot.metadata(), file.metadata()) {
|
|
(Ok(dm), Ok(fm)) => { fm.mtime() > dm.mtime() }
|
|
(_, _) => true
|
|
} {
|
|
debug!("Regenerating cached result {}", dot.display());
|
|
gen(&dot)?;
|
|
}
|
|
Ok(read(&dot)?)
|
|
}
|
|
|
|
/// Get the cached result
|
|
pub fn get_cached_json<T>(&self, file: &PathBuf, token: &str, gen: impl Fn() -> Result<T>) -> Result<T>
|
|
where T: de::DeserializeOwned + ?Sized + ser::Serialize {
|
|
self.get_cached(&file, token, |f| {
|
|
let open = File::open(f)?;
|
|
let reader = BufReader::new(open);
|
|
let val: T = serde_json::from_reader(reader)?;
|
|
Ok(val)
|
|
}, |f| {
|
|
let res = gen()?;
|
|
fs::write(f, serde_json::to_string(&res)?)?;
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
/// Get cached mime type
|
|
pub fn get_mime(&self, file: &PathBuf) -> Result<String> {
|
|
self.get_cached(&file, "mime", |f| {
|
|
Ok(fs::read_to_string(f)?)
|
|
}, |f| {
|
|
let mut guesser = self.mime_db.guess_mime_type();
|
|
write_sf(f, guesser.path(file).guess().mime_type().to_string())?;
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
/// Process a single file
|
|
pub fn get_thumb(&self, file: &PathBuf) -> Result<Vec<u8>> {
|
|
self.get_cached(file, "thumb-128.png", |thumb| {
|
|
Ok(fs::read(thumb)?)
|
|
}, |thumb| {
|
|
debug!("Generating thumbnail for {}\nto {}", file.display(), thumb.display());
|
|
let mime = self.get_mime(file)?;
|
|
if let Some(t) = self.thumbnailers.find(&*mime) {
|
|
t.gen(file.to_str().context("Orig file failed to convert to str")?,
|
|
thumb.to_str().context("New file failed to convert to str")?, 128)?;
|
|
}
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
/// Process a directory
|
|
pub fn process_dir(&self, dir: &PathBuf) -> Result<()> {
|
|
// List files
|
|
let files: Vec<(DirEntry, Metadata)> = dir.to_owned().read_dir()?
|
|
.filter_map(|x| x.ok())
|
|
.filter_map(|x| {
|
|
let meta = x.metadata();
|
|
Some((x, meta.ok()?))
|
|
}).collect();
|
|
|
|
// Recurse into directories
|
|
files.iter().for_each(|(f, m)| {
|
|
if f.path().is_dir() {
|
|
// Recurse into directory
|
|
let _ = self.process_dir(&f.path());
|
|
}
|
|
});
|
|
|
|
// Check if directory listing needs update
|
|
let d_info = self.dot_path(&dir).with_extension(".json");
|
|
let d_mtime = d_info.metadata()?.mtime();
|
|
if files.iter().any(|(f, m)| m.mtime() > d_mtime) {
|
|
// Update directory listing
|
|
let paths: Vec<ReturnPath> = files.iter()
|
|
.filter_map(|(f, m)| Some(ReturnPath {
|
|
name: f.file_name().to_str()?.to_string(),
|
|
file_type: f.path().file_type().to_string(),
|
|
mtime: m.mtime(),
|
|
})).collect();
|
|
|
|
let json = serde_json::to_string(&paths)?;
|
|
// fs::write(d_info, json)?;
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|