mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
fallback + error handling in fal handlers
This commit is contained in:
1 parent
6b5f296048
commit
0cc7a9b434
5 files changed
+322
-113
No files matched your search
+61
-20
@@ -1,12 +1,11 @@
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::endpoints::webhooks::process_success::resolve_file_metadata::resolve_file_metadata;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
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_type::MediaFileType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::webhook_api::hydrated::hydrated_webhook_contents::ImageData;
|
||||
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
|
||||
use images::encoding::webp_bytes_to_png_bytes::webp_bytes_to_png_bytes;
|
||||
@@ -23,15 +22,21 @@ pub async fn process_image_payload(
|
||||
image_data: &ImageData,
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
let image_url = image_data.url
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("no `url` in image payload"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No `url` in image payload");
|
||||
AdvancedCommonWebError::server_error_with_message("no `url` in image payload")
|
||||
})?;
|
||||
|
||||
// Download with a retry if the first attempt returns suspiciously few bytes.
|
||||
let mut file_bytes = http_download_url_to_bytes(image_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download image: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download image from {}: {:?}", image_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
if file_bytes.len() <= 10 {
|
||||
warn!(
|
||||
@@ -41,16 +46,24 @@ pub async fn process_image_payload(
|
||||
);
|
||||
file_bytes = http_download_url_to_bytes(image_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download image on retry: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download image on retry from {}: {:?}", image_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
// Resolve mime type: magic bytes first, fal content_type as fallback.
|
||||
let metadata = resolve_file_metadata(&file_bytes, image_data.content_type.as_deref())
|
||||
.ok_or_else(|| anyhow!(
|
||||
"Could not determine file type for image (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
image_data.content_type,
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
warn!(
|
||||
"Could not determine file type for image (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
image_data.content_type,
|
||||
);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Could not determine file type for image (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(), image_data.content_type))
|
||||
})?;
|
||||
|
||||
info!("File type: {}, extension: {:?}, source: {:?}",
|
||||
metadata.mime_type, metadata.file_extension, metadata.source);
|
||||
@@ -59,10 +72,18 @@ pub async fn process_image_payload(
|
||||
FileExtension::Webp => {
|
||||
info!("Artcraft can't handle WebP images yet; converting to PNG...");
|
||||
|
||||
let converted = webp_bytes_to_png_bytes(&file_bytes)?;
|
||||
let converted = webp_bytes_to_png_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to convert WebP to PNG: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let converted_metadata = resolve_file_metadata(&converted, Some("image/png"))
|
||||
.ok_or_else(|| anyhow!("Failed to determine file type after WebP→PNG conversion"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Failed to determine file type after WebP→PNG conversion");
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
"Failed to determine file type after WebP→PNG conversion")
|
||||
})?;
|
||||
|
||||
info!("Converted file type: {}, extension: {:?}",
|
||||
converted_metadata.mime_type, converted_metadata.file_extension);
|
||||
@@ -93,16 +114,28 @@ async fn upload_single_image_bytes(
|
||||
file_bytes: &[u8],
|
||||
mime_type: &str,
|
||||
file_extension: FileExtension,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
|
||||
let media_file_type = MediaFileType::try_from_mime_type(mime_type)
|
||||
.ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Unsupported media file type: {}", mime_type);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Unsupported media file type: {}", mime_type))
|
||||
})?;
|
||||
|
||||
let extension_with_period = file_extension.extension_with_period();
|
||||
|
||||
let file_size_bytes = file_bytes.len();
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)?;
|
||||
let image_info = ImageInfo::decode_image_from_bytes(&file_bytes)?;
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to hash image bytes: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
let image_info = ImageInfo::decode_image_from_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to decode image info: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(&extension_with_period));
|
||||
|
||||
@@ -112,7 +145,11 @@ async fn upload_single_image_bytes(
|
||||
public_upload_path.get_full_object_path_str(),
|
||||
file_bytes.as_ref(),
|
||||
mime_type)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to upload image to bucket: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(job.maybe_creator_user_token.as_ref())
|
||||
@@ -130,8 +167,12 @@ async fn upload_single_image_bytes(
|
||||
.frame_height(image_info.height())
|
||||
.checksum_sha2(&file_hash)
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to insert image media file record: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Image media file uploaded with token: {}", media_token);
|
||||
|
||||
Ok(media_token)
|
||||
|
||||
+63
-23
@@ -1,12 +1,11 @@
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::endpoints::webhooks::process_success::resolve_file_metadata::resolve_file_metadata;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
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_type::MediaFileType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::webhook_api::hydrated::hydrated_webhook_contents::ImagesData;
|
||||
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
|
||||
use images::encoding::webp_bytes_to_png_bytes::webp_bytes_to_png_bytes;
|
||||
@@ -24,7 +23,7 @@ pub async fn process_images_payload(
|
||||
images_data: &[ImagesData],
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
) -> AnyhowResult<(Option<MediaFileToken>, Option<BatchGenerationToken>)> {
|
||||
) -> Result<(Option<MediaFileToken>, Option<BatchGenerationToken>), AdvancedCommonWebError> {
|
||||
|
||||
let mut maybe_media_token = None;
|
||||
|
||||
@@ -52,15 +51,22 @@ async fn upload_image(
|
||||
server_state: &ServerState,
|
||||
image: &ImagesData,
|
||||
maybe_batch_token: Option<&BatchGenerationToken>,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
let image_url = image.url
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("no `url` in image payload"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No `url` in image payload");
|
||||
AdvancedCommonWebError::server_error_with_message("no `url` in image payload")
|
||||
})?;
|
||||
|
||||
// Download with a retry if the first attempt returns suspiciously few bytes.
|
||||
let mut file_bytes = http_download_url_to_bytes(image_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download image: {:?}", e))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to download image from {}: {:?}", image_url, e);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Failed to download image: {:?}", e))
|
||||
})?;
|
||||
|
||||
if file_bytes.len() <= 10 {
|
||||
warn!(
|
||||
@@ -70,16 +76,25 @@ async fn upload_image(
|
||||
);
|
||||
file_bytes = http_download_url_to_bytes(image_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download image on retry: {:?}", e))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to download image on retry from {}: {:?}", image_url, e);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Failed to download image on retry: {:?}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
// Resolve mime type: magic bytes first, fal content_type as fallback.
|
||||
let metadata = resolve_file_metadata(&file_bytes, image.content_type.as_deref())
|
||||
.ok_or_else(|| anyhow!(
|
||||
"Could not determine file type for image (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
image.content_type,
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
warn!(
|
||||
"Could not determine file type for image (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
image.content_type,
|
||||
);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Could not determine file type for image (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(), image.content_type))
|
||||
})?;
|
||||
|
||||
info!("File type: {}, extension: {:?}, source: {:?}",
|
||||
metadata.mime_type, metadata.file_extension, metadata.source);
|
||||
@@ -88,10 +103,18 @@ async fn upload_image(
|
||||
FileExtension::Webp => {
|
||||
info!("Artcraft can't handle WebP images yet; converting to PNG...");
|
||||
|
||||
let converted = webp_bytes_to_png_bytes(&file_bytes)?;
|
||||
let converted = webp_bytes_to_png_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to convert WebP to PNG: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let converted_metadata = resolve_file_metadata(&converted, Some("image/png"))
|
||||
.ok_or_else(|| anyhow!("Failed to determine file type after WebP→PNG conversion"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Failed to determine file type after WebP→PNG conversion");
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
"Failed to determine file type after WebP→PNG conversion")
|
||||
})?;
|
||||
|
||||
info!("Converted file type: {}, extension: {:?}",
|
||||
converted_metadata.mime_type, converted_metadata.file_extension);
|
||||
@@ -99,7 +122,6 @@ async fn upload_image(
|
||||
upload_image_bytes(
|
||||
job,
|
||||
server_state,
|
||||
image,
|
||||
&converted,
|
||||
&converted_metadata.mime_type,
|
||||
converted_metadata.file_extension,
|
||||
@@ -110,7 +132,6 @@ async fn upload_image(
|
||||
upload_image_bytes(
|
||||
job,
|
||||
server_state,
|
||||
image,
|
||||
&file_bytes,
|
||||
&metadata.mime_type,
|
||||
metadata.file_extension,
|
||||
@@ -123,23 +144,34 @@ async fn upload_image(
|
||||
async fn upload_image_bytes(
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
_image: &ImagesData,
|
||||
file_bytes: &[u8],
|
||||
mime_type: &str,
|
||||
file_extension: FileExtension,
|
||||
maybe_batch_token: Option<&BatchGenerationToken>,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
|
||||
let media_file_type = MediaFileType::try_from_mime_type(mime_type)
|
||||
.ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Unsupported media file type: {}", mime_type);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Unsupported media file type: {}", mime_type))
|
||||
})?;
|
||||
|
||||
info!("MediaFileType: {:?}", media_file_type);
|
||||
|
||||
let extension_with_period = file_extension.extension_with_period();
|
||||
|
||||
let file_size_bytes = file_bytes.len();
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)?;
|
||||
let image_info = ImageInfo::decode_image_from_bytes(&file_bytes)?;
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to hash image bytes: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
let image_info = ImageInfo::decode_image_from_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to decode image info: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(&extension_with_period));
|
||||
|
||||
@@ -149,7 +181,11 @@ async fn upload_image_bytes(
|
||||
public_upload_path.get_full_object_path_str(),
|
||||
file_bytes.as_ref(),
|
||||
mime_type)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to upload image to bucket: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let mut query_builder = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(job.maybe_creator_user_token.as_ref())
|
||||
@@ -173,7 +209,11 @@ async fn upload_image_bytes(
|
||||
|
||||
let media_token = query_builder
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to insert image media file record: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
info!("Image media file uploaded with token: {} ; possible batch token: {:?}",
|
||||
media_token, maybe_batch_token);
|
||||
|
||||
+104
-34
@@ -1,13 +1,12 @@
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::endpoints::webhooks::process_success::resolve_file_metadata::resolve_file_metadata;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
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_engine_category::MediaFileEngineCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::webhook_api::hydrated::hydrated_webhook_contents::{ModelGlbData, ThumbnailData};
|
||||
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
|
||||
use log::{info, warn};
|
||||
@@ -20,7 +19,7 @@ const PREFIX : Option<&str> = Some("artcraft_");
|
||||
|
||||
/*
|
||||
Example payload, from Hunyuan 3d 3.0,
|
||||
|
||||
|
||||
{
|
||||
"model_glb": {
|
||||
"url": "https://v3b.fal.media/files/b/0a8afeff/8uc6VVLTpmVrupLqxxwOg_model.glb",
|
||||
@@ -52,7 +51,7 @@ const PREFIX : Option<&str> = Some("artcraft_");
|
||||
},
|
||||
"seed": null
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
pub async fn process_model_glb_payload(
|
||||
@@ -60,15 +59,21 @@ pub async fn process_model_glb_payload(
|
||||
maybe_thumbnail_data: Option<&ThumbnailData>,
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
let mesh_url = model_glb_data.url
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("no `url` in model glb payload"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No `url` in model glb payload");
|
||||
AdvancedCommonWebError::server_error_with_message("no `url` in model glb payload")
|
||||
})?;
|
||||
|
||||
// Download with a retry if the first attempt returns suspiciously few bytes.
|
||||
let mut file_bytes = http_download_url_to_bytes(mesh_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download mesh: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download mesh from {}: {:?}", mesh_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
if file_bytes.len() <= 10 {
|
||||
warn!(
|
||||
@@ -78,28 +83,45 @@ pub async fn process_model_glb_payload(
|
||||
);
|
||||
file_bytes = http_download_url_to_bytes(mesh_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download mesh on retry: {:?}", e))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to download mesh on retry from {}: {:?}", mesh_url, e);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Failed to download mesh on retry: {:?}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
// Resolve mime type: magic bytes first, fal content_type as fallback.
|
||||
let metadata = resolve_file_metadata(&file_bytes, model_glb_data.content_type.as_deref())
|
||||
.ok_or_else(|| anyhow!(
|
||||
"Could not determine file type for mesh (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
model_glb_data.content_type,
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
warn!(
|
||||
"Could not determine file type for mesh (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
model_glb_data.content_type,
|
||||
);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Could not determine file type for mesh (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(), model_glb_data.content_type))
|
||||
})?;
|
||||
|
||||
let mime_type = metadata.mime_type.as_str();
|
||||
|
||||
info!("Mime type of mesh: {}, source: {:?}", mime_type, metadata.source);
|
||||
|
||||
let media_file_type = MediaFileType::try_from_mime_type(mime_type)
|
||||
.ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Unsupported media file type: {}", mime_type);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Unsupported media file type: {}", mime_type))
|
||||
})?;
|
||||
|
||||
let extension_with_period = metadata.file_extension.extension_with_period();
|
||||
|
||||
let file_size_bytes = file_bytes.len();
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)?;
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to hash mesh bytes: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(&extension_with_period));
|
||||
|
||||
@@ -109,7 +131,11 @@ pub async fn process_model_glb_payload(
|
||||
public_upload_path.get_full_object_path_str(),
|
||||
file_bytes.as_ref(),
|
||||
&mime_type)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to upload mesh to bucket: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(job.maybe_creator_user_token.as_ref())
|
||||
@@ -126,17 +152,21 @@ pub async fn process_model_glb_payload(
|
||||
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
|
||||
.checksum_sha2(&file_hash)
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to insert glb media file record: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
info!("Glb media file uploaded with token: {}", media_token);
|
||||
|
||||
|
||||
let result = try_to_attach_thumbnail(
|
||||
maybe_thumbnail_data,
|
||||
job,
|
||||
server_state,
|
||||
&media_token,
|
||||
).await;
|
||||
|
||||
|
||||
// NB: Fail open
|
||||
if let Err(err) = result {
|
||||
warn!("Could not attach thumbnail as cover image to media file: {:?}", err);
|
||||
@@ -150,20 +180,31 @@ async fn try_to_attach_thumbnail(
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
glb_media_token: &MediaFileToken,
|
||||
) -> AnyhowResult<()> {
|
||||
) -> Result<(), AdvancedCommonWebError> {
|
||||
let thumbnail_data = maybe_thumbnail_data
|
||||
.ok_or_else(|| anyhow!("no thumbnail data in extracted contents"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No thumbnail data in extracted contents");
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
"no thumbnail data in extracted contents")
|
||||
})?;
|
||||
|
||||
info!("Fal Thumbnail Data: {:?}", thumbnail_data);
|
||||
|
||||
let thumbnail_url = thumbnail_data.url
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("no `url` in thumbnail payload"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No `url` in thumbnail payload");
|
||||
AdvancedCommonWebError::server_error_with_message("no `url` in thumbnail payload")
|
||||
})?;
|
||||
|
||||
// Download with a retry if the first attempt returns suspiciously few bytes.
|
||||
let mut file_bytes = http_download_url_to_bytes(thumbnail_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download thumbnail image: {:?}", e))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to download thumbnail image from {}: {:?}", thumbnail_url, e);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Failed to download thumbnail image: {:?}", e))
|
||||
})?;
|
||||
|
||||
if file_bytes.len() <= 10 {
|
||||
warn!(
|
||||
@@ -173,28 +214,45 @@ async fn try_to_attach_thumbnail(
|
||||
);
|
||||
file_bytes = http_download_url_to_bytes(thumbnail_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download thumbnail on retry: {:?}", e))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to download thumbnail on retry from {}: {:?}", thumbnail_url, e);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Failed to download thumbnail on retry: {:?}", e))
|
||||
})?;
|
||||
}
|
||||
|
||||
// Resolve mime type: magic bytes first, fal content_type as fallback.
|
||||
let metadata = resolve_file_metadata(&file_bytes, thumbnail_data.content_type.as_deref())
|
||||
.ok_or_else(|| anyhow!(
|
||||
"Could not determine file type for thumbnail (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
thumbnail_data.content_type,
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
warn!(
|
||||
"Could not determine file type for thumbnail (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
thumbnail_data.content_type,
|
||||
);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Could not determine file type for thumbnail (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(), thumbnail_data.content_type))
|
||||
})?;
|
||||
|
||||
let mime_type = metadata.mime_type.as_str();
|
||||
|
||||
info!("Mime type of thumbnail image: {}, source: {:?}", mime_type, metadata.source);
|
||||
|
||||
let media_file_type = MediaFileType::try_from_mime_type(mime_type)
|
||||
.ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Unsupported thumbnail media file type: {}", mime_type);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Unsupported media file type: {}", mime_type))
|
||||
})?;
|
||||
|
||||
let extension_with_period = metadata.file_extension.extension_with_period();
|
||||
|
||||
let file_size_bytes = file_bytes.len();
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)?;
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to hash thumbnail bytes: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(&extension_with_period));
|
||||
|
||||
@@ -204,7 +262,11 @@ async fn try_to_attach_thumbnail(
|
||||
public_upload_path.get_full_object_path_str(),
|
||||
file_bytes.as_ref(),
|
||||
&mime_type)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to upload thumbnail to bucket: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let thumbnail_media_token = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(job.maybe_creator_user_token.as_ref())
|
||||
@@ -222,7 +284,11 @@ async fn try_to_attach_thumbnail(
|
||||
.is_intermediate_system_file(true)
|
||||
.checksum_sha2(&file_hash)
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to insert thumbnail media file record: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
let query_result = set_media_file_cover_image(UpdateArgs {
|
||||
media_file_token: glb_media_token,
|
||||
@@ -230,5 +296,9 @@ async fn try_to_attach_thumbnail(
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await;
|
||||
|
||||
if let Err(err) = query_result {
|
||||
warn!("Failed to set cover image on media file {}: {:?}", glb_media_token, err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+45
-16
@@ -1,13 +1,12 @@
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::endpoints::webhooks::process_success::resolve_file_metadata::resolve_file_metadata;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
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_engine_category::MediaFileEngineCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::webhook_api::hydrated::hydrated_webhook_contents::ModelMeshData;
|
||||
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
|
||||
use log::{info, warn};
|
||||
@@ -21,15 +20,21 @@ pub async fn process_model_mesh_payload(
|
||||
model_mesh_data: &ModelMeshData,
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
let mesh_url = model_mesh_data.url
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("no `url` in model mesh payload"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No `url` in model mesh payload");
|
||||
AdvancedCommonWebError::server_error_with_message("no `url` in model mesh payload")
|
||||
})?;
|
||||
|
||||
// Download with a retry if the first attempt returns suspiciously few bytes.
|
||||
let mut file_bytes = http_download_url_to_bytes(mesh_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download mesh: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download mesh from {}: {:?}", mesh_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
if file_bytes.len() <= 10 {
|
||||
warn!(
|
||||
@@ -39,28 +44,44 @@ pub async fn process_model_mesh_payload(
|
||||
);
|
||||
file_bytes = http_download_url_to_bytes(mesh_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download mesh on retry: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download mesh on retry from {}: {:?}", mesh_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
// Resolve mime type: magic bytes first, fal content_type as fallback.
|
||||
let metadata = resolve_file_metadata(&file_bytes, model_mesh_data.content_type.as_deref())
|
||||
.ok_or_else(|| anyhow!(
|
||||
"Could not determine file type for mesh (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
model_mesh_data.content_type,
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
warn!(
|
||||
"Could not determine file type for mesh (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
model_mesh_data.content_type,
|
||||
);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Could not determine file type for mesh (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(), model_mesh_data.content_type))
|
||||
})?;
|
||||
|
||||
let mime_type = metadata.mime_type.as_str();
|
||||
|
||||
info!("Mime type of mesh: {}, source: {:?}", mime_type, metadata.source);
|
||||
|
||||
let media_file_type = MediaFileType::try_from_mime_type(mime_type)
|
||||
.ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Unsupported media file type: {}", mime_type);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Unsupported media file type: {}", mime_type))
|
||||
})?;
|
||||
|
||||
let extension_with_period = metadata.file_extension.extension_with_period();
|
||||
|
||||
let file_size_bytes = file_bytes.len();
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)?;
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)
|
||||
.map_err(|err| {
|
||||
warn!("Failed to hash mesh bytes: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(&extension_with_period));
|
||||
|
||||
@@ -70,7 +91,11 @@ pub async fn process_model_mesh_payload(
|
||||
public_upload_path.get_full_object_path_str(),
|
||||
file_bytes.as_ref(),
|
||||
&mime_type)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to upload mesh to bucket: {:?}", err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(job.maybe_creator_user_token.as_ref())
|
||||
@@ -87,8 +112,12 @@ pub async fn process_model_mesh_payload(
|
||||
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
|
||||
.checksum_sha2(&file_hash)
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Failed to insert mesh media file record: {:?}", err);
|
||||
AdvancedCommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
info!("Mesh media file uploaded with token: {}", media_token);
|
||||
|
||||
Ok(media_token)
|
||||
|
||||
+49
-20
@@ -1,12 +1,11 @@
|
||||
use crate::http_server::common_responses::advanced_common_web_error::AdvancedCommonWebError;
|
||||
use crate::http_server::endpoints::webhooks::process_success::resolve_file_metadata::resolve_file_metadata;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::http_download_url_to_bytes::http_download_url_to_bytes;
|
||||
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_type::MediaFileType;
|
||||
use errors::AnyhowResult;
|
||||
use fal_client::webhook_api::hydrated::hydrated_webhook_contents::VideoData;
|
||||
use filesys::path_to_string::path_to_string;
|
||||
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
|
||||
@@ -25,15 +24,21 @@ pub async fn process_video_payload(
|
||||
video_data: &VideoData,
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
) -> Result<MediaFileToken, AdvancedCommonWebError> {
|
||||
let video_url = video_data.url
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow!("no `url` in video payload"))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("No `url` in video payload");
|
||||
AdvancedCommonWebError::server_error_with_message("no `url` in video payload")
|
||||
})?;
|
||||
|
||||
// Download with a retry if the first attempt returns suspiciously few bytes.
|
||||
let mut file_bytes = http_download_url_to_bytes(video_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download video: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download video from {}: {:?}", video_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
|
||||
if file_bytes.len() <= 10 {
|
||||
warn!(
|
||||
@@ -43,16 +48,24 @@ pub async fn process_video_payload(
|
||||
);
|
||||
file_bytes = http_download_url_to_bytes(video_url)
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to download video on retry: {:?}", e))?;
|
||||
.map_err(|err| {
|
||||
warn!("Failed to download video on retry from {}: {:?}", video_url, err);
|
||||
AdvancedCommonWebError::from_anyhow_error(err)
|
||||
})?;
|
||||
}
|
||||
|
||||
// Resolve mime type: magic bytes first, fal content_type as fallback.
|
||||
let metadata = resolve_file_metadata(&file_bytes, video_data.content_type.as_deref())
|
||||
.ok_or_else(|| anyhow!(
|
||||
"Could not determine file type for video (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
video_data.content_type,
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
warn!(
|
||||
"Could not determine file type for video (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(),
|
||||
video_data.content_type,
|
||||
);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Could not determine file type for video (bytes: {}, fal content_type: {:?})",
|
||||
file_bytes.len(), video_data.content_type))
|
||||
})?;
|
||||
|
||||
info!("File type: {}, extension: {:?}, source: {:?}",
|
||||
metadata.mime_type, metadata.file_extension, metadata.source);
|
||||
@@ -60,26 +73,34 @@ pub async fn process_video_payload(
|
||||
let mime_type = metadata.mime_type.as_str();
|
||||
|
||||
let media_file_type = MediaFileType::try_from_mime_type(mime_type)
|
||||
.ok_or_else(|| anyhow!("Unsupported media file type: {}", mime_type))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Unsupported media file type: {}", mime_type);
|
||||
AdvancedCommonWebError::server_error_with_message(
|
||||
&format!("Unsupported media file type: {}", mime_type))
|
||||
})?;
|
||||
|
||||
let extension_with_period = metadata.file_extension.extension_with_period();
|
||||
|
||||
let file_size_bytes = file_bytes.len();
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)?;
|
||||
let file_hash = sha256_hash_bytes(&file_bytes)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to hash video bytes: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let mut maybe_duration_millis = None;
|
||||
let mut maybe_frame_width = None;
|
||||
let mut maybe_frame_height = None;
|
||||
|
||||
|
||||
if let Ok(mut file) = NamedTempFile::new() {
|
||||
// NB: Fallible
|
||||
// NB: Fallible
|
||||
let _r = file.write_all(&file_bytes);
|
||||
|
||||
|
||||
if let Ok(video_info) = ffprobe_get_info(&file.path()) {
|
||||
maybe_duration_millis = video_info.duration
|
||||
.as_ref()
|
||||
.map(|duration| duration.millis as u64);
|
||||
|
||||
|
||||
maybe_frame_width = video_info.dimensions
|
||||
.as_ref()
|
||||
.map(|ref dims| dims.width as u32);
|
||||
@@ -98,7 +119,11 @@ pub async fn process_video_payload(
|
||||
public_upload_path.get_full_object_path_str(),
|
||||
file_bytes.as_ref(),
|
||||
&mime_type)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to upload video to bucket: {:?}", e);
|
||||
AdvancedCommonWebError::from_anyhow_error(e)
|
||||
})?;
|
||||
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
.maybe_creator_user(job.maybe_creator_user_token.as_ref())
|
||||
@@ -117,8 +142,12 @@ pub async fn process_video_payload(
|
||||
.checksum_sha2(&file_hash)
|
||||
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Failed to insert video media file record: {:?}", e);
|
||||
AdvancedCommonWebError::from_error(e)
|
||||
})?;
|
||||
|
||||
info!("Video media file uploaded with token: {}", media_token);
|
||||
|
||||
let thumbnail_task_result =
|
||||
|
||||
Reference in new issue
Block a user