diff --git a/lofty/src/id3/v2/tag.rs b/lofty/src/id3/v2/tag.rs index 2166502e1..f69b3352a 100644 --- a/lofty/src/id3/v2/tag.rs +++ b/lofty/src/id3/v2/tag.rs @@ -649,6 +649,10 @@ pub(super) fn new_text_frame<'a>(id: FrameId<'a>, value: impl Into> Frame::Text(TextInformationFrame::new(id, TextEncoding::UTF8, value)) } +pub(super) fn new_url_frame<'a>(id: FrameId<'a>, value: impl Into>) -> Frame<'a> { + Frame::Url(UrlLinkFrame::new(id, value)) +} + pub(super) fn new_user_text_frame<'a>( description: impl Into>, content: impl Into>, diff --git a/lofty/src/id3/v2/tag/conversion.rs b/lofty/src/id3/v2/tag/conversion.rs index 8eb164d0d..f5ea3afd6 100644 --- a/lofty/src/id3/v2/tag/conversion.rs +++ b/lofty/src/id3/v2/tag/conversion.rs @@ -13,7 +13,9 @@ use crate::tag::companion_tag::CompanionTag; use crate::tag::{Tag, TagItem, TagType}; use super::V4_MULTI_VALUE_SEPARATOR; -use crate::id3::v2::tag::{new_text_frame, new_timestamp_frame, new_user_text_frame}; +use crate::id3::v2::tag::{ + new_text_frame, new_timestamp_frame, new_url_frame, new_user_text_frame, +}; use crate::id3::v2::util::mappings::TIPL_MAPPINGS; use crate::mp4::AdvisoryRating; use crate::tag::items::{Lang, Timestamp}; @@ -282,7 +284,7 @@ pub(crate) fn from_tag<'a>( }, // TIPL key-value mappings - _ => { + _ if TIPL_MAPPINGS.iter().any(|(k, _)| *k == item_key) => { let (_, tipl_key) = TIPL_MAPPINGS.iter().find(|(k, _)| *k == item_key)?; let (value, _) = take_item_text_and_description(item)?; @@ -293,6 +295,25 @@ pub(crate) fn from_tag<'a>( // TIPL is collected at the end None }, + + // Anything else + _ => { + let Ok(id) = FrameId::try_from(item_key) else { + return None; + }; + + if id.as_str().starts_with('T') { + let (value, _) = take_item_text_and_description(item)?; + return Some(new_text_frame(id, value)); + } + + if id.as_str().starts_with('W') { + let (value, _) = take_item_text_and_description(item)?; + return Some(new_url_frame(id, value)); + } + + None + }, } } diff --git a/lofty/src/id3/v2/tag/tests.rs b/lofty/src/id3/v2/tag/tests.rs index e03569dd5..fd2e32c43 100644 --- a/lofty/src/id3/v2/tag/tests.rs +++ b/lofty/src/id3/v2/tag/tests.rs @@ -1536,3 +1536,36 @@ fn multi_item_tag_dump() { let artist_tag = tag.get_text(&FrameId::new("TPE1").unwrap()).unwrap(); assert_eq!(artist_tag, "Foo\0Bar"); } + +#[test_log::test] +fn single_value_frame() { + let mut tag = Tag::new(TagType::Id3v2); + + // TBPM should be deduplicated during the conversion, taking whatever happens to be first + tag.push(TagItem::new( + ItemKey::IntegerBpm, + ItemValue::Text(String::from("120")), + )); + tag.push(TagItem::new( + ItemKey::IntegerBpm, + ItemValue::Text(String::from("130")), + )); + tag.push(TagItem::new( + ItemKey::IntegerBpm, + ItemValue::Text(String::from("140")), + )); + + let mut id3v2 = Vec::new(); + tag.dump_to(&mut id3v2, WriteOptions::default()).unwrap(); + + let tag = read_tag_with_options( + &id3v2, + ParseOptions::new().parsing_mode(ParsingMode::Strict), + ); + + // The other BPM values were discarded, **NOT** merged + assert_eq!(tag.len(), 1); + + let artist_tag = tag.get_text(&FrameId::new("TBPM").unwrap()).unwrap(); + assert_eq!(artist_tag, "120"); +}