Skip to content

Commit

Permalink
VorbisComments: Support combined TRACKNUMBER fields
Browse files Browse the repository at this point in the history
Support <current>/<total> and vinyl track number (such as A2, b5) in
TRACKNUMBER field
Closes: #499
  • Loading branch information
RocketRide9 committed Jan 11, 2025
1 parent af0ce91 commit 4a18f79
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 0 deletions.
62 changes: 62 additions & 0 deletions lofty/src/ogg/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::config::{ParseOptions, ParsingMode};
use crate::error::{ErrorKind, LoftyError, Result};
use crate::macros::{decode_err, err, parse_mode_choice};
use crate::picture::{MimeType, Picture, PictureInformation, PictureType};
use crate::tag::Accessor;
use crate::util::text::{utf16_decode, utf8_decode, utf8_decode_str};

use std::borrow::Cow;
Expand Down Expand Up @@ -167,6 +168,67 @@ where
},
}
},
// Support the case of TRACKNUMBER being equal to current/total
// and vinyl track numbers (a3, B5, etc)
k if k.eq_ignore_ascii_case(b"TRACKNUMBER") => {
match utf8_decode_str(value) {
Ok(value) => {
let mut value_chars = value.chars();
let first = value_chars.next();
if first.is_none() {
// short-circuit if value is empty
// to avoid too much nesting
tag.items.push(
(String::from("TRACKNUMBER"), value.to_owned())
);
break;
}
let first = first.unwrap().to_ascii_lowercase();
if first.is_ascii_alphabetic() {
// try to parse as vinyl tracknumber
let disk: u8 = first as u8 - b'a' + 1;
let track: &str = value_chars.as_str();
let track: Option<u32> = track.parse().ok();

if let Some(n) = track {
tag.set_track(n);
} else {
tag.items.push(
(String::from("TRACKNUMBER"), value.to_owned())
);
}
tag.set_disk(u32::from(disk));

} else {
// try to parse as current/total
let mut value_split = value.splitn(2, '/');
let track_number: Option<u32> =
value_split.next().and_then(|b| b.parse().ok());
let track_total: Option<u32> =
value_split.next().and_then(|b| b.parse().ok());

if let Some(n) = track_number {
tag.set_track(n);
} else {
tag.items.push(
(String::from("TRACKNUMBER"), value.to_owned())
);
}
if let Some(n) = track_total {
tag.set_track_total(n);
}
}
},
Err(e) => {
if parse_mode == ParsingMode::Strict {
return Err(e);
}

log::warn!("Non UTF-8 value found, discarding field {key:?}");
continue;
},
}
},
// The valid range is 0x20..=0x7D not including 0x3D
k if k.iter().all(|c| (b' '..=b'}').contains(c) && *c != b'=') => {
// SAFETY: We just verified that all of the bytes fall within the subset of ASCII
Expand Down
Binary file added lofty/tests/files/assets/issue_499.opus
Binary file not shown.
Binary file added lofty/tests/files/assets/issue_499_2.opus
Binary file not shown.
28 changes: 28 additions & 0 deletions lofty/tests/files/ogg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,34 @@ fn flac_with_id3v2() {
assert!(flac_file.vorbis_comments().is_some());
}

// case TRACKNUMBER=11/22 (<current>/<total>)
#[test_log::test]
fn opus_issue_499() {
use lofty::ogg::OpusFile;

let file = std::fs::read("tests/files/assets/issue_499.opus").unwrap();
let opus_file =
OpusFile::read_from(&mut std::io::Cursor::new(file), ParseOptions::new()).unwrap();

let comments = opus_file.vorbis_comments();
assert_eq!(comments.track(), Some(11));
assert_eq!(comments.track_total(), Some(22));
}

// case TRACKNUMBER=a5 (<disc><number>)
#[test_log::test]
fn opus_issue_499_2() {
use lofty::ogg::OpusFile;

let file = std::fs::read("tests/files/assets/issue_499_2.opus").unwrap();
let opus_file =
OpusFile::read_from(&mut std::io::Cursor::new(file), ParseOptions::new()).unwrap();

let comments = opus_file.vorbis_comments();
assert_eq!(comments.track(), Some(5));
assert_eq!(comments.disk(), Some(1));
}

#[test_log::test]
fn flac_remove_id3v2() {
crate::remove_tag!("tests/files/assets/flac_with_id3v2.flac", TagType::Id3v2);
Expand Down

0 comments on commit 4a18f79

Please sign in to comment.