use std::io;
use lofty::tag::{Accessor, ItemKey, Tag, TagType};
use lunar_lib::database::{DatabaseError, EntryIdExt};
use crate::{
config::ExportConfig,
database::LibraryDb,
library::{
loudnorm::LoudnormAnalysis,
metadata::LoftyTagRefAccessors,
track::{
ResolvedTrack, Track, TrackId,
lyric_data::LyricData,
track_meta::{TrackAlbumInfo, TrackMeta},
},
},
lyrics::LyricFormat,
media_container::{ContainerFormat, MediaContainer},
utils::hash_file,
};
impl Track {
#[must_use]
pub fn new(container: MediaContainer, metadata: TrackMeta) -> io::Result<Self> {
Ok(Self {
id: TrackId::new(hash_file(container.path())?),
container,
metadata,
loudnorm_analysis: None,
})
}
}
impl Track {
#[must_use]
pub fn id(&self) -> TrackId {
self.id
}
#[must_use]
pub fn container(&self) -> &MediaContainer {
&self.container
}
#[must_use]
pub fn loudnorm_analysis(&self) -> Option<&LoudnormAnalysis> {
self.loudnorm_analysis.as_ref()
}
#[must_use]
pub fn is_single(&self) -> bool {
self.metadata.album.is_none()
}
#[must_use]
pub fn metadata(&self) -> &TrackMeta {
&self.metadata
}
#[must_use]
pub fn metadata_mut(&mut self) -> &mut TrackMeta {
&mut self.metadata
}
pub fn album(&self, db: &LibraryDb) -> Result<Option<TrackAlbumInfo>, DatabaseError> {
if let Some(album_id) = self.metadata.album {
Ok(Some({
let album = album_id.db_get(db)?.expect("Dangling album reference");
let reference = album
.track_refs()
.iter()
.find(|t| t.id == self.id)
.expect("Track not found in album");
let track_num = reference.track_num;
let disc_num = reference.disc_num;
TrackAlbumInfo {
album,
track_num,
disc_num,
}
}))
} else {
Ok(None)
}
}
}
impl ResolvedTrack {
#[must_use]
pub fn metadata_key_values(&self, export_settings: &ExportConfig) -> Tag {
let mut tags = match self.container().format {
ContainerFormat::Flac | ContainerFormat::Ogg => Tag::new(TagType::VorbisComments),
ContainerFormat::Ape => Tag::new(TagType::Ape),
ContainerFormat::Mpa | ContainerFormat::Wav | ContainerFormat::Aiff => {
Tag::new(TagType::Id3v2)
}
};
if let Some((al, ar, tn, dn)) = self.album_info() {
tags.insert_text(ItemKey::AlbumTitle, al.name().to_owned());
tags.set_artists(ar.iter().map(|a| &**a), ItemKey::AlbumArtist);
if let Some(track_total) = al.track_total {
tags.set_track_total(track_total);
}
if let Some(disc_total) = al.disc_total {
tags.set_disk_total(disc_total);
}
if let Some(track_num) = tn {
tags.set_track(track_num);
}
if let Some(disc_num) = dn {
tags.set_disk_total(disc_num);
}
} else if export_settings.singles_as_albums {
tags.insert_text(
ItemKey::AlbumTitle,
self.track.metadata().safe_title().to_owned(),
);
tags.set_artists(self.artists().iter().map(|a| &**a), ItemKey::AlbumArtist);
tags.set_track_total(1);
tags.set_disk_total(1);
tags.set_track(1);
tags.set_disk_total(1);
}
tags.set_artists(self.artists().iter().map(|a| &**a), ItemKey::TrackArtist);
if let Some(date) = self.metadata.date {
tags.insert_text(
ItemKey::RecordingDate,
date.format("%Y-%m-%dT%H:%M:%S").to_string(),
);
}
for genre in &self.metadata.genre {
tags.set_genre(genre.to_owned());
}
if let Some(title) = &self.metadata.title {
Accessor::set_title(&mut tags, title.to_owned());
}
if let Some(lyric_data) = &self.metadata.lyric_data {
match lyric_data {
LyricData::Instrumental => (),
LyricData::Plain(lyrics) => {
tags.insert_text(ItemKey::UnsyncLyrics, lyrics.to_string());
}
LyricData::Synced(lyrics) => {
tags.insert_text(
ItemKey::Lyrics,
lyrics.to_lyrics(LyricFormat::Lrc { a2: false }),
);
}
}
}
if let Some(loudnorm_analysis) = self.loudnorm_analysis() {
tags.insert_text(
ItemKey::ReplayGainTrackGain,
format!("{} dB", loudnorm_analysis.calculated_gain_db()),
);
tags.insert_text(
ItemKey::ReplayGainTrackPeak,
format!("{}", loudnorm_analysis.calculated_replay_gain_peak()),
);
}
self.metadata.other.iter().for_each(|(k, v)| {
if let Some(item_key) = ItemKey::from_key(TagType::VorbisComments, k) {
tags.insert_text(item_key, v.to_owned());
}
});
tags
}
}