Skip to main content

selene_core/library/track/
track_meta.rs

1use std::{collections::HashMap, convert::Infallible};
2
3use chrono::{DateTime, Utc};
4use lunar_lib::{
5    formatter::{FormatTable, Taggable},
6    paths::sys::sanitize_str,
7};
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    database::{DatabaseEntry, DatabaseError, Patchable, patch_option_replace},
12    library::{
13        album::{Album, AlbumId},
14        artist::{Artist, ArtistGroup},
15        cover_art::CoverArt,
16        track::{UNKNOWN_TITLE, lyric_data::LyricData},
17    },
18};
19
20/// Track metadata. Defines metadata to be read from and stored on files
21#[derive(Debug, Deserialize, Serialize, Clone, Default)]
22pub struct TrackMeta {
23    pub(crate) album: Option<AlbumId>,
24    pub(crate) artists: ArtistGroup,
25    pub date: Option<DateTime<Utc>>,
26    pub genre: Option<String>,
27    pub lyric_data: Option<LyricData>,
28    pub cover_art: Option<CoverArt>,
29    pub other: HashMap<String, String>,
30    pub title: Option<String>,
31}
32
33impl TrackMeta {
34    #[must_use]
35    pub fn new() -> Self {
36        Self {
37            album: None,
38            artists: ArtistGroup::new(),
39            date: None,
40            genre: None,
41            lyric_data: None,
42            other: HashMap::new(),
43            title: None,
44            cover_art: None,
45        }
46    }
47
48    pub fn album(&self) -> Result<Option<Album>, DatabaseError> {
49        if let Some(album_id) = self.album {
50            Ok(Some(
51                Album::db_get(album_id)?.expect("Dangling album reference"),
52            ))
53        } else {
54            Ok(None)
55        }
56    }
57
58    pub fn artists(&self) -> Result<Vec<Artist>, DatabaseError> {
59        self.artists.artists()
60    }
61
62    pub fn safe_title(&self) -> &str {
63        self.title.as_deref().unwrap_or(UNKNOWN_TITLE)
64    }
65}
66
67impl Patchable<TrackMeta> for TrackMeta {
68    fn patch(&mut self, patch: TrackMeta) {
69        let TrackMeta {
70            album,
71            artists,
72            date,
73            genre,
74            lyric_data,
75            cover_art,
76            other,
77            title,
78        } = patch;
79
80        patch_option_replace(&mut self.album, album);
81        self.artists.patch(artists);
82        patch_option_replace(&mut self.date, date);
83        patch_option_replace(&mut self.genre, genre);
84        patch_option_replace(&mut self.lyric_data, lyric_data);
85        patch_option_replace(&mut self.cover_art, cover_art);
86        self.other.extend(other);
87        patch_option_replace(&mut self.title, title);
88    }
89}
90
91impl Taggable for TrackMeta {
92    type Err = Infallible;
93
94    fn fill_table(&self, table: &mut FormatTable) -> Result<(), Self::Err> {
95        if let Some(value) = self.date {
96            table.add_entry("date", value.to_string());
97        }
98        if let Some(value) = &self.title {
99            table.add_entry("title", sanitize_str(value));
100        }
101        if let Some(value) = &self.genre {
102            table.add_entry("genre", sanitize_str(value));
103        }
104        table.add_table(self.other.clone());
105
106        if let Some(lyric_data) = &self.lyric_data {
107            match lyric_data {
108                LyricData::Instrumental => table.add_entry("instrumental", "1"),
109                LyricData::Plain(_) => {
110                    table.add_entry("plain_lyrics", "1");
111                    table.add_entry("lyrics", "1");
112                }
113                LyricData::Synced(_) => {
114                    table.add_entry("synced_lyrics", "1");
115                    table.add_entry("lyrics", "1");
116                }
117            }
118        }
119
120        Ok(())
121    }
122}
123
124pub struct TrackAlbumInfo {
125    pub album: Album,
126    pub track_num: Option<u16>,
127    pub disc_num: Option<u16>,
128}