Skip to main content

selene_core/library/
album.rs

1use std::{ops::Deref, str::FromStr, sync::Arc};
2
3use blake3::{Hash, hash};
4use chrono::{DateTime, Utc};
5use lunar_lib::{
6    database::{EntryId, EntryIdIteratorExt, TransactionError},
7    iterator_ext::IteratorExtensions,
8};
9use serde::{Deserialize, Serialize};
10
11use crate::{
12    database::{LibraryDb, Resolveable},
13    library::{
14        artist::{Artist, ArtistId},
15        image_art::ImageArt,
16        track::{Track, TrackId},
17    },
18};
19
20pub mod frontend_impls;
21pub mod trait_impls;
22
23pub const UNKNOWN_ALBUM: &str = "UNKNOWN ALBUM";
24
25#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
26pub struct AlbumId(Hash);
27
28impl EntryId for AlbumId {
29    type Entry = Album;
30    type IdDb = LibraryDb;
31}
32
33impl Deref for AlbumId {
34    type Target = [u8; 32];
35
36    fn deref(&self) -> &Self::Target {
37        self.0.as_bytes()
38    }
39}
40
41impl FromStr for AlbumId {
42    type Err = <Hash as FromStr>::Err;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        Ok(Self(Hash::from_str(s)?))
46    }
47}
48
49impl AlbumId {
50    fn new(name: &str) -> Self {
51        Self(hash(name.as_bytes()))
52    }
53
54    #[must_use]
55    pub fn to_hash(&self) -> Hash {
56        self.0
57    }
58
59    #[must_use]
60    pub fn to_selene_id(&self) -> String {
61        format!("album:{}", self.0)
62    }
63}
64
65#[derive(Debug, Clone, Deserialize, Serialize)]
66pub struct Album {
67    id: AlbumId,
68
69    pub name: String,
70    pub art: Option<ImageArt>,
71
72    /// Do not modify this variable without handling dangling references
73    pub(crate) tracks: Vec<TrackReference>,
74
75    pub(crate) artists: Vec<ArtistId>,
76    pub disc_total: Option<u32>,
77    pub genre: Vec<String>,
78    pub track_total: Option<u32>,
79    pub date: Option<DateTime<Utc>>,
80}
81
82// Core
83impl Album {
84    pub(crate) fn new(name: String, artists: Vec<ArtistId>, tracks: Vec<TrackReference>) -> Self {
85        let hash = AlbumId::new(&name);
86
87        Self {
88            artists,
89            art: None,
90            disc_total: None,
91            genre: Vec::new(),
92            id: hash,
93            name,
94            track_total: None,
95            date: None,
96            tracks,
97        }
98    }
99}
100
101// Accessors
102impl Album {
103    #[must_use]
104    pub fn id(&self) -> AlbumId {
105        self.id
106    }
107
108    #[must_use]
109    pub fn name(&self) -> &str {
110        &self.name
111    }
112
113    pub fn tracks(&self, db: &LibraryDb) -> Result<Vec<Track>, TransactionError> {
114        self.track_refs().iter().map(|t| t.id).db_get_batch(db)
115    }
116
117    pub fn tracks_cache(&self, db: &LibraryDb) -> Result<Vec<Arc<Track>>, TransactionError> {
118        self.track_refs().iter().map(|t| t.id).cache_get_batch(db)
119    }
120
121    #[must_use]
122    pub fn artists_raw(&self) -> &[ArtistId] {
123        &self.artists
124    }
125
126    #[must_use]
127    pub fn artists(&self, db: &LibraryDb) -> Result<Vec<Artist>, TransactionError> {
128        self.artists.iter().copied().db_get_batch(db)
129    }
130
131    #[must_use]
132    pub fn artists_cache(&self, db: &LibraryDb) -> Result<Vec<Arc<Artist>>, TransactionError> {
133        self.artists.iter().copied().cache_get_batch(db)
134    }
135
136    #[must_use]
137    pub fn track_refs(&self) -> &[TrackReference] {
138        &self.tracks
139    }
140}
141
142#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
143pub struct TrackReference {
144    pub id: TrackId,
145    pub track_num: Option<u32>,
146    pub disc_num: Option<u32>,
147}
148
149impl PartialEq for TrackReference {
150    fn eq(&self, other: &Self) -> bool {
151        self.id == other.id
152    }
153}
154
155impl Eq for TrackReference {}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct ResolvedAlbum {
159    pub album: Arc<Album>,
160    pub artists: Vec<Arc<Artist>>,
161
162    pub tracks: Vec<(Arc<Track>, Vec<Arc<Artist>>)>,
163}
164
165impl Deref for ResolvedAlbum {
166    type Target = Album;
167
168    fn deref(&self) -> &Self::Target {
169        &self.album
170    }
171}
172
173impl Resolveable for Album {
174    type Resolved = ResolvedAlbum;
175
176    fn resolve(album: Arc<Self>, db: &Self::Db) -> Result<Self::Resolved, TransactionError> {
177        let artists = album.artists_cache(db)?;
178
179        let tracks = album.tracks_cache(db)?;
180        let track_artists = tracks
181            .iter()
182            .try_map(|t| t.metadata.artists_cache(db))?
183            .to_vec();
184
185        Ok(ResolvedAlbum {
186            album,
187            artists,
188            tracks: tracks.into_iter().zip(track_artists).collect(),
189        })
190    }
191}