Integrate watch folder monitoring into GTK app

This commit is contained in:
2026-03-06 17:53:41 +02:00
parent f301f1a78b
commit 6cf5a6e80c

View File

@@ -659,6 +659,114 @@ fn build_ui(app: &adw::Application) {
}
}
}
// Start watch folder monitoring for active folders
start_watch_folder_monitoring(&ui);
}
fn start_watch_folder_monitoring(ui: &WizardUi) {
let config_store = pixstrip_core::storage::ConfigStore::new();
let config = config_store.load().unwrap_or_default();
let active_folders: Vec<_> = config.watch_folders.iter()
.filter(|f| f.active && f.path.exists())
.cloned()
.collect();
if active_folders.is_empty() {
return;
}
let (tx, rx) = std::sync::mpsc::channel::<pixstrip_core::watcher::WatchEvent>();
// Start a watcher for each active folder
for folder in &active_folders {
let watcher = pixstrip_core::watcher::FolderWatcher::new();
let folder_tx = tx.clone();
if let Err(e) = watcher.start(folder, folder_tx) {
eprintln!("Failed to start watching {}: {}", folder.path.display(), e);
continue;
}
// Leak the watcher so it stays alive for the lifetime of the app
std::mem::forget(watcher);
}
// Build a lookup from folder path to preset name
let folder_presets: std::collections::HashMap<std::path::PathBuf, String> = active_folders
.iter()
.map(|f| (f.path.clone(), f.preset_name.clone()))
.collect();
let toast_overlay = ui.toast_overlay.clone();
// Poll the channel from the main loop
glib::timeout_add_local(std::time::Duration::from_millis(500), move || {
let mut batch: Vec<(std::path::PathBuf, String)> = Vec::new();
// Drain all pending events
while let Ok(event) = rx.try_recv() {
match event {
pixstrip_core::watcher::WatchEvent::NewImage(path) => {
// Find which watch folder this belongs to
for (folder_path, preset_name) in &folder_presets {
if path.starts_with(folder_path) {
batch.push((path.clone(), preset_name.clone()));
break;
}
}
}
pixstrip_core::watcher::WatchEvent::Error(e) => {
eprintln!("Watch folder error: {}", e);
}
}
}
if !batch.is_empty() {
// Group by preset name and process
let preset_store = pixstrip_core::storage::PresetStore::new();
let mut by_preset: std::collections::HashMap<String, Vec<std::path::PathBuf>> =
std::collections::HashMap::new();
for (path, preset) in batch {
by_preset.entry(preset).or_default().push(path);
}
for (preset_name, files) in by_preset {
if let Ok(presets) = preset_store.list() {
if let Some(preset) = presets.iter().find(|p| p.name == preset_name) {
let count = files.len();
let preset = preset.clone();
std::thread::spawn(move || {
// Build output dir next to the first file
let output_dir = files.first()
.and_then(|f| f.parent())
.map(|p| p.join("processed"))
.unwrap_or_else(|| std::path::PathBuf::from("processed"));
let input_dir = files.first()
.and_then(|f| f.parent())
.unwrap_or_else(|| std::path::Path::new("."))
.to_path_buf();
let mut job = preset.to_job(&input_dir, &output_dir);
for file in &files {
job.add_source(file);
}
let executor = pixstrip_core::executor::PipelineExecutor::new();
let _ = executor.execute(&job, |_| {});
});
let toast = adw::Toast::new(&format!(
"Watch: processing {} new image{}",
count,
if count == 1 { "" } else { "s" }
));
toast.set_timeout(3);
toast_overlay.add_toast(toast);
}
}
}
}
glib::ControlFlow::Continue
});
}
fn build_menu() -> gtk::gio::Menu {