use std::{ops::Deref, str::FromStr, sync::Arc};
use blake3::{Hash, hash};
use chrono::{DateTime, Utc};
use lunar_lib::{
database::{DatabaseEntry, EntryId, TransactionError, caching::Cacheable},
iterator_ext::IteratorExtensions,
};
use serde::{Deserialize, Serialize};
use crate::{
database::{LibraryDb, Resolveable},
library::{
artist::{Artist, ArtistId},
image_art::ImageArt,
track::{Track, TrackId},
},
};
pub mod frontend_impls;
pub mod trait_impls;
pub const UNKNOWN_ALBUM: &str = "UNKNOWN ALBUM";
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AlbumId(Hash);
impl EntryId for AlbumId {
type Entry = Album;
type IdDb = LibraryDb;
}
impl Deref for AlbumId {
type Target = [u8; 32];
fn deref(&self) -> &Self::Target {
self.0.as_bytes()
}
}
impl FromStr for AlbumId {
type Err = <Hash as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(Hash::from_str(s)?))
}
}
impl AlbumId {
fn new(name: &str) -> Self {
Self(hash(name.as_bytes()))
}
#[must_use]
pub fn to_hash(&self) -> Hash {
self.0
}
#[must_use]
pub fn to_selene_id(&self) -> String {
format!("album:{}", self.0)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Album {
id: AlbumId,
pub name: String,
pub art: Option<ImageArt>,
pub(crate) tracks: Vec<TrackReference>,
pub(crate) artists: Vec<ArtistId>,
pub disc_total: Option<u32>,
pub genre: Vec<String>,
pub track_total: Option<u32>,
pub date: Option<DateTime<Utc>>,
}
impl Album {
pub(crate) fn new(name: String, artists: Vec<ArtistId>, tracks: Vec<TrackReference>) -> Self {
let hash = AlbumId::new(&name);
Self {
artists,
art: None,
disc_total: None,
genre: Vec::new(),
id: hash,
name,
track_total: None,
date: None,
tracks,
}
}
}
impl Album {
#[must_use]
pub fn id(&self) -> AlbumId {
self.id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
pub fn tracks(&self, db: &LibraryDb) -> Result<Vec<Track>, TransactionError> {
Track::db_get_batch(self.track_refs().iter().map(|t| t.id), db)
}
pub fn tracks_cache(&self, db: &LibraryDb) -> Result<Vec<Arc<Track>>, TransactionError> {
Track::cache_get_batch_from(self.track_refs().iter().map(|t| t.id), db)
}
#[must_use]
pub fn artists_raw(&self) -> &[ArtistId] {
&self.artists
}
#[must_use]
pub fn artists(&self, db: &LibraryDb) -> Result<Vec<Artist>, TransactionError> {
Artist::db_get_batch(&self.artists, db)
}
#[must_use]
pub fn artists_cache(&self, db: &LibraryDb) -> Result<Vec<Arc<Artist>>, TransactionError> {
Artist::cache_get_batch_from(&self.artists, db)
}
#[must_use]
pub fn track_refs(&self) -> &[TrackReference] {
&self.tracks
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
pub struct TrackReference {
pub id: TrackId,
pub track_num: Option<u32>,
pub disc_num: Option<u32>,
}
impl PartialEq for TrackReference {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for TrackReference {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedAlbum {
pub album: Arc<Album>,
pub artists: Vec<Arc<Artist>>,
pub tracks: Vec<(Arc<Track>, Vec<Arc<Artist>>)>,
}
impl Deref for ResolvedAlbum {
type Target = Album;
fn deref(&self) -> &Self::Target {
&self.album
}
}
impl Resolveable for Album {
type Resolved = ResolvedAlbum;
fn resolve(album: Arc<Self>, db: &Self::Db) -> Result<Self::Resolved, TransactionError> {
let artists = album.artists_cache(db)?;
let tracks = album.tracks_cache(db)?;
let track_artists = tracks
.iter()
.try_map(|t| t.metadata.artists_cache(db))?
.to_vec();
Ok(ResolvedAlbum {
album,
artists,
tracks: tracks.into_iter().zip(track_artists).collect(),
})
}
}