From 87508b69084b85a8d194de7378373b22f602ebe7 Mon Sep 17 00:00:00 2001 From: shinya Date: Fri, 30 Jan 2026 17:20:53 +0100 Subject: [PATCH] updated functions and refactor --- Cargo.toml | 2 +- src/cli.rs | 1 - src/json_parsing.rs | 68 ++++++++++++++++++++++++++++++++++++++------- src/main.rs | 27 +++++++++++++++--- src/music.rs | 29 +++++++++++++++++-- 5 files changed, 108 insertions(+), 19 deletions(-) delete mode 100644 src/cli.rs diff --git a/Cargo.toml b/Cargo.toml index 297c5b1..e14d0d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lrclib-downloader" +name = "lrcli" version = "0.1.0" edition = "2024" diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/cli.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/json_parsing.rs b/src/json_parsing.rs index 8d5b340..769fb35 100644 --- a/src/json_parsing.rs +++ b/src/json_parsing.rs @@ -1,15 +1,63 @@ use crate::lrclib; +use serde_json::Value; +use std::collections::HashMap; +use std::fs; +use std::io::{self, Write}; use std::time::Duration; -pub fn get_lyrics_text(artist_name: &str, track_name: &str, album_name: &str, duration: Duration) { - match lrclib::get_lyrics(artist_name, track_name, album_name, duration) { - Some(lyrics_json) => match serde_json::from_str::(&lyrics_json) { - Ok(parsed) => { - let sync_lyrics = &parsed["syncedLyrics"]; - println!("{sync_lyrics}\n"); - } - Err(e) => eprintln!("Failed to parse lyrics JSON: {e}"), - }, - None => eprintln!("Failed to fetch lyrics for {track_name}"), +const CONFIG_PATH: &str = "/tmp/lrcli.json"; + +type Config = HashMap>; + +pub fn get_lyrics_text( + artist_name: &str, + track_name: &str, + album_name: &str, + duration: Duration, +) -> Result { + let lyrics_json = lrclib::get_lyrics(artist_name, track_name, album_name, duration) + .ok_or_else(|| format!("Failed to fetch lyrics for {track_name}"))?; + + let parsed: Value = serde_json::from_str(&lyrics_json) + .map_err(|e| format!("Failed to parse lyrics JSON: {e}"))?; + + let synced_lyrics = parsed + .get("syncedLyrics") + .ok_or_else(|| "Missing `syncedLyrics` field".to_string())?; + + Ok(synced_lyrics.clone()) +} +pub fn load_config() -> io::Result { + match fs::read_to_string(CONFIG_PATH) { + Ok(contents) => { + let cfg: Config = serde_json::from_str(&contents) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + Ok(cfg) + } + Err(err) if err.kind() == io::ErrorKind::NotFound => { + Ok(HashMap::new()) // no file yet → empty config + } + Err(err) => Err(err), } } + +pub fn save_config(config: &Config) -> io::Result<()> { + let json = serde_json::to_string_pretty(config) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; + + let mut file = fs::File::create(CONFIG_PATH)?; + file.write_all(json.as_bytes())?; + Ok(()) +} + +pub fn add_song(directory: &str, filename: &str, status: &str) -> io::Result<()> { + let mut config = load_config()?; + + let dir_entry = config + .entry(directory.to_string()) + .or_insert_with(HashMap::new); + + dir_entry.insert(filename.to_string(), status.to_string()); + + save_config(&config) +} diff --git a/src/main.rs b/src/main.rs index 02227cc..e0b15ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,31 @@ use clap::Parser; -use std::path::PathBuf; - +use core::time; +use std::path::{Path, PathBuf}; +use std::thread; +use std::time::Duration; mod json_parsing; mod lrclib; mod music; #[derive(Parser, Debug)] -#[command(version, about, long_about = None)] +#[command( + version, + about = "Download synced lyrics (LRC) for music files using LRCLIB", + long_about = "Fetches time-synced lyrics from LRCLIB for one or more audio files. \ +Supports batch downloads, directory processing, and watching for newly added tracks." +)] struct Args { + /// One or more audio files to fetch lyrics for #[arg(short, long, num_args = 1..)] file: Option>, + /// Target directory for downloaded lyrics. + /// When specified, lyrics will be saved alongside files in this directory #[arg(short, long)] directory: Option, + /// Watch the directory for new audio files and fetch lyrics automatically. + /// Requires --directory #[arg(short, long)] watch: bool, } @@ -37,7 +49,14 @@ fn main() { eprintln!("Failed to process directory {}: {e}", directory.display()); } } else { - println!("Watch directory for new files") + let sleep = time::Duration::from_secs(10); + watch(directory, sleep); } } } + +fn watch(path: &Path, wait: Duration) { + loop { + thread::sleep(wait); + } +} diff --git a/src/music.rs b/src/music.rs index a1e285e..8d29a07 100644 --- a/src/music.rs +++ b/src/music.rs @@ -37,19 +37,42 @@ pub fn get_song_file(path: &Path) -> Result<(), MusicError> { return Ok(()); } - let info = get_song_info(path)?; + let pathbuf = path.to_path_buf(); + let absolute_path = fs::canonicalize(&pathbuf).unwrap(); + let info = get_song_info(&absolute_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( + match json_parsing::get_lyrics_text( &info.artist_name, &info.track_name, &info.album_name, info.duration, - ); + ) { + Ok(lyrics) => { + json_parsing::add_song( + absolute_path.parent().unwrap().to_str().unwrap(), + absolute_path.file_name().unwrap().to_str().unwrap(), + "Synchronized", + ); + + println!("{lyrics}") + } + Err(e) => { + json_parsing::add_song( + absolute_path.parent().unwrap().to_str().unwrap(), + absolute_path.file_name().unwrap().to_str().unwrap(), + "NotFound", + ); + eprintln!( + "Failed to fetch lyrics for {}, error: {e}", + pathbuf.to_str().unwrap() + ) + } + }; Ok(()) }