diff --git a/crates/api_clients/beeble_client/src/error/beeble_specific_api_error.rs b/crates/api_clients/beeble_client/src/error/beeble_specific_api_error.rs index b94c513b5a..3c527ac13c 100644 --- a/crates/api_clients/beeble_client/src/error/beeble_specific_api_error.rs +++ b/crates/api_clients/beeble_client/src/error/beeble_specific_api_error.rs @@ -24,6 +24,13 @@ pub enum BeebleSpecificApiError { message: String, webhook_url: String, }, + + /// 400 Bad Request — the video has too many frames. + VideoHasTooManyFrames { + max_frames: u64, + detected_frames: u64, + message: String, + }, } impl Error for BeebleSpecificApiError {} @@ -39,6 +46,9 @@ impl Display for BeebleSpecificApiError { Self::BadWebhookUrl { message, webhook_url } => { write!(f, "Beeble API: Bad webhook URL '{}': {}", webhook_url, message) } + Self::VideoHasTooManyFrames { max_frames, detected_frames, message } => { + write!(f, "Beeble API: Video has too many frames ({} detected, max {}): {}", detected_frames, max_frames, message) + } } } } diff --git a/crates/api_clients/beeble_client/src/requests/start_generation/handle_error_response.rs b/crates/api_clients/beeble_client/src/requests/start_generation/handle_error_response.rs index 737bd0f634..efaffaacc6 100644 --- a/crates/api_clients/beeble_client/src/requests/start_generation/handle_error_response.rs +++ b/crates/api_clients/beeble_client/src/requests/start_generation/handle_error_response.rs @@ -28,6 +28,16 @@ pub fn handle_error_response( webhook_url: context.maybe_callback_url.unwrap_or_default().to_string(), }.into()) } + 400 if response_body.contains("VIDEO_TOO_MANY_FRAMES") => { + let message = extract_error_message(response_body) + .unwrap_or_else(|| "Video has too many frames".to_string()); + let (detected, max) = extract_frame_counts(&message); + Err(BeebleSpecificApiError::VideoHasTooManyFrames { + max_frames: max, + detected_frames: detected, + message, + }.into()) + } 409 => Err(BeebleSpecificApiError::IdempotencyConflict.into()), 429 => Err(BeebleSpecificApiError::RateLimited.into()), _ => { @@ -45,3 +55,18 @@ fn extract_error_message(body: &str) -> Option { let parsed: serde_json::Value = serde_json::from_str(body).ok()?; parsed.get("error")?.get("message")?.as_str().map(|s| s.to_string()) } + +/// Parse frame counts from a message like "Video has 419 frames, which exceeds the maximum of 240." +fn extract_frame_counts(message: &str) -> (u64, u64) { + let numbers: Vec = message + .split(|c: char| !c.is_ascii_digit()) + .filter(|s| !s.is_empty()) + .filter_map(|s| s.parse().ok()) + .collect(); + + match numbers.as_slice() { + [detected, max, ..] => (*detected, *max), + [detected] => (*detected, 0), + _ => (0, 0), + } +}