Skip to main content

selene_core/library/
track.rs

1use std::{ops::Deref, str::FromStr, sync::Arc};
2
3use crate::{
4    database::LibraryDb,
5    library::{
6        album::Album, artist::Artist, loudnorm::LoudnormAnalysis, track::track_meta::TrackMeta,
7    },
8    media_container::MediaContainer,
9};
10use blake3::Hash;
11use lunar_lib::database::EntryId;
12use serde::{Deserialize, Serialize};
13
14pub(crate) mod internal;
15
16pub mod core_impls;
17pub mod frontend_impls;
18pub mod trait_impls;
19
20pub mod cover_art;
21pub mod lyric_data;
22pub mod track_meta;
23
24pub const UNKNOWN_TITLE: &str = "UNKNOWN TITLE";
25
26/// Track root. Defines file info, container info, metadata info
27#[derive(Serialize, Deserialize, Debug, Clone)]
28pub struct Track {
29    id: TrackId,
30    pub(crate) container: MediaContainer,
31
32    pub(crate) metadata: TrackMeta,
33
34    pub(crate) loudnorm_analysis: Option<LoudnormAnalysis>,
35}
36
37#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
38pub struct TrackId(Hash);
39
40impl EntryId for TrackId {
41    type Entry = Track;
42    type IdDb = LibraryDb;
43}
44
45impl Deref for TrackId {
46    type Target = [u8; 32];
47
48    fn deref(&self) -> &Self::Target {
49        self.0.as_bytes()
50    }
51}
52
53impl FromStr for TrackId {
54    type Err = <Hash as FromStr>::Err;
55
56    fn from_str(s: &str) -> Result<Self, Self::Err> {
57        Ok(Self(Hash::from_str(s)?))
58    }
59}
60
61impl TrackId {
62    pub(crate) fn new(id: Hash) -> Self {
63        Self(id)
64    }
65
66    #[must_use]
67    pub fn to_hash(&self) -> Hash {
68        self.0
69    }
70
71    #[must_use]
72    pub fn to_selene_id(&self) -> String {
73        format!("track:{}", self.0)
74    }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ResolvedTrack {
79    pub(crate) track: Arc<Track>,
80
81    pub(crate) album: Option<Arc<Album>>,
82    pub(crate) album_artists: Option<Vec<Arc<Artist>>>,
83
84    pub(crate) artists: Vec<Arc<Artist>>,
85}
86
87impl ResolvedTrack {
88    #[must_use] 
89    pub fn album_info(&self) -> Option<(&Arc<Album>, &[Arc<Artist>], Option<u32>, Option<u32>)> {
90        let album = self.album.as_ref()?;
91        let artists = self
92            .album_artists
93            .as_ref()
94            .expect("Resolved track album artists must be some if album is some");
95
96        let (track_num, disc_num) = album
97            .tracks
98            .iter()
99            .find_map(|t| (t.id == self.id).then_some((t.track_num, t.disc_num)))
100            .unwrap();
101
102        Some((album, artists, track_num, disc_num))
103    }
104
105    #[must_use] 
106    pub fn artists(&self) -> &[Arc<Artist>] {
107        &self.artists
108    }
109}
110
111impl Deref for ResolvedTrack {
112    type Target = Track;
113
114    fn deref(&self) -> &Self::Target {
115        &self.track
116    }
117}