feat(library): extract and display album artwork
- Extract embedded artwork from audio files during scan - Save extracted artwork to cache directory (~/.cache/qbz/artwork) - Fallback to folder artwork (cover.jpg, folder.jpg, front.jpg, etc.) - Add get_artwork_cache_dir helper function - Artwork now displays in album cards and detail views
This commit is contained in:
1 parent
e8a61a45ce
commit
f6dbf2ee2a
3 files changed
+45
-2
No files matched your search
@@ -6,7 +6,7 @@ use tauri::State;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::library::{
|
use crate::library::{
|
||||||
cue_to_tracks, CueParser, LibraryDatabase, LibraryError, LibraryScanner, LibraryStats,
|
cue_to_tracks, get_artwork_cache_dir, CueParser, LibraryDatabase, LibraryScanner, LibraryStats,
|
||||||
LocalAlbum, LocalArtist, LocalTrack, MetadataExtractor, ScanError, ScanProgress, ScanStatus,
|
LocalAlbum, LocalArtist, LocalTrack, MetadataExtractor, ScanError, ScanProgress, ScanStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -158,7 +158,15 @@ pub async fn library_scan(state: State<'_, LibraryState>) -> Result<(), String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
match MetadataExtractor::extract(audio_path) {
|
match MetadataExtractor::extract(audio_path) {
|
||||||
Ok(track) => {
|
Ok(mut track) => {
|
||||||
|
// Try to extract embedded artwork, fallback to folder artwork
|
||||||
|
let artwork_cache = get_artwork_cache_dir();
|
||||||
|
if let Some(artwork_path) = MetadataExtractor::extract_artwork(audio_path, &artwork_cache) {
|
||||||
|
track.artwork_path = Some(artwork_path);
|
||||||
|
} else if let Some(folder_art) = MetadataExtractor::find_folder_artwork(audio_path) {
|
||||||
|
track.artwork_path = Some(folder_art);
|
||||||
|
}
|
||||||
|
|
||||||
let db = state.db.lock().await;
|
let db = state.db.lock().await;
|
||||||
if let Err(e) = db.insert_track(&track) {
|
if let Err(e) = db.insert_track(&track) {
|
||||||
all_errors.push(ScanError {
|
all_errors.push(ScanError {
|
||||||
|
|||||||
@@ -197,6 +197,31 @@ impl MetadataExtractor {
|
|||||||
}
|
}
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look for folder artwork (cover.jpg, folder.jpg, etc.)
|
||||||
|
/// Returns the path if found
|
||||||
|
pub fn find_folder_artwork(audio_file_path: &Path) -> Option<String> {
|
||||||
|
let parent_dir = audio_file_path.parent()?;
|
||||||
|
|
||||||
|
// Common artwork filenames in order of preference
|
||||||
|
const ARTWORK_NAMES: &[&str] = &[
|
||||||
|
"cover.jpg", "cover.jpeg", "cover.png",
|
||||||
|
"folder.jpg", "folder.jpeg", "folder.png",
|
||||||
|
"front.jpg", "front.jpeg", "front.png",
|
||||||
|
"album.jpg", "album.jpeg", "album.png",
|
||||||
|
"Cover.jpg", "Cover.jpeg", "Cover.png",
|
||||||
|
"Folder.jpg", "Folder.jpeg", "Folder.png",
|
||||||
|
];
|
||||||
|
|
||||||
|
for name in ARTWORK_NAMES {
|
||||||
|
let artwork_path = parent_dir.join(name);
|
||||||
|
if artwork_path.exists() {
|
||||||
|
return Some(artwork_path.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ pub fn get_db_path() -> PathBuf {
|
|||||||
data_dir.join("library.db")
|
data_dir.join("library.db")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get artwork cache directory
|
||||||
|
pub fn get_artwork_cache_dir() -> PathBuf {
|
||||||
|
let cache_dir = dirs::cache_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join("qbz")
|
||||||
|
.join("artwork");
|
||||||
|
std::fs::create_dir_all(&cache_dir).ok();
|
||||||
|
cache_dir
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize library state
|
/// Initialize library state
|
||||||
pub fn init_library_state() -> Result<LibraryState, LibraryError> {
|
pub fn init_library_state() -> Result<LibraryState, LibraryError> {
|
||||||
let db_path = get_db_path();
|
let db_path = get_db_path();
|
||||||
|
|||||||
Reference in new issue
Block a user