commit c51290f55aa0b4113b8b69cd06059ff9b539d44a from: mtmn date: Thu May 7 10:54:10 2026 UTC feat: add --read to print existing tags commit - fa65d5076a5aad16151cd4e0a1ad9c52ab3c8ad3 commit + c51290f55aa0b4113b8b69cd06059ff9b539d44a blob - def8d0fd6637d38e8eda7ef83f11880a84e880b7 blob + d71e382216f13f4b7bbd57d20583ae2f733d8de6 --- src/main.rs +++ src/main.rs @@ -20,17 +20,17 @@ use walkdir::WalkDir; Fetch metadata for an artist and album: $ hakuna --artist 'Djrum' --album 'Under Tangled Silence' - Preview tags for a file: - $ hakuna --read file.mp3 + Show existing tags from a file: + $ hakuna -r file.mp3 - Write tags to a file: - $ hakuna -d DISCOGS_TOKEN --write file.mp3 + Fetch and write tags to a file: + $ hakuna -d DISCOGS_TOKEN -w file.mp3 Edit tags manually: $ hakuna --edit file.mp3 Process a directory: - $ hakuna -d DISCOGS_TOKEN --write /path/to/music/" + $ hakuna -d DISCOGS_TOKEN -w /path/to/music/" )] #[allow(clippy::struct_excessive_bools)] struct Args { @@ -43,16 +43,16 @@ struct Args { album: Option, /// File(s) to process - #[arg(required_unless_present_any = ["artist", "album"])] + #[arg(required_unless_present_any = ["artist", "album", "show_tags"])] files: Vec, /// Write tags to file - #[arg(short, long, default_value_t = false)] + #[arg(short = 'w', long, default_value_t = false)] write: bool, - /// Read tags from file and show what would be written - #[arg(short, long, default_value_t = true)] - read: bool, + /// Show existing tags from files + #[arg(short = 'r', long, default_value_t = false)] + show_tags: bool, /// Edit artist, album, genre, and label manually #[arg(short, long, default_value_t = false)] @@ -115,8 +115,14 @@ async fn main() -> Result<()> { eprintln!("Failed to edit file {}: {e:?}", file_path.display()); } } + } else if args.show_tags { + for file_path in all_files { + if let Err(e) = tagging::show_file_tags(&file_path) { + eprintln!("Failed to read tags from {}: {e:?}", file_path.display()); + } + } } else { - tagging::process_files_batch(&ctx, &all_files, args.read, args.write).await?; + tagging::process_files_batch(&ctx, &all_files, args.write, args.write).await?; } } else if let (Some(artist), Some(album)) = (args.artist, args.album) { let result = metadata::fetch::process_query(&ctx, &artist, &album).await?; blob - 26ca26f34bfcbe80759e3bd7f13f33f49d87f04f blob + ad2f0e02618e8907d78c9cdca35c47ea8e61ab88 --- src/tagging.rs +++ src/tagging.rs @@ -97,6 +97,37 @@ pub async fn process_files_batch( Ok(()) } +pub fn show_file_tags(path: &Path) -> Result<()> { + let abs_path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf()); + + if !abs_path.exists() { + return Err(anyhow::anyhow!( + "File does not exist: {}", + abs_path.display() + )); + } + + let (tagged_file, artist, album) = read_file_tags(&abs_path)?; + + println!("{artist} - {album}"); + + let tag = tagged_file.primary_tag().context("No primary tag found")?; + + if let Some(genre_item) = tag.get(ItemKey::Genre) + && let Some(genre) = genre_item.value().text() + { + println!(" Genre: {genre}"); + } + + if let Some(label_item) = tag.get(ItemKey::Label) + && let Some(label) = label_item.value().text() + { + println!(" Label: {label}"); + } + + Ok(()) +} + fn write_file_tags(path: &Path, metadata: &FetchedMetadata) -> Result<()> { let file = File::open(path).with_context(|| format!("Failed to open file: {}", path.display()))?;