use std::convert::Infallible;
use chrono::{DateTime, Utc};
use lunar_lib::{
database::{DatabaseEntry, DatabaseError},
formatter::{FormatTable, Taggable},
paths::sys::sanitize_str,
};
use serde::{Deserialize, Serialize};
use crate::{
database::{LibraryDb, Patchable, patch_option_replace, patch_vec},
library::{
album::{Album, AlbumId},
artist::{Artist, ArtistGroup},
track::{UNKNOWN_TITLE, cover_art::CoverArt, 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: Vec<String>,
pub lyric_data: Option<LyricData>,
pub art: Option<CoverArt>,
pub other: Vec<(String, String)>,
pub title: Option<String>,
}
impl TrackMeta {
#[must_use]
pub fn new() -> Self {
Self {
title: None,
artists: ArtistGroup::new(),
album: None,
date: None,
genre: Vec::new(),
art: None,
lyric_data: None,
other: Vec::new(),
}
}
pub fn album(&self, db: &LibraryDb) -> Result<Option<Album>, DatabaseError> {
if let Some(album_id) = self.album {
Ok(Some(
Album::db_get_from(album_id, db)?.expect("Dangling album reference"),
))
} else {
Ok(None)
}
}
pub fn main_artist(&self, db: &LibraryDb) -> Result<Option<Artist>, DatabaseError> {
self.artists.main_artist(db)
}
pub fn artists(&self, db: &LibraryDb) -> Result<Vec<Artist>, DatabaseError> {
self.artists.artists(db)
}
#[must_use]
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,
art: cover_art,
other,
title,
} = patch;
patch_option_replace(&mut self.album, album);
self.artists.patch(artists);
patch_option_replace(&mut self.date, date);
patch_vec(&mut self.genre, genre);
patch_option_replace(&mut self.lyric_data, lyric_data);
patch_option_replace(&mut self.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 !self.genre.is_empty() {
table.add_entry("genre", sanitize_str(self.genre.join(";")));
}
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<u32>,
pub disc_num: Option<u32>,
}