Fix pipeline order, add selective metadata stripping, rename case/regex

- Move watermark step after compress in processing pipeline to match
  design doc order (resize, adjustments, convert, compress, metadata,
  watermark, rename)
- Implement selective EXIF metadata stripping for Privacy and Custom
  modes using little_exif tag filtering (GPS, camera, software,
  timestamps, copyright categories)
- Add case conversion support to rename (none/lower/upper/title)
- Add regex find-and-replace on original filenames
- Wire case and regex controls in rename step UI to JobConfig
- Add regex crate dependency to pixstrip-core
This commit is contained in:
2026-03-06 18:12:18 +02:00
parent 5104d66aaf
commit a666fbad05
10 changed files with 208 additions and 17 deletions

View File

@@ -149,6 +149,43 @@ fn is_leap(year: u64) -> bool {
(year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}
/// Apply case conversion to a filename (without extension)
/// case_mode: 0=none, 1=lowercase, 2=uppercase, 3=title case
pub fn apply_case_conversion(name: &str, case_mode: u32) -> String {
match case_mode {
1 => name.to_lowercase(),
2 => name.to_uppercase(),
3 => {
// Title case: capitalize first letter of each word (split on _ - space)
name.split(|c: char| c == '_' || c == '-' || c == ' ')
.map(|word| {
let mut chars = word.chars();
match chars.next() {
Some(first) => {
let upper: String = first.to_uppercase().collect();
upper + &chars.as_str().to_lowercase()
}
None => String::new(),
}
})
.collect::<Vec<_>>()
.join("_")
}
_ => name.to_string(),
}
}
/// Apply regex find-and-replace on a filename
pub fn apply_regex_replace(name: &str, find: &str, replace: &str) -> String {
if find.is_empty() {
return name.to_string();
}
match regex::Regex::new(find) {
Ok(re) => re.replace_all(name, replace).into_owned(),
Err(_) => name.to_string(),
}
}
pub fn resolve_collision(path: &Path) -> PathBuf {
if !path.exists() {
return path.to_path_buf();