diff --git a/src/api/types.rs b/src/api/types.rs index 327151a..6c5b23e 100644 --- a/src/api/types.rs +++ b/src/api/types.rs @@ -9,22 +9,68 @@ pub struct BillingInfo { pub monthly_usage: u64, pub monthly_limit: u64, pub is_active: bool, - pub plan: Plan, + #[serde(default)] + pub is_past_due: bool, + /// Suno no longer nests the active plan under a `plan` key. Instead + /// `subscription_type` is `false` on the free tier or the plan's + /// `plan_key` string (e.g. `"pro"`) when subscribed, and the full catalog + /// of plans (with pricing/features) is listed separately in `plans`. + #[serde(default)] + pub subscription_type: SubscriptionType, pub models: Vec, - pub period: String, - pub renews_on: Option, + #[serde(default)] + pub plans: Vec, + #[serde(default)] + pub accessible_features: Vec, #[serde(default)] pub remaster_model_types: Vec, } #[derive(Debug, Deserialize, Serialize)] -pub struct Plan { - pub name: String, +#[serde(untagged)] +pub enum SubscriptionType { + /// No active paid subscription (free tier). + None(bool), + /// Active plan, identified by its `plan_key` (e.g. "pro"). + Plan(String), +} + +impl Default for SubscriptionType { + fn default() -> Self { + SubscriptionType::None(false) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PlanOption { pub plan_key: String, + pub name: String, #[serde(default)] pub usage_plan_features: Vec, } +impl BillingInfo { + /// Human-readable name of the account's current plan, resolved from + /// `subscription_type` against the `plans` catalog (falls back to the + /// raw plan key, or "Free" when there's no active subscription). + pub fn plan_name(&self) -> String { + match &self.subscription_type { + SubscriptionType::Plan(key) => self + .plans + .iter() + .find(|p| &p.plan_key == key) + .map(|p| p.name.clone()) + .unwrap_or_else(|| key.clone()), + SubscriptionType::None(_) => self + .plans + .iter() + .find(|p| p.plan_key == "free") + .map(|p| p.name.clone()) + .unwrap_or_else(|| "Free".to_string()), + } + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct Feature { pub name: String, diff --git a/src/cli.rs b/src/cli.rs index c3b20f8..f4c1cb5 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -178,6 +178,10 @@ pub struct DescribeArgs { #[arg(short, long)] pub prompt: String, + /// Song title + #[arg(short, long)] + pub title: Option, + /// Style tags (optional, guides the generation) #[arg(long)] pub tags: Option, @@ -494,6 +498,8 @@ pub enum ModelVersion { V45Plus, #[value(name = "v4.5")] V45, + #[value(name = "v4.5-all")] + V45All, #[value(name = "v4")] V4, #[value(name = "v3.5")] @@ -511,6 +517,7 @@ impl ModelVersion { Self::V5 => "chirp-crow", Self::V45Plus => "chirp-bluejay", Self::V45 => "chirp-auk", + Self::V45All => "chirp-auk-turbo", Self::V4 => "chirp-v4", Self::V35 => "chirp-v3-5", Self::V3 => "chirp-v3-0", @@ -524,6 +531,7 @@ impl ModelVersion { Self::V5 => "v5", Self::V45Plus => "v4.5+", Self::V45 => "v4.5", + Self::V45All => "v4.5-all", Self::V4 => "v4", Self::V35 => "v3.5", Self::V3 => "v3", diff --git a/src/main.rs b/src/main.rs index 947e981..c84dccd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -221,7 +221,8 @@ async fn run() -> Result<(), CliError> { } eprintln!( "Authenticated! Plan: {}, Credits: {}", - info.plan.name, info.total_credits_left + info.plan_name(), + info.total_credits_left ); } @@ -344,6 +345,7 @@ async fn run() -> Result<(), CliError> { // text is sent in the same `prompt` field as custom mode. let mut req = GenerateRequest::new(args.model.to_api_key(), "inspiration"); req.prompt = args.prompt; + req.title = args.title; req.tags = tags; req.make_instrumental = args.instrumental; req.persona_id = args.persona.clone(); @@ -609,7 +611,8 @@ async fn run() -> Result<(), CliError> { let info = client.billing_info().await?; eprintln!( "Auth: OK — {}, {} credits", - info.plan.name, info.total_credits_left + info.plan_name(), + info.total_credits_left ); } Err(CliError::AuthExpired) => { diff --git a/src/output/table.rs b/src/output/table.rs index 6759597..a403d93 100644 --- a/src/output/table.rs +++ b/src/output/table.rs @@ -37,17 +37,13 @@ pub fn billing(info: &BillingInfo) { .apply_modifier(UTF8_ROUND_CORNERS) .set_header(vec!["Field", "Value"]); - table.add_row(vec!["Plan", &info.plan.name]); + table.add_row(vec!["Plan", &info.plan_name()]); table.add_row(vec!["Credits Left", &info.total_credits_left.to_string()]); table.add_row(vec![ "Monthly Usage", &format!("{} / {}", info.monthly_usage, info.monthly_limit), ]); table.add_row(vec!["Active", &info.is_active.to_string()]); - table.add_row(vec!["Period", &info.period]); - if let Some(ref renew) = info.renews_on { - table.add_row(vec!["Renews On", renew]); - } println!("{table}"); }