diff --git a/crates/schema/public/enums/src/by_table/media_files/media_file_type.rs b/crates/schema/public/enums/src/by_table/media_files/media_file_type.rs index 36b1deb009..16f488627b 100644 --- a/crates/schema/public/enums/src/by_table/media_files/media_file_type.rs +++ b/crates/schema/public/enums/src/by_table/media_files/media_file_type.rs @@ -112,10 +112,10 @@ impl MediaFileType { "image/jpeg" => Some(Self::Jpg), "image/png" => Some(Self::Png), "video/mp4" => Some(Self::Mp4), + "model/gltf-binary" => Some(Self::Glb), //"audio/wav" => Some(Self::Wav), // NB: Not sure if this is correct //"audio/mpeg" => Some(Self::Mp3), // NB: Not sure if this is correct //"image/gif" => Some(Self::Gif), // NB: Not sure if this is correct - //"model/gltf-binary" => Some(Self::Glb), // NB: Not sure if this is correct //"model/gltf+json" => Some(Self::Gltf), // NB: Not sure if this is correct _ => None, } diff --git a/crates/service/job/inference_job/src/job/job_loop/process_single_job.rs b/crates/service/job/inference_job/src/job/job_loop/process_single_job.rs index fb673ebdd4..f598635b55 100644 --- a/crates/service/job/inference_job/src/job/job_loop/process_single_job.rs +++ b/crates/service/job/inference_job/src/job/job_loop/process_single_job.rs @@ -360,7 +360,9 @@ async fn old_dispatch( return Err(ProcessSingleJobError::InvalidJob( anyhow!("invalid job category for dispatch: {:?}", job.inference_category))) } - InferenceCategory::VideoGeneration | InferenceCategory::BackgroundRemoval => { + InferenceCategory::VideoGeneration + | InferenceCategory::BackgroundRemoval + | InferenceCategory::ObjectGeneration => { // These are handled by the new dispatch return Err(ProcessSingleJobError::InvalidJob( anyhow!("these job types do not support the old-style dispatch: {:?}", job.inference_category))) diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/fal_webhook_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/fal_webhook_handler.rs index 5ed7102c30..140c99f114 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/fal_webhook_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/fal_webhook_handler.rs @@ -2,6 +2,7 @@ use std::fmt; use std::sync::Arc; use crate::http_server::endpoints::webhooks::handle_image_payload::handle_image_payload; +use crate::http_server::endpoints::webhooks::handle_model_mesh_payload::handle_model_mesh_payload; use crate::http_server::endpoints::webhooks::handle_video_payload::handle_video_payload; use crate::state::server_state::ServerState; use actix_web::error::ResponseError; @@ -144,9 +145,8 @@ pub async fn fal_webhook_handler( handle_image_payload(payload_obj, &job, &server_state).await?; } else if payload_obj.contains_key("video") { handle_video_payload(payload_obj, &job, &server_state).await?; - } - if payload_obj.contains_key("video") { - handle_video_payload(payload_obj, &job, &server_state).await?; + } else if payload_obj.contains_key("model_mesh") { + handle_model_mesh_payload(payload_obj, &job, &server_state).await?; } } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/handle_model_mesh_payload.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/handle_model_mesh_payload.rs new file mode 100644 index 0000000000..2fa938b841 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/handle_model_mesh_payload.rs @@ -0,0 +1,102 @@ +use crate::http_server::endpoints::media_files::upload::upload_error::MediaFileUploadError; +use crate::state::server_state::ServerState; +use crate::util::http_download_url_to_bytes::http_download_url_to_bytes; +use crate::util::http_download_url_to_tempfile::http_download_url_to_tempfile; +use actix_web::web; +use anyhow::anyhow; +use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath; +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 errors::AnyhowResult; +use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes; +use images::image_info::image_info::ImageInfo; +use log::{info, warn}; +use mimetypes::mimetype_info::mimetype_info::MimetypeInfo; +use mysql_queries::queries::generic_inference::web::get_inference_job_by_fal_id::FalJobDetails; +use mysql_queries::queries::media_files::create::insert_builder::media_file_insert_builder::MediaFileInsertBuilder; +use serde_json::{Map, Value}; +use std::sync::Arc; + +const PREFIX : Option<&str> = Some("artcraft_"); + +#[derive(Deserialize, Debug)] +pub struct FalWebhookModelMesh { + pub content_type: Option, + pub file_name: Option, + pub file_size: Option, + pub url: Option, +} + +pub async fn handle_model_mesh_payload( + payload: &Map, + job: &FalJobDetails, + server_state: &ServerState, +) -> AnyhowResult<()> { + + let model_mesh_value = payload.get("model_mesh") + .ok_or_else(|| anyhow!("no `model_mesh` key in payload"))?; + + info!("Fal Model Mesh Payload: {:?}", model_mesh_value); + + let mesh: FalWebhookModelMesh = serde_json::from_value(model_mesh_value.clone())?; + + let mesh_url = mesh.url + .as_deref() + .ok_or_else(|| anyhow!("no `url` in image payload"))?; + + //let mime_type = mesh.content_type + // .as_deref() + // .ok_or_else(|| anyhow!("no `content_type` in mesh payload"))?; + + let file_bytes = http_download_url_to_bytes(mesh_url) + .await + .map_err(|e| anyhow!("Failed to download mesh: {:?}", e))?; + + let mimetype_info = MimetypeInfo::get_for_bytes(&file_bytes) + .ok_or_else(|| anyhow!("Failed to get mimetype info"))?; + + let mime_type = mimetype_info.mime_type(); + + info!("Mime type of mesh: {}", mime_type); + + let media_file_type = MediaFileType::try_from_mime_type(mime_type) + .ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?; + + let extension_with_period = mimetype_info.file_extension() + .map(|ext| ext.extension_with_period()) + .ok_or_else(|| anyhow!("Failed to get file extension from mimetype info"))?; + + let file_size_bytes = file_bytes.len(); + let file_hash = sha256_hash_bytes(&file_bytes)?; + + let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(&extension_with_period)); + + info!("Uploading media to bucket path: {}", public_upload_path.get_full_object_path_str()); + + server_state.public_bucket_client.upload_file_with_content_type_process( + public_upload_path.get_full_object_path_str(), + file_bytes.as_ref(), + &mime_type) + .await?; + + let media_token = MediaFileInsertBuilder::new() + .maybe_creator_user(job.maybe_creator_user_token.as_ref()) + .maybe_creator_anonymous_visitor(job.maybe_creator_anonymous_visitor_token.as_ref()) + .creator_ip_address(&job.creator_ip_address) + .public_bucket_directory_hash(&public_upload_path) + .media_file_class(MediaFileClass::Dimensional) + .media_file_type(media_file_type) + .media_file_origin_category(MediaFileOriginCategory::Inference) + //.media_file_origin_product_category(MediaFileOriginProductCategory::Unknown) + .mime_type(mime_type) + .file_size_bytes(file_size_bytes as u64) + .checksum_sha2(&file_hash) + .insert_pool(&server_state.mysql_pool) + .await?; + + info!("Mesh media file uploaded with token: {}", media_token); + + Ok(()) +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/mod.rs index 21ba1676fe..49cfe089d9 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/webhooks/mod.rs @@ -1,3 +1,4 @@ pub mod fal_webhook_handler; pub mod handle_image_payload; +pub mod handle_model_mesh_payload; pub mod handle_video_payload; \ No newline at end of file