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 {
|
fn build_menu() -> gtk::gio::Menu {
|
||||||
|
|||||||
Reference in New Issue
Block a user