Skip to content

Commit bcfc6ad

Browse files
committed
ID3v2: Fix single-value item conversions
1 parent 46f2d68 commit bcfc6ad

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

lofty/src/id3/v2/tag.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,10 @@ pub(super) fn new_text_frame<'a>(id: FrameId<'a>, value: impl Into<Cow<'a, str>>
649649
Frame::Text(TextInformationFrame::new(id, TextEncoding::UTF8, value))
650650
}
651651

652+
pub(super) fn new_url_frame<'a>(id: FrameId<'a>, value: impl Into<Cow<'a, str>>) -> Frame<'a> {
653+
Frame::Url(UrlLinkFrame::new(id, value))
654+
}
655+
652656
pub(super) fn new_user_text_frame<'a>(
653657
description: impl Into<Cow<'a, str>>,
654658
content: impl Into<Cow<'a, str>>,

lofty/src/id3/v2/tag/conversion.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use crate::tag::companion_tag::CompanionTag;
1313
use crate::tag::{Tag, TagItem, TagType};
1414

1515
use super::V4_MULTI_VALUE_SEPARATOR;
16-
use crate::id3::v2::tag::{new_text_frame, new_timestamp_frame, new_user_text_frame};
16+
use crate::id3::v2::tag::{
17+
new_text_frame, new_timestamp_frame, new_url_frame, new_user_text_frame,
18+
};
1719
use crate::id3::v2::util::mappings::TIPL_MAPPINGS;
1820
use crate::mp4::AdvisoryRating;
1921
use crate::tag::items::{Lang, Timestamp};
@@ -282,7 +284,7 @@ pub(crate) fn from_tag<'a>(
282284
},
283285

284286
// TIPL key-value mappings
285-
_ => {
287+
_ if TIPL_MAPPINGS.iter().any(|(k, _)| *k == item_key) => {
286288
let (_, tipl_key) = TIPL_MAPPINGS.iter().find(|(k, _)| *k == item_key)?;
287289

288290
let (value, _) = take_item_text_and_description(item)?;
@@ -293,6 +295,25 @@ pub(crate) fn from_tag<'a>(
293295
// TIPL is collected at the end
294296
None
295297
},
298+
299+
// Anything else
300+
_ => {
301+
let Ok(id) = FrameId::try_from(item_key) else {
302+
return None;
303+
};
304+
305+
if id.as_str().starts_with('T') {
306+
let (value, _) = take_item_text_and_description(item)?;
307+
return Some(new_text_frame(id, value));
308+
}
309+
310+
if id.as_str().starts_with('W') {
311+
let (value, _) = take_item_text_and_description(item)?;
312+
return Some(new_url_frame(id, value));
313+
}
314+
315+
None
316+
},
296317
}
297318
}
298319

lofty/src/id3/v2/tag/tests.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,3 +1536,36 @@ fn multi_item_tag_dump() {
15361536
let artist_tag = tag.get_text(&FrameId::new("TPE1").unwrap()).unwrap();
15371537
assert_eq!(artist_tag, "Foo\0Bar");
15381538
}
1539+
1540+
#[test_log::test]
1541+
fn single_value_frame() {
1542+
let mut tag = Tag::new(TagType::Id3v2);
1543+
1544+
// TBPM should be deduplicated during the conversion, taking whatever happens to be first
1545+
tag.push(TagItem::new(
1546+
ItemKey::IntegerBpm,
1547+
ItemValue::Text(String::from("120")),
1548+
));
1549+
tag.push(TagItem::new(
1550+
ItemKey::IntegerBpm,
1551+
ItemValue::Text(String::from("130")),
1552+
));
1553+
tag.push(TagItem::new(
1554+
ItemKey::IntegerBpm,
1555+
ItemValue::Text(String::from("140")),
1556+
));
1557+
1558+
let mut id3v2 = Vec::new();
1559+
tag.dump_to(&mut id3v2, WriteOptions::default()).unwrap();
1560+
1561+
let tag = read_tag_with_options(
1562+
&id3v2,
1563+
ParseOptions::new().parsing_mode(ParsingMode::Strict),
1564+
);
1565+
1566+
// The other BPM values were discarded, **NOT** merged
1567+
assert_eq!(tag.len(), 1);
1568+
1569+
let artist_tag = tag.get_text(&FrameId::new("TBPM").unwrap()).unwrap();
1570+
assert_eq!(artist_tag, "120");
1571+
}

0 commit comments

Comments
 (0)