selene-core 0.9.0-alpha.2

selene-core is the backend for Selene, a local-first music player
Documentation
// use std::{collections::HashMap, sync::Arc};

// use barber::ProgressRenderer;
// use image::ImageError;
// use lunar_lib::{
//     database::{Db, Entry, Id, TransactionError},
//     iterator_ext::IteratorExtensions,
//     paths::{data_dir, sys::sanitize_str},
// };
// use thiserror::Error;

// use crate::{
//     database::Library,
//     library::{
//         album::Album,
//         image_art::CacheableArt,
//         linking::{album_track_linking::album_track_linking, lyric_linking::link_lrc_files},
//         track::Track,
//     },
//     lyrics::synced_lyrics::LyricParseError,
// };

// mod album_track_linking;
// mod lyric_linking;

// #[derive(Debug, Error)]
// pub enum LinkingError {
//     #[error("IoError: {0}")]
//     Io(#[from] std::io::Error),

//     #[error("ImageError: {0}")]
//     Image(#[from] ImageError),

//     #[error("Transaction Error: {0}")]
//     Transaction(#[from] TransactionError),

//     #[error("LyricParse Error: {0}")]
//     LyricParse(#[from] LyricParseError),
// }

// pub fn smart_link(progress_renderer: Arc<dyn ProgressRenderer>) -> Result<(), LinkingError> {
//     let db = Db::<Library>::open().unwrap();
//     let (mut all_tracks, mut all_albums) = {
//         let all_tracks: HashMap<Id<Track>, (Entry<Track>, bool)> = Entry::<Track>::db_iter(&db)
//             .try_to_vec()?
//             .into_iter()
//             .map(|t| (t.id(), (t, false)))
//             .collect();
//         let all_albums: HashMap<Id<Album>, (Entry<Album>, bool)> = Entry::<Album>::db_iter(&db)
//             .try_to_vec()?
//             .into_iter()
//             .map(|t| (t.id(), (t, false)))
//             .collect();

//         (all_tracks, all_albums)
//     };

//     link_lrc_files(all_tracks.values_mut(), progress_renderer.clone())?;

//     {
//         let mut track_groups: HashMap<_, Vec<_>> = HashMap::new();
//         for (track, changed) in all_tracks.values_mut() {
//             let parent = track.container().path().parent().unwrap().to_owned();
//             track_groups
//                 .entry(parent)
//                 .or_default()
//                 .push((track, changed));
//         }
//         album_track_linking(progress_renderer, &mut all_albums, &mut track_groups)?;
//     }

//     for (album, changed) in all_albums.values_mut() {
//         if album.art.is_some() {
//             continue;
//         }

//         let first_art = album
//             .tracks
//             .iter()
//             .find_map(|tr| all_tracks.get(&tr.id)?.0.metadata().art.as_ref());

//         let Some(cover_art) = first_art else { continue };
//         let hash = cover_art.hash();

//         let all_same = album
//             .tracks
//             .iter()
//             .filter_map(|tr| all_tracks.get(&tr.id))
//             .all(|(t, _)| t.metadata().art.as_ref().is_some_and(|a| a.hash() == hash));

//         if all_same {
//             let export_path = data_dir().join(format!("album_art/{}", sanitize_str(album.name())));
//             album.art = Some(cover_art.export_to_image_art(&export_path)?);
//             *changed = true;
//         }
//     }

//     let changed_tracks = all_tracks
//         .into_iter()
//         .filter_map(|(_, (track, changed))| changed.then_some(track))
//         .to_vec();
//     let changed_albums = all_albums
//         .into_iter()
//         .filter_map(|(_, (album, changed))| changed.then_some(album))
//         .to_vec();

//     db.transaction(false, None, |cas_tx| {
//         for track in &changed_tracks {
//             track.clone().tx_upsert(cas_tx)?;
//         }
//         for album in &changed_albums {
//             album.clone().tx_upsert(cas_tx)?;
//         }
//         Ok(())
//     })
//     .map_err(TransactionError::from)?;

//     Ok(())
// }