Skip to content

Commit 5eb032a

Browse files
committed
FLAC: Do not error on multiple VorbisComments when not strict
This is not allowed [by spec](https://xiph.org/flac/format.html#def_VORBIS_COMMENT), but is still possible 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** like other formats (such as ID3v2 in MP3) where multiple tags are valid.
1 parent 3755fbf commit 5eb032a

File tree

5 files changed

+49
-2
lines changed

5 files changed

+49
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- `Ilst::remove` will now return all of the removed atoms
2424
- `Ilst::insert_picture` will now combine all pictures into a single `covr` atom
2525
- `Ilst::insert` will now merge atoms with the same identifier into a single atom
26+
- **FLAC**: Allow multiple Vorbis Comment blocks when not using `ParsingMode::Strict`
27+
- This is not allowed [by spec](https://xiph.org/flac/format.html#def_VORBIS_COMMENT), but is still possible
28+
to encounter in the wild. Now we will just tag whichever tag happens to be latest in the stream and
29+
use it, they **will not be merged**.
2630

2731
## [0.15.0] - 2023-07-11
2832

src/flac/read.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::id3::{find_id3v2, ID3FindResults};
1111
use crate::macros::decode_err;
1212
use crate::ogg::read::read_comments;
1313
use crate::picture::Picture;
14-
use crate::probe::ParseOptions;
14+
use crate::probe::{ParseOptions, ParsingMode};
1515

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

@@ -74,7 +74,18 @@ where
7474
}
7575

7676
if block.ty == BLOCK_ID_VORBIS_COMMENTS {
77-
if flac_file.vorbis_comments_tag.is_some() {
77+
// NOTE: According to the spec
78+
//
79+
// <https://xiph.org/flac/format.html#def_VORBIS_COMMENT>:
80+
// "There may be only one VORBIS_COMMENT block in a stream."
81+
//
82+
// But of course, we can't ever expect any spec compliant inputs, so we just
83+
// take whatever happens to be the latest block in the stream. This is safe behavior,
84+
// as when writing to a file with multiple tags, we end up removing all `VORBIS_COMMENT`
85+
// blocks anyway.
86+
if flac_file.vorbis_comments_tag.is_some()
87+
&& parse_options.parsing_mode == ParsingMode::Strict
88+
{
7889
decode_err!(@BAIL Flac, "Streams are only allowed one Vorbis Comments block per stream");
7990
}
8091

4.64 KB
Binary file not shown.

tests/files/flac.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use lofty::flac::FlacFile;
2+
use lofty::{Accessor, AudioFile, ParseOptions, ParsingMode};
3+
4+
use std::fs::File;
5+
use std::io::Seek;
6+
7+
#[test]
8+
fn multiple_vorbis_comments() {
9+
let mut file = File::open("tests/files/assets/two_vorbis_comments.flac").unwrap();
10+
11+
// Reading a file with multiple VORBIS_COMMENT blocks should error when using `Strict`, as it is
12+
// not allowed by spec.
13+
assert!(FlacFile::read_from(
14+
&mut file,
15+
ParseOptions::new()
16+
.read_properties(false)
17+
.parsing_mode(ParsingMode::Strict)
18+
)
19+
.is_err());
20+
21+
file.rewind().unwrap();
22+
23+
// But by default, we should just take the last tag in the stream
24+
let f = FlacFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap();
25+
26+
// The first tag has the artist "Artist 1", the second has "Artist 2".
27+
assert_eq!(
28+
f.vorbis_comments().unwrap().artist().as_deref(),
29+
Some("Artist 2")
30+
);
31+
}

tests/files/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod aac;
22
mod aiff;
33
mod ape;
4+
mod flac;
45
mod mp4;
56
mod mpc;
67
mod mpeg;

0 commit comments

Comments
 (0)