mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-24 10:39:28 -04:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef36378e7a | ||
|
|
19e27f440c | ||
|
|
7b4835a44e |
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -28,6 +28,16 @@ pub fn handle_error_response<T>(
|
||||
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<String> {
|
||||
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<u64> = 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),
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -69,6 +69,9 @@ pub enum MediaFileOriginProductCategory {
|
||||
#[serde(rename = "video_gen")]
|
||||
VideoGeneration,
|
||||
|
||||
#[serde(rename = "video_edit")]
|
||||
VideoEdit,
|
||||
|
||||
#[serde(rename = "world_gen")]
|
||||
WorldGeneration,
|
||||
|
||||
@@ -105,6 +108,7 @@ impl MediaFileOriginProductCategory {
|
||||
Self::Mocap => "mocap",
|
||||
Self::ImageGeneration => "image_gen",
|
||||
Self::VideoGeneration => "video_gen",
|
||||
Self::VideoEdit => "video_edit",
|
||||
Self::WorldGeneration => "world_gen",
|
||||
Self::VideoFilter => "video_filter",
|
||||
Self::Workflow => "workflow",
|
||||
@@ -126,6 +130,7 @@ impl MediaFileOriginProductCategory {
|
||||
"mocap" => Ok(Self::Mocap),
|
||||
"image_gen" => Ok(Self::ImageGeneration),
|
||||
"video_gen" => Ok(Self::VideoGeneration),
|
||||
"video_edit" => Ok(Self::VideoEdit),
|
||||
"world_gen" => Ok(Self::WorldGeneration),
|
||||
"video_filter" => Ok(Self::VideoFilter),
|
||||
"workflow" => Ok(Self::Workflow),
|
||||
@@ -150,6 +155,7 @@ impl MediaFileOriginProductCategory {
|
||||
Self::Mocap,
|
||||
Self::ImageGeneration,
|
||||
Self::VideoGeneration,
|
||||
Self::VideoEdit,
|
||||
Self::WorldGeneration,
|
||||
Self::VideoFilter,
|
||||
Self::Workflow,
|
||||
@@ -180,6 +186,7 @@ mod tests {
|
||||
assert_serialization(MediaFileOriginProductCategory::Mocap, "mocap");
|
||||
assert_serialization(MediaFileOriginProductCategory::ImageGeneration, "image_gen");
|
||||
assert_serialization(MediaFileOriginProductCategory::VideoGeneration, "video_gen");
|
||||
assert_serialization(MediaFileOriginProductCategory::VideoEdit, "video_edit");
|
||||
assert_serialization(MediaFileOriginProductCategory::WorldGeneration, "world_gen");
|
||||
assert_serialization(MediaFileOriginProductCategory::VideoFilter, "video_filter");
|
||||
assert_serialization(MediaFileOriginProductCategory::Workflow, "workflow");
|
||||
@@ -200,6 +207,7 @@ mod tests {
|
||||
assert_eq!(MediaFileOriginProductCategory::Mocap.to_str(), "mocap");
|
||||
assert_eq!(MediaFileOriginProductCategory::ImageGeneration.to_str(), "image_gen");
|
||||
assert_eq!(MediaFileOriginProductCategory::VideoGeneration.to_str(), "video_gen");
|
||||
assert_eq!(MediaFileOriginProductCategory::VideoEdit.to_str(), "video_edit");
|
||||
assert_eq!(MediaFileOriginProductCategory::WorldGeneration.to_str(), "world_gen");
|
||||
assert_eq!(MediaFileOriginProductCategory::VideoFilter.to_str(), "video_filter");
|
||||
assert_eq!(MediaFileOriginProductCategory::Workflow.to_str(), "workflow");
|
||||
@@ -220,6 +228,7 @@ mod tests {
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("mocap").unwrap(), MediaFileOriginProductCategory::Mocap);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("image_gen").unwrap(),MediaFileOriginProductCategory::ImageGeneration);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("video_gen").unwrap(), MediaFileOriginProductCategory::VideoGeneration);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("video_edit").unwrap(), MediaFileOriginProductCategory::VideoEdit);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("world_gen").unwrap(), MediaFileOriginProductCategory::WorldGeneration);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("video_filter").unwrap(), MediaFileOriginProductCategory::VideoFilter);
|
||||
assert_eq!(MediaFileOriginProductCategory::from_str("workflow").unwrap(), MediaFileOriginProductCategory::Workflow);
|
||||
@@ -228,7 +237,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileOriginProductCategory::all_variants();
|
||||
assert_eq!(variants.len(), 16);
|
||||
assert_eq!(variants.len(), 17);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Unknown));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::FaceAnimator));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::FaceFusion));
|
||||
@@ -242,6 +251,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Mocap));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::ImageGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VideoGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VideoEdit));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::WorldGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::VideoFilter));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileOriginProductCategory::Workflow));
|
||||
|
||||
+18
-2
@@ -8,6 +8,8 @@ use sqlx::Acquire;
|
||||
use artcraft_api_defs::generate::video::edit::beeble_switchx_edit_video::{
|
||||
BeebleSwitchXEditVideoRequest, BeebleSwitchXEditVideoResponse,
|
||||
};
|
||||
use beeble_client::error::beeble_error::BeebleError;
|
||||
use beeble_client::error::beeble_specific_api_error::BeebleSpecificApiError;
|
||||
use beeble_client::requests::create_upload_url::upload_bytes::upload_bytes_to_beeble;
|
||||
use beeble_client::requests::start_generation::start_generation::{
|
||||
start_generation, BeebleAlphaMode, BeebleGenerationType, StartGenerationArgs,
|
||||
@@ -218,8 +220,22 @@ pub async fn beeble_switchx_edit_video_gen_handler(
|
||||
idempotency_key: Some(request.uuid_idempotency_token.clone()),
|
||||
},
|
||||
}).await.map_err(|err| {
|
||||
error!("Beeble start_generation failed: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
warn!("Beeble start_generation failed: {:?}", err);
|
||||
match err {
|
||||
BeebleError::ApiSpecific(BeebleSpecificApiError::VideoHasTooManyFrames {
|
||||
max_frames,
|
||||
detected_frames,
|
||||
ref message,
|
||||
}) => {
|
||||
AdvancedCommonWebError::BadInputWithSimpleMessage(format!(
|
||||
"Video has too many frames: {} (max: {}); {}",
|
||||
detected_frames, max_frames, message
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
let external_job_id = &beeble_result.id;
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ use enums::by_table::debug_logs::debug_log_type::DebugLogType;
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use ffmpeg_utils::ffprobe::ffprobe_get_info::ffprobe_get_info;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
@@ -366,6 +367,7 @@ async fn download_and_upload_video(
|
||||
.media_file_class(MediaFileClass::Video)
|
||||
.media_file_type(media_file_type)
|
||||
.media_file_origin_category(MediaFileOriginCategory::Inference)
|
||||
.media_file_origin_product_category(MediaFileOriginProductCategory::VideoEdit)
|
||||
.mime_type(mime_type)
|
||||
.file_size_bytes(file_size_bytes as u64)
|
||||
.maybe_frame_width(maybe_frame_width)
|
||||
|
||||
Reference in New Issue
Block a user