use super::SunoClient;
use super::types::{Clip, GenerateRequest, GenerateResponse};
use crate::core::CliError;
impl SunoClient {
pub async fn generate(&self, req: &GenerateRequest) -> Result<Vec<Clip>, CliError> {
if req.token.is_none() {
let mut challenge = self.generation_challenge().await?;
if challenge.required && self.try_refresh_jwt_for_challenge_recheck().await? {
challenge = self.generation_challenge().await?;
}
if challenge.required {
return Err(generation_challenge_error(&challenge));
}
}
self.with_auth_retry(|| async {
let resp = self.post("/api/generate/v2-web/").json(req).send().await?;
let resp = self.check_response(resp).await?;
let result: GenerateResponse = resp.json().await?;
Ok(result.clips)
})
.await
}
pub async fn get_clips(&self, ids: &[String]) -> Result<Vec<Clip>, CliError> {
let mut all_clips = Vec::new();
for chunk in ids.chunks(2) {
let ids_param = chunk.join(",");
let path = format!("/api/feed/?ids={ids_param}");
let clips: Vec<Clip> = self
.with_auth_retry(|| async {
let resp = self.get(&path).send().await?;
let resp = self.check_response(resp).await?;
let clips: Vec<Clip> = resp.json().await?;
Ok(clips)
})
.await?;
all_clips.extend(clips);
}
Ok(all_clips)
}
}
fn generation_challenge_error(challenge: &super::challenge::GenerationChallenge) -> CliError {
let version = challenge
.captcha_version
.map(|version| version.to_string())
.unwrap_or_else(|| "unknown".to_string());
CliError::Config(format!(
"Suno requires a generation challenge (captcha_version={version}). When stored Clerk refresh material is available, Sunox refreshes the JWT once and repeats the challenge preflight before showing this message. Complete a manual generation challenge in the Suno web app and retry, provide a valid challenge token with --token <token>, or force the browser-backed solver with --captcha."
))
}