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

@@ -78,6 +78,9 @@ pub struct JobConfig {
pub rename_counter_start: u32,
pub rename_counter_padding: u32,
pub rename_template: String,
pub rename_case: u32, // 0=none, 1=lower, 2=upper, 3=title
pub rename_find: String,
pub rename_replace: String,
// Output
pub preserve_dir_structure: bool,
pub overwrite_behavior: u8,
@@ -389,6 +392,9 @@ fn build_ui(app: &adw::Application) {
rename_counter_start: 1,
rename_counter_padding: 3,
rename_template: String::new(),
rename_case: 0,
rename_find: String::new(),
rename_replace: String::new(),
preserve_dir_structure: false,
overwrite_behavior: match app_cfg.overwrite_behavior {
pixstrip_core::config::OverwriteBehavior::Ask => 0,
@@ -1847,6 +1853,9 @@ fn run_processing(_window: &adw::ApplicationWindow, ui: &WizardUi) {
} else {
Some(cfg.rename_template.clone())
},
case_mode: cfg.rename_case,
regex_find: cfg.rename_find.clone(),
regex_replace: cfg.rename_replace.clone(),
});
}
@@ -2786,6 +2795,9 @@ fn build_preset_from_config(cfg: &JobConfig, name: &str) -> pixstrip_core::prese
} else {
Some(cfg.rename_template.clone())
},
case_mode: cfg.rename_case,
regex_find: cfg.rename_find.clone(),
regex_replace: cfg.rename_replace.clone(),
})
} else {
None

View File

@@ -264,6 +264,9 @@ pub fn build_rename_page(state: &AppState) -> adw::NavigationPage {
counter_start: cfg.rename_counter_start,
counter_padding: cfg.rename_counter_padding,
template: None,
case_mode: cfg.rename_case,
regex_find: cfg.rename_find.clone(),
regex_replace: cfg.rename_replace.clone(),
};
let result =
rename_cfg.apply_simple(name, ext, (i + 1) as u32);
@@ -319,12 +322,36 @@ pub fn build_rename_page(state: &AppState) -> adw::NavigationPage {
}
{
let jc = state.job_config.clone();
let up = update_preview;
let up = update_preview.clone();
template_row.connect_changed(move |row| {
jc.borrow_mut().rename_template = row.text().to_string();
up();
});
}
{
let jc = state.job_config.clone();
let up = update_preview.clone();
case_row.connect_selected_notify(move |row| {
jc.borrow_mut().rename_case = row.selected();
up();
});
}
{
let jc = state.job_config.clone();
let up = update_preview.clone();
find_row.connect_changed(move |row| {
jc.borrow_mut().rename_find = row.text().to_string();
up();
});
}
{
let jc = state.job_config.clone();
let up = update_preview;
replace_row.connect_changed(move |row| {
jc.borrow_mut().rename_replace = row.text().to_string();
up();
});
}
scrolled.set_child(Some(&content));