use std::path::Path; use lofty::file::TaggedFileExt; use lofty::tag::{ItemKey, ItemValue}; /// A single key-value metadata item from a tag. pub struct TagEntry { pub key: String, pub value: String, } /// Read all metadata items from a music file. /// Returns `None` if no tags are present, otherwise a list of all tag entries. pub fn read_all_metadata(path: &Path) -> Result>, lofty::error::LoftyError> { let tagged_file = lofty::read_from_path(path)?; let Some(tag) = tagged_file.primary_tag().or_else(|| tagged_file.first_tag()) else { return Ok(None); }; let entries = tag .items() .filter_map(|item| { let value = match item.value() { ItemValue::Text(t) | ItemValue::Locator(t) => t.clone(), ItemValue::Binary(b) => format!("<{} bytes>", b.len()), }; Some(TagEntry { key: format!("{:?}", item.key()), value, }) }) .collect(); Ok(Some(entries)) } /// Extract the artist name from a music file. pub fn read_artist_name(path: &Path) -> Result, lofty::error::LoftyError> { let tagged_file = lofty::read_from_path(path)?; let Some(tag) = tagged_file.primary_tag().or_else(|| tagged_file.first_tag()) else { return Ok(None); }; Ok(tag.get_string(ItemKey::TrackArtist).map(String::from)) } /// Extract the MusicBrainz artist ID from a music file. pub fn read_artist_mbid(path: &Path) -> Result, lofty::error::LoftyError> { let tagged_file = lofty::read_from_path(path)?; let Some(tag) = tagged_file.primary_tag().or_else(|| tagged_file.first_tag()) else { return Ok(None); }; Ok(tag.get_string(ItemKey::MusicBrainzArtistId).map(String::from)) } /// Extract the MusicBrainz recording ID from a music file. pub fn read_track_mbid(path: &Path) -> Result, lofty::error::LoftyError> { let tagged_file = lofty::read_from_path(path)?; let Some(tag) = tagged_file.primary_tag().or_else(|| tagged_file.first_tag()) else { return Ok(None); }; Ok(tag.get_string(ItemKey::MusicBrainzRecordingId).map(String::from)) }