Tag: Support ItemKey::ParentalAdvisory for Ilst and ID3v2

This commit is contained in:
Serial 2024-04-27 11:48:53 -04:00 committed by Alex
parent 1474efa9a3
commit c1f117f5a4
5 changed files with 91 additions and 0 deletions

View file

@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **Tag**: Support `ItemKey::ParentalAdvisory` for `Ilst` and `Id3v2Tag` ([issue](https://github.com/Serial-ATA/lofty-rs/issues/99)) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/388))
- This will allow for generic edits to the iTunes-style parental advisory tag. Note that this will use the
numeric representation. For more information, see: https://docs.mp3tag.de/mapping/#itunesadvisory.
### Changed
- **VorbisComments**/**ApeTag**: Verify contents of `ItemKey::FlagCompilation` during `Tag` merge ([PR](https://github.com/Serial-ATA/lofty-rs/pull/387))

View file

@ -17,6 +17,7 @@ use crate::id3::v2::util::pairs::{
format_number_pair, set_number, NUMBER_PAIR_KEYS, NUMBER_PAIR_SEPARATOR,
};
use crate::id3::v2::KeyValueFrame;
use crate::mp4::AdvisoryRating;
use crate::picture::{Picture, PictureType, TOMBSTONE_PICTURE};
use crate::tag::{
try_parse_year, Accessor, ItemKey, ItemValue, MergeTag, SplitTag, Tag, TagExt, TagItem, TagType,
@ -713,6 +714,18 @@ fn new_text_frame(id: FrameId<'_>, value: String, flags: FrameFlags) -> Frame<'_
}
}
fn new_user_text_frame(description: String, content: String, flags: FrameFlags) -> Frame<'static> {
Frame {
id: FrameId::Valid(Cow::Borrowed(USER_DEFINED_TEXT_FRAME_ID)),
value: FrameValue::UserText(ExtendedTextFrame {
encoding: TextEncoding::UTF8,
description,
content,
}),
flags,
}
}
fn new_comment_frame(content: String, flags: FrameFlags) -> Frame<'static> {
Frame {
id: FrameId::Valid(Cow::Borrowed(COMMENT_FRAME_ID)),
@ -1407,6 +1420,28 @@ impl MergeTag for SplitTagRemainder {
));
}
'rate: {
if let Some(advisory_rating) = tag.take_strings(&ItemKey::ParentalAdvisory).next() {
let Ok(rating) = advisory_rating.parse::<u8>() else {
log::warn!(
"Parental advisory rating is not a number: {advisory_rating}, discarding"
);
break 'rate;
};
let Ok(parsed_rating) = AdvisoryRating::try_from(rating) else {
log::warn!("Parental advisory rating is out of range: {rating}, discarding");
break 'rate;
};
merged.frames.push(new_user_text_frame(
"ITUNESADVISORY".to_string(),
parsed_rating.as_u8().to_string(),
FrameFlags::default(),
));
}
}
// Insert all remaining items as single frames and deduplicate as needed
for item in tag.items {
merged.insert_item(item);

View file

@ -1332,3 +1332,25 @@ fn flag_item_conversion() {
Some("0")
);
}
#[test]
fn itunes_advisory_roundtrip() {
use crate::mp4::{AdvisoryRating, Ilst};
let mut tag = Ilst::new();
tag.set_advisory_rating(AdvisoryRating::Explicit);
let tag: Tag = tag.into();
let tag: Id3v2Tag = tag.into();
assert_eq!(tag.frames.len(), 1);
let frame = tag.get_user_text("ITUNESADVISORY");
assert!(frame.is_some());
assert_eq!(frame.unwrap(), "1");
let tag: Tag = tag.into();
let tag: Ilst = tag.into();
assert_eq!(tag.advisory_rating(), Some(AdvisoryRating::Explicit));
}

View file

@ -654,6 +654,11 @@ impl SplitTag for Ilst {
false // Atom consumed
});
if let Some(rating) = self.advisory_rating() {
tag.insert_text(ItemKey::ParentalAdvisory, rating.as_u8().to_string());
let _ = self.remove(&ADVISORY_RATING);
}
(SplitTagRemainder(self), tag)
}
}
@ -715,6 +720,29 @@ impl MergeTag for SplitTagRemainder {
data: AtomDataStorage::Single(AtomData::Bool(data)),
})
},
ItemKey::ParentalAdvisory => {
let Ok(rating) = text.parse::<u8>() else {
log::warn!(
"Parental advisory rating is not a number: {}, discarding",
text
);
continue;
};
let Ok(parsed_rating) = AdvisoryRating::try_from(rating) else {
log::warn!(
"Parental advisory rating is out of range: {rating}, discarding"
);
continue;
};
merged.atoms.push(Atom {
ident: ident.into_owned(),
data: AtomDataStorage::Single(AtomData::SignedInteger(i32::from(
parsed_rating.as_u8(),
))),
})
},
_ => merged.atoms.push(Atom {
ident: ident.into_owned(),
data: AtomDataStorage::Single(AtomData::UTF8(text)),

View file

@ -169,6 +169,7 @@ gen_map!(
"TRCK" => TrackNumber,
"TRCK" => TrackTotal,
"POPM" => Popularimeter,
"ITUNESADVISORY" => ParentalAdvisory,
"TDRC" => RecordingDate,
"TDOR" => OriginalReleaseDate,
"TSRC" => Isrc,