1use std::path::{Path, PathBuf};
2
3use lofty::{read_from_path, Accessor, ItemKey, TaggedFileExt};
4
5use crate::error::{MtagError, MtagResult};
6
7#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct TrackMetadata {
14 pub source_path: PathBuf,
16 pub artist: Option<String>,
18 pub album: Option<String>,
20 pub album_artist: Option<String>,
22 pub disc: Option<String>,
24 pub track: Option<String>,
26 pub title: Option<String>,
28}
29
30pub fn read_track_metadata(path: &Path) -> MtagResult<TrackMetadata> {
37 let tagged_file = read_from_path(path).map_err(|source| MtagError::ReadMetadata {
38 path: path.to_path_buf(),
39 source,
40 })?;
41 let tag = tagged_file
42 .primary_tag()
43 .or_else(|| tagged_file.first_tag())
44 .ok_or_else(|| MtagError::MissingTags {
45 path: path.to_path_buf(),
46 })?;
47
48 Ok(TrackMetadata {
49 source_path: path.to_path_buf(),
50 artist: clean_string(tag.artist().as_deref()),
51 album: clean_string(tag.album().as_deref()),
52 album_artist: clean_string(tag.get_string(&ItemKey::AlbumArtist)),
53 disc: tag.disk().map(|disk| disk.to_string()),
54 track: tag.track().map(|track| track.to_string()),
55 title: clean_string(tag.title().as_deref()),
56 })
57}
58
59fn clean_string(value: Option<&str>) -> Option<String> {
60 value
61 .map(str::trim)
62 .filter(|value| !value.is_empty())
63 .map(ToOwned::to_owned)
64}