Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ParseOptions: Add ParseOptions::read_tags #406

Merged
merged 1 commit into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- **ParseOptions**: `ParseOptions::read_tags` to skip the parsing of tags ([issue](https://github.com/Serial-ATA/lofty-rs/issues/251)) ([PR](https://github.com/Serial-ATA/lofty-rs/pull/406))

## [0.20.1] - 2024-07-02

### Fixed
Expand Down
24 changes: 13 additions & 11 deletions lofty/src/aac/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,20 @@ where

stream_len -= u64::from(header.size);

let id3v2 = parse_id3v2(reader, header, parse_mode)?;
if let Some(existing_tag) = &mut file.id3v2_tag {
log::warn!("Duplicate ID3v2 tag found, appending frames to previous tag");

// https://github.com/Serial-ATA/lofty-rs/issues/87
// Duplicate tags should have their frames appended to the previous
for frame in id3v2.frames {
existing_tag.insert(frame);
if parse_options.read_tags {
let id3v2 = parse_id3v2(reader, header, parse_mode)?;
if let Some(existing_tag) = &mut file.id3v2_tag {
log::warn!("Duplicate ID3v2 tag found, appending frames to previous tag");

// https://github.com/Serial-ATA/lofty-rs/issues/87
// Duplicate tags should have their frames appended to the previous
for frame in id3v2.frames {
existing_tag.insert(frame);
}
continue;
}
continue;
file.id3v2_tag = Some(id3v2);
}
file.id3v2_tag = Some(id3v2);

// Skip over the footer
if skip_footer {
Expand Down Expand Up @@ -94,7 +96,7 @@ where
}

#[allow(unused_variables)]
let ID3FindResults(header, id3v1) = find_id3v1(reader, true)?;
let ID3FindResults(header, id3v1) = find_id3v1(reader, parse_options.read_tags)?;

if header.is_some() {
stream_len -= 128;
Expand Down
34 changes: 21 additions & 13 deletions lofty/src/ape/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::id3::v1::tag::Id3v1Tag;
use crate::id3::v2::read::parse_id3v2;
use crate::id3::v2::tag::Id3v2Tag;
use crate::id3::{find_id3v1, find_id3v2, find_lyrics3v2, FindId3v2Config, ID3FindResults};
use crate::macros::decode_err;
use crate::macros::{decode_err, err};

use std::io::{Read, Seek, SeekFrom};

Expand All @@ -27,10 +27,14 @@ where
let mut id3v1_tag: Option<Id3v1Tag> = None;
let mut ape_tag: Option<ApeTag> = None;

let find_id3v2_config = if parse_options.read_tags {
FindId3v2Config::READ_TAG
} else {
FindId3v2Config::NO_READ_TAG
};

// ID3v2 tags are unsupported in APE files, but still possible
if let ID3FindResults(Some(header), Some(content)) =
find_id3v2(data, FindId3v2Config::READ_TAG)?
{
if let ID3FindResults(Some(header), content) = find_id3v2(data, find_id3v2_config)? {
log::warn!("Encountered an ID3v2 tag. This tag cannot be rewritten to the APE file!");

stream_len -= u64::from(header.size);
Expand All @@ -40,10 +44,12 @@ where
stream_len -= 10;
}

let reader = &mut &*content;
if let Some(content) = content {
let reader = &mut &*content;

let id3v2 = parse_id3v2(reader, header, parse_options.parsing_mode)?;
id3v2_tag = Some(id3v2);
let id3v2 = parse_id3v2(reader, header, parse_options.parsing_mode)?;
id3v2_tag = Some(id3v2);
}
}

let mut found_mac = false;
Expand Down Expand Up @@ -76,14 +82,16 @@ where
})?;

if &remaining[..4] != b"AGEX" {
decode_err!(@BAIL Ape, "Found incomplete APE tag");
err!(FakeTag)
}

let ape_header = read_ape_header(data, false)?;
stream_len -= u64::from(ape_header.size);

let ape = read_ape_tag_with_header(data, ape_header)?;
ape_tag = Some(ape);
if parse_options.read_tags {
let ape = read_ape_tag_with_header(data, ape_header)?;
ape_tag = Some(ape);
}
},
_ => {
decode_err!(@BAIL Ape, "Invalid data found while reading header, expected any of [\"MAC \", \"APETAGEX\", \"ID3\"]")
Expand All @@ -96,7 +104,7 @@ where
// Starts with ['T', 'A', 'G']
// Exactly 128 bytes long (including the identifier)
#[allow(unused_variables)]
let ID3FindResults(id3v1_header, id3v1) = find_id3v1(data, true)?;
let ID3FindResults(id3v1_header, id3v1) = find_id3v1(data, parse_options.read_tags)?;

if id3v1_header.is_some() {
stream_len -= 128;
Expand All @@ -117,9 +125,9 @@ where
// Strongly recommended to be at the end of the file
data.seek(SeekFrom::Current(-32))?;

if let Some((tag, header)) = read_ape_tag(data, true)? {
if let (tag, Some(header)) = read_ape_tag(data, true, parse_options.read_tags)? {
stream_len -= u64::from(header.size);
ape_tag = Some(tag);
ape_tag = tag;
}

let file_length = data.stream_position()?;
Expand Down
31 changes: 19 additions & 12 deletions lofty/src/ape/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,9 +622,11 @@ mod tests {
let tag = crate::tag::utils::test_utils::read_path("tests/tags/assets/test.apev2");
let mut reader = Cursor::new(tag);

let (parsed_tag, _) = crate::ape::tag::read::read_ape_tag(&mut reader, false)
.unwrap()
.unwrap();
let (Some(parsed_tag), _) =
crate::ape::tag::read::read_ape_tag(&mut reader, false, true).unwrap()
else {
unreachable!();
};

assert_eq!(expected_tag.len(), parsed_tag.len());

Expand All @@ -638,9 +640,11 @@ mod tests {
let tag_bytes = crate::tag::utils::test_utils::read_path("tests/tags/assets/test.apev2");
let mut reader = Cursor::new(tag_bytes);

let (parsed_tag, _) = crate::ape::tag::read::read_ape_tag(&mut reader, false)
.unwrap()
.unwrap();
let (Some(parsed_tag), _) =
crate::ape::tag::read::read_ape_tag(&mut reader, false, true).unwrap()
else {
unreachable!();
};

let mut writer = Vec::new();
parsed_tag
Expand All @@ -649,9 +653,11 @@ mod tests {

let mut temp_reader = Cursor::new(writer);

let (temp_parsed_tag, _) = crate::ape::tag::read::read_ape_tag(&mut temp_reader, false)
.unwrap()
.unwrap();
let (Some(temp_parsed_tag), _) =
crate::ape::tag::read::read_ape_tag(&mut temp_reader, false, true).unwrap()
else {
unreachable!()
};

assert_eq!(parsed_tag, temp_parsed_tag);
}
Expand All @@ -661,9 +667,10 @@ mod tests {
let tag_bytes = crate::tag::utils::test_utils::read_path("tests/tags/assets/test.apev2");
let mut reader = Cursor::new(tag_bytes);

let (ape, _) = crate::ape::tag::read::read_ape_tag(&mut reader, false)
.unwrap()
.unwrap();
let (Some(ape), _) = crate::ape::tag::read::read_ape_tag(&mut reader, false, true).unwrap()
else {
unreachable!()
};

let tag: Tag = ape.into();

Expand Down
12 changes: 8 additions & 4 deletions lofty/src/ape/tag/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,20 @@ where
pub(crate) fn read_ape_tag<R: Read + Seek>(
reader: &mut R,
footer: bool,
) -> Result<Option<(ApeTag, ApeHeader)>> {
read_tag: bool,
) -> Result<(Option<ApeTag>, Option<ApeHeader>)> {
let mut ape_preamble = [0; 8];
reader.read_exact(&mut ape_preamble)?;

let mut ape_tag = None;
if &ape_preamble == APE_PREAMBLE {
let ape_header = header::read_ape_header(reader, footer)?;
if read_tag {
ape_tag = Some(read_ape_tag_with_header(reader, ape_header)?);
}

let ape = read_ape_tag_with_header(reader, ape_header)?;
return Ok(Some((ape, ape_header)));
return Ok((ape_tag, Some(ape_header)));
}

Ok(None)
Ok((None, None))
}
8 changes: 4 additions & 4 deletions lofty/src/ape/tag/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ where
let mut header_ape_tag = (false, (0, 0));

let start = file.stream_position()?;
match read::read_ape_tag(file, false)? {
Some((mut existing_tag, header)) => {
match read::read_ape_tag(file, false, true)? {
(Some(mut existing_tag), Some(header)) => {
if write_options.respect_read_only {
// Only keep metadata around that's marked read only
existing_tag.items.retain(|i| i.read_only);
Expand All @@ -61,7 +61,7 @@ where

header_ape_tag = (true, (start, start + u64::from(header.size)))
},
None => {
_ => {
file.seek(SeekFrom::Current(-8))?;
},
}
Expand All @@ -80,7 +80,7 @@ where

// Also check this tag for any read only items
let start = file.stream_position()? as usize + 32;
if let Some((mut existing_tag, header)) = read::read_ape_tag(file, true)? {
if let (Some(mut existing_tag), Some(header)) = read::read_ape_tag(file, true, true)? {
if write_options.respect_read_only {
existing_tag.items.retain(|i| i.read_only);

Expand Down
18 changes: 18 additions & 0 deletions lofty/src/config/parse_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#[non_exhaustive]
pub struct ParseOptions {
pub(crate) read_properties: bool,
pub(crate) read_tags: bool,
pub(crate) parsing_mode: ParsingMode,
pub(crate) max_junk_bytes: usize,
}
Expand All @@ -15,6 +16,7 @@ impl Default for ParseOptions {
/// ```rust,ignore
/// ParseOptions {
/// read_properties: true,
/// read_tags: true,
/// parsing_mode: ParsingMode::BestAttempt,
/// max_junk_bytes: 1024
/// }
Expand Down Expand Up @@ -46,6 +48,7 @@ impl ParseOptions {
pub const fn new() -> Self {
Self {
read_properties: true,
read_tags: true,
parsing_mode: Self::DEFAULT_PARSING_MODE,
max_junk_bytes: Self::DEFAULT_MAX_JUNK_BYTES,
}
Expand All @@ -66,6 +69,21 @@ impl ParseOptions {
*self
}

/// Whether or not to read the tags
///
/// # Examples
///
/// ```rust
/// use lofty::config::ParseOptions;
///
/// // By default, `read_tags` is enabled. Here, we don't want to read them.
/// let parsing_options = ParseOptions::new().read_tags(false);
/// ```
pub fn read_tags(&mut self, read_tags: bool) -> Self {
self.read_tags = read_tags;
*self
}

/// The parsing mode to use, see [`ParsingMode`] for details
///
/// # Examples
Expand Down
14 changes: 9 additions & 5 deletions lofty/src/flac/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ where
properties: FlacProperties::default(),
};

let find_id3v2_config = if parse_options.read_tags {
FindId3v2Config::READ_TAG
} else {
FindId3v2Config::NO_READ_TAG
};

// It is possible for a FLAC file to contain an ID3v2 tag
if let ID3FindResults(Some(header), Some(content)) =
find_id3v2(data, FindId3v2Config::READ_TAG)?
{
if let ID3FindResults(Some(header), Some(content)) = find_id3v2(data, find_id3v2_config)? {
log::warn!("Encountered an ID3v2 tag. This tag cannot be rewritten to the FLAC file!");

let reader = &mut &*content;
Expand Down Expand Up @@ -78,7 +82,7 @@ where
decode_err!(@BAIL Flac, "Encountered a zero-sized metadata block");
}

if block.ty == BLOCK_ID_VORBIS_COMMENTS {
if block.ty == BLOCK_ID_VORBIS_COMMENTS && parse_options.read_tags {
log::debug!("Encountered a Vorbis Comments block, parsing");

// NOTE: According to the spec
Expand Down Expand Up @@ -106,7 +110,7 @@ where
continue;
}

if block.ty == BLOCK_ID_PICTURE {
if block.ty == BLOCK_ID_PICTURE && parse_options.read_tags {
log::debug!("Encountered a FLAC picture block, parsing");

match Picture::from_flac_bytes(&block.content, false, parse_options.parsing_mode) {
Expand Down
12 changes: 6 additions & 6 deletions lofty/src/iff/aiff/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ where

while chunks.next(data).is_ok() {
match &chunks.fourcc {
b"ID3 " | b"id3 " => {
b"ID3 " | b"id3 " if parse_options.read_tags => {
let tag = chunks.id3_chunk(data, parse_options.parsing_mode)?;
if let Some(existing_tag) = id3v2_tag.as_mut() {
log::warn!("Duplicate ID3v2 tag found, appending frames to previous tag");
Expand All @@ -95,12 +95,12 @@ where
stream_len = chunks.size;
chunks.skip(data)?;
},
b"ANNO" => {
b"ANNO" if parse_options.read_tags => {
annotations.push(chunks.read_pstring(data, None)?);
},
// These four chunks are expected to appear at most once per file,
// so there's no need to replace anything we already read
b"COMT" if comments.is_empty() => {
b"COMT" if comments.is_empty() && parse_options.read_tags => {
if chunks.size < 2 {
continue;
}
Expand All @@ -123,13 +123,13 @@ where

chunks.correct_position(data)?;
},
b"NAME" if text_chunks.name.is_none() => {
b"NAME" if text_chunks.name.is_none() && parse_options.read_tags => {
text_chunks.name = Some(chunks.read_pstring(data, None)?);
},
b"AUTH" if text_chunks.author.is_none() => {
b"AUTH" if text_chunks.author.is_none() && parse_options.read_tags => {
text_chunks.author = Some(chunks.read_pstring(data, None)?);
},
b"(c) " if text_chunks.copyright.is_none() => {
b"(c) " if text_chunks.copyright.is_none() && parse_options.read_tags => {
text_chunks.copyright = Some(chunks.read_pstring(data, None)?);
},
_ => chunks.skip(data)?,
Expand Down
4 changes: 2 additions & 2 deletions lofty/src/iff/wav/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ where
data.read_exact(&mut list_type)?;

match &list_type {
b"INFO" => {
b"INFO" if parse_options.read_tags => {
let end = data.stream_position()? + u64::from(chunks.size - 4);
super::tag::read::parse_riff_info(data, &mut chunks, end, &mut riff_info)?;
},
Expand All @@ -88,7 +88,7 @@ where
},
}
},
b"ID3 " | b"id3 " => {
b"ID3 " | b"id3 " if parse_options.read_tags => {
let tag = chunks.id3_chunk(data, parse_options.parsing_mode)?;
if let Some(existing_tag) = id3v2_tag.as_mut() {
log::warn!("Duplicate ID3v2 tag found, appending frames to previous tag");
Expand Down
Loading