Fix pipeline order, add selective metadata stripping, rename case/regex
This commit is contained in:
@@ -356,11 +356,6 @@ impl PipelineExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// Watermark (after resize so watermark is at correct scale)
|
||||
if let Some(ref config) = job.watermark {
|
||||
img = apply_watermark(img, config)?;
|
||||
}
|
||||
|
||||
// Determine output format
|
||||
let output_format = if let Some(ref convert) = job.convert {
|
||||
let input_fmt = source.original_format.unwrap_or(ImageFormat::Jpeg);
|
||||
@@ -401,9 +396,13 @@ impl PipelineExecutor {
|
||||
let dims = Some((img.width(), img.height()));
|
||||
let original_ext = source.path.extension()
|
||||
.and_then(|e| e.to_str());
|
||||
// Apply regex on the stem before template expansion
|
||||
let working_stem = crate::operations::rename::apply_regex_replace(
|
||||
stem, &rename.regex_find, &rename.regex_replace,
|
||||
);
|
||||
let new_name = crate::operations::rename::apply_template_full(
|
||||
template,
|
||||
stem,
|
||||
&working_stem,
|
||||
ext,
|
||||
rename.counter_start + index as u32,
|
||||
dims,
|
||||
@@ -411,6 +410,17 @@ impl PipelineExecutor {
|
||||
Some(&source.path),
|
||||
None,
|
||||
);
|
||||
// Apply case conversion to the final name (without extension)
|
||||
let new_name = if rename.case_mode > 0 {
|
||||
if let Some(dot_pos) = new_name.rfind('.') {
|
||||
let (name_part, ext_part) = new_name.split_at(dot_pos);
|
||||
format!("{}{}", crate::operations::rename::apply_case_conversion(name_part, rename.case_mode), ext_part)
|
||||
} else {
|
||||
crate::operations::rename::apply_case_conversion(&new_name, rename.case_mode)
|
||||
}
|
||||
} else {
|
||||
new_name
|
||||
};
|
||||
job.output_dir.join(new_name)
|
||||
} else {
|
||||
let new_name = rename.apply_simple(stem, ext, index as u32 + 1);
|
||||
@@ -444,16 +454,31 @@ impl PipelineExecutor {
|
||||
std::fs::create_dir_all(parent).map_err(PixstripError::Io)?;
|
||||
}
|
||||
|
||||
// Watermark (after compress settings determined, before encode)
|
||||
if let Some(ref config) = job.watermark {
|
||||
img = apply_watermark(img, config)?;
|
||||
}
|
||||
|
||||
// Encode and save
|
||||
encoder.encode_to_file(&img, &output_path, output_format, quality)?;
|
||||
|
||||
// Metadata stripping: re-encoding through the image crate naturally
|
||||
// strips all EXIF/metadata. No additional action is needed for
|
||||
// StripAll, Privacy, or Custom modes. KeepAll mode would require
|
||||
// copying EXIF tags back from the source file using little_exif.
|
||||
// Metadata handling: re-encoding strips all EXIF by default.
|
||||
// KeepAll: copy everything back from source.
|
||||
// Privacy/Custom: copy metadata back, then selectively strip certain tags.
|
||||
// StripAll: do nothing (already stripped by re-encoding).
|
||||
if let Some(ref meta_config) = job.metadata {
|
||||
if matches!(meta_config, crate::operations::MetadataConfig::KeepAll) {
|
||||
copy_metadata_from_source(&source.path, &output_path);
|
||||
match meta_config {
|
||||
crate::operations::MetadataConfig::KeepAll => {
|
||||
copy_metadata_from_source(&source.path, &output_path);
|
||||
}
|
||||
crate::operations::MetadataConfig::StripAll => {
|
||||
// Already stripped by re-encoding - nothing to do
|
||||
}
|
||||
_ => {
|
||||
// Privacy or Custom: copy all metadata back, then strip unwanted tags
|
||||
copy_metadata_from_source(&source.path, &output_path);
|
||||
strip_selective_metadata(&output_path, meta_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,3 +534,67 @@ fn copy_metadata_from_source(source: &std::path::Path, output: &std::path::Path)
|
||||
};
|
||||
let _: std::result::Result<(), std::io::Error> = metadata.write_to_file(output);
|
||||
}
|
||||
|
||||
fn strip_selective_metadata(
|
||||
path: &std::path::Path,
|
||||
config: &crate::operations::MetadataConfig,
|
||||
) {
|
||||
use little_exif::exif_tag::ExifTag;
|
||||
use little_exif::metadata::Metadata;
|
||||
|
||||
// Read the metadata we just wrote back
|
||||
let Ok(source_meta) = Metadata::new_from_path(path) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Build a set of tag IDs to strip
|
||||
let mut strip_ids: Vec<u16> = Vec::new();
|
||||
|
||||
if config.should_strip_gps() {
|
||||
// GPSInfo pointer (0x8825) - removing it strips the GPS sub-IFD reference
|
||||
strip_ids.push(ExifTag::GPSInfo(Vec::new()).as_u16());
|
||||
}
|
||||
|
||||
if config.should_strip_camera() {
|
||||
strip_ids.push(ExifTag::Make(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::Model(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::LensModel(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::LensMake(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::SerialNumber(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::LensSerialNumber(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::LensInfo(Vec::new()).as_u16());
|
||||
}
|
||||
|
||||
if config.should_strip_software() {
|
||||
strip_ids.push(ExifTag::Software(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::MakerNote(Vec::new()).as_u16());
|
||||
}
|
||||
|
||||
if config.should_strip_timestamps() {
|
||||
strip_ids.push(ExifTag::ModifyDate(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::DateTimeOriginal(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::CreateDate(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::SubSecTime(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::SubSecTimeOriginal(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::SubSecTimeDigitized(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::OffsetTime(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::OffsetTimeOriginal(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::OffsetTimeDigitized(String::new()).as_u16());
|
||||
}
|
||||
|
||||
if config.should_strip_copyright() {
|
||||
strip_ids.push(ExifTag::Copyright(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::Artist(String::new()).as_u16());
|
||||
strip_ids.push(ExifTag::OwnerName(String::new()).as_u16());
|
||||
}
|
||||
|
||||
// Build new metadata with only the tags we want to keep
|
||||
let mut new_meta = Metadata::new();
|
||||
for tag in source_meta.data() {
|
||||
if !strip_ids.contains(&tag.as_u16()) {
|
||||
new_meta.set_tag(tag.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let _: std::result::Result<(), std::io::Error> = new_meta.write_to_file(path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user