use std::slice::Iter;
#[cfg(feature = "daemon")]
use {
rust_utils::logging::Log,
std::{
env, fs,
collections::HashSet
},
lofty::{read_from_path, AudioFile, Accessor, ItemKey, TaggedFileExt}
};
mod album;
mod artist;
mod playlist;
mod song;
pub use self::{
song::Song,
album::Album,
artist::Artist,
playlist::{Playlist, PlaylistStore, PlaylistAddable}
};
#[derive(Clone, serde_derive::Serialize, serde_derive::Deserialize)]
pub struct Library(Vec<Artist>);
impl Library {
pub fn find_song(&self, path: &str) -> Option<Song> {
for artist in self.iter() {
for album in &artist.albums {
for song in &album.songs {
if song.get_path() == path {
return Some(song.clone());
}
}
}
}
None
}
pub fn iter(&self) -> Iter<Artist> {
self.0.iter()
}
pub fn get_artist(&self, index: usize) -> Option<&Artist> {
self.0.get(index)
}
}
#[cfg(feature = "daemon")]
impl Library {
pub fn load(first_load: bool, rescan: bool, music_dir: &str, log: &Log) -> Self {
fs::create_dir_all(format!("{music_dir}/.mucl")).unwrap_or(());
if rescan {
fs::remove_file(format!("{music_dir}/.mucl/library")).unwrap();
return Self::gen_lib(music_dir, log);
}
let encoded = if let Ok(l) = fs::read(format!("{music_dir}/.mucl/library")) {
l
}
else {
return Self::gen_lib(music_dir, log);
};
if let Ok(l) = bincode::deserialize(&encoded) {
if first_load {
log.line_basic("Refreshing playlists...", true);
let mut playlists = PlaylistStore::load(&music_dir, &l);
playlists.gen_thumbnails();
}
l
}
else {
Self::gen_lib(music_dir, log)
}
}
fn gen_lib(music_dir: &str, log: &Log) -> Self {
log.line_basic("Scanning music library...", true);
let mut songs: Vec<(Song, u32)> = Vec::new();
let mut albums: Vec<Album> = Vec::new();
let mut artists: Vec<Artist> = Vec::new();
let mut file_list = Vec::new();
env::set_current_dir(music_dir).expect("What went wrong?!");
for entry in glob::glob("*/*/*.*").unwrap() {
file_list.push(format!("{}", entry.unwrap().display()));
}
for entry in file_list {
if let Ok(song_file) = read_from_path(&entry) {
let tag = &song_file.tags()[0];
let title = tag.title();
let album = tag.album();
let artist = tag.artist();
let year: u32 = tag.get_string(&ItemKey::RecordingDate).unwrap_or("0").parse().unwrap_or(0);
let t_num_raw = tag.get_string(&ItemKey::TrackNumber).unwrap_or("0");
let track_num: u32 = if t_num_raw.contains('/') {
t_num_raw.split('/').next().unwrap().parse().unwrap_or(0)
}
else {
t_num_raw.parse().unwrap_or(0)
};
let properties = song_file.properties();
let duration = properties.duration().as_secs();
let path = format!("{music_dir}/{entry}");
songs.push((
Song::new(
title.as_deref().unwrap_or("<unknown>"),
album.as_deref().unwrap_or("<unknown>"),
artist.as_deref().unwrap_or("<unknown>"),
duration + 1, track_num, &path
),
year
));
}
}
let mut album_names = HashSet::new();
for (song, year) in &songs {
if album_names.insert(&song.album) {
albums.push(Album::new(&song.album, &song.artist, *year));
}
}
for (song, _) in &songs {
for album in &mut albums {
if song.album == album.name {
album.add_song(song);
}
}
}
for album in &mut albums {
album.sort_songs();
}
let mut artist_names = HashSet::new();
for album in &albums {
if artist_names.insert(&album.artist) {
artists.push(Artist::new(&album.artist));
}
}
for album in &albums {
for artist in &mut artists {
if album.artist == artist.name {
artist.add_album(album);
}
}
}
for artist in &mut artists {
artist.sort_albums();
artist.albums.reverse();
}
let lib = Library(artists);
let encoded = bincode::serialize(&lib).expect("What went wrong?!");
fs::write(format!("{}/.mucl/library", music_dir), encoded).expect("What went wrong?!");
log.line_basic("Refreshing playlists...", true);
let mut playlists = PlaylistStore::load(music_dir, &lib);
playlists.gen_thumbnails();
lib
}
}