use std::{convert::Infallible, sync::Arc};
use chrono::{DateTime, Utc};
use lunar_lib::{
database::{DatabaseEntry, TransactionError, caching::Cacheable},
formatter::{FormatTable, Taggable},
paths::sys::sanitize_str,
};
use serde::{Deserialize, Serialize};
use crate::{
database::LibraryDb,
library::{
album::{Album, AlbumId},
artist::{Artist, ArtistGroup, ArtistId},
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: Vec<ArtistId>,
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: Vec::new(),
album: None,
date: None,
genre: Vec::new(),
art: None,
lyric_data: None,
other: Vec::new(),
}
}
#[must_use]
pub fn artists_raw(&self) -> &[ArtistId] {
&self.artists
}
pub fn artists(&self, db: &LibraryDb) -> Result<Vec<Artist>, TransactionError> {
Artist::db_get_batch(&self.artists, db)
}
pub fn artists_cache(&self, db: &LibraryDb) -> Result<Vec<Arc<Artist>>, TransactionError> {
Artist::cache_get_batch_from(&self.artists, db)
}
#[must_use]
pub fn album_raw(&self) -> Option<AlbumId> {
self.album
}
pub fn album(&self, db: &LibraryDb) -> Result<Option<Album>, TransactionError> {
if let Some(album_id) = self.album {
Ok(Some(
Album::db_get(album_id, db)?.expect("Dangling album reference"),
))
} else {
Ok(None)
}
}
pub fn album_cache(&self, db: &LibraryDb) -> Result<Option<Arc<Album>>, TransactionError> {
if let Some(album_id) = self.album {
Ok(Some(
Album::cache_get_from(album_id, db)?.expect("Dangling album reference"),
))
} else {
Ok(None)
}
}
pub fn main_artist(&self, db: &LibraryDb) -> Result<Option<Artist>, TransactionError> {
self.artists.main_artist(db)
}
#[must_use]
pub fn safe_title(&self) -> &str {
self.title.as_deref().unwrap_or(UNKNOWN_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(";")));
}
Ok(())
}
}
pub struct TrackAlbumInfo {
pub album: Album,
pub track_num: Option<u32>,
pub disc_num: Option<u32>,
}