use std::{collections::HashMap, convert::Infallible};
use chrono::{DateTime, Utc};
use lunar_lib::{
formatter::{FormatTable, Taggable},
paths::sys::sanitize_str,
};
use serde::{Deserialize, Serialize};
use crate::{
database::{DatabaseEntry, DatabaseError, Patchable, patch_option_replace},
library::{
album::{Album, AlbumId},
artist::{Artist, ArtistGroup},
cover_art::CoverArt,
track::{UNKNOWN_TITLE, lyric_data::LyricData},
},
};
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct TrackMeta {
pub(crate) album: Option<AlbumId>,
pub(crate) artists: ArtistGroup,
pub date: Option<DateTime<Utc>>,
pub genre: Option<String>,
pub lyric_data: Option<LyricData>,
pub cover_art: Option<CoverArt>,
pub other: HashMap<String, String>,
pub title: Option<String>,
}
impl TrackMeta {
#[must_use]
pub fn new() -> Self {
Self {
album: None,
artists: ArtistGroup::new(),
date: None,
genre: None,
lyric_data: None,
other: HashMap::new(),
title: None,
cover_art: None,
}
}
pub fn album(&self) -> Result<Option<Album>, DatabaseError> {
if let Some(album_id) = self.album {
Ok(Some(
Album::db_get(album_id)?.expect("Dangling album reference"),
))
} else {
Ok(None)
}
}
pub fn artists(&self) -> Result<Vec<Artist>, DatabaseError> {
self.artists.artists()
}
pub fn safe_title(&self) -> &str {
self.title.as_deref().unwrap_or(UNKNOWN_TITLE)
}
}
impl Patchable<TrackMeta> for TrackMeta {
fn patch(&mut self, patch: TrackMeta) {
let TrackMeta {
album,
artists,
date,
genre,
lyric_data,
cover_art,
other,
title,
} = patch;
patch_option_replace(&mut self.album, album);
self.artists.patch(artists);
patch_option_replace(&mut self.date, date);
patch_option_replace(&mut self.genre, genre);
patch_option_replace(&mut self.lyric_data, lyric_data);
patch_option_replace(&mut self.cover_art, cover_art);
self.other.extend(other);
patch_option_replace(&mut self.title, title);
}
}
impl Taggable for TrackMeta {
type Err = Infallible;
fn fill_table(&self, table: &mut FormatTable) -> Result<(), Self::Err> {
if let Some(value) = self.date {
table.add_entry("date", value.to_string());
}
if let Some(value) = &self.title {
table.add_entry("title", sanitize_str(value));
}
if let Some(value) = &self.genre {
table.add_entry("genre", sanitize_str(value));
}
table.add_table(self.other.clone());
if let Some(lyric_data) = &self.lyric_data {
match lyric_data {
LyricData::Instrumental => table.add_entry("instrumental", "1"),
LyricData::Plain(_) => {
table.add_entry("plain_lyrics", "1");
table.add_entry("lyrics", "1");
}
LyricData::Synced(_) => {
table.add_entry("synced_lyrics", "1");
table.add_entry("lyrics", "1");
}
}
}
Ok(())
}
}
pub struct TrackAlbumInfo {
pub album: Album,
pub track_num: Option<u16>,
pub disc_num: Option<u16>,
}