mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2025-01-18 23:23:53 +00:00
Put ID3v2 tag restrictions behind a feature, cleanup
This commit is contained in:
parent
98ab6d4bce
commit
575d7af692
18 changed files with 137 additions and 103 deletions
|
@ -18,6 +18,7 @@ filepath = { version = "0.1.1", optional = true } # wav/aiff only supports paths
|
|||
ogg_pager = { version = "0.1.7", optional = true }
|
||||
# Mp4
|
||||
mp4ameta = {version = "0.11.0", optional = true}
|
||||
simdutf8 = { version = "0.1.3", optional = true }
|
||||
# Case insensitive keys (APE/FLAC/Opus/Vorbis)
|
||||
unicase = { version = "2.6.0"}
|
||||
|
||||
|
@ -33,11 +34,12 @@ cfg-if = "1.0.0"
|
|||
|
||||
[features]
|
||||
default = ["mp4_atoms", "vorbis_comments", "ape", "id3v1", "id3v2", "aiff_text_chunks", "riff_info_list", "quick_tag_accessors"]
|
||||
mp4_atoms = []
|
||||
mp4_atoms = ["simdutf8"]
|
||||
vorbis_comments = ["ogg_pager"]
|
||||
ape = []
|
||||
id3v1 = []
|
||||
id3v2 = ["flate2"]
|
||||
id3v2_restrictions = []
|
||||
aiff_text_chunks = []
|
||||
riff_info_list = []
|
||||
quick_tag_accessors = ["paste"]
|
||||
|
|
14
src/error.rs
14
src/error.rs
|
@ -16,7 +16,7 @@ pub enum LoftyError {
|
|||
/// Provided an empty file
|
||||
#[error("File contains no data")]
|
||||
EmptyFile,
|
||||
/// Attempting to write an abnormally large amount of data
|
||||
/// Attempting to read/write an abnormally large amount of data
|
||||
#[error("An abnormally large amount of data was provided, and an overflow occurred")]
|
||||
TooMuchData,
|
||||
|
||||
|
@ -64,6 +64,9 @@ pub enum LoftyError {
|
|||
/// Arises when a tag is expected (Ex. found an "ID3 " chunk in a WAV file), but isn't found
|
||||
#[error("Reading: Expected a tag, found invalid data")]
|
||||
FakeTag,
|
||||
/// Arises when an atom contains invalid data
|
||||
#[error("MP4 Atom: {0}")]
|
||||
BadAtom(&'static str),
|
||||
/// Errors that arise while reading/writing to WAV files
|
||||
#[error("Riff: {0}")]
|
||||
Wav(&'static str),
|
||||
|
@ -82,9 +85,12 @@ pub enum LoftyError {
|
|||
/// Errors that arise while reading/writing to OGG files
|
||||
#[error("OGG: {0}")]
|
||||
Ogg(&'static str),
|
||||
/// Errors that arise while reading/writing to MPEG files
|
||||
#[error("MPEG: {0}")]
|
||||
Mpeg(&'static str),
|
||||
/// Errors that arise while reading/writing to MP3 files
|
||||
#[error("MP3: {0}")]
|
||||
Mp3(&'static str),
|
||||
/// Errors that arise while reading/writing to MP4 files
|
||||
#[error("MP4: {0}")]
|
||||
Mp4(&'static str),
|
||||
/// Errors that arise while reading/writing to APE files
|
||||
#[error("APE: {0}")]
|
||||
Ape(&'static str),
|
||||
|
|
|
@ -88,8 +88,6 @@
|
|||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! NOTE: All of these are enabled by default
|
||||
//!
|
||||
//! ## QOL
|
||||
//! * `quick_tag_accessors` - Adds easier getters/setters for string values (Ex. [`Tag::artist`]), adds an extra dependency
|
||||
//!
|
||||
|
@ -104,6 +102,9 @@
|
|||
//! * `riff_info_list`
|
||||
//! * `vorbis_comments`
|
||||
//!
|
||||
//! ## Utilities
|
||||
//! * `id3v2_restrictions` - Parses ID3v2 extended headers and exposes flags for fine grained control
|
||||
//!
|
||||
//! # Notes on ID3v2
|
||||
//!
|
||||
//! See [`id3`](crate::id3) for important warnings and notes on reading tags.
|
||||
|
@ -181,6 +182,7 @@ pub mod id3 {
|
|||
//! The solution is to use [`ItemKey::Id3v2Specific`](crate::ItemKey::Id3v2Specific) alongside [`Id3v2Frame`](crate::id3::Id3v2Frame).
|
||||
//!
|
||||
//! NOTE: Unlike the above issue, this one does not require unchecked insertion.
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
pub use crate::logic::id3::v2::restrictions::*;
|
||||
pub use crate::logic::id3::v2::util::encapsulated_object::{
|
||||
GEOBInformation, GeneralEncapsulatedObject,
|
||||
|
|
|
@ -50,15 +50,18 @@ impl AudioFile for ApeFile {
|
|||
}
|
||||
|
||||
impl ApeFile {
|
||||
fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the ID3v2 tag if it exists
|
||||
pub fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
self.id3v2.as_ref()
|
||||
}
|
||||
|
||||
fn id3v1_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the ID3v1 tag if it exists
|
||||
pub fn id3v1_tag(&self) -> Option<&Tag> {
|
||||
self.id3v1.as_ref()
|
||||
}
|
||||
|
||||
fn ape_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the APEv1/2 tag if it exists
|
||||
pub fn ape_tag(&self) -> Option<&Tag> {
|
||||
self.ape.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,17 +126,17 @@ fn parse_text(id: &str, content: &mut &[u8]) -> Result<TagItem> {
|
|||
|
||||
let text = decode_text(content, encoding, false)?.unwrap_or_else(String::new);
|
||||
|
||||
Ok(TagItem::new(
|
||||
ItemKey::Id3v2Specific(Id3v2Frame::Text(id.to_string(), encoding)),
|
||||
ItemValue::Text(text),
|
||||
))
|
||||
let key = ItemKey::from_key(&TagType::Id3v2(Id3v2Version::V4), id)
|
||||
.unwrap_or_else(|| ItemKey::Id3v2Specific(Id3v2Frame::Text(id.to_string(), encoding)));
|
||||
|
||||
Ok(TagItem::new(key, ItemValue::Text(text)))
|
||||
}
|
||||
|
||||
fn parse_link(id: &str, content: &mut &[u8]) -> Result<TagItem> {
|
||||
let link = decode_text(content, TextEncoding::Latin1, false)?.unwrap_or_else(String::new);
|
||||
|
||||
Ok(TagItem::new(
|
||||
ItemKey::Id3v2Specific(Id3v2Frame::URL(id.to_string())),
|
||||
ItemValue::Locator(link),
|
||||
))
|
||||
let key = ItemKey::from_key(&TagType::Id3v2(Id3v2Version::V4), id)
|
||||
.unwrap_or_else(|| ItemKey::Id3v2Specific(Id3v2Frame::URL(id.to_string())));
|
||||
|
||||
Ok(TagItem::new(key, ItemValue::Locator(link)))
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use byteorder::{BigEndian, ByteOrder};
|
|||
|
||||
mod frame;
|
||||
pub(crate) mod read;
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
pub(crate) mod restrictions;
|
||||
pub(crate) mod util;
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use crate::error::Result;
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
use crate::logic::id3::decode_u32;
|
||||
use crate::logic::id3::v2::frame::content::FrameContent;
|
||||
use crate::logic::id3::v2::frame::Frame;
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
use crate::logic::id3::v2::restrictions::{
|
||||
ImageSizeRestrictions, TagRestrictions, TagSizeRestrictions, TextSizeRestrictions,
|
||||
};
|
||||
|
@ -11,6 +13,7 @@ use crate::{LoftyError, TagType};
|
|||
|
||||
use std::io::Read;
|
||||
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
pub(crate) fn parse_id3v2(bytes: &mut &[u8]) -> Result<Tag> {
|
||||
|
@ -46,9 +49,11 @@ pub(crate) fn parse_id3v2(bytes: &mut &[u8]) -> Result<Tag> {
|
|||
footer: (version == Id3v2Version::V4 || version == Id3v2Version::V3)
|
||||
&& flags & 0x10 == 0x10,
|
||||
crc: false, // Retrieved later if applicable
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
restrictions: (false, TagRestrictions::default()), // Retrieved later if applicable
|
||||
};
|
||||
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
if flags_parsed.extended_header {
|
||||
let extended_size = decode_u32(bytes.read_u32::<BigEndian>()?);
|
||||
|
||||
|
@ -79,8 +84,17 @@ pub(crate) fn parse_id3v2(bytes: &mut &[u8]) -> Result<Tag> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "id3v2_restrictions"))]
|
||||
let mut tag = Tag::new(TagType::Id3v2(Id3v2Version::V4));
|
||||
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
let mut tag = {
|
||||
let mut tag = Tag::new(TagType::Id3v2(Id3v2Version::V4));
|
||||
tag.set_flags(flags_parsed);
|
||||
|
||||
tag
|
||||
};
|
||||
|
||||
loop {
|
||||
match Frame::read(bytes, version)? {
|
||||
None => break,
|
||||
|
@ -97,6 +111,7 @@ pub(crate) fn parse_id3v2(bytes: &mut &[u8]) -> Result<Tag> {
|
|||
Ok(tag)
|
||||
}
|
||||
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
fn parse_restrictions(bytes: &mut &[u8]) -> Result<TagRestrictions> {
|
||||
// We don't care about the length byte
|
||||
let _data_length = bytes.read_u8()?;
|
||||
|
@ -110,7 +125,7 @@ fn parse_restrictions(bytes: &mut &[u8]) -> Result<TagRestrictions> {
|
|||
restriction_flags & 0x80 == 0x80,
|
||||
restriction_flags & 0x40 == 0x40,
|
||||
) {
|
||||
(false, false) => {},
|
||||
(false, false) => {}, // default
|
||||
(false, true) => restrictions.size = TagSizeRestrictions::S_64F_128K,
|
||||
(true, false) => restrictions.size = TagSizeRestrictions::S_32F_40K,
|
||||
(true, true) => restrictions.size = TagSizeRestrictions::S_32F_4K,
|
||||
|
@ -126,7 +141,7 @@ fn parse_restrictions(bytes: &mut &[u8]) -> Result<TagRestrictions> {
|
|||
restriction_flags & 0x10 == 0x10,
|
||||
restriction_flags & 0x08 == 0x08,
|
||||
) {
|
||||
(false, false) => {},
|
||||
(false, false) => {}, // default
|
||||
(false, true) => restrictions.text_fields_size = TextSizeRestrictions::C_1024,
|
||||
(true, false) => restrictions.text_fields_size = TextSizeRestrictions::C_128,
|
||||
(true, true) => restrictions.text_fields_size = TextSizeRestrictions::C_30,
|
||||
|
@ -142,7 +157,7 @@ fn parse_restrictions(bytes: &mut &[u8]) -> Result<TagRestrictions> {
|
|||
restriction_flags & 0x02 == 0x02,
|
||||
restriction_flags & 0x01 == 0x01,
|
||||
) {
|
||||
(false, false) => {},
|
||||
(false, false) => {}, // default
|
||||
(false, true) => restrictions.image_size = ImageSizeRestrictions::P_256,
|
||||
(true, false) => restrictions.image_size = ImageSizeRestrictions::P_64,
|
||||
(true, true) => restrictions.image_size = ImageSizeRestrictions::P_64_64,
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{ItemKey, ItemValue, Tag, TagItem};
|
|||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::time::Duration;
|
||||
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
/// An AIFF file
|
||||
pub struct AiffFile {
|
||||
|
@ -45,11 +45,13 @@ impl AudioFile for AiffFile {
|
|||
}
|
||||
|
||||
impl AiffFile {
|
||||
fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the ID3v2 tag if it exists
|
||||
pub fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
self.id3v2.as_ref()
|
||||
}
|
||||
|
||||
fn text_chunks(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the text chunks tag if it exists
|
||||
pub fn text_chunks(&self) -> Option<&Tag> {
|
||||
self.text_chunks.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
use crate::types::file::AudioFile;
|
||||
use crate::{
|
||||
FileProperties, FileType, ItemKey, ItemValue, LoftyError, Result, Tag, TagItem, TagType,
|
||||
TaggedFile,
|
||||
FileProperties, ItemKey, ItemValue, LoftyError, Result, Tag, TagItem, TagType
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::logic::id3::v2::read::parse_id3v2;
|
||||
|
@ -53,11 +50,13 @@ impl AudioFile for WavFile {
|
|||
}
|
||||
|
||||
impl WavFile {
|
||||
fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the ID3v2 tag if it exists
|
||||
pub fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
self.id3v2.as_ref()
|
||||
}
|
||||
|
||||
fn riff_info(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the RIFF INFO tag if it exists
|
||||
pub fn riff_info(&self) -> Option<&Tag> {
|
||||
self.riff_info.as_ref()
|
||||
}
|
||||
}
|
||||
|
@ -276,33 +275,6 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn find_info_list<T>(data: &mut T) -> Result<()>
|
||||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
loop {
|
||||
let mut chunk_name = [0; 4];
|
||||
data.read_exact(&mut chunk_name)?;
|
||||
|
||||
if &chunk_name == b"LIST" {
|
||||
data.seek(SeekFrom::Current(4))?;
|
||||
|
||||
let mut list_type = [0; 4];
|
||||
data.read_exact(&mut list_type)?;
|
||||
|
||||
if &list_type == b"INFO" {
|
||||
data.seek(SeekFrom::Current(-8))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
data.seek(SeekFrom::Current(-8))?;
|
||||
}
|
||||
|
||||
let size = data.read_u32::<LittleEndian>()?;
|
||||
data.seek(SeekFrom::Current(i64::from(size)))?;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "format-riff")] {
|
||||
pub(crate) fn write_to(data: &mut File, metadata: HashMap<String, String>) -> Result<()> {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
pub(crate) mod ape;
|
||||
pub(crate) mod iff;
|
||||
pub(crate) mod mp4;
|
||||
pub(crate) mod mpeg;
|
||||
pub(crate) mod ogg;
|
||||
|
||||
|
|
|
@ -49,21 +49,21 @@ impl Header {
|
|||
0 => MpegVersion::V2_5,
|
||||
2 => MpegVersion::V2,
|
||||
3 => MpegVersion::V1,
|
||||
_ => return Err(LoftyError::Mpeg("Frame header has an invalid version")),
|
||||
_ => return Err(LoftyError::Mp3("Frame header has an invalid version")),
|
||||
};
|
||||
|
||||
let layer = match (header >> 17) & 0b11 {
|
||||
1 => Layer::Layer3,
|
||||
2 => Layer::Layer2,
|
||||
3 => Layer::Layer1,
|
||||
_ => return Err(LoftyError::Mpeg("Frame header uses a reserved layer")),
|
||||
_ => return Err(LoftyError::Mp3("Frame header uses a reserved layer")),
|
||||
};
|
||||
|
||||
let bitrate = (header >> 12) & 0b1111;
|
||||
let sample_rate = (header >> 10) & 0b11;
|
||||
|
||||
if sample_rate == 0 {
|
||||
return Err(LoftyError::Mpeg("Frame header has a sample rate of 0"));
|
||||
return Err(LoftyError::Mp3("Frame header has a sample rate of 0"));
|
||||
}
|
||||
|
||||
let mode = match (header >> 6) & 0b11 {
|
||||
|
@ -71,7 +71,7 @@ impl Header {
|
|||
1 => Mode::JointStereo,
|
||||
2 => Mode::DualChannel,
|
||||
3 => Mode::SingleChannel,
|
||||
_ => return Err(LoftyError::Mpeg("Unreachable error")),
|
||||
_ => return Err(LoftyError::Mp3("Unreachable error")),
|
||||
};
|
||||
|
||||
let layer_index = (layer as usize).saturating_sub(1);
|
||||
|
@ -121,14 +121,14 @@ impl XingHeader {
|
|||
match &header {
|
||||
b"Xing" | b"Info" => {
|
||||
if reader_len < 16 {
|
||||
return Err(LoftyError::Mpeg("Xing header has an invalid size (< 16)"));
|
||||
return Err(LoftyError::Mp3("Xing header has an invalid size (< 16)"));
|
||||
}
|
||||
|
||||
let mut flags = [0; 4];
|
||||
reader.read_exact(&mut flags)?;
|
||||
|
||||
if flags[3] & 0x03 != 0x03 {
|
||||
return Err(LoftyError::Mpeg(
|
||||
return Err(LoftyError::Mp3(
|
||||
"Xing header doesn't have required flags set (0x0001 and 0x0002)",
|
||||
));
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ impl XingHeader {
|
|||
},
|
||||
b"VBRI" => {
|
||||
if reader_len < 32 {
|
||||
return Err(LoftyError::Mpeg("VBRI header has an invalid size (< 32)"));
|
||||
return Err(LoftyError::Mp3("VBRI header has an invalid size (< 32)"));
|
||||
}
|
||||
|
||||
// Skip 6 bytes
|
||||
|
@ -154,7 +154,7 @@ impl XingHeader {
|
|||
|
||||
Ok(Self { frames, size })
|
||||
},
|
||||
_ => Err(LoftyError::Mpeg("No Xing, LAME, or VBRI header located")),
|
||||
_ => Err(LoftyError::Mp3("No Xing, LAME, or VBRI header located")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,15 +47,18 @@ impl AudioFile for MpegFile {
|
|||
}
|
||||
|
||||
impl MpegFile {
|
||||
fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the ID3v2 tag if it exists
|
||||
pub fn id3v2_tag(&self) -> Option<&Tag> {
|
||||
self.id3v2.as_ref()
|
||||
}
|
||||
|
||||
fn id3v1_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the ID3v1 tag if it exists
|
||||
pub fn id3v1_tag(&self) -> Option<&Tag> {
|
||||
self.id3v1.as_ref()
|
||||
}
|
||||
|
||||
fn ape_tag(&self) -> Option<&Tag> {
|
||||
/// Returns a reference to the APEv1/2 tag if it exists
|
||||
pub fn ape_tag(&self) -> Option<&Tag> {
|
||||
self.ape.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,12 +127,12 @@ where
|
|||
continue;
|
||||
}
|
||||
},
|
||||
_ => return Err(LoftyError::Mpeg("File contains an invalid frame")),
|
||||
_ => return Err(LoftyError::Mp3("File contains an invalid frame")),
|
||||
}
|
||||
}
|
||||
|
||||
if first_mpeg_frame.0.is_none() {
|
||||
return Err(LoftyError::Mpeg("Unable to find an MPEG frame"));
|
||||
return Err(LoftyError::Mp3("Unable to find an MPEG frame"));
|
||||
}
|
||||
|
||||
let first_mpeg_frame = (first_mpeg_frame.0.unwrap(), first_mpeg_frame.1);
|
||||
|
|
|
@ -6,9 +6,6 @@ use crate::types::file::AudioFile;
|
|||
use crate::types::properties::FileProperties;
|
||||
use crate::types::tag::{Tag, TagType};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
use crate::logic::id3::v2::Id3v2Version;
|
||||
use crate::{FileProperties, LoftyError, Result, Tag, TagType};
|
||||
|
||||
use crate::logic::ape::ApeFile;
|
||||
use crate::logic::iff::aiff::AiffFile;
|
||||
use crate::logic::iff::wav::WavFile;
|
||||
use crate::logic::mp4::Mp4File;
|
||||
use crate::logic::mpeg::MpegFile;
|
||||
use crate::logic::ogg::flac::FlacFile;
|
||||
use crate::logic::ogg::opus::OpusFile;
|
||||
use crate::logic::ogg::vorbis::VorbisFile;
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
/// Provides various methods for interaction with a file
|
||||
pub trait AudioFile {
|
||||
/// Read a file from a reader
|
||||
|
@ -180,6 +182,20 @@ impl From<MpegFile> for TaggedFile {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Mp4File> for TaggedFile {
|
||||
fn from(input: Mp4File) -> Self {
|
||||
Self {
|
||||
ty: FileType::MP4,
|
||||
properties: input.properties,
|
||||
tags: if let Some(ilst) = input.ilst {
|
||||
vec![ilst]
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ApeFile> for TaggedFile {
|
||||
fn from(input: ApeFile) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -83,7 +83,7 @@ macro_rules! item_keys {
|
|||
}
|
||||
|
||||
item_keys!(
|
||||
ALLOWED_UNKNOWN => [TagType::Ape, TagType::VorbisComments];
|
||||
ALLOWED_UNKNOWN => [TagType::Ape, TagType::VorbisComments, TagType::Mp4Atom];
|
||||
// Titles
|
||||
AlbumTitle => [
|
||||
TagType::Id3v2(_) => "TALB", TagType::Mp4Atom => "\u{a9}alb",
|
||||
|
|
|
@ -9,31 +9,6 @@ use byteorder::WriteBytesExt;
|
|||
#[cfg(any(feature = "vorbis_comments", feature = "id3v2",))]
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
#[cfg(feature = "ape")]
|
||||
pub const APE_PICTYPES: [&str; 21] = [
|
||||
"Other",
|
||||
"Png Icon",
|
||||
"Icon",
|
||||
"Front",
|
||||
"Back",
|
||||
"Leaflet",
|
||||
"Media",
|
||||
"Lead Artist",
|
||||
"Artist",
|
||||
"Conductor",
|
||||
"Band",
|
||||
"Composer",
|
||||
"Lyricist",
|
||||
"Recording Location",
|
||||
"During Recording",
|
||||
"During Performance",
|
||||
"Video Capture",
|
||||
"Fish",
|
||||
"Illustration",
|
||||
"Band Logotype",
|
||||
"Publisher Logotype",
|
||||
];
|
||||
|
||||
/// Mime types for covers.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum MimeType {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use super::item::ItemKey;
|
||||
use super::picture::{Picture, PictureType};
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
use crate::logic::id3::v2::restrictions::TagRestrictions;
|
||||
#[cfg(feature = "id3v2")]
|
||||
use crate::logic::id3::v2::Id3v2Version;
|
||||
|
||||
#[cfg(feature = "quick_tag_accessors")]
|
||||
|
@ -36,6 +38,7 @@ macro_rules! common_items {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "id3v2", feature = "ape"))]
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
/// **(ID3v2/APEv2 ONLY)** Various flags to describe the content of an item
|
||||
|
@ -43,21 +46,27 @@ macro_rules! common_items {
|
|||
/// It is not an error to attempt to write flags to a format that doesn't support them.
|
||||
/// They will just be ignored.
|
||||
pub struct TagItemFlags {
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Preserve frame on tag edit
|
||||
pub tag_alter_preservation: bool,
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Preserve frame on file edit
|
||||
pub file_alter_preservation: bool,
|
||||
#[cfg(any(feature = "id3v2", feature = "ape"))]
|
||||
/// **(ID3v2/APEv2 ONLY)** Item cannot be written to
|
||||
pub read_only: bool,
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Frame belongs in a group
|
||||
///
|
||||
/// In addition to setting this flag, a group identifier byte must be added.
|
||||
/// All frames with the same group identifier byte belong to the same group.
|
||||
pub grouping_identity: (bool, u8),
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Frame is zlib compressed
|
||||
///
|
||||
/// It is **required** `data_length_indicator` be set if this is set.
|
||||
pub compression: bool,
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Frame is encrypted
|
||||
///
|
||||
/// NOTE: Since the encryption method is unknown, lofty cannot do anything with these frames
|
||||
|
@ -65,12 +74,14 @@ pub struct TagItemFlags {
|
|||
/// In addition to setting this flag, an encryption method symbol must be added.
|
||||
/// The method symbol **must** be > 0x80.
|
||||
pub encryption: (bool, u8),
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Frame is unsynchronised
|
||||
///
|
||||
/// In short, this makes all "0xFF 0x00" combinations into "0xFF 0x00 0x00" to avoid confusion
|
||||
/// with the MPEG frame header, which is often identified by its "frame sync" (11 set bits).
|
||||
/// It is preferred an ID3v2 tag is either *completely* unsynchronised or not unsynchronised at all.
|
||||
pub unsynchronisation: bool,
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Frame has a data length indicator
|
||||
///
|
||||
/// The data length indicator is the size of the frame if the flags were all zeroed out.
|
||||
|
@ -170,16 +181,34 @@ pub enum ItemValue {
|
|||
Text(String),
|
||||
/// **(APE/ID3v2 ONLY)** Any UTF-8 encoded locator of external information
|
||||
Locator(String),
|
||||
/// **(APE/ID3v2 ONLY)** Binary information
|
||||
/// **(APE/ID3v2/MP4 ONLY)** Binary information
|
||||
///
|
||||
/// In the case of ID3v2, this is the type of a [`Id3v2Frame::EncapsulatedObject`](crate::id3::Id3v2Frame::EncapsulatedObject) **and** any unknown frame.
|
||||
///
|
||||
/// For APEv2, no uses of this item type are documented, there's no telling what it could be.
|
||||
Binary(Vec<u8>),
|
||||
/// Any 32 bit unsigned integer
|
||||
///
|
||||
/// This is most commonly used for items such as track and disc numbers
|
||||
UInt(u32),
|
||||
/// **(MP4 ONLY)** Any 64 bit unsigned integer
|
||||
///
|
||||
/// There are no common [`ItemKey`]s that use this
|
||||
UInt64(u64),
|
||||
/// Any 32 bit signed integer
|
||||
///
|
||||
/// There are no common [`ItemKey`]s that use this
|
||||
Int(i32),
|
||||
/// **(MP4 ONLY)** Any 64 bit signed integer
|
||||
///
|
||||
/// There are no common [`ItemKey`]s that use this
|
||||
Int64(i64),
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** The content of a synchronized text frame, see [`SynchronizedText`](crate::id3::SynchronizedText)
|
||||
SynchronizedText(Vec<(u32, String)>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "id3v2")]
|
||||
#[derive(Default, Copy, Clone)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
/// **(ID3v2 ONLY)** Flags that apply to the entire tag
|
||||
|
@ -198,6 +227,7 @@ pub struct TagFlags {
|
|||
///
|
||||
/// This is calculated if the tag is written
|
||||
pub crc: bool,
|
||||
#[cfg(feature = "id3v2_restrictions")]
|
||||
/// Restrictions on the tag
|
||||
///
|
||||
/// NOTE: This **requires** `extended_header` to be set. Otherwise, it will be ignored.
|
||||
|
@ -214,6 +244,7 @@ pub struct Tag {
|
|||
tag_type: TagType,
|
||||
pictures: Vec<Picture>,
|
||||
items: Vec<TagItem>,
|
||||
#[cfg(feature = "id3v2")]
|
||||
flags: TagFlags,
|
||||
}
|
||||
|
||||
|
@ -263,6 +294,14 @@ impl Tag {
|
|||
flags: TagFlags::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "id3v2")]
|
||||
/// **(ID3v2 ONLY)** Restrict the tag's flags
|
||||
pub fn set_flags(&mut self, flags: TagFlags) {
|
||||
if let TagType::Id3v2(_) = self.tag_type {
|
||||
self.flags = flags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
|
|
Loading…
Reference in a new issue