updated functions and refactor
This commit is contained in:
parent
6dd66b582b
commit
87508b6908
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lrclib-downloader"
|
name = "lrcli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -1,15 +1,63 @@
|
|||||||
use crate::lrclib;
|
use crate::lrclib;
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::{self, Write};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub fn get_lyrics_text(artist_name: &str, track_name: &str, album_name: &str, duration: Duration) {
|
const CONFIG_PATH: &str = "/tmp/lrcli.json";
|
||||||
match lrclib::get_lyrics(artist_name, track_name, album_name, duration) {
|
|
||||||
Some(lyrics_json) => match serde_json::from_str::<serde_json::Value>(&lyrics_json) {
|
type Config = HashMap<String, HashMap<String, String>>;
|
||||||
Ok(parsed) => {
|
|
||||||
let sync_lyrics = &parsed["syncedLyrics"];
|
pub fn get_lyrics_text(
|
||||||
println!("{sync_lyrics}\n");
|
artist_name: &str,
|
||||||
}
|
track_name: &str,
|
||||||
Err(e) => eprintln!("Failed to parse lyrics JSON: {e}"),
|
album_name: &str,
|
||||||
},
|
duration: Duration,
|
||||||
None => eprintln!("Failed to fetch lyrics for {track_name}"),
|
) -> 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 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 json_parsing;
|
||||||
mod lrclib;
|
mod lrclib;
|
||||||
mod music;
|
mod music;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[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 {
|
struct Args {
|
||||||
|
/// One or more audio files to fetch lyrics for
|
||||||
#[arg(short, long, num_args = 1..)]
|
#[arg(short, long, num_args = 1..)]
|
||||||
file: Option<Vec<PathBuf>>,
|
file: Option<Vec<PathBuf>>,
|
||||||
|
|
||||||
|
/// Target directory for downloaded lyrics.
|
||||||
|
/// When specified, lyrics will be saved alongside files in this directory
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Watch the directory for new audio files and fetch lyrics automatically.
|
||||||
|
/// Requires --directory
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
watch: bool,
|
watch: bool,
|
||||||
}
|
}
|
||||||
@ -37,7 +49,14 @@ fn main() {
|
|||||||
eprintln!("Failed to process directory {}: {e}", directory.display());
|
eprintln!("Failed to process directory {}: {e}", directory.display());
|
||||||
}
|
}
|
||||||
} else {
|
} 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(());
|
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!("Track: {}", info.track_name);
|
||||||
println!("Artist: {}", info.artist_name);
|
println!("Artist: {}", info.artist_name);
|
||||||
println!("Album: {}", info.album_name);
|
println!("Album: {}", info.album_name);
|
||||||
println!("Duration: {:?}\n", info.duration);
|
println!("Duration: {:?}\n", info.duration);
|
||||||
|
|
||||||
json_parsing::get_lyrics_text(
|
match json_parsing::get_lyrics_text(
|
||||||
&info.artist_name,
|
&info.artist_name,
|
||||||
&info.track_name,
|
&info.track_name,
|
||||||
&info.album_name,
|
&info.album_name,
|
||||||
info.duration,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user