mirror of
https://github.com/arabianq/lyrics_fetcher.git
synced 2026-04-28 08:01:22 +00:00
refactor
This commit is contained in:
+55
-39
@@ -52,8 +52,6 @@ async fn main() -> Result<()> {
|
|||||||
let threads = args.threads;
|
let threads = args.threads;
|
||||||
let overwrite = args.overwrite_existing;
|
let overwrite = args.overwrite_existing;
|
||||||
let allow_inaccurate = args.allow_inaccurate;
|
let allow_inaccurate = args.allow_inaccurate;
|
||||||
let lyrics_sources_raw = args.sources;
|
|
||||||
let extensions: Vec<String> = args.extensions.iter().map(|s| s.to_lowercase()).collect();
|
|
||||||
|
|
||||||
if !music_dir_path.exists() {
|
if !music_dir_path.exists() {
|
||||||
eprintln!("[ERROR] The specified music directory does not exist.");
|
eprintln!("[ERROR] The specified music directory does not exist.");
|
||||||
@@ -65,22 +63,29 @@ async fn main() -> Result<()> {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if lyrics_sources_raw.is_empty() {
|
if args.sources.is_empty() {
|
||||||
eprintln!("[ERROR] At least one source must be specified.");
|
eprintln!("[ERROR] At least one source must be specified.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if extensions.is_empty() {
|
if args.extensions.is_empty() {
|
||||||
eprintln!("[ERROR] At least one file extension must be specified.");
|
eprintln!("[ERROR] At least one file extension must be specified.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lyrics_sources_raw: Vec<String> = lyrics_sources_raw
|
let lyrics_sources_raw: Vec<String> = args
|
||||||
.iter()
|
.sources
|
||||||
|
.into_iter()
|
||||||
.unique()
|
.unique()
|
||||||
.map(|s| s.to_lowercase())
|
.map(|s| s.to_lowercase())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let extensions: Vec<String> = args
|
||||||
|
.extensions
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_lowercase())
|
||||||
|
.collect();
|
||||||
|
|
||||||
println!("[INFO] Music directory: {}", music_dir_path.display());
|
println!("[INFO] Music directory: {}", music_dir_path.display());
|
||||||
println!("[INFO] Number of threads: {}", threads);
|
println!("[INFO] Number of threads: {}", threads);
|
||||||
println!("[INFO] Sources: {}", lyrics_sources_raw.join(", "));
|
println!("[INFO] Sources: {}", lyrics_sources_raw.join(", "));
|
||||||
@@ -106,36 +111,48 @@ async fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let sources = Arc::new(sources);
|
let sources = Arc::new(sources);
|
||||||
let semaphone = Arc::new(Semaphore::new(threads));
|
let semaphore = Arc::new(Semaphore::new(threads));
|
||||||
let mut entries = WalkDir::new(music_dir_path);
|
let mut entries = WalkDir::new(music_dir_path);
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
while let Some(entry) = entries.next().await {
|
while let Some(entry) = entries.next().await {
|
||||||
match entry {
|
let entry = match entry {
|
||||||
Ok(entry) => {
|
Ok(e) => e,
|
||||||
if entry.file_type().await?.is_file()
|
Err(e) => {
|
||||||
&& extensions.contains(
|
eprintln!("[ERROR] Failed to read directory entry: {}", e);
|
||||||
&entry
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_file = entry
|
||||||
|
.file_type()
|
||||||
|
.await
|
||||||
|
.map(|ft| ft.is_file())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !is_file {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ext = entry
|
||||||
.path()
|
.path()
|
||||||
.extension()
|
.extension()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.to_lowercase(),
|
.to_lowercase();
|
||||||
)
|
if !extensions.contains(&ext) {
|
||||||
{
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let file_path = entry.path().to_path_buf();
|
let file_path = entry.path().to_path_buf();
|
||||||
let semaphone_clone = Arc::clone(&semaphone);
|
let semaphore_clone = Arc::clone(&semaphore);
|
||||||
let sources_clone = Arc::clone(&sources);
|
let sources_clone = Arc::clone(&sources);
|
||||||
|
|
||||||
let task = tokio::spawn(async move {
|
let task = tokio::spawn(async move {
|
||||||
let _permit = semaphone_clone.acquire().await.unwrap();
|
let _permit = semaphore_clone.acquire().await.unwrap();
|
||||||
process_file(&file_path, sources_clone, overwrite, allow_inaccurate).await;
|
process_file(&file_path, sources_clone, overwrite, allow_inaccurate).await;
|
||||||
});
|
});
|
||||||
tasks.push(task);
|
tasks.push(task);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => eprintln!("[ERROR] Failed to read directory entry: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for task in tasks {
|
for task in tasks {
|
||||||
let _ = task.await;
|
let _ = task.await;
|
||||||
@@ -150,12 +167,19 @@ async fn process_file(
|
|||||||
overwrite: bool,
|
overwrite: bool,
|
||||||
allow_inaccurate: bool,
|
allow_inaccurate: bool,
|
||||||
) {
|
) {
|
||||||
let tag = Tag::async_read_from_path(&file_path).await;
|
let mut tag = match Tag::async_read_from_path(file_path).await {
|
||||||
|
Ok(tag) => tag,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"[ERROR] Failed to read ID3 tag for file '{}': {}",
|
||||||
|
file_path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match tag {
|
if !overwrite && tag.lyrics().next().is_some() {
|
||||||
Ok(tag) => {
|
|
||||||
let lyrics: Vec<&Lyrics> = tag.lyrics().collect();
|
|
||||||
if !lyrics.is_empty() && !overwrite {
|
|
||||||
println!(
|
println!(
|
||||||
"[INFO] File '{}' already has lyrics, skipping (use --overwrite to force)",
|
"[INFO] File '{}' already has lyrics, skipping (use --overwrite to force)",
|
||||||
file_path.display()
|
file_path.display()
|
||||||
@@ -182,7 +206,6 @@ async fn process_file(
|
|||||||
source.name()
|
source.name()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut tag = tag.clone();
|
|
||||||
tag.remove_all_lyrics();
|
tag.remove_all_lyrics();
|
||||||
tag.remove_all_synchronised_lyrics();
|
tag.remove_all_synchronised_lyrics();
|
||||||
|
|
||||||
@@ -192,30 +215,23 @@ async fn process_file(
|
|||||||
text: lyrics.to_string(),
|
text: lyrics.to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
tag.write_to_path(&file_path, Version::Id3v24)
|
if let Err(e) = tag.write_to_path(file_path, Version::Id3v24) {
|
||||||
.map_err(|e| {
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[ERROR] Failed to write tags into {}: {}",
|
"[ERROR] Failed to write tags into {}: {}",
|
||||||
file_path.display(),
|
file_path.display(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
})
|
}
|
||||||
.ok();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!(
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
"[ERROR] Failed to fetch lyrics for file '{}' from source '{}': {}",
|
"[ERROR] Failed to fetch lyrics for file '{}' from source '{}': {}",
|
||||||
file_path.display(),
|
file_path.display(),
|
||||||
source.name(),
|
source.name(),
|
||||||
e
|
e
|
||||||
),
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!(
|
|
||||||
"[ERROR] Failed to read ID3 tag for file '{}': {}",
|
|
||||||
file_path.display(),
|
|
||||||
e
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::sources::LyricsSource;
|
use crate::sources::LyricsSource;
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Ok, Result, anyhow};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use id3::{Tag, TagLike};
|
use id3::{Tag, TagLike};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
@@ -116,7 +116,7 @@ impl LyricsSource for GeniusSource {
|
|||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| anyhow!("No results found on Genius"))?
|
.ok_or_else(|| anyhow!("No results found on Genius"))?
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("No accurate match found on Genius"));
|
return Ok("".to_string());
|
||||||
};
|
};
|
||||||
|
|
||||||
let lyrics_url = &selected.result.url;
|
let lyrics_url = &selected.result.url;
|
||||||
|
|||||||
Reference in New Issue
Block a user