FLAC: Don't error by default on invalid picture types

FLAC picture blocks have `u32::MAX` possible picture types, but only 20 are actually defined. In the [spec] it states, "Others are reserved and should not be used."

We used to only allow a picture type of up to 255 to be in sync with ID3v2's APIC, where this model comes from. Now any picture type can be specified and it will just be clamped to 255, so long as `ParsingMode::Strict` is not being used.
This commit is contained in:
Serial 2023-09-30 12:40:09 -04:00 committed by Alex
parent 15d9ca812c
commit 9ae927ceb0
4 changed files with 33 additions and 13 deletions

View file

@ -24,10 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Ilst::remove` will now return all of the removed atoms - `Ilst::remove` will now return all of the removed atoms
- `Ilst::insert_picture` will now combine all pictures into a single `covr` atom - `Ilst::insert_picture` will now combine all pictures into a single `covr` atom
- `Ilst::insert` will now merge atoms with the same identifier into a single atom - `Ilst::insert` will now merge atoms with the same identifier into a single atom
- **FLAC**: Allow multiple Vorbis Comment blocks when not using `ParsingMode::Strict` - **FLAC**:
- This is not allowed [by spec](https://xiph.org/flac/format.html#def_VORBIS_COMMENT), but is still possible - Allow multiple Vorbis Comment blocks when not using `ParsingMode::Strict`
to encounter in the wild. Now we will just tag whichever tag happens to be latest in the stream and - This is not allowed [by spec](https://xiph.org/flac/format.html#def_VORBIS_COMMENT), but is still possible
use it, they **will not be merged**. to encounter in the wild. Now we will just tag whichever tag happens to be latest in the stream and
use it, they **will not be merged**.
- Allow picture types greater than 255 when not using `ParsingMode::Strict`
- This is not allowed [by spec](https://xiph.org/flac/format.html#metadata_block_picture), but has been encountered
in the wild. Now we will just cap the picture type at 255.
## Fixed ## Fixed
- **WavPack**: Custom sample rates will no longer be overwritten - **WavPack**: Custom sample rates will no longer be overwritten

View file

@ -100,9 +100,17 @@ where
} }
if block.ty == BLOCK_ID_PICTURE { if block.ty == BLOCK_ID_PICTURE {
flac_file match Picture::from_flac_bytes(&block.content, false, parse_options.parsing_mode) {
.pictures Ok(picture) => flac_file.pictures.push(picture),
.push(Picture::from_flac_bytes(&block.content, false)?) Err(e) => {
if parse_options.parsing_mode == ParsingMode::Strict {
return Err(e);
}
log::warn!("Unable to read FLAC picture block, discarding");
continue;
},
}
} }
} }

View file

@ -94,7 +94,7 @@ where
match key { match key {
k if k.eq_ignore_ascii_case(b"METADATA_BLOCK_PICTURE") => { k if k.eq_ignore_ascii_case(b"METADATA_BLOCK_PICTURE") => {
match Picture::from_flac_bytes(value, true) { match Picture::from_flac_bytes(value, true, parse_mode) {
Ok(picture) => tag.pictures.push(picture), Ok(picture) => tag.pictures.push(picture),
Err(e) => { Err(e) => {
if parse_mode == ParsingMode::Strict { if parse_mode == ParsingMode::Strict {

View file

@ -1,5 +1,6 @@
use crate::error::{ErrorKind, LoftyError, Result}; use crate::error::{ErrorKind, LoftyError, Result};
use crate::macros::err; use crate::macros::err;
use crate::probe::ParsingMode;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
@ -625,18 +626,25 @@ impl Picture {
/// ///
/// This function will return [`NotAPicture`][ErrorKind::NotAPicture] if /// This function will return [`NotAPicture`][ErrorKind::NotAPicture] if
/// at any point it's unable to parse the data /// at any point it's unable to parse the data
pub fn from_flac_bytes(bytes: &[u8], encoded: bool) -> Result<(Self, PictureInformation)> { pub fn from_flac_bytes(
bytes: &[u8],
encoded: bool,
parse_mode: ParsingMode,
) -> Result<(Self, PictureInformation)> {
if encoded { if encoded {
let data = base64::engine::general_purpose::STANDARD let data = base64::engine::general_purpose::STANDARD
.decode(bytes) .decode(bytes)
.map_err(|_| LoftyError::new(ErrorKind::NotAPicture))?; .map_err(|_| LoftyError::new(ErrorKind::NotAPicture))?;
Self::from_flac_bytes_inner(&data) Self::from_flac_bytes_inner(&data, parse_mode)
} else { } else {
Self::from_flac_bytes_inner(bytes) Self::from_flac_bytes_inner(bytes, parse_mode)
} }
} }
fn from_flac_bytes_inner(content: &[u8]) -> Result<(Self, PictureInformation)> { fn from_flac_bytes_inner(
content: &[u8],
parse_mode: ParsingMode,
) -> Result<(Self, PictureInformation)> {
use crate::macros::try_vec; use crate::macros::try_vec;
let mut size = content.len(); let mut size = content.len();
@ -652,7 +660,7 @@ impl Picture {
// ID3v2 APIC uses a single byte for picture type. // ID3v2 APIC uses a single byte for picture type.
// Anything greater than that is probably invalid, so // Anything greater than that is probably invalid, so
// we just stop early // we just stop early
if pic_ty > 255 { if pic_ty > 255 && parse_mode == ParsingMode::Strict {
err!(NotAPicture); err!(NotAPicture);
} }