use std::{
collections::{HashMap, HashSet},
fs::{self, File},
io::BufReader,
path::PathBuf,
sync::Arc,
};
use barber::{ProgressBar, ProgressRenderer};
use lunar_lib::warn;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use crate::{cache_dir, config::common::common_config, library::track::TrackId, utils::hash_file};
pub mod album;
pub mod artist;
pub mod collection;
pub mod track;
pub mod cover_art;
pub mod edit;
pub mod export;
pub mod metadata;
mod migrate;
pub use migrate::*;
mod orphan_relinking;
pub use orphan_relinking::*;
mod import;
pub use import::*;
mod scan;
pub use scan::*;
pub fn hash_source_files(
progress_renderer: Arc<dyn ProgressRenderer>,
) -> std::io::Result<HashMap<TrackId, PathBuf>> {
let sources = common_config().get_source_files();
let cache_file = cache_dir().join("source_hashes.cbor");
let mut source_hashes = if cache_file.is_file() {
let file = File::open(&cache_file)?;
let reader = BufReader::new(file);
match ciborium::from_reader(reader) {
Ok(result) => result,
Err(err) => {
warn!(
"Failed to reach cache file '{file}': {err}",
file = cache_file.display()
);
HashMap::new()
}
}
} else {
HashMap::new()
};
let cached_sources: HashSet<&PathBuf> = source_hashes.values().collect();
let not_hashed: Vec<PathBuf> = sources
.par_iter()
.filter(|source| !cached_sources.contains(source))
.cloned()
.collect();
if not_hashed.is_empty() {
return Ok(source_hashes);
}
let progress_bar = ProgressBar::new(0, not_hashed.len(), progress_renderer.clone());
progress_bar.set_label("Hashing sources...");
for source in not_hashed {
let hash = hash_file(&source)?;
source_hashes.insert(TrackId::new(hash), source);
progress_bar.increment();
}
fs::create_dir_all(cache_dir())?;
let writer = fs::OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(cache_file)?;
let _ = ciborium::into_writer(&source_hashes, writer);
progress_bar.flush();
Ok(source_hashes)
}