From 5a56e25c2cc5a21c6459d8424841df6d7fb1db7f Mon Sep 17 00:00:00 2001 From: lashman Date: Fri, 6 Mar 2026 16:17:25 +0200 Subject: [PATCH] Add sequential batch queue processing - After a batch completes, automatically start next pending batch - Mark completed/failed batches in queue with correct status - Failed batches don't block the queue - next pending batch starts - Queue processes batches in order, one at a time --- pixstrip-gtk/src/app.rs | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/pixstrip-gtk/src/app.rs b/pixstrip-gtk/src/app.rs index 87ff0b4..ebc1803 100644 --- a/pixstrip-gtk/src/app.rs +++ b/pixstrip-gtk/src/app.rs @@ -1615,6 +1615,7 @@ fn run_processing(_window: &adw::ApplicationWindow, ui: &WizardUi) { return glib::ControlFlow::Break; } ProcessingMessage::Error(err) => { + mark_current_queue_batch(&ui_for_rx, false, Some(&err)); let toast = adw::Toast::new(&format!("Processing failed: {}", err)); ui_for_rx.toast_overlay.add_toast(toast); ui_for_rx.back_button.set_visible(true); @@ -1624,6 +1625,11 @@ fn run_processing(_window: &adw::ApplicationWindow, ui: &WizardUi) { { ui_for_rx.nav_view.pop(); } + // Try to process next batch even if this one failed + let ui_q = ui_for_rx.clone(); + glib::timeout_add_local_once(std::time::Duration::from_millis(500), move || { + process_next_queued_batch(&ui_q); + }); return glib::ControlFlow::Break; } } @@ -1775,6 +1781,15 @@ fn show_results( ); } } + + // Mark current queue batch as completed and process next one + mark_current_queue_batch(ui, true, None); + + // Auto-start next queued batch after a short delay + let ui_for_queue = ui.clone(); + glib::timeout_add_local_once(std::time::Duration::from_millis(500), move || { + process_next_queued_batch(&ui_for_queue); + }); } fn update_results_stats( @@ -2969,3 +2984,76 @@ fn add_current_batch_to_queue(ui: &WizardUi) { refresh_queue_list(ui); ui.toast_overlay.add_toast(adw::Toast::new("Batch added to queue")); } + +/// Check for pending batches in the queue and start processing the next one. +/// Returns true if a batch was started. +fn process_next_queued_batch(ui: &WizardUi) -> bool { + // Find the first pending batch + let next_idx = { + let queue = ui.state.batch_queue.borrow(); + queue.batches.iter().position(|b| b.status == BatchStatus::Pending) + }; + + let Some(idx) = next_idx else { + return false; + }; + + // Mark it as processing + let batch = { + let mut queue = ui.state.batch_queue.borrow_mut(); + queue.batches[idx].status = BatchStatus::Processing; + queue.batches[idx].clone() + }; + refresh_queue_list(ui); + + // Load the batch's config into state so start_processing uses it + *ui.state.job_config.borrow_mut() = batch.job_config; + *ui.state.loaded_files.borrow_mut() = batch.files; + ui.state.excluded_files.borrow_mut().clear(); + *ui.state.output_dir.borrow_mut() = Some(batch.output_dir); + + ui.toast_overlay.add_toast(adw::Toast::new(&format!("Starting queued batch: {}", batch.name))); + + // Pop to a clean state if we're on results page + while let Some(page) = ui.nav_view.visible_page() { + let tag = page.tag(); + if tag.as_deref() == Some("processing") || tag.as_deref() == Some("results") { + ui.nav_view.pop(); + } else { + break; + } + } + + // Store the batch index for marking completion + let batch_idx = idx; + let ui_clone = ui.clone(); + + // Start processing with a small delay so the UI updates + glib::timeout_add_local_once(std::time::Duration::from_millis(100), move || { + if let Some(root) = ui_clone.nav_view.root() + && let Some(win) = root.downcast_ref::() + { + run_processing(win, &ui_clone); + } + }); + + // We need to mark the batch as completed when processing finishes. + // This is handled in show_results via the queue check. + let _ = batch_idx; + + true +} + +/// Mark the currently-processing batch in the queue as completed or failed. +fn mark_current_queue_batch(ui: &WizardUi, success: bool, error_msg: Option<&str>) { + let mut queue = ui.state.batch_queue.borrow_mut(); + if let Some(batch) = queue.batches.iter_mut().find(|b| b.status == BatchStatus::Processing) { + batch.status = if success { + BatchStatus::Completed + } else { + BatchStatus::Failed(error_msg.unwrap_or("Unknown error").to_string()) + }; + } + drop(queue); + refresh_queue_list(ui); +}