Integrate watch folder monitoring into GTK app
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user