Skip to content

Commit

Permalink
Tag: Support ItemKey::ParentalAdvisory for Ilst and ID3v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA committed Apr 27, 2024
1 parent 1474efa commit f7acba7
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
35 changes: 35 additions & 0 deletions lofty/src/id3/v2/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions lofty/src/id3/v2/tag/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
28 changes: 28 additions & 0 deletions lofty/src/mp4/ilst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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)),
Expand Down
1 change: 1 addition & 0 deletions lofty/src/tag/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ gen_map!(
"TRCK" => TrackNumber,
"TRCK" => TrackTotal,
"POPM" => Popularimeter,
"ITUNESADVISORY" => ParentalAdvisory,
"TDRC" => RecordingDate,
"TDOR" => OriginalReleaseDate,
"TSRC" => Isrc,
Expand Down

0 comments on commit f7acba7

Please sign in to comment.