mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-13 06:02:32 +00:00
Add rtng
atom support
This commit is contained in:
parent
c70e8ee454
commit
4c0b7c2273
4 changed files with 86 additions and 9 deletions
|
@ -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>,
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
BIN
tests/tags/assets/advisory_rating.ilst
Normal file
BIN
tests/tags/assets/advisory_rating.ilst
Normal file
Binary file not shown.
Loading…
Reference in a new issue