diff --git a/src-tauri/src/discogs/mod.rs b/src-tauri/src/discogs/mod.rs index d50f9b8ea..718201e44 100644 --- a/src-tauri/src/discogs/mod.rs +++ b/src-tauri/src/discogs/mod.rs @@ -147,20 +147,31 @@ impl DiscogsClient { /// Search for album artwork options /// Returns up to 10 image options from the first matching release + /// If catalog_number is provided, searches by that first, then falls back to artist + album pub async fn search_artwork_options( &self, artist: &str, album: &str, + catalog_number: Option<&str>, ) -> Result, String> { - // Build search query - let query = format!("{} {}", artist, album); + // Build search query - prefer catalog number if available + let query = if let Some(catno) = catalog_number.filter(|s| !s.trim().is_empty()) { + catno.to_string() + } else { + format!("{} {}", artist, album) + }; let url = format!( "{}/search?q={}&type=release", DISCOGS_PROXY_URL, urlencoding::encode(&query) ); - log::debug!("Searching Discogs artwork options for: {} - {}", artist, album); + log::debug!( + "Searching Discogs artwork options for: {} - {} (catalog: {:?})", + artist, + album, + catalog_number + ); let response = self.client.get(&url).send().await .map_err(|e| format!("Failed to search Discogs: {}", e))?; diff --git a/src-tauri/src/library/commands.rs b/src-tauri/src/library/commands.rs index 88ccce790..21cd89039 100644 --- a/src-tauri/src/library/commands.rs +++ b/src-tauri/src/library/commands.rs @@ -1720,11 +1720,19 @@ pub async fn playlist_get_offline_available( pub async fn discogs_search_artwork( artist: String, album: String, + catalog_number: Option, ) -> Result, String> { - log::info!("Command: discogs_search_artwork {} - {}", artist, album); + log::info!( + "Command: discogs_search_artwork {} - {} (catalog: {:?})", + artist, + album, + catalog_number + ); let client = DiscogsClient::new(); - client.search_artwork_options(&artist, &album).await + client + .search_artwork_options(&artist, &album, catalog_number.as_deref()) + .await } /// Download and save Discogs artwork diff --git a/src-tauri/src/library/cue_parser.rs b/src-tauri/src/library/cue_parser.rs index 8daea1920..c33e58bd5 100644 --- a/src-tauri/src/library/cue_parser.rs +++ b/src-tauri/src/library/cue_parser.rs @@ -257,6 +257,7 @@ pub fn cue_to_tracks( disc_number: inferred_disc, year: None, genre: None, + catalog_number: None, duration_secs: duration, format: format.clone(), bit_depth: properties.bit_depth, diff --git a/src-tauri/src/library/database.rs b/src-tauri/src/library/database.rs index 752c8157c..560d47f75 100644 --- a/src-tauri/src/library/database.rs +++ b/src-tauri/src/library/database.rs @@ -309,6 +309,24 @@ impl LibraryDatabase { ).map_err(|e| LibraryError::Database(format!("Migration failed: {}", e)))?; } + // Check if catalog_number column exists + let has_catalog_number: bool = self + .conn + .query_row( + "SELECT COUNT(*) FROM pragma_table_info('local_tracks') WHERE name = 'catalog_number'", + [], + |row| row.get::<_, i64>(0), + ) + .map(|count| count > 0) + .unwrap_or(false); + + if !has_catalog_number { + log::info!("Running migration: adding catalog_number to local_tracks"); + self.conn + .execute_batch("ALTER TABLE local_tracks ADD COLUMN catalog_number TEXT;") + .map_err(|e| LibraryError::Database(format!("Migration failed: {}", e)))?; + } + Ok(()) } @@ -513,11 +531,11 @@ impl LibraryDatabase { .execute( r#"INSERT OR REPLACE INTO local_tracks (file_path, title, artist, album, album_artist, track_number, - disc_number, year, genre, duration_secs, format, bit_depth, + disc_number, year, genre, catalog_number, duration_secs, format, bit_depth, sample_rate, channels, file_size_bytes, cue_file_path, cue_start_secs, cue_end_secs, artwork_path, last_modified, indexed_at, album_group_key, album_group_title) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#, + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#, params![ track.file_path, track.title, @@ -528,6 +546,7 @@ impl LibraryDatabase { track.disc_number, track.year, track.genre, + track.catalog_number, track.duration_secs, track.format.to_string(), track.bit_depth, @@ -627,6 +646,7 @@ impl LibraryDatabase { ELSE MIN(artist) END as artist, MIN(year) as year, + MIN(catalog_number) as catalog_number, MAX(CASE WHEN artwork_path IS NOT NULL THEN artwork_path END) as artwork, COUNT(*) as track_count, SUM(duration_secs) as total_duration, @@ -640,6 +660,7 @@ impl LibraryDatabase { COALESCE(album_group_title, album) as title, COALESCE(album_artist, artist) as artist, year, + catalog_number, artwork_path, duration_secs, format, @@ -664,6 +685,7 @@ impl LibraryDatabase { ELSE MIN(artist) END as artist, MIN(year) as year, + MIN(catalog_number) as catalog_number, MAX(CASE WHEN artwork_path IS NOT NULL THEN artwork_path END) as artwork, COUNT(*) as track_count, SUM(duration_secs) as total_duration, @@ -677,6 +699,7 @@ impl LibraryDatabase { COALESCE(album_group_title, album) as title, COALESCE(album_artist, artist) as artist, year, + catalog_number, artwork_path, duration_secs, format, @@ -705,25 +728,26 @@ impl LibraryDatabase { let group_key: String = row.get(0)?; let album: String = row.get(1)?; let artist: String = row.get(2)?; - let artwork_path: Option = row.get(4)?; - + let artwork_path: Option = row.get(5)?; + log::info!("Album {} by {}: artwork_path = {:?}", album, artist, artwork_path); - + Ok(LocalAlbum { id: group_key.clone(), title: album, artist, year: row.get(3)?, + catalog_number: row.get(4)?, artwork_path, - track_count: row.get(5)?, - total_duration_secs: row.get(6)?, + track_count: row.get(6)?, + total_duration_secs: row.get(7)?, format: Self::parse_format( - &row.get::<_, Option>(7)?.unwrap_or_default(), + &row.get::<_, Option>(8)?.unwrap_or_default(), ), - bit_depth: row.get(8)?, - sample_rate: row.get::<_, Option>(9)?.unwrap_or(44100), + bit_depth: row.get(9)?, + sample_rate: row.get::<_, Option>(10)?.unwrap_or(44100), directory_path: row - .get::<_, Option>(10)? + .get::<_, Option>(11)? .unwrap_or_else(|| group_key.clone()), }) }) @@ -973,6 +997,7 @@ impl LibraryDatabase { disc_number: row.get(7)?, year: row.get(8)?, genre: row.get(9)?, + catalog_number: row.get(26).ok().flatten(), duration_secs: row.get(10)?, format: Self::parse_format(&row.get::<_, String>(11)?), bit_depth: row.get(12)?, @@ -1531,6 +1556,7 @@ impl LibraryDatabase { disc_number: row.get(9)?, year: row.get(10)?, genre: row.get(11)?, + catalog_number: None, duration_secs: row.get(12)?, format: Self::parse_format(&row.get::<_, String>(13)?), bit_depth: row.get(14)?, @@ -1579,6 +1605,7 @@ impl LibraryDatabase { disc_number: row.get(9)?, year: row.get(10)?, genre: row.get(11)?, + catalog_number: None, duration_secs: row.get(12)?, format: Self::parse_format(&row.get::<_, String>(13)?), bit_depth: row.get(14)?, diff --git a/src-tauri/src/library/metadata.rs b/src-tauri/src/library/metadata.rs index 26487959f..fba9f2edb 100644 --- a/src-tauri/src/library/metadata.rs +++ b/src-tauri/src/library/metadata.rs @@ -325,6 +325,7 @@ impl MetadataExtractor { .or(inferred_disc), year: tag.year().map(|y| y as u32), genre: tag.genre().map(|s| s.to_string()), + catalog_number: tag.get_string(&ItemKey::CatalogNumber).map(|s| s.to_string()), duration_secs, format, bit_depth, @@ -363,6 +364,7 @@ impl MetadataExtractor { disc_number: inferred_disc, year: None, genre: None, + catalog_number: None, duration_secs, format, bit_depth, diff --git a/src-tauri/src/library/models.rs b/src-tauri/src/library/models.rs index 3b126cad0..66572fca5 100644 --- a/src-tauri/src/library/models.rs +++ b/src-tauri/src/library/models.rs @@ -51,6 +51,7 @@ pub struct LocalTrack { pub disc_number: Option, pub year: Option, pub genre: Option, + pub catalog_number: Option, // Audio properties pub duration_secs: u64, @@ -88,6 +89,7 @@ impl Default for LocalTrack { disc_number: None, year: None, genre: None, + catalog_number: None, duration_secs: 0, format: AudioFormat::Unknown, bit_depth: None, @@ -120,6 +122,7 @@ pub struct LocalAlbum { pub title: String, pub artist: String, pub year: Option, + pub catalog_number: Option, pub artwork_path: Option, pub track_count: u32, pub total_duration_secs: u64, diff --git a/src/lib/components/views/LocalLibraryView.svelte b/src/lib/components/views/LocalLibraryView.svelte index d32df45b8..c0461cbe7 100644 --- a/src/lib/components/views/LocalLibraryView.svelte +++ b/src/lib/components/views/LocalLibraryView.svelte @@ -58,6 +58,7 @@ title: string; artist: string; year?: number; + catalog_number?: string; artwork_path?: string; track_count: number; total_duration_secs: number; @@ -834,7 +835,8 @@ const options = await invoke('discogs_search_artwork', { artist: selectedAlbum.artist, - album: selectedAlbum.title + album: selectedAlbum.title, + catalogNumber: selectedAlbum.catalog_number || null }); discogsImageOptions = options; @@ -1419,6 +1421,7 @@

{selectedAlbum.artist}

{/if}

+ {#if selectedAlbum.catalog_number}Cat# {selectedAlbum.catalog_number} • {/if} {#if selectedAlbum.year}{selectedAlbum.year} • {/if} {selectedAlbum.track_count} tracks • {formatTotalDuration(selectedAlbum.total_duration_secs)}