Compare commits

..

1 Commits

Author SHA1 Message Date
b73ffce048 tsk-50: Create functionality to extract song properties (#52)
All checks were successful
Rust Build / Check (push) Successful in 32s
Release Tagging / release (push) Successful in 36s
Rust Build / Test Suite (push) Successful in 33s
Rust Build / Clippy (push) Successful in 43s
Rust Build / Rustfmt (push) Successful in 48s
Rust Build / build (push) Successful in 36s
Closes #50

Reviewed-on: #52
Co-authored-by: phoenix <kundeng00@pm.me>
Co-committed-by: phoenix <kundeng00@pm.me>
2025-11-06 17:31:37 +00:00
5 changed files with 137 additions and 26 deletions

2
Cargo.lock generated
View File

@@ -104,7 +104,7 @@ dependencies = [
[[package]]
name = "icarus_meta"
version = "0.4.4"
version = "0.4.5"
dependencies = [
"imghdr",
"infer",

View File

@@ -1,6 +1,6 @@
[package]
name = "icarus_meta"
version = "0.4.4"
version = "0.4.5"
edition = "2024"
rust-version = "1.90"

View File

@@ -19,10 +19,7 @@ pub mod test_util {
Err(err) => Err(err),
}
}
pub fn get_full_path(
directory: &String,
filename: &String,
) -> Result<String, std::io::Error> {
pub fn get_full_path(directory: &str, filename: &str) -> Result<String, std::io::Error> {
match path_buf(directory, filename) {
Ok(pf) => Ok(pf.display().to_string()),
Err(err) => Err(err),
@@ -39,6 +36,12 @@ pub mod test_util {
std::fs::copy(src_path, dest_path)
}
pub fn remove_file(filepath: &str) -> Result<(), std::io::Error> {
let f_path = std::path::Path::new(filepath);
std::fs::remove_file(f_path)
}
pub fn get_data_from_file(source_path: &str) -> Result<Vec<u8>, std::io::Error> {
match std::fs::File::open(source_path) {
Ok(mut file) => {
@@ -52,13 +55,23 @@ pub mod test_util {
}
}
pub fn file_exists(directory: &String, filename: &String) -> Result<bool, std::io::Error> {
pub fn file_exists(directory: &str, filename: &str) -> Result<bool, std::io::Error> {
match path_buf(directory, filename) {
Ok(pf) => Ok(pf.exists()),
Err(err) => Err(err),
}
}
pub fn generate_newfilepath(directory: &str) -> Result<String, std::io::Error> {
match generate_filename() {
Ok(filename) => match get_full_path(directory, &filename) {
Ok(filepath) => Ok(filepath),
Err(err) => Err(err),
},
Err(err) => Err(err),
}
}
pub fn generate_filename() -> Result<String, std::io::Error> {
let mut filename = String::from("track-");
let length = 20;
@@ -78,10 +91,7 @@ pub mod test_util {
Ok(format!("{filename}.flac"))
}
fn path_buf(
directory: &String,
filename: &String,
) -> Result<std::path::PathBuf, std::io::Error> {
fn path_buf(directory: &str, filename: &str) -> Result<std::path::PathBuf, std::io::Error> {
let dir_path = std::path::Path::new(&directory);
Ok(dir_path.join(filename))
}

View File

@@ -1,14 +1,11 @@
use lofty::file::AudioFile;
pub fn get_duration(song_path: &String) -> Result<std::time::Duration, std::io::Error> {
match std::fs::File::open(song_path) {
pub fn get_properties(songpath: &str) -> Result<lofty::flac::FlacProperties, std::io::Error> {
match std::fs::File::open(songpath) {
Ok(mut content) => {
match lofty::flac::FlacFile::read_from(&mut content, lofty::config::ParseOptions::new())
{
Ok(flac_file) => {
let properties = flac_file.properties();
Ok(properties.duration())
}
Ok(flac_file) => Ok(*flac_file.properties()),
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}

View File

@@ -1,4 +1,35 @@
pub mod properties;
pub mod audio;
#[derive(Clone, Debug, Default)]
pub struct SongProperties {
pub duration: std::time::Duration,
pub sample_rate: u32,
pub bitrate: u32,
pub overall_bitrate: u32,
pub bit_depth: u8,
pub channels: u8,
}
pub fn get_song_properties(song_path: &str) -> Result<SongProperties, std::io::Error> {
match audio::get_properties(song_path) {
Ok(flac_properties) => Ok(SongProperties {
duration: flac_properties.duration(),
sample_rate: flac_properties.sample_rate(),
bitrate: flac_properties.audio_bitrate(),
overall_bitrate: flac_properties.overall_bitrate(),
bit_depth: flac_properties.bit_depth(),
channels: flac_properties.channels(),
}),
Err(err) => Err(err),
}
}
pub fn get_duration(song_path: &str) -> Result<std::time::Duration, std::io::Error> {
match get_song_properties(song_path) {
Ok(song_properties) => Ok(song_properties.duration),
Err(err) => Err(err),
}
}
#[cfg(test)]
mod tests {
@@ -12,16 +43,89 @@ mod tests {
match test_util::util::file_exists(&dir, &filename) {
Ok(_) => {
let filepath = test_util::util::get_full_path(&dir, &filename).unwrap();
match super::properties::get_duration(&filepath) {
Ok(duration) => {
let song_duration: u64 = 41;
let fetched_song_duration = duration.as_secs();
let new_filepath = test_util::util::generate_newfilepath(&dir).unwrap();
assert_eq!(
song_duration, fetched_song_duration,
"Durations should match, but they don't {song_duration} {fetched_song_duration} ({duration:?})"
);
match test_util::util::copy_file(&filepath, &new_filepath) {
Ok(_) => match super::get_duration(&new_filepath) {
Ok(duration) => {
test_util::util::remove_file(&new_filepath).unwrap();
let song_duration: u64 = 41;
let fetched_song_duration = duration.as_secs();
assert_eq!(
song_duration, fetched_song_duration,
"Durations should match, but they don't {song_duration} {fetched_song_duration} ({duration:?})"
);
}
Err(err) => {
assert!(false, "Error: {err:?}");
}
},
Err(err) => {
assert!(false, "Error: {err:?}");
}
}
}
Err(err) => {
assert!(false, "Error: {err:?}");
}
}
}
#[test]
fn test_song_properties() {
let filename = test_util::util::get_filename(1);
let dir = String::from(test_util::util::TESTFILEDIRECTORY);
match test_util::util::file_exists(&dir, &filename) {
Ok(_) => {
let filepath = test_util::util::get_full_path(&dir, &filename).unwrap();
let new_filepath = test_util::util::generate_newfilepath(&dir).unwrap();
match test_util::util::copy_file(&filepath, &new_filepath) {
Ok(_) => match super::get_song_properties(&new_filepath) {
Ok(song_properties) => {
test_util::util::remove_file(&new_filepath).unwrap();
let song_duration: u64 = 41;
let bitrate: u32 = 1;
let overall_bitrate: u32 = 3;
let bit_depth: u8 = 24;
let channels: u8 = 2;
let fetched_song_duration = song_properties.duration.as_secs();
let fetched_bitrate = song_properties.bitrate;
let fetched_overall_bitrate = song_properties.overall_bitrate;
let fetched_bit_depth = song_properties.bit_depth;
let fetched_channels = song_properties.channels;
assert_eq!(
song_duration, fetched_song_duration,
"Durations should match, but they don't {song_duration} {fetched_song_duration} ({song_properties:?})"
);
assert_eq!(
bitrate, fetched_bitrate,
"Bitrates do not match {bitrate:?} {fetched_bitrate:?} {song_properties:?}"
);
assert_eq!(
overall_bitrate, fetched_overall_bitrate,
"Overall bitrates do not match {overall_bitrate:?} {fetched_overall_bitrate:?} {song_properties:?}"
);
assert_eq!(
bit_depth, fetched_bit_depth,
"Bit depth do not match {bit_depth:?} {fetched_bit_depth:?} {song_properties:?}"
);
assert_eq!(
channels, fetched_channels,
"Channels do not match {channels:?} {fetched_channels:?} {song_properties:?}"
);
}
Err(err) => {
assert!(false, "Error: {err:?}");
}
},
Err(err) => {
assert!(false, "Error: {err:?}");
}