library.rs (2720B)
1 use std::{ 2 collections::BTreeMap, 3 fs, 4 path::{Path, PathBuf}, 5 }; 6 7 #[derive(Debug, Clone)] 8 pub struct Library { 9 pub artists: BTreeMap<String, BTreeMap<String, Vec<Track>>>, 10 } 11 12 #[derive(Debug, Clone)] 13 pub struct Track { 14 pub title: String, 15 pub path: PathBuf, 16 } 17 18 pub struct LibraryItem { 19 pub artist: String, 20 pub album: String, 21 pub title: String, 22 pub path: PathBuf, 23 } 24 25 impl Library { 26 pub fn build(items: Vec<LibraryItem>) -> Self { 27 let mut artists = BTreeMap::new(); 28 for item in items { 29 artists 30 .entry(item.artist) 31 .or_insert_with(BTreeMap::new) 32 .entry(item.album) 33 .or_insert_with(Vec::new) 34 .push(Track { 35 title: item.title, 36 path: item.path, 37 }); 38 } 39 Self { artists } 40 } 41 } 42 43 pub fn scan_library(path: &str) -> Vec<LibraryItem> { 44 let mut items = Vec::new(); 45 if path.is_empty() { 46 return items; 47 } 48 let root = Path::new(path); 49 if root.exists() && root.is_dir() { 50 visit_dirs(root, &mut items); 51 } 52 items 53 } 54 55 fn visit_dirs(dir: &Path, items: &mut Vec<LibraryItem>) { 56 if let Ok(entries) = fs::read_dir(dir) { 57 for entry in entries.flatten() { 58 let path = entry.path(); 59 if path.is_dir() { 60 visit_dirs(&path, items); 61 } else if path.is_file() && is_music_file(&path) { 62 let title = path 63 .file_stem() 64 .unwrap_or_default() 65 .to_string_lossy() 66 .to_string(); 67 68 let album = path 69 .parent() 70 .and_then(|p| p.file_name()) 71 .map(|n| n.to_string_lossy().to_string()) 72 .unwrap_or_else(|| "Unknown Album".to_string()); 73 74 let artist = path 75 .parent() 76 .and_then(|p| p.parent()) 77 .and_then(|p| p.file_name()) 78 .map(|n| n.to_string_lossy().to_string()) 79 .unwrap_or_else(|| "Unknown Artist".to_string()); 80 81 items.push(LibraryItem { 82 artist, 83 album, 84 title, 85 path, 86 }); 87 } 88 } 89 } 90 } 91 92 fn is_music_file(path: &PathBuf) -> bool { 93 let ext = path 94 .extension() 95 .and_then(|s| s.to_str()) 96 .unwrap_or("") 97 .to_lowercase(); 98 matches!(ext.as_str(), "flac" | "mp3" | "ogg" | "wav") 99 }