max / audiofiles
- Co-Authored-By
- Claude Opus 4.8 <noreply@anthropic.com>
46 files changed,
+293 insertions,
-371 deletions
| @@ -396,8 +396,8 @@ | |||
| 396 | 396 | #[test] | |
| 397 | 397 | fn clamp_prevents_overflow() { | |
| 398 | 398 | // Simulate very loud mixed audio | |
| 399 | - | let buf = vec![1.5f32, -1.5, 0.5, -0.5]; | |
| 400 | - | let mut data = vec![0.0f32; 4]; | |
| 399 | + | let buf = [1.5f32, -1.5, 0.5, -0.5]; | |
| 400 | + | let mut data = [0.0f32; 4]; | |
| 401 | 401 | for (out, &mix) in data.iter_mut().zip(buf.iter()) { | |
| 402 | 402 | *out = mix.clamp(-1.0, 1.0); | |
| 403 | 403 | } |
| @@ -292,14 +292,12 @@ | |||
| 292 | 292 | let now = chrono::Utc::now(); | |
| 293 | 293 | ||
| 294 | 294 | // Clock rollback detection: if now is before last_seen_date, expire immediately | |
| 295 | - | if let Some(ref last) = trial.last_seen_date { | |
| 296 | - | if let Ok(last_seen) = chrono::DateTime::parse_from_rfc3339(last) { | |
| 297 | - | if now.signed_duration_since(last_seen).num_hours() < -1 { | |
| 295 | + | if let Some(ref last) = trial.last_seen_date | |
| 296 | + | && let Ok(last_seen) = chrono::DateTime::parse_from_rfc3339(last) | |
| 297 | + | && now.signed_duration_since(last_seen).num_hours() < -1 { | |
| 298 | 298 | // Allow up to 1 hour of drift (DST, NTP correction) | |
| 299 | 299 | return 0; | |
| 300 | 300 | } | |
| 301 | - | } | |
| 302 | - | } | |
| 303 | 301 | ||
| 304 | 302 | let elapsed = now.signed_duration_since(first); | |
| 305 | 303 | 30 - elapsed.num_days() |
| @@ -164,8 +164,7 @@ | |||
| 164 | 164 | return Some(key); | |
| 165 | 165 | } | |
| 166 | 166 | // Fall back to bundled synckit.toml | |
| 167 | - | parse_synckit_toml_key().map(String::from) | |
| 168 | - | } | |
| 167 | + | parse_synckit_toml_key()} | |
| 169 | 168 | ||
| 170 | 169 | /// Save an API key to the data directory for future launches. | |
| 171 | 170 | #[cfg(test)] | |
| @@ -526,8 +525,8 @@ | |||
| 526 | 525 | let ctx = ui.ctx().clone(); | |
| 527 | 526 | let ctx = &ctx; | |
| 528 | 527 | // Poll tray menu events | |
| 529 | - | if let Some(ref tray) = self.tray { | |
| 530 | - | if let Some(action) = tray.poll() { | |
| 528 | + | if let Some(ref tray) = self.tray | |
| 529 | + | && let Some(action) = tray.poll() { | |
| 531 | 530 | match action { | |
| 532 | 531 | tray::TrayAction::ShowWindow => { | |
| 533 | 532 | ctx.send_viewport_cmd(ViewportCommand::Focus); | |
| @@ -542,11 +541,10 @@ | |||
| 542 | 541 | } | |
| 543 | 542 | } | |
| 544 | 543 | } | |
| 545 | - | } | |
| 546 | 544 | ||
| 547 | 545 | // Update tray tooltip based on playback state | |
| 548 | - | if let Some(ref tray) = self.tray { | |
| 549 | - | if let Some(ref browser) = self.browser { | |
| 546 | + | if let Some(ref tray) = self.tray | |
| 547 | + | && let Some(ref browser) = self.browser { | |
| 550 | 548 | let playing = browser.shared.preview.lock().playing; | |
| 551 | 549 | if playing { | |
| 552 | 550 | tray.set_tooltip(&browser.status); | |
| @@ -554,23 +552,21 @@ | |||
| 554 | 552 | tray.set_tooltip("audiofiles"); | |
| 555 | 553 | } | |
| 556 | 554 | } | |
| 557 | - | } | |
| 558 | 555 | ||
| 559 | 556 | // Check if sync pulled remote changes → refresh browser contents | |
| 560 | - | if let Some(ref sync) = self.sync_manager { | |
| 561 | - | if sync.status().needs_refresh { | |
| 557 | + | if let Some(ref sync) = self.sync_manager | |
| 558 | + | && sync.status().needs_refresh { | |
| 562 | 559 | if let Some(ref mut browser) = self.browser { | |
| 563 | 560 | browser.refresh_vfs_list(); | |
| 564 | 561 | browser.refresh_contents(); | |
| 565 | 562 | } | |
| 566 | 563 | sync.clear_needs_refresh(); | |
| 567 | 564 | } | |
| 568 | - | } | |
| 569 | 565 | ||
| 570 | 566 | // ── Sync setup actions (before draw, so UI sees results this frame) ── | |
| 571 | 567 | // ── Vault actions ── | |
| 572 | - | if let Some(ref mut browser) = self.browser { | |
| 573 | - | if let Some(action) = browser.settings.pending_action.take() { | |
| 568 | + | if let Some(ref mut browser) = self.browser | |
| 569 | + | && let Some(action) = browser.settings.pending_action.take() { | |
| 574 | 570 | use audiofiles_browser::state::VaultAction; | |
| 575 | 571 | match action { | |
| 576 | 572 | VaultAction::SwitchVault(path) => { | |
| @@ -581,12 +577,11 @@ | |||
| 581 | 577 | let switch_path = path.clone(); | |
| 582 | 578 | if self.with_vault_registry(|reg| vault::create_vault(reg, &name, &path)) { | |
| 583 | 579 | self.switch_vault(switch_path); | |
| 584 | - | if loose_files { | |
| 585 | - | if let Some(ref mut browser) = self.browser { | |
| 580 | + | if loose_files | |
| 581 | + | && let Some(ref mut browser) = self.browser { | |
| 586 | 582 | let _ = browser.backend.set_config("loose_files", "1"); | |
| 587 | 583 | browser.settings.is_loose_files = true; | |
| 588 | 584 | } | |
| 589 | - | } | |
| 590 | 585 | return; | |
| 591 | 586 | } | |
| 592 | 587 | } | |
| @@ -637,7 +632,6 @@ | |||
| 637 | 632 | } | |
| 638 | 633 | } | |
| 639 | 634 | } | |
| 640 | - | } | |
| 641 | 635 | ||
| 642 | 636 | // ── VFS Mirror: sync if dirty ── | |
| 643 | 637 | if let Some(ref mut browser) = self.browser { | |
| @@ -960,7 +954,7 @@ | |||
| 960 | 954 | // Empty file → falls through to bundled synckit.toml key | |
| 961 | 955 | if std::env::var("AF_SYNC_API_KEY").is_err() { | |
| 962 | 956 | let key = load_api_key(dir.path()); | |
| 963 | - | assert_eq!(key, parse_synckit_toml_key().map(String::from)); | |
| 957 | + | assert_eq!(key, parse_synckit_toml_key()); | |
| 964 | 958 | } | |
| 965 | 959 | } | |
| 966 | 960 | ||
| @@ -970,7 +964,7 @@ | |||
| 970 | 964 | std::fs::write(dir.path().join("sync_api_key"), " \n ").unwrap(); | |
| 971 | 965 | if std::env::var("AF_SYNC_API_KEY").is_err() { | |
| 972 | 966 | let key = load_api_key(dir.path()); | |
| 973 | - | assert_eq!(key, parse_synckit_toml_key().map(String::from)); | |
| 967 | + | assert_eq!(key, parse_synckit_toml_key()); | |
| 974 | 968 | } | |
| 975 | 969 | } | |
| 976 | 970 | ||
| @@ -979,7 +973,7 @@ | |||
| 979 | 973 | let dir = tempfile::tempdir().unwrap(); | |
| 980 | 974 | if std::env::var("AF_SYNC_API_KEY").is_err() { | |
| 981 | 975 | let key = load_api_key(dir.path()); | |
| 982 | - | assert_eq!(key, parse_synckit_toml_key().map(String::from)); | |
| 976 | + | assert_eq!(key, parse_synckit_toml_key()); | |
| 983 | 977 | } | |
| 984 | 978 | } | |
| 985 | 979 |
| @@ -137,7 +137,8 @@ | |||
| 137 | 137 | ||
| 138 | 138 | assert_eq!(rgba.len(), SIZE * SIZE * 4); | |
| 139 | 139 | // Middle bar (index 2) is full height — pixel at (9, 0) should be coloured | |
| 140 | - | let mid_offset = (0 * SIZE + 9) * 4; | |
| 140 | + | // (row 0, col 9) -> offset (0 * SIZE + 9) * 4 | |
| 141 | + | let mid_offset = 9 * 4; | |
| 141 | 142 | assert_eq!(&rgba[mid_offset..mid_offset + 4], &bar_colour); | |
| 142 | 143 | // Corner (0,0) should be transparent | |
| 143 | 144 | assert_eq!(&rgba[0..4], &[0, 0, 0, 0]); |
| @@ -187,8 +187,8 @@ | |||
| 187 | 187 | Ok(resp) if resp.status().is_success() => { | |
| 188 | 188 | match resp.json::<UpdateResponse>().await { | |
| 189 | 189 | Ok(update) => { | |
| 190 | - | if let Ok(remote) = Version::parse(&update.version) { | |
| 191 | - | if remote > current && is_trusted_download_url(&update.url) { | |
| 190 | + | if let Ok(remote) = Version::parse(&update.version) | |
| 191 | + | && remote > current && is_trusted_download_url(&update.url) { | |
| 192 | 192 | tracing::info!("Update available: v{}", update.version); | |
| 193 | 193 | let mut s = status.lock(); | |
| 194 | 194 | s.available = true; | |
| @@ -196,7 +196,6 @@ | |||
| 196 | 196 | s.notes = update.notes; | |
| 197 | 197 | s.download_url = update.url; | |
| 198 | 198 | } | |
| 199 | - | } | |
| 200 | 199 | } | |
| 201 | 200 | Err(e) => { | |
| 202 | 201 | tracing::warn!("Failed to parse update response: {e}"); |
| @@ -74,11 +74,10 @@ | |||
| 74 | 74 | }); | |
| 75 | 75 | ui.add_space(theme::space::SM); | |
| 76 | 76 | ui.horizontal(|ui| { | |
| 77 | - | if ui.button("Choose different location...").clicked() { | |
| 78 | - | if let Some(path) = rfd::FileDialog::new().pick_folder() { | |
| 77 | + | if ui.button("Choose different location...").clicked() | |
| 78 | + | && let Some(path) = rfd::FileDialog::new().pick_folder() { | |
| 79 | 79 | self.vault_setup_path = Some(path); | |
| 80 | 80 | } | |
| 81 | - | } | |
| 82 | 81 | if is_custom && ui.button("Use default").clicked() { | |
| 83 | 82 | self.vault_setup_path = None; | |
| 84 | 83 | } |
| @@ -149,7 +149,8 @@ | |||
| 149 | 149 | WHERE vn.id IS NULL AND s.deleted_at IS NULL", | |
| 150 | 150 | ) { | |
| 151 | 151 | Ok(mut stmt) => { | |
| 152 | - | let rows = stmt | |
| 152 | + | ||
| 153 | + | stmt | |
| 153 | 154 | .query_map([], |row| { | |
| 154 | 155 | Ok(( | |
| 155 | 156 | row.get::<_, String>(0)?, | |
| @@ -159,8 +160,7 @@ | |||
| 159 | 160 | }) | |
| 160 | 161 | .ok() | |
| 161 | 162 | .map(|iter| iter.flatten().collect::<Vec<_>>()) | |
| 162 | - | .unwrap_or_default(); | |
| 163 | - | rows | |
| 163 | + | .unwrap_or_default() | |
| 164 | 164 | } | |
| 165 | 165 | Err(e) => { | |
| 166 | 166 | error!("Cleanup worker failed to query orphans: {e}"); | |
| @@ -206,11 +206,10 @@ | |||
| 206 | 206 | ) { | |
| 207 | 207 | Ok(_) => { | |
| 208 | 208 | // Delete from disk | |
| 209 | - | if let Ok(path) = store.sample_path(hash, ext) { | |
| 210 | - | if path.exists() { | |
| 209 | + | if let Ok(path) = store.sample_path(hash, ext) | |
| 210 | + | && path.exists() { | |
| 211 | 211 | let _ = std::fs::remove_file(&path); | |
| 212 | 212 | } | |
| 213 | - | } | |
| 214 | 213 | removed += 1; | |
| 215 | 214 | } | |
| 216 | 215 | Err(e) => { |
| @@ -403,23 +403,21 @@ | |||
| 403 | 403 | if input.key_pressed(egui::Key::E) { | |
| 404 | 404 | if state.edit.show_window { | |
| 405 | 405 | state.close_edit_window(); | |
| 406 | - | } else if let Some(node) = state.selected_node() { | |
| 407 | - | if let Some(hash) = &node.node.sample_hash { | |
| 406 | + | } else if let Some(node) = state.selected_node() | |
| 407 | + | && let Some(hash) = &node.node.sample_hash { | |
| 408 | 408 | let hash = hash.clone(); | |
| 409 | 409 | state.open_edit_window(&hash); | |
| 410 | 410 | } | |
| 411 | - | } | |
| 412 | 411 | } | |
| 413 | 412 | // "F" toggles the floating Sample Forge window for the selected sample | |
| 414 | 413 | if input.key_pressed(egui::Key::F) && !shift { | |
| 415 | 414 | if state.forge.show_window { | |
| 416 | 415 | state.close_forge_window(); | |
| 417 | - | } else if let Some(node) = state.selected_node() { | |
| 418 | - | if let Some(hash) = &node.node.sample_hash { | |
| 416 | + | } else if let Some(node) = state.selected_node() | |
| 417 | + | && let Some(hash) = &node.node.sample_hash { | |
| 419 | 418 | let hash = hash.clone(); | |
| 420 | 419 | state.open_forge_window(&hash); | |
| 421 | 420 | } | |
| 422 | - | } | |
| 423 | 421 | } | |
| 424 | 422 | // "L" toggles loop | |
| 425 | 423 | if input.key_pressed(egui::Key::L) { | |
| @@ -434,28 +432,23 @@ | |||
| 434 | 432 | state.toggle_detail(); | |
| 435 | 433 | } | |
| 436 | 434 | // Shift+F: find similar | |
| 437 | - | if shift && input.key_pressed(egui::Key::F) { | |
| 438 | - | if let Some(node) = state.selected_node() { | |
| 439 | - | if let Some(hash) = &node.node.sample_hash { | |
| 435 | + | if shift && input.key_pressed(egui::Key::F) | |
| 436 | + | && let Some(node) = state.selected_node() | |
| 437 | + | && let Some(hash) = &node.node.sample_hash { | |
| 440 | 438 | let hash = hash.clone(); | |
| 441 | 439 | state.find_similar(&hash); | |
| 442 | 440 | } | |
| 443 | - | } | |
| 444 | - | } | |
| 445 | 441 | // Shift+D: find duplicates | |
| 446 | - | if shift && input.key_pressed(egui::Key::D) { | |
| 447 | - | if let Some(node) = state.selected_node() { | |
| 448 | - | if let Some(hash) = &node.node.sample_hash { | |
| 442 | + | if shift && input.key_pressed(egui::Key::D) | |
| 443 | + | && let Some(node) = state.selected_node() | |
| 444 | + | && let Some(hash) = &node.node.sample_hash { | |
| 449 | 445 | let hash = hash.clone(); | |
| 450 | 446 | state.find_near_duplicates(&hash); | |
| 451 | 447 | } | |
| 452 | - | } | |
| 453 | - | } | |
| 454 | 448 | // Cmd+Shift+M: bulk move (Cmd+M alone conflicts with macOS minimize) | |
| 455 | - | if input.modifiers.command && input.modifiers.shift && input.key_pressed(egui::Key::M) { | |
| 456 | - | if state.selection.count() > 1 { | |
| 449 | + | if input.modifiers.command && input.modifiers.shift && input.key_pressed(egui::Key::M) | |
| 450 | + | && state.selection.count() > 1 { | |
| 457 | 451 | state.open_bulk_move_modal(); | |
| 458 | 452 | } | |
| 459 | - | } | |
| 460 | 453 | }); | |
| 461 | 454 | } |
| @@ -216,11 +216,10 @@ | |||
| 216 | 216 | Ok(_) => Ok(ImportFileResult::Imported(hash, ext)), | |
| 217 | 217 | Err(CoreError::NameConflict(_)) => Ok(ImportFileResult::Duplicate), | |
| 218 | 218 | Err(e) => { | |
| 219 | - | if let CoreError::Db(ref sqlite_err) = e { | |
| 220 | - | if sqlite_err.to_string().contains("UNIQUE") { | |
| 219 | + | if let CoreError::Db(ref sqlite_err) = e | |
| 220 | + | && sqlite_err.to_string().contains("UNIQUE") { | |
| 221 | 221 | return Ok(ImportFileResult::Duplicate); | |
| 222 | 222 | } | |
| 223 | - | } | |
| 224 | 223 | Err(e) | |
| 225 | 224 | } | |
| 226 | 225 | } |