use std::sync::Arc;
use barber::{ProgressBar, ProgressRenderer};
use lunar_lib::database::{Database, DatabaseEntry, DatabaseError, TransactionError};
use thiserror::Error;
use crate::{
database::{LibraryDb, entry_extensions::EntryExtensions},
library::track::{Track, lyric_data::LyricData},
lrclib::SearchQuery,
};
#[derive(Debug, Error)]
pub enum LyricSearchError {
#[error("DatabaseError: {0}")]
Database(#[from] DatabaseError),
#[error("Transaction Error: {0}")]
Transaction(#[from] TransactionError),
#[error("ReqwestError: {0}")]
Reqwest(#[from] reqwest::Error),
}
pub fn find_needs_lyrics(synced_only: bool) -> Result<Vec<Track>, DatabaseError> {
let mut tracks = Track::db_get_all()?;
tracks.retain(|track| {
track
.metadata
.lyric_data
.as_ref()
.is_none_or(|l| matches!(l, LyricData::Plain(_)) && synced_only)
});
Ok(tracks)
}
pub fn find_lyrics_for(
tracks: Vec<Track>,
progress_renderer: Arc<dyn ProgressRenderer>,
synced_only: bool,
) -> Result<(), LyricSearchError> {
let progress_bar = ProgressBar::new(0, tracks.len(), progress_renderer);
'track_loop: for mut track in tracks {
let Some(track_title) = track.metadata.title.as_deref() else {
progress_bar.set_label("Ignored track with unknown title");
continue;
};
progress_bar.set_label(&format!("Searching lyrics for {track_title}..."));
progress_bar.increment();
let (album, artists) = {
let db = LibraryDb::open().unwrap();
(track.album(&db)?, track.metadata.artists(&db)?)
};
let album_title = album.map(|a| a.album.name);
let results = SearchQuery::new(track_title)
.album_title(album_title.as_deref())
.main_artist(Some(artists[0].name()))
.run()?;
if results.is_empty() {
continue;
}
for result in &results {
if let Some(synced_lyrics) = result.synced_lyrics() {
track.metadata.lyric_data = Some(LyricData::Synced(synced_lyrics));
track.db_patch(None)?;
continue 'track_loop;
}
}
for result in &results {
if let Some(synced_lyrics) = result.plain_lyrics() {
if !synced_only {
track.metadata.lyric_data = Some(LyricData::Plain(synced_lyrics));
}
track.db_patch(None)?;
continue 'track_loop;
}
}
track.metadata.lyric_data = Some(LyricData::Instrumental);
track.db_patch(None)?;
}
Ok(())
}