Add conversion of images when configured
Signed-off-by: trivernis <trivernis@protonmail.com>pull/4/head
parent
3acfe9c6e2
commit
63ea60b10a
@ -0,0 +1,176 @@
|
|||||||
|
use crate::utils::caching::CacheStorage;
|
||||||
|
use crate::utils::downloads::download_path;
|
||||||
|
use image::imageops::FilterType;
|
||||||
|
use image::io::Reader as ImageReader;
|
||||||
|
use image::{GenericImageView, ImageFormat, ImageResult};
|
||||||
|
use mime::Mime;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ImageConverter {
|
||||||
|
images: Vec<Arc<Mutex<PendingImage>>>,
|
||||||
|
target_format: Option<ImageFormat>,
|
||||||
|
target_size: Option<(u32, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageConverter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
images: Vec::new(),
|
||||||
|
target_format: None,
|
||||||
|
target_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_target_size(&mut self, target_size: (u32, u32)) {
|
||||||
|
self.target_size = Some(target_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_target_format(&mut self, target_format: ImageFormat) {
|
||||||
|
self.target_format = Some(target_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an image to convert
|
||||||
|
pub fn add_image(&mut self, path: PathBuf) -> Arc<Mutex<PendingImage>> {
|
||||||
|
let image = Arc::new(Mutex::new(PendingImage::new(path)));
|
||||||
|
self.images.push(image.clone());
|
||||||
|
|
||||||
|
image
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts all images
|
||||||
|
pub fn convert_all(&mut self) {
|
||||||
|
self.images.par_iter().for_each(|image| {
|
||||||
|
let mut image = image.lock();
|
||||||
|
if let Err(e) = image.convert(self.target_format.clone(), self.target_size.clone()) {
|
||||||
|
log::error!("Failed to embed image {:?}: {}", image.path, e)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PendingImage {
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub data: Option<Vec<u8>>,
|
||||||
|
cache: CacheStorage,
|
||||||
|
pub mime: Mime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingImage {
|
||||||
|
pub fn new(path: PathBuf) -> Self {
|
||||||
|
let mime = get_mime(&path);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
data: None,
|
||||||
|
cache: CacheStorage::new(),
|
||||||
|
mime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the image to the specified target format (specified by target_extension)
|
||||||
|
pub fn convert(
|
||||||
|
&mut self,
|
||||||
|
target_format: Option<ImageFormat>,
|
||||||
|
target_size: Option<(u32, u32)>,
|
||||||
|
) -> ImageResult<()> {
|
||||||
|
let format = target_format
|
||||||
|
.or_else(|| {
|
||||||
|
self.path
|
||||||
|
.extension()
|
||||||
|
.and_then(|extension| ImageFormat::from_extension(extension))
|
||||||
|
})
|
||||||
|
.unwrap_or(ImageFormat::Png);
|
||||||
|
let output_path = self.get_output_path(format, target_size);
|
||||||
|
self.mime = get_mime(&output_path);
|
||||||
|
|
||||||
|
if self.cache.has_file(&output_path) {
|
||||||
|
self.data = Some(self.cache.read(&output_path)?)
|
||||||
|
} else {
|
||||||
|
self.convert_image(format, target_size)?;
|
||||||
|
|
||||||
|
if let Some(data) = &self.data {
|
||||||
|
self.cache.write(&output_path, data)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the image
|
||||||
|
fn convert_image(
|
||||||
|
&mut self,
|
||||||
|
format: ImageFormat,
|
||||||
|
target_size: Option<(u32, u32)>,
|
||||||
|
) -> ImageResult<()> {
|
||||||
|
let mut image = ImageReader::open(self.get_path()?)?.decode()?;
|
||||||
|
|
||||||
|
if let Some((width, height)) = target_size {
|
||||||
|
let dimensions = image.dimensions();
|
||||||
|
|
||||||
|
if dimensions.0 > width || dimensions.1 > height {
|
||||||
|
image = image.resize(width, height, FilterType::Lanczos3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let data = Vec::new();
|
||||||
|
let mut writer = Cursor::new(data);
|
||||||
|
|
||||||
|
image.write_to(&mut writer, format)?;
|
||||||
|
self.data = Some(writer.into_inner());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path of the file
|
||||||
|
fn get_path(&self) -> io::Result<PathBuf> {
|
||||||
|
if !self.path.exists() {
|
||||||
|
if self.cache.has_file(&self.path) {
|
||||||
|
return Ok(self.cache.get_file_path(&self.path));
|
||||||
|
}
|
||||||
|
if let Some(data) = download_path(self.path.to_string_lossy().to_string()) {
|
||||||
|
self.cache.write(&self.path, data)?;
|
||||||
|
return Ok(self.cache.get_file_path(&self.path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(self.path.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the output file name after converting the image
|
||||||
|
fn get_output_path(
|
||||||
|
&self,
|
||||||
|
target_format: ImageFormat,
|
||||||
|
target_size: Option<(u32, u32)>,
|
||||||
|
) -> PathBuf {
|
||||||
|
let mut path = self.path.clone();
|
||||||
|
let mut file_name = path.file_stem().unwrap().to_string_lossy().to_string();
|
||||||
|
let extension = target_format.extensions_str()[0];
|
||||||
|
let type_name = format!("{:?}", target_format);
|
||||||
|
|
||||||
|
if let Some(target_size) = target_size {
|
||||||
|
file_name += &*format!("-{}-{}", target_size.0, target_size.1);
|
||||||
|
}
|
||||||
|
file_name += format!("-{}-converted", type_name).as_str();
|
||||||
|
path.set_file_name(file_name);
|
||||||
|
path.set_extension(extension);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mime(path: &PathBuf) -> Mime {
|
||||||
|
let mime = mime_guess::from_ext(
|
||||||
|
path.clone()
|
||||||
|
.extension()
|
||||||
|
.and_then(|e| e.to_str())
|
||||||
|
.unwrap_or("png"),
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
.unwrap_or(mime::IMAGE_PNG);
|
||||||
|
mime
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
pub mod caching;
|
pub mod caching;
|
||||||
pub mod downloads;
|
pub mod downloads;
|
||||||
|
pub mod image_converting;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
|
Loading…
Reference in New Issue