From 99729d38883cd700a2de15d7e615c695ba1eb17f Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:18:15 -0600 Subject: [PATCH] refactor(v2): inline library_backfill_downloads delegate Inlines the body of `library_backfill_downloads` from crate::library::commands::* directly into commands_v2/legacy_compat.rs, mapping the legacy String error type to RuntimeError::Internal at every return path. Adjusts the v2 return type to refer to crate::library::BackfillReport (the canonical re-export from qbz-library) instead of the deleted crate::library::commands path. Deletes the legacy #[tauri::command] definition in library/commands.rs. MetadataExtractor and BackfillReport are reused unchanged from their crate::library re-exports. No frontend invoke changes required. Step 6 of the legacy cleanup plan, group E (backfill downloads). cc --- src-tauri/src/commands_v2/legacy_compat.rs | 153 +++++++++++++++++++- src-tauri/src/library/commands.rs | 155 --------------------- 2 files changed, 149 insertions(+), 159 deletions(-) diff --git a/src-tauri/src/commands_v2/legacy_compat.rs b/src-tauri/src/commands_v2/legacy_compat.rs index a57e6a23b..3cf5cf5c9 100644 --- a/src-tauri/src/commands_v2/legacy_compat.rs +++ b/src-tauri/src/commands_v2/legacy_compat.rs @@ -308,10 +308,155 @@ pub async fn v2_share_album_songlink( pub async fn v2_library_backfill_downloads( state: State<'_, LibraryState>, offline_cache_state: State<'_, crate::offline_cache::OfflineCacheState>, -) -> Result { - crate::library::commands::library_backfill_downloads(state, offline_cache_state) - .await - .map_err(RuntimeError::Internal) +) -> Result { + use crate::library::{BackfillReport, MetadataExtractor}; + + log::info!("Command: v2_library_backfill_downloads"); + + let mut report = BackfillReport { + total_downloads: 0, + added_tracks: 0, + repaired_tracks: 0, + skipped_tracks: 0, + failed_tracks: Vec::new(), + }; + + // Get all ready cached tracks directly from offline cache DB + let cached_tracks = { + let cache_db_opt__ = offline_cache_state.db.lock().await; + let cache_db = cache_db_opt__ + .as_ref() + .ok_or_else(|| RuntimeError::Internal("No active session - please log in".to_string()))?; + + let mut stmt = cache_db + .conn() + .prepare("SELECT track_id, title, artist, album, album_id, duration_secs, file_path, quality, bit_depth, sample_rate FROM cached_tracks WHERE status = 'ready'") + .map_err(|e| RuntimeError::Internal(format!("Failed to query cached tracks: {}", e)))?; + + let rows = stmt + .query_map([], |row| { + Ok(( + row.get::<_, i64>(0)? as u64, // track_id + row.get::<_, String>(1)?, // title + row.get::<_, String>(2)?, // artist + row.get::<_, Option>(3)?, // album + row.get::<_, i64>(5)? as u64, // duration_secs + row.get::<_, String>(6)?, // file_path + row.get::<_, Option>(8)?.map(|v| v as u32), // bit_depth + row.get::<_, Option>(9)?, // sample_rate + )) + }) + .map_err(|e| RuntimeError::Internal(format!("Failed to map rows: {}", e)))?; + + rows.collect::, _>>() + .map_err(|e| RuntimeError::Internal(format!("Failed to collect cached tracks: {}", e)))? + }; // cache_db lock is dropped here + + report.total_downloads = cached_tracks.len(); + + let library_db_opt__ = state.db.lock().await; + let library_db = library_db_opt__ + .as_ref() + .ok_or_else(|| RuntimeError::Internal("No active session - please log in".to_string()))?; + + for (track_id, title, artist, album, duration_secs, file_path, bit_depth, sample_rate) in + cached_tracks + { + // Strategy: Try to match by qobuz_track_id first, then by file_path + // This handles both intact downloads and downloads damaged by scanner + + let exists_by_id = library_db + .track_exists_by_qobuz_id(track_id) + .unwrap_or(false); + + let exists_by_path = library_db.track_exists_by_path(&file_path).unwrap_or(false); + + if exists_by_id { + // Track exists with correct qobuz_track_id (not damaged) + // Check if it just needs source repair + match library_db.is_qobuz_cached_track_by_path(&file_path) { + Ok(true) => { + // Already marked as cached track, nothing to do + report.skipped_tracks += 1; + } + Ok(false) => { + // Has qobuz_track_id but lost source marker - unusual case + log::info!( + "Repairing source for track with intact ID {}: {}", + track_id, + title + ); + match library_db.repair_qobuz_cached_track_by_path(track_id, &file_path) { + Ok(true) => report.repaired_tracks += 1, + Ok(false) => report.skipped_tracks += 1, + Err(e) => { + log::warn!("Failed to repair track {}: {}", track_id, e); + report.failed_tracks.push(title); + } + } + } + Err(e) => { + log::warn!( + "Failed to check cached track status for {}: {}", + track_id, + e + ); + report.failed_tracks.push(title); + } + } + continue; + } + + if exists_by_path { + // Track exists by path but lost qobuz_track_id (damaged by scanner) + log::info!( + "Repairing damaged cached track (lost ID) {}: {}", + track_id, + title + ); + match library_db.repair_qobuz_cached_track_by_path(track_id, &file_path) { + Ok(true) => report.repaired_tracks += 1, + Ok(false) => report.skipped_tracks += 1, + Err(e) => { + log::warn!("Failed to repair track by path {}: {}", track_id, e); + report.failed_tracks.push(title); + } + } + continue; + } + + // Track doesn't exist - extract track/disc number from file tags + let (track_num, disc_num) = + match MetadataExtractor::extract(std::path::Path::new(&file_path)) { + Ok(meta) => (meta.track_number, meta.disc_number), + Err(e) => { + log::warn!("Could not extract metadata from {}: {}", file_path, e); + (None, None) + } + }; + + // Insert as new + match library_db.insert_qobuz_cached_track_direct( + track_id, + &title, + &artist, + album.as_deref(), + duration_secs, + &file_path, + bit_depth, + sample_rate, + track_num, + disc_num, + ) { + Ok(_) => report.added_tracks += 1, + Err(e) => { + log::warn!("Failed to insert track {}: {}", track_id, e); + report.failed_tracks.push(title); + } + } + } + + Ok(report) } #[tauri::command] diff --git a/src-tauri/src/library/commands.rs b/src-tauri/src/library/commands.rs index 732f7336e..ee9fdd477 100644 --- a/src-tauri/src/library/commands.rs +++ b/src-tauri/src/library/commands.rs @@ -2444,161 +2444,6 @@ pub async fn library_get_hidden_albums( db.get_hidden_albums().map_err(|e| e.to_string()) } -// === Qobuz Downloads Integration === - -#[tauri::command] -pub async fn library_backfill_downloads( - state: State<'_, LibraryState>, - offline_cache_state: State<'_, crate::offline_cache::OfflineCacheState>, -) -> Result { - log::info!("Command: library_backfill_downloads"); - - let mut report = BackfillReport { - total_downloads: 0, - added_tracks: 0, - repaired_tracks: 0, - skipped_tracks: 0, - failed_tracks: Vec::new(), - }; - - // Get all ready cached tracks directly from offline cache DB - let cached_tracks = { - let cache_db_opt__ = offline_cache_state.db.lock().await; - let cache_db = cache_db_opt__ - .as_ref() - .ok_or("No active session - please log in")?; - - let mut stmt = cache_db - .conn() - .prepare("SELECT track_id, title, artist, album, album_id, duration_secs, file_path, quality, bit_depth, sample_rate FROM cached_tracks WHERE status = 'ready'") - .map_err(|e| format!("Failed to query cached tracks: {}", e))?; - - let rows = stmt - .query_map([], |row| { - Ok(( - row.get::<_, i64>(0)? as u64, // track_id - row.get::<_, String>(1)?, // title - row.get::<_, String>(2)?, // artist - row.get::<_, Option>(3)?, // album - row.get::<_, i64>(5)? as u64, // duration_secs - row.get::<_, String>(6)?, // file_path - row.get::<_, Option>(8)?.map(|v| v as u32), // bit_depth - row.get::<_, Option>(9)?, // sample_rate - )) - }) - .map_err(|e| format!("Failed to map rows: {}", e))?; - - rows.collect::, _>>() - .map_err(|e| format!("Failed to collect cached tracks: {}", e))? - }; // cache_db lock is dropped here - - report.total_downloads = cached_tracks.len(); - - let library_db_opt__ = state.db.lock().await; - let library_db = library_db_opt__ - .as_ref() - .ok_or("No active session - please log in")?; - - for (track_id, title, artist, album, duration_secs, file_path, bit_depth, sample_rate) in - cached_tracks - { - // Strategy: Try to match by qobuz_track_id first, then by file_path - // This handles both intact downloads and downloads damaged by scanner - - let exists_by_id = library_db - .track_exists_by_qobuz_id(track_id) - .unwrap_or(false); - - let exists_by_path = library_db.track_exists_by_path(&file_path).unwrap_or(false); - - if exists_by_id { - // Track exists with correct qobuz_track_id (not damaged) - // Check if it just needs source repair - match library_db.is_qobuz_cached_track_by_path(&file_path) { - Ok(true) => { - // Already marked as cached track, nothing to do - report.skipped_tracks += 1; - } - Ok(false) => { - // Has qobuz_track_id but lost source marker - unusual case - log::info!( - "Repairing source for track with intact ID {}: {}", - track_id, - title - ); - match library_db.repair_qobuz_cached_track_by_path(track_id, &file_path) { - Ok(true) => report.repaired_tracks += 1, - Ok(false) => report.skipped_tracks += 1, - Err(e) => { - log::warn!("Failed to repair track {}: {}", track_id, e); - report.failed_tracks.push(title); - } - } - } - Err(e) => { - log::warn!( - "Failed to check cached track status for {}: {}", - track_id, - e - ); - report.failed_tracks.push(title); - } - } - continue; - } - - if exists_by_path { - // Track exists by path but lost qobuz_track_id (damaged by scanner) - log::info!( - "Repairing damaged cached track (lost ID) {}: {}", - track_id, - title - ); - match library_db.repair_qobuz_cached_track_by_path(track_id, &file_path) { - Ok(true) => report.repaired_tracks += 1, - Ok(false) => report.skipped_tracks += 1, - Err(e) => { - log::warn!("Failed to repair track by path {}: {}", track_id, e); - report.failed_tracks.push(title); - } - } - continue; - } - - // Track doesn't exist - extract track/disc number from file tags - let (track_num, disc_num) = - match MetadataExtractor::extract(std::path::Path::new(&file_path)) { - Ok(meta) => (meta.track_number, meta.disc_number), - Err(e) => { - log::warn!("Could not extract metadata from {}: {}", file_path, e); - (None, None) - } - }; - - // Insert as new - match library_db.insert_qobuz_cached_track_direct( - track_id, - &title, - &artist, - album.as_deref(), - duration_secs, - &file_path, - bit_depth, - sample_rate, - track_num, - disc_num, - ) { - Ok(_) => report.added_tracks += 1, - Err(e) => { - log::warn!("Failed to insert track {}: {}", track_id, e); - report.failed_tracks.push(title); - } - } - } - - Ok(report) -} - // === Artist Images Management === // ArtistImageInfo is now defined in qbz-library, re-exported via crate::library