mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2025-03-04 23:07:20 +00:00
ID3v2: Pass parsing mode to frame parsing
This commit is contained in:
parent
464be3cb14
commit
21aa7d929d
11 changed files with 48 additions and 25 deletions
|
@ -48,7 +48,7 @@ where
|
||||||
|
|
||||||
stream_len -= u64::from(header.size);
|
stream_len -= u64::from(header.size);
|
||||||
|
|
||||||
let id3v2 = parse_id3v2(reader, header)?;
|
let id3v2 = parse_id3v2(reader, header, parse_mode)?;
|
||||||
if let Some(existing_tag) = &mut file.id3v2_tag {
|
if let Some(existing_tag) = &mut file.id3v2_tag {
|
||||||
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
||||||
// Duplicate tags should have their frames appended to the previous
|
// Duplicate tags should have their frames appended to the previous
|
||||||
|
|
|
@ -40,7 +40,7 @@ where
|
||||||
|
|
||||||
let reader = &mut &*content;
|
let reader = &mut &*content;
|
||||||
|
|
||||||
let id3v2 = parse_id3v2(reader, header)?;
|
let id3v2 = parse_id3v2(reader, header, parse_options.parsing_mode)?;
|
||||||
id3v2_tag = Some(id3v2);
|
id3v2_tag = Some(id3v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ where
|
||||||
if let ID3FindResults(Some(header), Some(content)) = find_id3v2(data, true)? {
|
if let ID3FindResults(Some(header), Some(content)) = find_id3v2(data, true)? {
|
||||||
let reader = &mut &*content;
|
let reader = &mut &*content;
|
||||||
|
|
||||||
let id3v2 = parse_id3v2(reader, header)?;
|
let id3v2 = parse_id3v2(reader, header, parse_options.parsing_mode)?;
|
||||||
flac_file.id3v2_tag = Some(id3v2);
|
flac_file.id3v2_tag = Some(id3v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::id3::v2::items::{
|
||||||
};
|
};
|
||||||
use crate::id3::v2::Id3v2Version;
|
use crate::id3::v2::Id3v2Version;
|
||||||
use crate::macros::err;
|
use crate::macros::err;
|
||||||
|
use crate::probe::ParsingMode;
|
||||||
use crate::util::text::TextEncoding;
|
use crate::util::text::TextEncoding;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
@ -15,6 +16,7 @@ pub(super) fn parse_content<R: Read>(
|
||||||
reader: &mut R,
|
reader: &mut R,
|
||||||
id: &str,
|
id: &str,
|
||||||
version: Id3v2Version,
|
version: Id3v2Version,
|
||||||
|
parse_mode: ParsingMode,
|
||||||
) -> Result<Option<FrameValue>> {
|
) -> Result<Option<FrameValue>> {
|
||||||
Ok(match id {
|
Ok(match id {
|
||||||
// The ID was previously upgraded, but the content remains unchanged, so version is necessary
|
// The ID was previously upgraded, but the content remains unchanged, so version is necessary
|
||||||
|
|
|
@ -5,13 +5,18 @@ use crate::id3::v2::frame::content::parse_content;
|
||||||
use crate::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream};
|
use crate::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream};
|
||||||
use crate::id3::v2::{FrameFlags, FrameId, FrameValue, Id3v2Version};
|
use crate::id3::v2::{FrameFlags, FrameId, FrameValue, Id3v2Version};
|
||||||
use crate::macros::try_vec;
|
use crate::macros::try_vec;
|
||||||
|
use crate::probe::ParsingMode;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
|
|
||||||
impl<'a> Frame<'a> {
|
impl<'a> Frame<'a> {
|
||||||
pub(crate) fn read<R>(reader: &mut R, version: Id3v2Version) -> Result<(Option<Self>, bool)>
|
pub(crate) fn read<R>(
|
||||||
|
reader: &mut R,
|
||||||
|
version: Id3v2Version,
|
||||||
|
parse_mode: ParsingMode,
|
||||||
|
) -> Result<(Option<Self>, bool)>
|
||||||
where
|
where
|
||||||
R: Read,
|
R: Read,
|
||||||
{
|
{
|
||||||
|
@ -91,14 +96,14 @@ impl<'a> Frame<'a> {
|
||||||
return handle_encryption(&mut compression_reader, size, id, flags);
|
return handle_encryption(&mut compression_reader, size, id, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse_frame(&mut compression_reader, id, flags, version);
|
return parse_frame(&mut compression_reader, id, flags, version, parse_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags.encryption.is_some() {
|
if flags.encryption.is_some() {
|
||||||
return handle_encryption(&mut unsynchronized_reader, size, id, flags);
|
return handle_encryption(&mut unsynchronized_reader, size, id, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse_frame(&mut unsynchronized_reader, id, flags, version);
|
return parse_frame(&mut unsynchronized_reader, id, flags, version, parse_mode);
|
||||||
},
|
},
|
||||||
// Possible combinations:
|
// Possible combinations:
|
||||||
//
|
//
|
||||||
|
@ -113,7 +118,7 @@ impl<'a> Frame<'a> {
|
||||||
return handle_encryption(&mut compression_reader, size, id, flags);
|
return handle_encryption(&mut compression_reader, size, id, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse_frame(&mut compression_reader, id, flags, version);
|
return parse_frame(&mut compression_reader, id, flags, version, parse_mode);
|
||||||
},
|
},
|
||||||
// Possible combinations:
|
// Possible combinations:
|
||||||
//
|
//
|
||||||
|
@ -126,7 +131,7 @@ impl<'a> Frame<'a> {
|
||||||
},
|
},
|
||||||
// Everything else that doesn't have special flags
|
// Everything else that doesn't have special flags
|
||||||
_ => {
|
_ => {
|
||||||
return parse_frame(&mut reader, id, flags, version);
|
return parse_frame(&mut reader, id, flags, version, parse_mode);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,8 +177,9 @@ fn parse_frame<R: Read>(
|
||||||
id: FrameId<'static>,
|
id: FrameId<'static>,
|
||||||
flags: FrameFlags,
|
flags: FrameFlags,
|
||||||
version: Id3v2Version,
|
version: Id3v2Version,
|
||||||
|
parse_mode: ParsingMode,
|
||||||
) -> Result<(Option<Frame<'static>>, bool)> {
|
) -> Result<(Option<Frame<'static>>, bool)> {
|
||||||
match parse_content(reader, id.as_str(), version)? {
|
match parse_content(reader, id.as_str(), version, parse_mode)? {
|
||||||
Some(value) => Ok((Some(Frame { id, value, flags }), false)),
|
Some(value) => Ok((Some(Frame { id, value, flags }), false)),
|
||||||
None => Ok((None, false)),
|
None => Ok((None, false)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,15 @@ use super::tag::Id3v2Tag;
|
||||||
use super::Id3v2Header;
|
use super::Id3v2Header;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::id3::v2::util::synchsafe::UnsynchronizedStream;
|
use crate::id3::v2::util::synchsafe::UnsynchronizedStream;
|
||||||
|
use crate::probe::ParsingMode;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
pub(crate) fn parse_id3v2<R>(bytes: &mut R, header: Id3v2Header) -> Result<Id3v2Tag>
|
pub(crate) fn parse_id3v2<R>(
|
||||||
|
bytes: &mut R,
|
||||||
|
header: Id3v2Header,
|
||||||
|
parse_mode: ParsingMode,
|
||||||
|
) -> Result<Id3v2Tag>
|
||||||
where
|
where
|
||||||
R: Read,
|
R: Read,
|
||||||
{
|
{
|
||||||
|
@ -16,12 +21,12 @@ where
|
||||||
if header.flags.unsynchronisation {
|
if header.flags.unsynchronisation {
|
||||||
// Unsynchronize the entire tag
|
// Unsynchronize the entire tag
|
||||||
let mut unsyncronized_reader = UnsynchronizedStream::new(tag_bytes);
|
let mut unsyncronized_reader = UnsynchronizedStream::new(tag_bytes);
|
||||||
ret = read_all_frames_into_tag(&mut unsyncronized_reader, header)?;
|
ret = read_all_frames_into_tag(&mut unsyncronized_reader, header, parse_mode)?;
|
||||||
|
|
||||||
// Get the `Take` back from the `UnsynchronizedStream`
|
// Get the `Take` back from the `UnsynchronizedStream`
|
||||||
tag_bytes = unsyncronized_reader.into_inner();
|
tag_bytes = unsyncronized_reader.into_inner();
|
||||||
} else {
|
} else {
|
||||||
ret = read_all_frames_into_tag(&mut tag_bytes, header)?;
|
ret = read_all_frames_into_tag(&mut tag_bytes, header, parse_mode)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Throw away the rest of the tag (padding, bad frames)
|
// Throw away the rest of the tag (padding, bad frames)
|
||||||
|
@ -29,7 +34,11 @@ where
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_all_frames_into_tag<R>(reader: &mut R, header: Id3v2Header) -> Result<Id3v2Tag>
|
fn read_all_frames_into_tag<R>(
|
||||||
|
reader: &mut R,
|
||||||
|
header: Id3v2Header,
|
||||||
|
parse_mode: ParsingMode,
|
||||||
|
) -> Result<Id3v2Tag>
|
||||||
where
|
where
|
||||||
R: Read,
|
R: Read,
|
||||||
{
|
{
|
||||||
|
@ -38,7 +47,7 @@ where
|
||||||
tag.set_flags(header.flags);
|
tag.set_flags(header.flags);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match Frame::read(reader, header.version)? {
|
match Frame::read(reader, header.version, parse_mode)? {
|
||||||
// No frame content found, and we can expect there are no more frames
|
// No frame content found, and we can expect there are no more frames
|
||||||
(None, true) => break,
|
(None, true) => break,
|
||||||
(Some(f), false) => drop(tag.insert(f)),
|
(Some(f), false) => drop(tag.insert(f)),
|
||||||
|
@ -53,9 +62,10 @@ where
|
||||||
#[test]
|
#[test]
|
||||||
fn zero_size_id3v2() {
|
fn zero_size_id3v2() {
|
||||||
use crate::id3::v2::read_id3v2_header;
|
use crate::id3::v2::read_id3v2_header;
|
||||||
|
use crate::ParsingMode;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
let mut f = Cursor::new(std::fs::read("tests/tags/assets/id3v2/zero.id3v2").unwrap());
|
let mut f = Cursor::new(std::fs::read("tests/tags/assets/id3v2/zero.id3v2").unwrap());
|
||||||
let header = read_id3v2_header(&mut f).unwrap();
|
let header = read_id3v2_header(&mut f).unwrap();
|
||||||
assert!(parse_id3v2(&mut f, header).is_ok());
|
assert!(parse_id3v2(&mut f, header, ParsingMode::Strict).is_ok());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1068,6 +1068,7 @@ impl<'a, I: Iterator<Item = FrameRef<'a>> + Clone + 'a> Id3v2TagRef<'a, I> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::ParsingMode;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::id3::v2::frame::MUSICBRAINZ_UFID_OWNER;
|
use crate::id3::v2::frame::MUSICBRAINZ_UFID_OWNER;
|
||||||
|
@ -1095,7 +1096,7 @@ mod tests {
|
||||||
let mut reader = std::io::Cursor::new(&tag_bytes[..]);
|
let mut reader = std::io::Cursor::new(&tag_bytes[..]);
|
||||||
|
|
||||||
let header = read_id3v2_header(&mut reader).unwrap();
|
let header = read_id3v2_header(&mut reader).unwrap();
|
||||||
crate::id3::v2::read::parse_id3v2(&mut reader, header).unwrap()
|
crate::id3::v2::read::parse_id3v2(&mut reader, header, ParsingMode::Strict).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1206,7 +1207,9 @@ mod tests {
|
||||||
let temp_reader = &mut &*writer;
|
let temp_reader = &mut &*writer;
|
||||||
|
|
||||||
let temp_header = read_id3v2_header(temp_reader).unwrap();
|
let temp_header = read_id3v2_header(temp_reader).unwrap();
|
||||||
let temp_parsed_tag = crate::id3::v2::read::parse_id3v2(temp_reader, temp_header).unwrap();
|
let temp_parsed_tag =
|
||||||
|
crate::id3::v2::read::parse_id3v2(temp_reader, temp_header, ParsingMode::Strict)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(parsed_tag, temp_parsed_tag);
|
assert_eq!(parsed_tag, temp_parsed_tag);
|
||||||
}
|
}
|
||||||
|
@ -1456,7 +1459,7 @@ mod tests {
|
||||||
let mut reader = &mut &writer[..];
|
let mut reader = &mut &writer[..];
|
||||||
|
|
||||||
let header = read_id3v2_header(&mut reader).unwrap();
|
let header = read_id3v2_header(&mut reader).unwrap();
|
||||||
assert!(crate::id3::v2::read::parse_id3v2(reader, header).is_ok());
|
assert!(crate::id3::v2::read::parse_id3v2(reader, header, ParsingMode::Strict).is_ok());
|
||||||
|
|
||||||
assert_eq!(writer[3..10], writer[writer.len() - 7..])
|
assert_eq!(writer[3..10], writer[writer.len() - 7..])
|
||||||
}
|
}
|
||||||
|
@ -1481,7 +1484,7 @@ mod tests {
|
||||||
let mut reader = &mut &writer[..];
|
let mut reader = &mut &writer[..];
|
||||||
|
|
||||||
let header = read_id3v2_header(&mut reader).unwrap();
|
let header = read_id3v2_header(&mut reader).unwrap();
|
||||||
let tag = crate::id3::v2::read::parse_id3v2(reader, header).unwrap();
|
let tag = crate::id3::v2::read::parse_id3v2(reader, header, ParsingMode::Strict).unwrap();
|
||||||
|
|
||||||
assert_eq!(tag.len(), 1);
|
assert_eq!(tag.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1798,7 +1801,8 @@ mod tests {
|
||||||
let mut reader = std::io::Cursor::new(&content[..]);
|
let mut reader = std::io::Cursor::new(&content[..]);
|
||||||
|
|
||||||
let header = read_id3v2_header(&mut reader).unwrap();
|
let header = read_id3v2_header(&mut reader).unwrap();
|
||||||
let reparsed = crate::id3::v2::read::parse_id3v2(&mut reader, header).unwrap();
|
let reparsed =
|
||||||
|
crate::id3::v2::read::parse_id3v2(&mut reader, header, ParsingMode::Strict).unwrap();
|
||||||
|
|
||||||
assert_eq!(id3v2, reparsed);
|
assert_eq!(id3v2, reparsed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ where
|
||||||
while chunks.next(data).is_ok() {
|
while chunks.next(data).is_ok() {
|
||||||
match &chunks.fourcc {
|
match &chunks.fourcc {
|
||||||
b"ID3 " | b"id3 " => {
|
b"ID3 " | b"id3 " => {
|
||||||
let tag = chunks.id3_chunk(data)?;
|
let tag = chunks.id3_chunk(data, parse_options.parsing_mode)?;
|
||||||
if let Some(existing_tag) = id3v2_tag.as_mut() {
|
if let Some(existing_tag) = id3v2_tag.as_mut() {
|
||||||
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
||||||
// Duplicate tags should have their frames appended to the previous
|
// Duplicate tags should have their frames appended to the previous
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::id3::v2::tag::Id3v2Tag;
|
use crate::id3::v2::tag::Id3v2Tag;
|
||||||
use crate::macros::{err, try_vec};
|
use crate::macros::{err, try_vec};
|
||||||
|
use crate::probe::ParsingMode;
|
||||||
|
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -91,7 +92,7 @@ impl<B: ByteOrder> Chunks<B> {
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id3_chunk<R>(&mut self, data: &mut R) -> Result<Id3v2Tag>
|
pub fn id3_chunk<R>(&mut self, data: &mut R, parse_mode: ParsingMode) -> Result<Id3v2Tag>
|
||||||
where
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
|
@ -103,7 +104,7 @@ impl<B: ByteOrder> Chunks<B> {
|
||||||
let reader = &mut &*content;
|
let reader = &mut &*content;
|
||||||
|
|
||||||
let header = read_id3v2_header(reader)?;
|
let header = read_id3v2_header(reader)?;
|
||||||
let id3v2 = parse_id3v2(reader, header)?;
|
let id3v2 = parse_id3v2(reader, header, parse_mode)?;
|
||||||
|
|
||||||
// Skip over the footer
|
// Skip over the footer
|
||||||
if id3v2.flags().footer {
|
if id3v2.flags().footer {
|
||||||
|
|
|
@ -88,7 +88,7 @@ where
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
b"ID3 " | b"id3 " => {
|
b"ID3 " | b"id3 " => {
|
||||||
let tag = chunks.id3_chunk(data)?;
|
let tag = chunks.id3_chunk(data, parse_options.parsing_mode)?;
|
||||||
if let Some(existing_tag) = id3v2_tag.as_mut() {
|
if let Some(existing_tag) = id3v2_tag.as_mut() {
|
||||||
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
||||||
// Duplicate tags should have their frames appended to the previous
|
// Duplicate tags should have their frames appended to the previous
|
||||||
|
|
|
@ -41,7 +41,7 @@ where
|
||||||
let header = read_id3v2_header(reader)?;
|
let header = read_id3v2_header(reader)?;
|
||||||
let skip_footer = header.flags.footer;
|
let skip_footer = header.flags.footer;
|
||||||
|
|
||||||
let id3v2 = parse_id3v2(reader, header)?;
|
let id3v2 = parse_id3v2(reader, header, parse_options.parsing_mode)?;
|
||||||
if let Some(existing_tag) = &mut file.id3v2_tag {
|
if let Some(existing_tag) = &mut file.id3v2_tag {
|
||||||
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
// https://github.com/Serial-ATA/lofty-rs/issues/87
|
||||||
// Duplicate tags should have their frames appended to the previous
|
// Duplicate tags should have their frames appended to the previous
|
||||||
|
|
Loading…
Add table
Reference in a new issue