vapor_parser/
lib.rs

1mod analyser;
2mod error;
3mod frames;
4mod id3v1;
5mod id3v2;
6mod utils;
7
8pub use analyser::Analyser;
9pub use error::{Error, Result};
10pub use frames::types::AudioFrame;
11pub use id3v1::ID3V1Tag;
12pub use id3v2::ID3V2Tag;
13
14#[cfg(test)]
15mod tests {
16    use crate::{
17        analyser::Analyser,
18        error::{Error, Result},
19        frames::utils::{compute_frame_index, get_frame_duration_from_header, get_offset_for_time},
20    };
21    use std::time::{Duration, Instant};
22    use tokio::{fs::File, io::BufReader, join};
23
24    async fn get_analyser(path: &str) -> Result<Analyser> {
25        let file = match File::open(path).await {
26            Ok(file) => file,
27            Err(_) => return Err(Error::Io),
28        };
29
30        let reader = BufReader::new(file);
31        let analyser = Analyser::new(reader);
32
33        Ok(analyser)
34    }
35
36    #[tokio::test]
37    async fn get_cbr320_audio_frames() {
38        let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
39        let audio_frames = analyser.get_audio_frames().await;
40        assert!(audio_frames.is_ok());
41        assert!(audio_frames.unwrap().len() > 0);
42    }
43
44    #[tokio::test]
45    async fn check_cbr320_xing_header() {
46        let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
47        let audio_frames = analyser.get_audio_frames().await.unwrap();
48        let xing = audio_frames
49            .iter()
50            .find(|frame| frame.xing_header.is_some())
51            .unwrap()
52            .xing_header
53            .as_ref()
54            .unwrap();
55
56        assert!(audio_frames.len().abs_diff(xing.frames.unwrap() as usize) < 10);
57    }
58
59    #[tokio::test]
60    async fn get_apev2_audio_frames() {
61        let analyser = get_analyser("test/APEv2.mp3").await.unwrap();
62        let audio_frames = analyser.get_audio_frames().await;
63        assert!(audio_frames.is_ok());
64        assert!(audio_frames.unwrap().len() > 0);
65    }
66
67    #[tokio::test]
68    async fn get_id3v1_audio_frames() {
69        let analyser = get_analyser("test/ID3v1.mp3").await.unwrap();
70        let audio_frames = analyser.get_audio_frames().await;
71        assert!(audio_frames.is_ok());
72        assert!(audio_frames.unwrap().len() > 0);
73    }
74
75    #[tokio::test]
76    async fn get_id3v2_audio_frames() {
77        let analyser = get_analyser("test/ID3v2.mp3").await.unwrap();
78        let audio_frames = analyser.get_audio_frames().await;
79        assert!(audio_frames.is_ok());
80        assert!(audio_frames.unwrap().len() > 0);
81    }
82
83    #[tokio::test]
84    async fn get_id3v2_with_bad_padding_audio_frames() {
85        let analyser = get_analyser("test/ID3v2WithBadPadding.mp3").await.unwrap();
86        let audio_frames = analyser.get_audio_frames().await;
87        assert!(audio_frames.is_ok());
88        assert!(audio_frames.unwrap().len() > 0);
89    }
90
91    #[tokio::test]
92    async fn get_id3v2_with_image_audio_frames() {
93        let analyser = get_analyser("test/ID3v2WithImage.mp3").await.unwrap();
94        let audio_frames = analyser.get_audio_frames().await;
95        assert!(audio_frames.is_ok());
96        assert!(audio_frames.unwrap().len() > 0);
97    }
98
99    #[tokio::test]
100    async fn get_sine_empty_id3_audio_frames() {
101        let analyser = get_analyser("test/SineEmptyID3.mp3").await.unwrap();
102        let audio_frames = analyser.get_audio_frames().await;
103        assert!(audio_frames.is_ok());
104        assert!(audio_frames.unwrap().len() > 0);
105    }
106
107    #[tokio::test]
108    async fn get_source_audio_frames() {
109        let analyser = get_analyser("test/source.mp3").await.unwrap();
110        let audio_frames = analyser.get_audio_frames().await;
111        assert!(audio_frames.is_ok());
112        assert!(audio_frames.unwrap().len() > 0);
113    }
114
115    #[tokio::test]
116    async fn get_truncated_audio_frames() {
117        let analyser = get_analyser("test/Truncated.mp3").await.unwrap();
118        let audio_frames = analyser.get_audio_frames().await;
119        assert!(audio_frames.is_ok());
120        assert!(audio_frames.unwrap().len() > 0);
121    }
122
123    #[tokio::test]
124    async fn get_vbr9_audio_frames() {
125        let analyser = get_analyser("test/VBR9.mp3").await.unwrap();
126        let audio_frames = analyser.get_audio_frames().await;
127        assert!(audio_frames.is_ok());
128        assert!(audio_frames.unwrap().len() > 0);
129    }
130
131    #[tokio::test]
132    async fn get_vbr0_audio_frames() {
133        let analyser = get_analyser("test/VBR0.mp3").await.unwrap();
134        let audio_frames = analyser.get_audio_frames().await;
135        assert!(audio_frames.is_ok());
136        assert!(audio_frames.unwrap().len() > 0);
137    }
138
139    #[tokio::test]
140    async fn compare_frame_count() {
141        let source_analyser = get_analyser("test/source.mp3").await.unwrap();
142        let id3v1_analyser = get_analyser("test/ID3v1.mp3").await.unwrap();
143        let id3v2_analyser = get_analyser("test/ID3v2.mp3").await.unwrap();
144        let cbr320_analyser = get_analyser("test/CBR320.mp3").await.unwrap();
145
146        let results = join!(
147            source_analyser.get_audio_frames(),
148            id3v1_analyser.get_audio_frames(),
149            id3v2_analyser.get_audio_frames(),
150            cbr320_analyser.get_audio_frames()
151        );
152
153        let source_audio_frames = results.0.unwrap();
154        let id3v1_audio_frames = results.1.unwrap();
155        let id3v2_audio_frames = results.2.unwrap();
156        let cbr320_audio_frames = results.3.unwrap();
157
158        assert!(source_audio_frames.len().abs_diff(id3v1_audio_frames.len()) < 5);
159        assert!(source_audio_frames.len().abs_diff(id3v2_audio_frames.len()) < 5);
160        assert!(
161            source_audio_frames
162                .len()
163                .abs_diff(cbr320_audio_frames.len())
164                < 5
165        );
166    }
167
168    #[tokio::test]
169    async fn get_xing_duration() {
170        let cbr320_analyser = get_analyser("test/CBR320.mp3").await.unwrap();
171        let source_analyser = get_analyser("test/source.mp3").await.unwrap();
172
173        let cbr320_start = Instant::now();
174        let cbr320_duration = cbr320_analyser.get_duration().await.unwrap();
175        let cbr320_elapsed_time = cbr320_start.elapsed();
176
177        let source_start = Instant::now();
178        let source_duration = source_analyser.get_duration().await.unwrap();
179        let source_elapsed_time = source_start.elapsed();
180
181        assert!(cbr320_elapsed_time < source_elapsed_time);
182        assert!(
183            cbr320_duration
184                .as_secs()
185                .abs_diff(source_duration.as_secs())
186                < 1
187        );
188    }
189
190    #[tokio::test]
191    async fn test_frame_index() {
192        let analyser = get_analyser("test/CBR320.mp3").await.unwrap();
193        let audio_frames = analyser.get_audio_frames().await.unwrap();
194        let frame_index = compute_frame_index(&audio_frames);
195
196        let mut time_1 = Duration::from_secs(0);
197        let mut time_2 = Duration::from_secs(1);
198        let mut time_3 = Duration::from_secs(2);
199
200        let mut offset_1 = 0;
201        let mut offset_2 = 0;
202        let mut offset_3 = 0;
203
204        let mut time = Duration::from_secs(0);
205        let mut offset = 0;
206        for (index, frame) in audio_frames.iter().enumerate() {
207            if index == 0 {
208                time_1 = time;
209                offset_1 = offset;
210            }
211
212            if index == 10 {
213                time_2 = time;
214                offset_2 = offset;
215            }
216
217            if index == 65 {
218                time_3 = time;
219                offset_3 = offset;
220            }
221
222            time += get_frame_duration_from_header(&frame.header);
223            offset = frame.offset;
224
225            if index > 65 {
226                break;
227            }
228        }
229
230        let frame_offset_1 = get_offset_for_time(&frame_index, time_1);
231        let frame_offset_2 = get_offset_for_time(&frame_index, time_2);
232        let frame_offset_3 = get_offset_for_time(&frame_index, time_3);
233
234        assert!(frame_offset_1 == offset_1);
235        assert!(frame_offset_2 == offset_2);
236        assert!(frame_offset_3 == offset_3);
237    }
238}