refactor and added watch

This commit is contained in:
shinya 2026-01-31 12:40:49 +01:00
parent b7b3a63cf5
commit ce3eaac9c2
3 changed files with 96 additions and 14 deletions

View File

@ -1,7 +1,7 @@
use crate::lrclib;
use serde_json::Value;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::io::{self, Write};
use std::path::Path;
@ -95,3 +95,36 @@ pub fn add_song(directory: &str, filename: &str, status: &str) -> io::Result<()>
save_config(&config)
}
pub fn return_all_processed_files(path: &Path) -> io::Result<HashSet<String>> {
let config = load_config()?;
let dir_str = path
.to_str()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid directory path"))?;
let filenames = config
.get(dir_str) // get inner HashMap for the directory
.map(|inner| inner.keys().cloned().collect()) // collect keys into HashSet
.unwrap_or_else(HashSet::new); // empty if directory not found
Ok(filenames)
}
// pub fn list_already_processed()
// pub fn list_directory_entries<P: AsRef<Path>>(path: P) -> io::Result<Vec<String>> {
// let mut entries = Vec::new();
// for entry in fs::read_dir(path)? {
// let entry = entry?;
// let file_type = entry.file_type()?;
// if file_type.is_file()
// && let Some(name) = entry.file_name().to_str()
// {
// entries.push(name.to_string());
// }
// }
// Ok(entries)
// }

View File

@ -4,6 +4,8 @@ use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;
use crate::music::get_all_music_files;
mod json_parsing;
mod lrclib;
mod music;
@ -46,7 +48,7 @@ fn main() {
}
} else if let Some(directory) = &args.directory {
if !args.watch {
if let Err(e) = music::get_all_music_files(directory) {
if let Err(e) = music::get_all_music_files(directory, false) {
eprintln!("Failed to process directory {}: {e}", directory.display());
}
} else {
@ -58,6 +60,7 @@ fn main() {
fn watch(path: &Path, wait: Duration) {
loop {
get_all_music_files(path, true);
thread::sleep(wait);
}
}

View File

@ -1,8 +1,10 @@
use crate::json_parsing::{self, add_song};
use audiotags::Tag;
use std::collections::HashSet;
use std::fs;
use std::path::Path;
use std::io;
use std::path::{Path, PathBuf};
use std::time::Duration;
use thiserror::Error;
@ -42,10 +44,10 @@ pub fn get_song_file(path: &Path) -> Result<(), MusicError> {
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);
println!(
"Track: {}, Artist: {}, Album: {}",
info.track_name, info.artist_name, info.album_name
);
match json_parsing::get_lyrics_text(
&info.artist_name,
@ -71,6 +73,7 @@ pub fn get_song_file(path: &Path) -> Result<(), MusicError> {
json_parsing::LyricsResult::Synced(lyrics)
| json_parsing::LyricsResult::Plain(lyrics) => {
json_parsing::save_lyrics_file(absolute_path, lyrics);
println!("Downloaded lyrics")
}
json_parsing::LyricsResult::Instrumental => {
println!("Instrumental track — no lyrics available");
@ -98,18 +101,42 @@ pub fn get_song_file(path: &Path) -> Result<(), MusicError> {
Ok(())
}
pub fn get_all_music_files(directory: &Path) -> Result<(), MusicError> {
pub fn get_all_music_files(directory: &Path, watch: bool) -> Result<(), MusicError> {
if !directory.is_dir() {
return Ok(());
}
for entry in fs::read_dir(directory)? {
let entry = entry?;
let path = entry.path();
let absolute = fs::canonicalize(directory).unwrap();
if entry.file_type()?.is_dir() {
get_all_music_files(&path)?;
} else if let Err(e) = get_song_file(&path) {
let already_processed_files: HashSet<String> =
json_parsing::return_all_processed_files(&absolute).unwrap();
let files_to_process: Vec<PathBuf> = if watch {
get_new_files(&absolute)?
} else {
let mut all_files = Vec::new();
for entry in fs::read_dir(&absolute)? {
let entry = entry?;
let path = entry.path();
if entry.file_type()?.is_dir() {
get_all_music_files(&path, watch)?; // recursive call for subdirectories
} else {
all_files.push(path);
}
}
all_files
};
// Process files
for path in files_to_process {
let path_string = path.file_name().unwrap().to_string_lossy().to_string();
// Skip already processed files in full-run mode
if !watch && already_processed_files.contains(&path_string) {
continue;
}
if let Err(e) = get_song_file(&path) {
eprintln!("Skipping file {}: {}", path.display(), e);
}
}
@ -141,3 +168,22 @@ pub fn get_song_info(path: &Path) -> Result<TrackInfo, MusicError> {
pub fn get_song_length(path: &Path) -> Result<Duration, MusicError> {
mp3_duration::from_path(path).map_err(|_| MusicError::DurationRead(path.display().to_string()))
}
pub fn get_new_files(path: &Path) -> io::Result<Vec<PathBuf>> {
// Get the already processed files
let processed: HashSet<String> = json_parsing::return_all_processed_files(path)?;
// Read all files in the directory
let mut new_files = Vec::new();
for entry in fs::read_dir(path)? {
let entry = entry?;
let file_name = entry.file_name().to_string_lossy().to_string();
// Only include files not in processed set
if !processed.contains(&file_name) && entry.path().is_file() {
new_files.push(entry.path());
}
}
Ok(new_files)
}