Skip to main content

max / audiofiles

Preview decode cancellation, cleanup UI routing, null-safe drag-drop Stop previous streaming decode thread before spawning a new one via decode_cancel flag. Route ImportMode::Cleaning to cleanup progress screen. Guard drag-drop import against empty VFS list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-04-13 21:58 UTC
Commit: 72e3502d38b6ae8b6ee9d165b7f45925b6516c69
Parent: e32c593
4 files changed, +38 insertions, -17 deletions
@@ -454,7 +454,6 @@ impl AudioFilesApp {
454 454 } else {
455 455 self.screen = AppScreen::VaultSetup;
456 456 }
457 - return;
458 457 }
459 458 });
460 459
@@ -563,10 +562,10 @@ impl AudioFilesApp {
563 562 self.vault_setup_path = Some(path);
564 563 }
565 564 }
566 - if self.vault_setup_path.is_some() {
567 - if ui.small_button("Reset to default").clicked() {
568 - self.vault_setup_path = None;
569 - }
565 + if self.vault_setup_path.is_some()
566 + && ui.small_button("Reset to default").clicked()
567 + {
568 + self.vault_setup_path = None;
570 569 }
571 570 });
572 571
@@ -1020,15 +1019,17 @@ impl AudioFilesApp {
1020 1019 }
1021 1020
1022 1021 if let Some(ref mut browser) = self.browser {
1023 - for path in dropped {
1024 - if path.is_dir() {
1025 - let strategy = audiofiles_browser::import::ImportStrategy::MergeIntoVfs {
1026 - vfs_id: browser.current_vfs_id(),
1027 - parent_id: browser.current_dir,
1028 - };
1029 - browser.start_folder_import(path, strategy);
1030 - } else {
1031 - browser.import_path(&path);
1022 + if let Some(vfs_id) = browser.current_vfs_id() {
1023 + for path in dropped {
1024 + if path.is_dir() {
1025 + let strategy = audiofiles_browser::import::ImportStrategy::MergeIntoVfs {
1026 + vfs_id,
1027 + parent_id: browser.current_dir,
1028 + };
1029 + browser.start_folder_import(path, strategy);
1030 + } else {
1031 + browser.import_path(&path);
1032 + }
1032 1033 }
1033 1034 }
1034 1035 audiofiles_browser::editor::draw_browser(ctx, browser, self.sync_manager.as_ref());
@@ -30,7 +30,8 @@ pub fn draw_browser(
30 30 }
31 31 ImportMode::Importing { .. }
32 32 | ImportMode::Analyzing { .. }
33 - | ImportMode::Exporting { .. } => {
33 + | ImportMode::Exporting { .. }
34 + | ImportMode::Cleaning { .. } => {
34 35 if state.poll_workers() {
35 36 ctx.request_repaint();
36 37 }
@@ -44,6 +45,9 @@ pub fn draw_browser(
44 45 ImportMode::Exporting { .. } => {
45 46 export_screens::draw_export_progress(ctx, state);
46 47 }
48 + ImportMode::Cleaning { .. } => {
49 + import_screens::draw_cleanup_progress(ctx, state);
50 + }
47 51 // poll_workers may have transitioned the mode
48 52 ImportMode::TagFolders { .. } => {
49 53 import_screens::draw_tag_folders(ctx, state);
@@ -4,6 +4,7 @@
4 4 //! to the cpal audio output thread through [`PreviewPlayback`] behind a `parking_lot::Mutex`.
5 5
6 6 use std::path::Path;
7 + use std::sync::atomic::Ordering;
7 8
8 9 use tracing::{instrument, warn};
9 10
@@ -276,6 +277,9 @@ pub fn start_streaming_decode(
276 277 .make(&codec_params, &DecoderOptions::default())
277 278 .map_err(|e| PreviewError::Decoder(e.to_string()))?;
278 279
280 + // Cancel any previous streaming decode thread
281 + shared.decode_cancel.store(true, Ordering::Release);
282 +
279 283 // Set up the initial buffer and playback state (not yet playing)
280 284 {
281 285 let capacity = n_frames_estimate.unwrap_or(source_sample_rate as usize * 60) * 2;
@@ -292,12 +296,22 @@ pub fn start_streaming_decode(
292 296 guard.total_frames_estimate = n_frames_estimate;
293 297 }
294 298
299 + // Clear cancel flag for the new decode thread
300 + shared.decode_cancel.store(false, Ordering::Release);
301 +
295 302 let shared = std::sync::Arc::clone(shared);
296 303 std::thread::spawn(move || {
297 304 let mut total_frames = 0usize;
298 305 let mut started = false;
299 306
300 307 loop {
308 + // Check cancellation before each packet
309 + if shared.decode_cancel.load(Ordering::Acquire) {
310 + let mut guard = shared.preview.lock();
311 + guard.streaming = false;
312 + return;
313 + }
314 +
301 315 let packet = match format.next_packet() {
302 316 Ok(p) => p,
303 317 Err(symphonia::core::errors::Error::IoError(ref e))
@@ -44,9 +44,11 @@ pub fn draw_configure_import(ctx: &egui::Context, state: &mut BrowserState) {
44 44 let current_vfs_id = state.current_vfs_id();
45 45 let current_dir = state.current_dir;
46 46 if ui.radio(is_flat, "Flat (all files in current directory)").clicked() && !is_flat {
47 - if let ImportMode::ConfigureImport { ref mut strategy, .. } = state.import_mode {
47 + if let (ImportMode::ConfigureImport { ref mut strategy, .. }, Some(vfs_id)) =
48 + (&mut state.import_mode, current_vfs_id)
49 + {
48 50 *strategy = ImportStrategy::Flat {
49 - vfs_id: current_vfs_id,
51 + vfs_id,
50 52 parent_id: current_dir,
51 53 };
52 54 }