use std::collections::HashMap;
use futures_util::stream::{self, StreamExt};
use suno_core::{AlignedLyrics, Manifest, SunoClient};
use crate::cli::task_output::eprint_t;
use crate::cli::wallclock;
use crate::clock::TokioClock;
use crate::http::ReqwestHttp;
const SYNCED_LYRICS_FETCH_WARNING: &str = "could not fetch synced lyrics for a clip; its synced lyrics are skipped this run and retried next run";
pub(crate) async fn resolve_synced_lyrics(
desired: &mut [suno_core::Desired],
manifest: &Manifest,
client: &SunoClient<TokioClock>,
http: &ReqwestHttp,
enabled: bool,
verbosity: i8,
concurrency: u32,
) -> (HashMap<String, AlignedLyrics>, Vec<suno_core::PendingCheck>) {
let mut synced: HashMap<String, AlignedLyrics> = HashMap::new();
let targets =
suno_core::synced_lyrics_targets(desired, manifest, wallclock::now_secs(), enabled);
let fetched = stream::iter(targets.iter())
.map(|id| async move { (id.clone(), client.aligned_lyrics(http, id).await) })
.buffered(concurrency.max(1) as usize)
.collect::<Vec<_>>()
.await;
for (id, result) in fetched {
match result {
Ok(aligned) => {
synced.insert(id, aligned);
}
Err(_) => {
if verbosity >= -1 {
eprint_t!("warning: {SYNCED_LYRICS_FETCH_WARNING}");
}
}
}
}
let pending = suno_core::apply_synced_lrc(desired, manifest, &synced);
(synced, pending)
}
pub(crate) fn record_synced_lyrics_checks(
manifest: &mut Manifest,
pending: &[suno_core::PendingCheck],
) {
let now = wallclock::now_secs();
for check in pending {
let durable = if check.empty {
true
} else {
match (&check.body_hash, manifest.get(&check.clip_id)) {
(Some(hash), Some(entry)) => {
entry.lrc.as_ref().map(|slot| &slot.hash) == Some(hash)
}
_ => false,
}
};
if !durable {
continue;
}
if let Some(entry) = manifest.entries.get_mut(&check.clip_id) {
entry.synced_lyrics = Some(suno_core::SyncedLyricsCheck {
version: suno_core::SYNCED_LRC_VERSION,
checked_unix: now,
empty: check.empty,
timed: check.timed,
});
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn synced_lyrics_fetch_warning_never_leaks_a_clip_id_or_url() {
let msg = SYNCED_LYRICS_FETCH_WARNING;
assert!(!msg.contains("/api/gen/"));
assert!(!msg.contains("aligned_lyrics"));
assert!(!msg.contains('{'), "no interpolation placeholder");
assert!(!msg.contains("http"));
}
}