updated functions and refactor
This commit is contained in:
parent
6dd66b582b
commit
87508b6908
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "lrclib-downloader"
|
||||
name = "lrcli"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -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::<serde_json::Value>(&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<String, HashMap<String, String>>;
|
||||
|
||||
pub fn get_lyrics_text(
|
||||
artist_name: &str,
|
||||
track_name: &str,
|
||||
album_name: &str,
|
||||
duration: Duration,
|
||||
) -> Result<Value, String> {
|
||||
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<Config> {
|
||||
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)
|
||||
}
|
||||
|
||||
27
src/main.rs
27
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<Vec<PathBuf>>,
|
||||
|
||||
/// Target directory for downloaded lyrics.
|
||||
/// When specified, lyrics will be saved alongside files in this directory
|
||||
#[arg(short, long)]
|
||||
directory: Option<PathBuf>,
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
29
src/music.rs
29
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(())
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user