Skip to content

Commit

Permalink
Auto convert files with the wrong extension
Browse files Browse the repository at this point in the history
  • Loading branch information
blopker committed Dec 23, 2024
1 parent 0c542a6 commit d602b7f
Showing 1 changed file with 125 additions and 148 deletions.
273 changes: 125 additions & 148 deletions src-tauri/src/compress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::macos;
use super::settings;
use caesium;
use caesium::parameters::CSParameters;
use image;
use image::DynamicImage;
use image::{self, ImageReader};
use image::{DynamicImage, ImageFormat};
use serde;
use specta::Type;
use std::fs;
Expand Down Expand Up @@ -43,7 +43,7 @@ pub enum FileEntryStatus {
Error,
}

#[derive(Debug, Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize, Type)]
#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize, Type)]
pub enum ImageType {
JPEG,
PNG,
Expand All @@ -52,6 +52,55 @@ pub enum ImageType {
TIFF,
}

impl ImageType {
// pub fn to_mime_type(&self) -> String {
// match self {
// ImageType::JPEG => "image/jpeg".to_string(),
// ImageType::PNG => "image/png".to_string(),
// ImageType::WEBP => "image/webp".to_string(),
// ImageType::GIF => "image/gif".to_string(),
// ImageType::TIFF => "image/tiff".to_string(),
// }
// }
pub fn extensions(&self) -> &[&str] {
match self {
ImageType::JPEG => ImageFormat::Jpeg.extensions_str(),
ImageType::PNG => ImageFormat::Png.extensions_str(),
ImageType::WEBP => ImageFormat::WebP.extensions_str(),
ImageType::GIF => ImageFormat::Gif.extensions_str(),
ImageType::TIFF => ImageFormat::Tiff.extensions_str(),
}
}
// pub fn from_extension(ext: &str) -> Option<Self> {
// match ext {
// "jpg" | "jpeg" => Some(ImageType::JPEG),
// "png" => Some(ImageType::PNG),
// "webp" => Some(ImageType::WEBP),
// "gif" => Some(ImageType::GIF),
// "tiff" => Some(ImageType::TIFF),
// _ => None,
// }
// }
pub fn to_casium_type(&self) -> caesium::SupportedFileTypes {
match self {
ImageType::JPEG => caesium::SupportedFileTypes::Jpeg,
ImageType::PNG => caesium::SupportedFileTypes::Png,
ImageType::WEBP => caesium::SupportedFileTypes::WebP,
ImageType::GIF => caesium::SupportedFileTypes::Gif,
ImageType::TIFF => caesium::SupportedFileTypes::Tiff,
}
}
pub fn preferred_extension(&self) -> &str {
match self {
ImageType::JPEG => "jpg",
ImageType::PNG => "png",
ImageType::WEBP => "webp",
ImageType::GIF => "gif",
ImageType::TIFF => "tiff",
}
}
}

#[derive(serde::Serialize, serde::Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct CompressResult {
Expand Down Expand Up @@ -94,17 +143,7 @@ pub async fn process_img(
// if not savings, delete temp file, return
// if out path is same as original, delete original
// move temp file to out path
let out_path = get_out_path(&parameters, &file.path);

if file.path == out_path && !parameters.should_overwrite {
return Err(CompressError {
error: "Image would be overwritten. Enable Overwrite in settings to allow this."
.to_string(),
error_type: CompressErrorType::WontOverwrite,
});
}

let original_img = match read_image(&file.path) {
let (original_img, original_image_type) = match read_image(&file.path) {
Ok(img) => img,
Err(err) => {
return Err(CompressError {
Expand All @@ -114,18 +153,19 @@ pub async fn process_img(
}
};

let out_path = get_out_path(&parameters, &file.path, &original_image_type);

if file.path == out_path && !parameters.should_overwrite {
return Err(CompressError {
error: "Image would be overwritten. Enable Overwrite in settings to allow this."
.to_string(),
error_type: CompressErrorType::WontOverwrite,
});
}

let csparams = create_csparameters(&parameters, original_img.width(), original_img.height());
drop(original_img);

let original_image_type = match guess_image_type(&file.path) {
Ok(img) => img,
Err(err) => {
return Err(CompressError {
error: err,
error_type: CompressErrorType::UnsupportedFileType,
})
}
};
let should_convert =
parameters.should_convert && parameters.convert_extension != original_image_type;

Expand Down Expand Up @@ -197,70 +237,6 @@ pub async fn process_img(
})
}

// #[derive(Debug)]
// struct ScanError {
// error: String,
// path: String,
// }

// pub fn scan_directory_for_images(
// path: impl Into<PathBuf>,
// ) -> impl Stream<Item = Result<PathBuf, ScanError>> {
// let (tx, rx) = mpsc::channel(100);
// let path = path.into();
// if !path.is_dir() {
// if is_image(&path) {
// tx.send(Ok(path));
// } else {
// tx.send(Err(ScanError {
// path: path.to_string_lossy().to_string(),
// error: "Not a directory or image".to_string(),
// }));
// }
// return ReceiverStream::new(rx);
// }
// tokio::spawn(async move {
// async fn visit_dirs(
// dir: PathBuf,
// tx: mpsc::Sender<Result<PathBuf, ScanError>>,
// ) -> Result<(), String> {
// let read_dir = tokio::fs::read_dir(&dir).await.map_err(|e| e.to_string())?;

// let mut read_dir = read_dir;
// while let Some(entry) = read_dir.next_entry().await.map_err(|e| e.to_string())? {
// let path = entry.path();
// if is_image(&path) {
// if tx.send(Ok(path)).await.is_err() {
// break;
// }
// } else if path.is_dir() {
// let future = visit_dirs(path.clone(), tx.clone());
// if let Err(e) = Box::pin(future).await {
// let _ = tx
// .send(Err(ScanError {
// path: path.to_string_lossy().to_string(),
// error: e,
// }))
// .await;
// }
// }
// }
// Ok(())
// }

// if let Err(e) = Box::pin(visit_dirs(path.clone(), tx.clone())).await {
// let _ = tx
// .send(Err(ScanError {
// path: path.to_string_lossy().to_string(),
// error: e,
// }))
// .await;
// }
// });

// ReceiverStream::new(rx)
// }

fn get_temp_path(path: &str) -> String {
// /original/path/test.png -> /original/path/.test.png
let path = Path::new(&path);
Expand All @@ -272,42 +248,62 @@ fn get_temp_path(path: &str) -> String {
.to_string()
}

fn read_image(path: &str) -> Result<DynamicImage, String> {
let image = image::open(&path);
match image {
Ok(image) => Ok(image),
Err(err) => Err(format!("Error: {}", err)),
fn read_image(path: &str) -> Result<(DynamicImage, ImageType), String> {
let image = ImageReader::open(&path)
.map_err(|e| e.to_string())?
.with_guessed_format();

let format = match &image {
Ok(image) => match image.format() {
Some(ImageFormat::Jpeg) => Some(ImageType::JPEG),
Some(ImageFormat::Png) => Some(ImageType::PNG),
Some(ImageFormat::WebP) => Some(ImageType::WEBP),
Some(ImageFormat::Gif) => Some(ImageType::GIF),
Some(ImageFormat::Tiff) => Some(ImageType::TIFF),
f => {
return Err(format!(
"Error: Unsupported image type: {}",
f.unwrap().to_mime_type()
))
}
},
Err(_) => None,
};

if format.is_none() {
return Err("Error: Unsupported image type.".to_string());
}
}

fn guess_image_type(path: &str) -> Result<ImageType, String> {
let kind =
infer::get_from_path(path).map_err(|e| format!("Error determining file type: {}", e))?;
match kind {
Some(kind) => match kind.mime_type() {
"image/jpeg" => Ok(ImageType::JPEG),
"image/png" => Ok(ImageType::PNG),
"image/webp" => Ok(ImageType::WEBP),
"image/gif" => Ok(ImageType::GIF),
"image/tiff" => Ok(ImageType::TIFF),
_ => Err(format!(
"Error: Unsupported image type: {}",
kind.mime_type()
)),
},
None => Err("Error: Could not determine image type.".to_string()),
match image {
Ok(image) => Ok((image.decode().map_err(|e| e.to_string())?, format.unwrap())),
Err(err) => Err(format!("Read error: {}", err)),
}
}

fn get_out_path(parameters: &settings::ProfileData, path: &str) -> String {
fn get_out_path(
parameters: &settings::ProfileData,
path: &str,
guessed_format: &ImageType,
) -> String {
let path = Path::new(&path);
let file_extension = path
.extension()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let original_extension = match guessed_format
.extensions()
.contains(&file_extension.as_str())
{
true => file_extension,
false => guessed_format.preferred_extension().to_string(),
};
let extension = match parameters.should_convert {
true => image_type_to_extension(parameters.convert_extension),
false => path
.extension()
.unwrap_or_default()
.to_string_lossy()
true => parameters
.convert_extension
.preferred_extension()
.to_string(),
false => original_extension,
};
let posfix = match parameters.add_posfix {
true => parameters.postfix.clone(),
Expand All @@ -316,16 +312,6 @@ fn get_out_path(parameters: &settings::ProfileData, path: &str) -> String {
format!("{}{}.{}", remove_extension(&path), posfix, extension)
}

fn image_type_to_extension(image_type: ImageType) -> String {
match image_type {
ImageType::JPEG => "jpg".to_string(),
ImageType::PNG => "png".to_string(),
ImageType::WEBP => "webp".to_string(),
ImageType::GIF => "gif".to_string(),
ImageType::TIFF => "tiff".to_string(),
}
}

fn create_csparameters(
parameters: &settings::ProfileData,
width: u32,
Expand Down Expand Up @@ -372,18 +358,11 @@ fn convert_image(
mut params: CSParameters,
image_type: ImageType,
) -> Result<String, String> {
let supported_type = match image_type {
ImageType::JPEG => caesium::SupportedFileTypes::Jpeg,
ImageType::PNG => caesium::SupportedFileTypes::Png,
ImageType::WEBP => caesium::SupportedFileTypes::WebP,
ImageType::GIF => caesium::SupportedFileTypes::Gif,
ImageType::TIFF => caesium::SupportedFileTypes::Tiff,
};
let result = caesium::convert(
path.to_string(),
out_path.to_string(),
&mut params,
supported_type,
image_type.to_casium_type(),
);

match result {
Expand Down Expand Up @@ -510,47 +489,45 @@ mod tests {

#[test]
fn test_convert_image_type() {
let result = image_type_to_extension(ImageType::JPEG);
let result = ImageType::JPEG.extensions()[0];
assert_eq!(result, "jpg".to_string());
}

#[test]
fn test_guess_image_type() {
let result = guess_image_type("test/test.jpg");
assert_eq!(result.unwrap(), ImageType::JPEG);
}

#[test]
fn test_get_out_path() {
let mut parameters = settings::ProfileData::new();
let mut result = get_out_path(&parameters, &"test/test.png".to_string());
let mut result = get_out_path(&parameters, &"test/test.png".to_string(), &ImageType::PNG);
assert_eq!(result, "test/test.min.png".to_string());

parameters = settings::ProfileData::new();
result = get_out_path(&parameters, &"test/test.jpeg".to_string());
result = get_out_path(&parameters, &"test/test.jpeg".to_string(), &ImageType::JPEG);
assert_eq!(result, "test/test.min.jpeg".to_string());

parameters = settings::ProfileData::new();
result = get_out_path(&parameters, &"test/test.jpg".to_string(), &ImageType::JPEG);
assert_eq!(result, "test/test.min.jpg".to_string());

parameters = settings::ProfileData::new();
parameters.should_convert = true;
parameters.convert_extension = ImageType::PNG;
result = get_out_path(&parameters, &"test/test.jpeg".to_string());
result = get_out_path(&parameters, &"test/test.jpeg".to_string(), &ImageType::JPEG);
assert_eq!(result, "test/test.min.png".to_string());

parameters = settings::ProfileData::new();
parameters.should_convert = false;
parameters.convert_extension = ImageType::PNG;
result = get_out_path(&parameters, &"test/test.jpeg".to_string());
result = get_out_path(&parameters, &"test/test.jpeg".to_string(), &ImageType::JPEG);
assert_eq!(result, "test/test.min.jpeg".to_string());

parameters = settings::ProfileData::new();
parameters.add_posfix = false;
result = get_out_path(&parameters, &"test/test.jpeg".to_string());
assert_eq!(result, "test/test.jpeg".to_string());
result = get_out_path(&parameters, &"test/test.jpeg".to_string(), &ImageType::PNG);
assert_eq!(result, "test/test.png".to_string());

parameters = settings::ProfileData::new();
parameters.postfix = ".bong".to_string();
result = get_out_path(&parameters, &"test/test.jpeg".to_string());
assert_eq!(result, "test/test.bong.jpeg".to_string());
result = get_out_path(&parameters, &"test/test.jpeg".to_string(), &ImageType::PNG);
assert_eq!(result, "test/test.bong.png".to_string());
}

#[test]
Expand Down

0 comments on commit d602b7f

Please sign in to comment.