lrcli/src/music.rs
2026-01-29 15:33:44 +01:00

100 lines
2.4 KiB
Rust

use crate::json_parsing;
use audiotags::Tag;
use std::fs;
use std::path::Path;
use std::time::Duration;
use thiserror::Error;
#[derive(Debug)]
pub struct TrackInfo {
pub track_name: String,
pub artist_name: String,
pub album_name: String,
pub duration: Duration,
}
#[derive(Debug, Error)]
pub enum MusicError {
#[error("failed to read directory: {0}")]
ReadDir(#[from] std::io::Error),
#[error("failed to read audio tags from {0}")]
TagRead(String),
#[error("failed to get song duration for {0}")]
DurationRead(String),
}
fn is_mp3(path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext.eq_ignore_ascii_case("mp3"))
.unwrap_or(false)
}
pub fn get_song_file(path: &Path) -> Result<(), MusicError> {
if !is_mp3(path) {
return Ok(());
}
let info = get_song_info(path)?;
println!("Track: {}", info.track_name);
println!("Artist: {}", info.artist_name);
println!("Album: {}", info.album_name);
println!("Duration: {:?}\n", info.duration);
json_parsing::get_lyrics_text(
&info.artist_name,
&info.track_name,
&info.album_name,
info.duration,
);
Ok(())
}
pub fn get_all_music_files(directory: &Path) -> Result<(), MusicError> {
if !directory.is_dir() {
return Ok(());
}
for entry in fs::read_dir(directory)? {
let entry = entry?;
let path = entry.path();
if entry.file_type()?.is_dir() {
get_all_music_files(&path)?;
} else if let Err(e) = get_song_file(&path) {
eprintln!("Skipping file {}: {}", path.display(), e);
}
}
Ok(())
}
pub fn get_song_info(path: &Path) -> Result<TrackInfo, MusicError> {
let tag = Tag::new()
.read_from_path(path)
.map_err(|_| MusicError::TagRead(path.display().to_string()))?;
let track_name = tag.title().unwrap_or("Unknown Track").to_string();
let artist_name = tag.artist().unwrap_or("Unknown Artist").to_string();
let album_name = tag
.album()
.map_or("Unknown Album".to_string(), |a| a.title.to_string());
let duration = get_song_length(path)?;
Ok(TrackInfo {
track_name,
artist_name,
album_name,
duration,
})
}
pub fn get_song_length(path: &Path) -> Result<Duration, MusicError> {
mp3_duration::from_path(path).map_err(|_| MusicError::DurationRead(path.display().to_string()))
}