Add rtng atom support

This commit is contained in:
Serial 2022-02-18 08:50:39 -05:00
parent c70e8ee454
commit 4c0b7c2273
No known key found for this signature in database
GPG key ID: DA95198DC17C4568
4 changed files with 86 additions and 9 deletions

View file

@ -63,6 +63,38 @@ pub enum AtomData {
}, },
} }
#[derive(Clone, Copy, Debug, PartialEq)]
/// The parental advisory rating
pub enum AdvisoryRating {
/// A rating of 0
None,
/// A rating of 2
Clean,
/// A rating of (1 || > 2)
Explicit,
}
impl AdvisoryRating {
/// Returns the rating as it appears in the `rtng` atom
pub fn as_u8(&self) -> u8 {
match self {
AdvisoryRating::None => 0,
AdvisoryRating::Clean => 2,
AdvisoryRating::Explicit => 4,
}
}
}
impl From<u8> for AdvisoryRating {
fn from(input: u8) -> Self {
match input {
0 => Self::None,
2 => Self::Clean,
_ => Self::Explicit,
}
}
}
pub(crate) struct AtomRef<'a> { pub(crate) struct AtomRef<'a> {
pub(crate) ident: AtomIdentRef<'a>, pub(crate) ident: AtomIdentRef<'a>,
pub(crate) data: AtomDataRef<'a>, pub(crate) data: AtomDataRef<'a>,

View file

@ -3,12 +3,13 @@ pub(super) mod constants;
pub(super) mod read; pub(super) mod read;
pub(crate) mod write; pub(crate) mod write;
use super::constants::BE_SIGNED_INTEGER;
use super::AtomIdent; use super::AtomIdent;
use crate::error::{LoftyError, Result}; use crate::error::{LoftyError, Result};
use crate::types::item::{ItemKey, ItemValue, TagItem}; use crate::types::item::{ItemKey, ItemValue, TagItem};
use crate::types::picture::{Picture, PictureType}; use crate::types::picture::{Picture, PictureType};
use crate::types::tag::{Accessor, Tag, TagIO, TagType}; use crate::types::tag::{Accessor, Tag, TagIO, TagType};
use atom::{Atom, AtomData, AtomDataRef, AtomIdentRef, AtomRef}; use atom::{AdvisoryRating, Atom, AtomData, AtomDataRef, AtomIdentRef, AtomRef};
use std::convert::TryInto; use std::convert::TryInto;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
@ -142,6 +143,34 @@ impl Ilst {
.retain(|a| !matches!(a.data(), AtomData::Picture(_))) .retain(|a| !matches!(a.data(), AtomData::Picture(_)))
} }
/// Returns the parental advisory rating according to the `rtng` atom
pub fn advisory_rating(&self) -> Option<AdvisoryRating> {
if let Some(Atom { data, .. }) = self.atom(&AtomIdent::Fourcc(*b"rtng")) {
let rating = match data {
AtomData::SignedInteger(si) => *si as u8,
AtomData::Unknown { data: c, .. } if !c.is_empty() => c[0],
_ => return None,
};
return Some(AdvisoryRating::from(rating));
}
None
}
/// Sets the advisory rating
pub fn set_advisory_rating(&mut self, advisory_rating: AdvisoryRating) {
let byte = advisory_rating.as_u8();
self.replace_atom(Atom {
ident: AtomIdent::Fourcc(*b"rtng"),
data: AtomData::Unknown {
code: BE_SIGNED_INTEGER,
data: vec![byte],
},
})
}
/// Returns the track number /// Returns the track number
pub fn track_number(&self) -> Option<u16> { pub fn track_number(&self) -> Option<u16> {
self.extract_number(*b"trkn", 4) self.extract_number(*b"trkn", 4)
@ -188,7 +217,8 @@ impl TagIO for Ilst {
} }
fn save_to_path<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), Self::Err> { fn save_to_path<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), Self::Err> {
self.save_to(&mut OpenOptions::new().read(true).write(true).open(path)?) let mut f = OpenOptions::new().read(true).write(true).open(path)?;
self.save_to(&mut f)
} }
fn save_to(&self, file: &mut File) -> std::result::Result<(), Self::Err> { fn save_to(&self, file: &mut File) -> std::result::Result<(), Self::Err> {
@ -410,9 +440,14 @@ fn item_key_to_ident(key: &ItemKey) -> Option<AtomIdentRef<'_>> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::mp4::{Atom, AtomData, AtomIdent, Ilst}; use crate::mp4::{AdvisoryRating, Atom, AtomData, AtomIdent, Ilst};
use crate::{ItemKey, Tag, TagIO, TagType}; use crate::{ItemKey, Tag, TagIO, TagType};
fn read_ilst(path: &str) -> Ilst {
let tag = crate::tag_utils::test_utils::read_path(path);
super::read::parse_ilst(&mut &tag[..], tag.len() as u64).unwrap()
}
fn verify_atom(ilst: &Ilst, ident: [u8; 4], data: &AtomData) { fn verify_atom(ilst: &Ilst, ident: [u8; 4], data: &AtomData) {
let atom = ilst.atom(&AtomIdent::Fourcc(ident)).unwrap(); let atom = ilst.atom(&AtomIdent::Fourcc(ident)).unwrap();
assert_eq!(atom.data(), data); assert_eq!(atom.data(), data);
@ -481,8 +516,7 @@ mod tests {
#[test] #[test]
fn ilst_re_read() { fn ilst_re_read() {
let tag = crate::tag_utils::test_utils::read_path("tests/tags/assets/test.ilst"); let parsed_tag = read_ilst("tests/tags/assets/test.ilst");
let parsed_tag = super::read::parse_ilst(&mut &tag[..], tag.len() as u64).unwrap();
let mut writer = Vec::new(); let mut writer = Vec::new();
parsed_tag.dump_to(&mut writer).unwrap(); parsed_tag.dump_to(&mut writer).unwrap();
@ -562,9 +596,7 @@ mod tests {
#[test] #[test]
fn issue_34() { fn issue_34() {
let tag_bytes = crate::tag_utils::test_utils::read_path("tests/tags/assets/issue_34.ilst"); let ilst = read_ilst("tests/tags/assets/issue_34.ilst");
let ilst = super::read::parse_ilst(&mut &tag_bytes[..], tag_bytes.len() as u64).unwrap();
verify_atom( verify_atom(
&ilst, &ilst,
@ -580,4 +612,17 @@ mod tests {
}, },
) )
} }
#[test]
fn advisory_rating() {
let ilst = read_ilst("tests/tags/assets/advisory_rating.ilst");
verify_atom(
&ilst,
*b"\xa9ART",
&AtomData::UTF8(String::from("Foo artist")),
);
assert_eq!(ilst.advisory_rating(), Some(AdvisoryRating::Explicit));
}
} }

View file

@ -23,7 +23,7 @@ cfg_if::cfg_if! {
pub(crate) mod ilst; pub(crate) mod ilst;
pub use atom_info::AtomIdent; pub use atom_info::AtomIdent;
pub use ilst::atom::{Atom, AtomData}; pub use ilst::atom::{Atom, AtomData, AdvisoryRating};
pub use ilst::Ilst; pub use ilst::Ilst;
/// This module contains the codes for all of the [Well-known data types](https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW34) /// This module contains the codes for all of the [Well-known data types](https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW34)

Binary file not shown.