122 lines
3.9 KiB
Rust
122 lines
3.9 KiB
Rust
use std::path::{Path, PathBuf};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::operations::*;
|
|
use crate::types::{ImageFormat, ImageSource};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ProcessingJob {
|
|
pub input_dir: PathBuf,
|
|
pub output_dir: PathBuf,
|
|
#[serde(skip)]
|
|
pub sources: Vec<ImageSource>,
|
|
pub resize: Option<ResizeConfig>,
|
|
pub resize_algorithm: ResizeAlgorithm,
|
|
pub rotation: Option<Rotation>,
|
|
pub flip: Option<Flip>,
|
|
pub adjustments: Option<AdjustmentsConfig>,
|
|
pub convert: Option<ConvertConfig>,
|
|
pub compress: Option<CompressConfig>,
|
|
pub metadata: Option<MetadataConfig>,
|
|
pub watermark: Option<WatermarkConfig>,
|
|
pub rename: Option<RenameConfig>,
|
|
pub overwrite_behavior: OverwriteAction,
|
|
pub preserve_directory_structure: bool,
|
|
pub progressive_jpeg: bool,
|
|
pub avif_speed: u8,
|
|
pub output_dpi: u32,
|
|
}
|
|
|
|
impl ProcessingJob {
|
|
pub fn new(input_dir: impl AsRef<Path>, output_dir: impl AsRef<Path>) -> Self {
|
|
Self {
|
|
input_dir: input_dir.as_ref().to_path_buf(),
|
|
output_dir: output_dir.as_ref().to_path_buf(),
|
|
sources: Vec::new(),
|
|
resize: None,
|
|
resize_algorithm: ResizeAlgorithm::default(),
|
|
rotation: None,
|
|
flip: None,
|
|
adjustments: None,
|
|
convert: None,
|
|
compress: None,
|
|
metadata: None,
|
|
watermark: None,
|
|
rename: None,
|
|
overwrite_behavior: OverwriteAction::default(),
|
|
preserve_directory_structure: false,
|
|
progressive_jpeg: false,
|
|
avif_speed: 6,
|
|
output_dpi: 0,
|
|
}
|
|
}
|
|
|
|
pub fn add_source(&mut self, path: impl AsRef<Path>) {
|
|
self.sources.push(ImageSource::from_path(path));
|
|
}
|
|
|
|
pub fn operation_count(&self) -> usize {
|
|
let mut count = 0;
|
|
if self.resize.is_some() { count += 1; }
|
|
if self.rotation.is_some() { count += 1; }
|
|
if self.flip.is_some() { count += 1; }
|
|
if self.adjustments.as_ref().is_some_and(|a| !a.is_noop()) { count += 1; }
|
|
if self.convert.is_some() { count += 1; }
|
|
if self.compress.is_some() { count += 1; }
|
|
if self.metadata.is_some() { count += 1; }
|
|
if self.watermark.is_some() { count += 1; }
|
|
if self.rename.is_some() { count += 1; }
|
|
count
|
|
}
|
|
|
|
/// Returns true if the job requires decoding/encoding pixel data.
|
|
/// When false, we can use a fast copy-and-rename path.
|
|
pub fn needs_pixel_processing(&self) -> bool {
|
|
self.resize.is_some()
|
|
|| matches!(self.rotation, Some(r) if !matches!(r, Rotation::None))
|
|
|| matches!(self.flip, Some(f) if !matches!(f, Flip::None))
|
|
|| self.adjustments.as_ref().is_some_and(|a| !a.is_noop())
|
|
|| self.convert.is_some()
|
|
|| self.compress.is_some()
|
|
|| self.watermark.is_some()
|
|
}
|
|
|
|
pub fn output_path_for(
|
|
&self,
|
|
source: &ImageSource,
|
|
output_format: Option<ImageFormat>,
|
|
) -> PathBuf {
|
|
let stem = source
|
|
.path
|
|
.file_stem()
|
|
.and_then(|s| s.to_str())
|
|
.unwrap_or("output");
|
|
|
|
let ext = output_format
|
|
.map(|f| f.extension())
|
|
.or_else(|| {
|
|
source
|
|
.path
|
|
.extension()
|
|
.and_then(|e| e.to_str())
|
|
})
|
|
.unwrap_or("bin");
|
|
|
|
let filename = format!("{}.{}", stem, ext);
|
|
|
|
if self.preserve_directory_structure {
|
|
// Maintain relative path from input_dir
|
|
if let Ok(rel) = source.path.strip_prefix(&self.input_dir) {
|
|
if let Some(parent) = rel.parent() {
|
|
if parent.components().count() > 0 {
|
|
return self.output_dir.join(parent).join(filename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.output_dir.join(filename)
|
|
}
|
|
}
|