mod analyser;
mod error;
mod frames;
mod id3v1;
mod id3v2;
mod utils;
pub use analyser::Analyser;
pub use error::{Error, Result};
pub use frames::types::AudioFrame;
pub use id3v1::ID3V1Tag;
pub use id3v2::ID3V2Tag;
#[cfg(test)]
mod tests {
use crate::{
analyser::Analyser,
error::{Error, Result},
frames::utils::{compute_frame_index, get_frame_duration_from_header, get_offset_for_time},
};
use std::time::{Duration, Instant};
use tokio::{fs::File, io::BufReader, join};
async fn get_analyser(path: &str) -> Result<Analyser> {
let file = match File::open(path).await {
Ok(file) => file,
Err(_) => return Err(Error::Io),
};
let reader = BufReader::new(file);
let analyser = Analyser::new(reader);
Ok(analyser)
}
#[tokio::test]
async fn get_cbr320_audio_frames() {
let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn check_cbr320_xing_header() {
let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await.unwrap();
let xing = audio_frames
.iter()
.find(|frame| frame.xing_header.is_some())
.unwrap()
.xing_header
.as_ref()
.unwrap();
assert!(audio_frames.len().abs_diff(xing.frames.unwrap() as usize) < 10);
}
#[tokio::test]
async fn get_apev2_audio_frames() {
let analyser = get_analyser("test/APEv2.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_id3v1_audio_frames() {
let analyser = get_analyser("test/ID3v1.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_id3v2_audio_frames() {
let analyser = get_analyser("test/ID3v2.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_id3v2_with_bad_padding_audio_frames() {
let analyser = get_analyser("test/ID3v2WithBadPadding.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_id3v2_with_image_audio_frames() {
let analyser = get_analyser("test/ID3v2WithImage.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_sine_empty_id3_audio_frames() {
let analyser = get_analyser("test/SineEmptyID3.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_source_audio_frames() {
let analyser = get_analyser("test/source.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_truncated_audio_frames() {
let analyser = get_analyser("test/Truncated.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_vbr9_audio_frames() {
let analyser = get_analyser("test/VBR9.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn get_vbr0_audio_frames() {
let analyser = get_analyser("test/VBR0.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await;
assert!(audio_frames.is_ok());
assert!(audio_frames.unwrap().len() > 0);
}
#[tokio::test]
async fn compare_frame_count() {
let source_analyser = get_analyser("test/source.mp3").await.unwrap();
let id3v1_analyser = get_analyser("test/ID3v1.mp3").await.unwrap();
let id3v2_analyser = get_analyser("test/ID3v2.mp3").await.unwrap();
let cbr320_analyser = get_analyser("test/CBR320.mp3").await.unwrap();
let results = join!(
source_analyser.get_audio_frames(),
id3v1_analyser.get_audio_frames(),
id3v2_analyser.get_audio_frames(),
cbr320_analyser.get_audio_frames()
);
let source_audio_frames = results.0.unwrap();
let id3v1_audio_frames = results.1.unwrap();
let id3v2_audio_frames = results.2.unwrap();
let cbr320_audio_frames = results.3.unwrap();
assert!(source_audio_frames.len().abs_diff(id3v1_audio_frames.len()) < 5);
assert!(source_audio_frames.len().abs_diff(id3v2_audio_frames.len()) < 5);
assert!(
source_audio_frames
.len()
.abs_diff(cbr320_audio_frames.len())
< 5
);
}
#[tokio::test]
async fn get_xing_duration() {
let cbr320_analyser = get_analyser("test/CBR320.mp3").await.unwrap();
let source_analyser = get_analyser("test/source.mp3").await.unwrap();
let cbr320_start = Instant::now();
let cbr320_duration = cbr320_analyser.get_duration().await.unwrap();
let cbr320_elapsed_time = cbr320_start.elapsed();
let source_start = Instant::now();
let source_duration = source_analyser.get_duration().await.unwrap();
let source_elapsed_time = source_start.elapsed();
assert!(cbr320_elapsed_time < source_elapsed_time);
assert!(
cbr320_duration
.as_secs()
.abs_diff(source_duration.as_secs())
< 1
);
}
#[tokio::test]
async fn test_frame_index() {
let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
let audio_frames = analyser.get_audio_frames().await.unwrap();
let frame_index = compute_frame_index(&audio_frames);
let mut time_1 = Duration::from_secs(0);
let mut time_2 = Duration::from_secs(1);
let mut time_3 = Duration::from_secs(2);
let mut offset_1 = 0;
let mut offset_2 = 0;
let mut offset_3 = 0;
let mut time = Duration::from_secs(0);
let mut offset = 0;
for (index, frame) in audio_frames.iter().enumerate() {
if index == 0 {
time_1 = time;
offset_1 = offset;
}
if index == 10 {
time_2 = time;
offset_2 = offset;
}
if index == 65 {
time_3 = time;
offset_3 = offset;
}
time += get_frame_duration_from_header(&frame.header);
offset = frame.offset;
if index > 65 {
break;
}
}
let frame_offset_1 = get_offset_for_time(&frame_index, time_1);
let frame_offset_2 = get_offset_for_time(&frame_index, time_2);
let frame_offset_3 = get_offset_for_time(&frame_index, time_3);
assert!(frame_offset_1 == offset_1);
assert!(frame_offset_2 == offset_2);
assert!(frame_offset_3 == offset_3);
}
}