selene_core/library/track/
track_meta.rs1use 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#[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}