Closed g-fb closed 2 months ago
In the Id3v2Tag
-> Tag
conversion, frames with descriptions are intentionally left behind, as Tag
has no way of retaining that information:
When dealing with tags using anything outside of basic text storage, it's best to use the concrete tags (Id3v2Tag
) in this case.
Hopefully in the future there will be a way to preserve more of this information in a generic way, but right now Tag
is pretty limited.
So I should be able to get the lyrics with a description if I use Id3v2Tag
?
If yes, then I don't understand how. I tried like this:
let mut tagged_file = lofty::read_from_path(file.path.clone()).unwrap();
let tag = tagged_file.tag_mut(TagType::Id3v2);
match tag {
Some(tag) => {
let tag: Id3v2Tag = <Tag as Clone>::clone(&(*tag)).into();
const LYRICS_ID: FrameId<'_> = FrameId::Valid(Cow::Borrowed("USLT"));
let lyrics = tag.get_text(&LYRICS_ID);
println!("file: {}\n{lyrics:#?}", file.path.clone());
}
None => ()
}
But I still can't get the ones with a description.
PS: I suck at rust.
The lyric is stored in ID3V2's USLT frame. I think you should try this code.
let mp3_file = match MpegFile::read_from(&mut file, ParseOptions::new()) {
Ok(value) => value,
Err(_) => return None,
};
let id3v2 = mp3_file.id3v2()?;
let frame = id3v2.get(&FrameId::Valid(Cow::Borrowed("USLT")))?;
match frame.content() {
lofty::id3::v2::FrameValue::UnsynchronizedText(lyric_frame) => {
return Some(lyric_frame.content.clone());
}
_ => return None,
}
I find that these two ways are the same, so please ignore it. 🥲
But I still can't get the ones with a description.
So a few things:
Into::<Id3v2Tag>::into(tag)
will only turn to the generic Tag
items into an Id3v2Tag
. As the USLT frame is held back in the initial Id3v2Tag
-> Tag
conversion, it will not exist during this second conversion. I plan on fixing this in #302.Id3v2Tag::get_text
only works for text frames (TALB, TIT2, etc..). I should improve the docs for that.The solution by @Ferry-200 is correct. If you read an MpegFile
directly, you get access to the Id3v2Tag
. You're currently using read_from_path
which will give you a TaggedFile
, and thus you only get access to Tag
.
I got it working.
Thank you both for the help.
Here's the working code
fn get_id3v2_lyrics(path: String) -> Result<String, String> {
let mp3_file = match MpegFile::read_from(&mut File::open(path.clone()).unwrap(), ParseOptions::new()) {
Ok(value) => value,
_ => return Err(format!("Can't read mpeg file: {path}")),
};
let id3v2 = match mp3_file.id3v2() {
Some(value) => value,
_ => return Err(format!("mpeg file doesn't have an id3v2 tag: {path}")),
};
let frame = match Id3v2Tag::get(&id3v2, &FrameId::Valid(Cow::Borrowed("USLT"))) {
Some(value) => value,
_ => return Err(format!("mpeg file doesn't have lyrics tag: {path}")),
};
let lyrics = match frame.content() {
FrameValue::UnsynchronizedText(value) => value,
_ => return Err(format!("Can't get lyrics for mpeg file: {path}")),
};
Ok(lyrics.content.clone())
}
Nice, I'll keep this pinned to remind me to make descriptions and languages available in TagItem
somehow.
Reproducer
I tried this code:
+ Tag2
Summary
Can't read lyrics tag when description is set.
Expected behavior
println!("{lyrics}");
prints "the lyrics"Assets
No response