eventing to support generation batches + inpainting results (#772)
This commit is contained in:
1 parent
ea7259d5e8
commit
2007c7af91
63 files changed
+2090
-149
No files matched your search
+28
-17
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n jobs.token as `job_token: tokens::tokens::generic_inference_jobs::InferenceJobToken`,\n\n jobs.status as `status: enums::common::job_status_plus::JobStatusPlus`,\n jobs.attempt_count,\n\n jobs.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n jobs.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n jobs.creator_ip_address,\n\n jobs.product_category as `product_category: enums::by_table::generic_inference_jobs::inference_job_product_category::InferenceJobProductCategory`,\n jobs.inference_category as `inference_category: enums::by_table::generic_inference_jobs::inference_category::InferenceCategory`,\n jobs.maybe_model_type,\n jobs.maybe_model_token,\n jobs.maybe_raw_inference_text,\n jobs.maybe_inference_args,\n\n jobs.frontend_failure_category as `maybe_frontend_failure_category: enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory`,\n\n jobs.on_success_result_entity_type as maybe_result_entity_type,\n jobs.on_success_result_entity_token as maybe_result_entity_token,\n\n model_weights.title as maybe_model_weights_title,\n tts_models.title as maybe_tts_model_title,\n voice_conversion_models.title as maybe_voice_conversion_model_title,\n\n tts_results.public_bucket_wav_audio_path as maybe_tts_public_bucket_path,\n voice_conversion_results.public_bucket_hash as maybe_voice_conversion_public_bucket_hash,\n\n media_files.public_bucket_directory_hash as maybe_media_file_public_bucket_directory_hash,\n media_files.maybe_public_bucket_prefix as maybe_media_file_public_bucket_prefix,\n media_files.maybe_public_bucket_extension as maybe_media_file_public_bucket_extension,\n\n jobs.assigned_worker as maybe_assigned_worker,\n jobs.assigned_cluster as maybe_assigned_cluster,\n\n jobs.is_keepalive_required,\n\n jobs.created_at,\n jobs.updated_at,\n\n jobs.first_started_at as maybe_first_started_at,\n jobs.successfully_completed_at as maybe_successfully_completed_at,\n\n NOW() as database_clock\n\nFROM generic_inference_jobs as jobs\n\nLEFT OUTER JOIN model_weights ON jobs.maybe_model_token = model_weights.token\nLEFT OUTER JOIN tts_models ON jobs.maybe_model_token = tts_models.token\nLEFT OUTER JOIN voice_conversion_models ON jobs.maybe_model_token = voice_conversion_models.token\n\nLEFT OUTER JOIN tts_results ON jobs.on_success_result_entity_token = tts_results.token\nLEFT OUTER JOIN voice_conversion_results ON jobs.on_success_result_entity_token = voice_conversion_results.token\nLEFT OUTER JOIN media_files ON jobs.on_success_result_entity_token = media_files.token\n\nWHERE jobs.token = ?\n ",
|
||||
"query": "\nSELECT\n jobs.token as `job_token: tokens::tokens::generic_inference_jobs::InferenceJobToken`,\n\n jobs.status as `status: enums::common::job_status_plus::JobStatusPlus`,\n jobs.attempt_count,\n\n jobs.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n jobs.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n jobs.creator_ip_address,\n\n jobs.product_category as `product_category: enums::by_table::generic_inference_jobs::inference_job_product_category::InferenceJobProductCategory`,\n jobs.inference_category as `inference_category: enums::by_table::generic_inference_jobs::inference_category::InferenceCategory`,\n jobs.maybe_model_type,\n jobs.maybe_model_token,\n jobs.maybe_raw_inference_text,\n jobs.maybe_inference_args,\n\n jobs.frontend_failure_category as `maybe_frontend_failure_category: enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory`,\n\n jobs.on_success_result_entity_type as maybe_result_entity_type,\n jobs.on_success_result_entity_token as maybe_result_entity_token,\n jobs.on_success_result_batch_token as maybe_result_batch_token,\n\n model_weights.title as maybe_model_weights_title,\n tts_models.title as maybe_tts_model_title,\n voice_conversion_models.title as maybe_voice_conversion_model_title,\n\n tts_results.public_bucket_wav_audio_path as maybe_tts_public_bucket_path,\n voice_conversion_results.public_bucket_hash as maybe_voice_conversion_public_bucket_hash,\n\n media_files.public_bucket_directory_hash as maybe_media_file_public_bucket_directory_hash,\n media_files.maybe_public_bucket_prefix as maybe_media_file_public_bucket_prefix,\n media_files.maybe_public_bucket_extension as maybe_media_file_public_bucket_extension,\n\n jobs.assigned_worker as maybe_assigned_worker,\n jobs.assigned_cluster as maybe_assigned_cluster,\n\n jobs.is_keepalive_required,\n\n jobs.created_at,\n jobs.updated_at,\n\n jobs.first_started_at as maybe_first_started_at,\n jobs.successfully_completed_at as maybe_successfully_completed_at,\n\n NOW() as database_clock\n\nFROM generic_inference_jobs as jobs\n\nLEFT OUTER JOIN model_weights ON jobs.maybe_model_token = model_weights.token\nLEFT OUTER JOIN tts_models ON jobs.maybe_model_token = tts_models.token\nLEFT OUTER JOIN voice_conversion_models ON jobs.maybe_model_token = voice_conversion_models.token\n\nLEFT OUTER JOIN tts_results ON jobs.on_success_result_entity_token = tts_results.token\nLEFT OUTER JOIN voice_conversion_results ON jobs.on_success_result_entity_token = voice_conversion_results.token\nLEFT OUTER JOIN media_files ON jobs.on_success_result_entity_token = media_files.token\n\nWHERE jobs.token = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -155,6 +155,16 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "maybe_result_batch_token",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_model_weights_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -164,7 +174,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "maybe_tts_model_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -174,7 +184,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 18,
|
||||
"name": "maybe_voice_conversion_model_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -184,7 +194,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 19,
|
||||
"name": "maybe_tts_public_bucket_path",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -194,7 +204,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 20,
|
||||
"name": "maybe_voice_conversion_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -204,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 21,
|
||||
"name": "maybe_media_file_public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -214,7 +224,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 22,
|
||||
"name": "maybe_media_file_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -224,7 +234,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 23,
|
||||
"name": "maybe_media_file_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -234,7 +244,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 24,
|
||||
"name": "maybe_assigned_worker",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -244,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 25,
|
||||
"name": "maybe_assigned_cluster",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -254,7 +264,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 26,
|
||||
"name": "is_keepalive_required",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -264,7 +274,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 27,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -274,7 +284,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 28,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -284,7 +294,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 29,
|
||||
"name": "maybe_first_started_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -294,7 +304,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 30,
|
||||
"name": "maybe_successfully_completed_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -304,7 +314,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 31,
|
||||
"name": "database_clock",
|
||||
"type_info": {
|
||||
"type": "Datetime",
|
||||
@@ -343,6 +353,7 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -351,5 +362,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "90366e1705a0343bdfe4fc472d701b8e1dbd3a2dabedaadb633ac3542de72fa7"
|
||||
"hash": "243afe0bc574f9f233c7797add9faf96440e2bafd52032e07951272fb1ec78b1"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nUPDATE generic_inference_jobs\nSET\n status = ?,\n on_success_result_entity_type = ?,\n on_success_result_entity_token = ?,\n on_success_result_batch_token = ?,\n failure_reason = NULL,\n internal_debugging_failure_reason = NULL,\n success_execution_millis = NULL,\n success_inference_execution_millis = NULL,\n retry_at = NULL,\n successfully_completed_at = NOW()\nWHERE token = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 5
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7e223e284b992b6eaf31bec057bae00fc4e085d68b064e370f0e3d92b82049b2"
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_mime_type,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n\n m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,\n\n m.maybe_title,\n m.maybe_text_transcript,\n\n m.maybe_origin_filename,\n\n m.maybe_duration_millis,\n\n m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,\n prompts.maybe_other_args as maybe_other_prompt_args,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.is_user_upload,\n m.is_intermediate_system_file,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nWHERE\n m.maybe_batch_token = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "maybe_mime_type",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_text_transcript",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
"flags": "BLOB | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 262140
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_origin_filename",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_duration_millis",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "maybe_other_prompt_args",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
"flags": "BLOB | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 262140
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "is_user_upload",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "is_intermediate_system_file",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "971471f349bcd22364b16b5d6c68e807ed897101eec1e8a62a7b8fab3359fa0c"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nUPDATE generic_inference_jobs\nSET\n status = ?,\n on_success_result_entity_type = ?,\n on_success_result_entity_token = ?,\n failure_reason = NULL,\n internal_debugging_failure_reason = NULL,\n success_execution_millis = NULL,\n success_inference_execution_millis = NULL,\n retry_at = NULL,\n successfully_completed_at = NOW()\nWHERE token = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f68a8917e639418a0bb952bef606c96299001e143c745ee30df99b72d1235d35"
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.maybe_mime_type,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,\n\n m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,\n\n m.maybe_title,\n m.maybe_text_transcript,\n\n m.maybe_origin_filename,\n\n m.maybe_duration_millis,\n\n m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,\n prompts.maybe_other_args as maybe_other_prompt_args,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.is_user_upload,\n m.is_intermediate_system_file,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nWHERE\n m.maybe_batch_token = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "maybe_mime_type",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_text_transcript",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
"flags": "BLOB | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 262140
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_origin_filename",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_duration_millis",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
"flags": "",
|
||||
"char_set": 63,
|
||||
"max_size": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "maybe_other_prompt_args",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
"flags": "BLOB | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 262140
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "is_user_upload",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "is_intermediate_system_file",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY",
|
||||
"char_set": 63,
|
||||
"max_size": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f75fedb86a93ac2542315ddb7ef0d46aa42ecc25a3cf9856219114064183337a"
|
||||
}
|
||||
Generated
+1
@@ -709,6 +709,7 @@ version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"enums",
|
||||
"enums_public",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
||||
@@ -7,5 +7,4 @@ pub mod jobs;
|
||||
pub mod media_files;
|
||||
pub mod prompts;
|
||||
pub mod recipes;
|
||||
pub mod shared_response_types;
|
||||
pub mod utils;
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::error::api_error::ApiError;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::shared_response_types::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::shared_response_types::media_links::MediaLinks;
|
||||
use crate::shared_response_types::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::shared_response_types::user_details_light::UserDetailsLight;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::filter_bad_response::filter_bad_response;
|
||||
use crate::utils::http_get_anonymous::http_get_anonymous;
|
||||
use artcraft_api_defs::common::responses::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use artcraft_api_defs::common::responses::media_links::MediaLinks;
|
||||
use artcraft_api_defs::common::responses::simple_entity_stats::SimpleEntityStats;
|
||||
use artcraft_api_defs::common::responses::user_details_light::UserDetailsLight;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType;
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::basic_json_get_request::basic_json_get_request;
|
||||
use artcraft_api_defs::media_file::list_batch_generated_media_files::{ListBatchGeneratedReduxMediaFilesSuccessResponse, LIST_BATCH_GENERATED_REDUX_MEDIA_FILES_URL_PATH};
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
|
||||
pub async fn list_batch_generated_redux_media_files(
|
||||
api_host: &ApiHost,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
batch_token: &BatchGenerationToken,
|
||||
) -> Result<ListBatchGeneratedReduxMediaFilesSuccessResponse, StorytellerError> {
|
||||
let url = format!("{}/{}", LIST_BATCH_GENERATED_REDUX_MEDIA_FILES_URL_PATH, batch_token.to_string());
|
||||
Ok(basic_json_get_request(
|
||||
api_host,
|
||||
&url,
|
||||
maybe_creds,
|
||||
).await?)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod get_media_file;
|
||||
pub mod list_batch_generated_redux_media_files;
|
||||
pub mod upload_image_media_file_from_bytes;
|
||||
pub mod upload_image_media_file_from_file;
|
||||
pub mod upload_new_engine_asset_from_file;
|
||||
|
||||
-4
@@ -2,10 +2,6 @@ use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::api_error::ApiError;
|
||||
use crate::error::client_error::ClientError;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::shared_response_types::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::shared_response_types::media_links::MediaLinks;
|
||||
use crate::shared_response_types::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::shared_response_types::user_details_light::UserDetailsLight;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::constants::{APPLICATION_JSON, USER_AGENT};
|
||||
use crate::utils::filter_bad_response::filter_bad_response;
|
||||
|
||||
-4
@@ -2,10 +2,6 @@ use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::api_error::ApiError;
|
||||
use crate::error::client_error::ClientError;
|
||||
use crate::error::storyteller_error::StorytellerError;
|
||||
use crate::shared_response_types::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::shared_response_types::media_links::MediaLinks;
|
||||
use crate::shared_response_types::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::shared_response_types::user_details_light::UserDetailsLight;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::constants::{APPLICATION_JSON, USER_AGENT};
|
||||
use crate::utils::filter_bad_response::filter_bad_response;
|
||||
|
||||
-4
@@ -1,9 +1,5 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::api_error::ApiError;
|
||||
use crate::shared_response_types::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::shared_response_types::media_links::MediaLinks;
|
||||
use crate::shared_response_types::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::shared_response_types::user_details_light::UserDetailsLight;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::constants::{APPLICATION_JSON, USER_AGENT};
|
||||
use crate::utils::filter_bad_response::filter_bad_response;
|
||||
|
||||
-4
@@ -1,9 +1,5 @@
|
||||
use crate::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::error::api_error::ApiError;
|
||||
use crate::shared_response_types::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::shared_response_types::media_links::MediaLinks;
|
||||
use crate::shared_response_types::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::shared_response_types::user_details_light::UserDetailsLight;
|
||||
use crate::utils::api_host::ApiHost;
|
||||
use crate::utils::constants::{APPLICATION_JSON, USER_AGENT};
|
||||
use crate::utils::filter_bad_response::filter_bad_response;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
use serde_derive::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
/// Links to media file locations (bucket, CDN, etc.)
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MediaLinks {
|
||||
/// Primary link to the asset via the CDN.
|
||||
pub cdn_url: Url,
|
||||
|
||||
/// Template to construct thumbnail URLs.
|
||||
/// Replace the string `{WIDTH}` with the desired width.
|
||||
/// Only relevant for image media files. (Video media files instead have
|
||||
/// video previews, which, in turn, have their own thumbnail templates.)
|
||||
pub maybe_thumbnail_template: Option<String>,
|
||||
|
||||
/// Video preview images (still and animated gif) for mp4 video files.
|
||||
/// These are only set for video media files.
|
||||
/// These are not set for "cover images" that are gif or webms, but rather video files.
|
||||
pub maybe_video_previews: Option<VideoPreviews>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct VideoPreviews {
|
||||
/// A static single frame preview image of the video.
|
||||
pub still: Url,
|
||||
/// An animated gif preview of the video.
|
||||
pub animated: Url,
|
||||
/// A template used to construct the still thumbnail URL.
|
||||
/// Replace the string `{WIDTH}` with the desired width.
|
||||
pub still_thumbnail_template: String,
|
||||
/// A template used to construct the animated thumbnail URL.
|
||||
/// Replace the string `{WIDTH}` with the desired width.
|
||||
pub animated_thumbnail_template: String,
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
pub mod cover_image_links;
|
||||
pub mod media_file_cover_image_details;
|
||||
pub mod media_links;
|
||||
pub mod simple_entity_stats;
|
||||
pub mod user_details_light;
|
||||
+22
-1
@@ -41,6 +41,7 @@ use storyteller_client::error::storyteller_error::StorytellerError;
|
||||
use storyteller_client::media_files::get_media_file::get_media_file;
|
||||
use storyteller_client::utils::api_host::ApiHost;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@@ -72,6 +73,21 @@ pub struct EnqueueContextualEditImageCommand {
|
||||
|
||||
/// Image quality.
|
||||
pub image_quality: Option<EditImageQuality>,
|
||||
|
||||
/// OPTIONAL.
|
||||
/// Name of the frontend caller.
|
||||
/// We'll use this to selectively trigger events.
|
||||
pub frontend_caller: Option<TauriCommandCaller>,
|
||||
|
||||
/// OPTIONAL.
|
||||
/// A frontend-defined identifier that we'll send back to the frontend
|
||||
/// as a Tauri event on task completion.
|
||||
pub frontend_subscriber_id: Option<String>,
|
||||
|
||||
/// OPTIONAL.
|
||||
/// A frontend-defined payload that we'll send back to the frontend
|
||||
/// as a Tauri event on task completion.
|
||||
pub frontend_subscriber_payload: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Copy, Clone)]
|
||||
@@ -237,7 +253,12 @@ pub async fn handle_request(
|
||||
};
|
||||
|
||||
let result = success_event
|
||||
.insert_into_task_database(task_database)
|
||||
.insert_into_task_database_with_frontend_payload(
|
||||
task_database,
|
||||
request.frontend_caller.clone(),
|
||||
request.frontend_subscriber_id.as_deref(),
|
||||
request.frontend_subscriber_payload.as_deref(),
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Err(err) = result {
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ pub async fn handle_flux_dev_juggernaut_inpaint_artcraft(
|
||||
provider: GenerationProvider::Artcraft,
|
||||
model: Some(GenerationModel::FluxDevJuggernaut),
|
||||
provider_job_id: Some(job_id.to_string()),
|
||||
task_type: TaskType::ImageGeneration,
|
||||
task_type: TaskType::ImageInpaintEdit,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ pub async fn handle_flux_pro_1_inpaint_artcraft(
|
||||
provider: GenerationProvider::Artcraft,
|
||||
model: Some(GenerationModel::FluxProKontextMax),
|
||||
provider_job_id: Some(job_id.to_string()),
|
||||
task_type: TaskType::ImageGeneration,
|
||||
task_type: TaskType::ImageInpaintEdit,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ impl TaskEnqueueSuccess{
|
||||
TaskType::VideoGeneration => GenerationAction::GenerateVideo,
|
||||
TaskType::BackgroundRemoval => GenerationAction::RemoveBackground,
|
||||
TaskType::ObjectGeneration => GenerationAction::ImageTo3d,
|
||||
TaskType::ImageInpaintEdit => GenerationAction::ImageInpaintEdit,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
use crate::core::events::basic_sendable_event_trait::{BasicEventStatus, BasicSendableEvent};
|
||||
use enums::tauri::ux::tauri_event_name::TauriEventName;
|
||||
use serde_derive::Serialize;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ImageEditCompleteEvent {
|
||||
// pub source_media_token: MediaFileToken,
|
||||
pub edited_images: Vec<EditedImage>,
|
||||
pub maybe_frontend_subscriber_id: Option<String>,
|
||||
pub maybe_frontend_subscriber_payload: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct EditedImage {
|
||||
pub media_token: MediaFileToken,
|
||||
pub cdn_url: Url,
|
||||
pub maybe_thumbnail_template: Option<String>,
|
||||
}
|
||||
|
||||
impl BasicSendableEvent for ImageEditCompleteEvent {
|
||||
const FRONTEND_EVENT_NAME: TauriEventName = TauriEventName::ImageEditCompleteEvent;
|
||||
const EVENT_STATUS: BasicEventStatus = BasicEventStatus::Success;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod canvas_background_removal_complete_event;
|
||||
pub mod image_edit_complete_event;
|
||||
pub mod refresh_account_state_event;
|
||||
pub mod show_provider_login_modal_event;
|
||||
@@ -65,4 +65,6 @@ pub enum GenerationAction {
|
||||
RemoveBackground,
|
||||
#[serde(rename = "image_to_3d")]
|
||||
ImageTo3d,
|
||||
#[serde(rename = "image_inpaint_edit")]
|
||||
ImageInpaintEdit,
|
||||
}
|
||||
@@ -7,5 +7,6 @@ pub fn to_generation_action(task_type: TaskType) -> GenerationAction {
|
||||
TaskType::VideoGeneration => GenerationAction::GenerateVideo,
|
||||
TaskType::BackgroundRemoval => GenerationAction::RemoveBackground,
|
||||
TaskType::ObjectGeneration => GenerationAction::ImageTo3d,
|
||||
TaskType::ImageInpaintEdit => GenerationAction::ImageInpaintEdit,
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
use crate::core::events::basic_sendable_event_trait::BasicSendableEvent;
|
||||
use crate::core::events::functional_events::canvas_background_removal_complete_event::CanvasBackgroundRemovalCompleteEvent;
|
||||
use crate::core::events::functional_events::image_edit_complete_event::{EditedImage, ImageEditCompleteEvent};
|
||||
use crate::core::events::generation_events::generation_complete_event::GenerationCompleteEvent;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use anyhow::anyhow;
|
||||
use artcraft_api_defs::jobs::list_session_jobs::{ListSessionJobsItem, ListSessionResultDetailsResponse};
|
||||
use enums::tauri::tasks::task_type::TaskType;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, warn};
|
||||
use sqlite_tasks::queries::list_tasks_by_provider_and_tokens::{list_tasks_by_provider_and_tokens, ListTasksArgs, Task};
|
||||
use storyteller_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use storyteller_client::media_files::list_batch_generated_redux_media_files::list_batch_generated_redux_media_files;
|
||||
use tauri::AppHandle;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub async fn maybe_handle_inpainting_complete_event(
|
||||
app: &AppHandle,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
task: &Task,
|
||||
job: &ListSessionJobsItem,
|
||||
) -> AnyhowResult<()> {
|
||||
|
||||
match task.task_type {
|
||||
TaskType::ImageInpaintEdit => {} // NB: Fall-through
|
||||
_ => return Ok(()),
|
||||
}
|
||||
|
||||
match task.frontend_caller {
|
||||
Some(TauriCommandCaller::ImageEditor) => {} // NB: Fall-through
|
||||
_ => return Ok(()),
|
||||
}
|
||||
|
||||
let job_result = match job.maybe_result {
|
||||
Some(ref res) => res,
|
||||
None => {
|
||||
error!("Job result is None for task: {:?}", task);
|
||||
return Ok(()); // No result, nothing to do
|
||||
},
|
||||
};
|
||||
|
||||
let event = match job_result.maybe_batch_token.as_ref() {
|
||||
Some(batch_token) => {
|
||||
handle_batch(
|
||||
app,
|
||||
app_env_configs,
|
||||
maybe_creds,
|
||||
task,
|
||||
job,
|
||||
job_result,
|
||||
batch_token
|
||||
).await?
|
||||
}
|
||||
None => {
|
||||
handle_single(
|
||||
app,
|
||||
task,
|
||||
job,
|
||||
job_result,
|
||||
).await?
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = event.send(&app) {
|
||||
error!("Failed to send ImageEditCompleteEvent: {:?}", err); // Fail open
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_single(
|
||||
app: &AppHandle,
|
||||
task: &Task,
|
||||
job: &ListSessionJobsItem,
|
||||
job_result: &ListSessionResultDetailsResponse,
|
||||
) -> AnyhowResult<ImageEditCompleteEvent> {
|
||||
Ok(ImageEditCompleteEvent {
|
||||
edited_images: vec![EditedImage {
|
||||
media_token: MediaFileToken::new_from_str(&job_result.entity_token),
|
||||
cdn_url: job_result.media_links.cdn_url.clone(),
|
||||
maybe_thumbnail_template: job_result.media_links.maybe_thumbnail_template.clone(),
|
||||
}],
|
||||
maybe_frontend_subscriber_id: task.frontend_subscriber_id.clone(),
|
||||
maybe_frontend_subscriber_payload: task.frontend_subscriber_payload.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn handle_batch(
|
||||
app: &AppHandle,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
maybe_creds: Option<&StorytellerCredentialSet>,
|
||||
task: &Task,
|
||||
job: &ListSessionJobsItem,
|
||||
job_result: &ListSessionResultDetailsResponse,
|
||||
batch_token: &BatchGenerationToken,
|
||||
) -> AnyhowResult<ImageEditCompleteEvent> {
|
||||
|
||||
let result = list_batch_generated_redux_media_files(
|
||||
&app_env_configs.storyteller_host,
|
||||
maybe_creds,
|
||||
batch_token,
|
||||
).await?;
|
||||
|
||||
if result.media_files.is_empty() {
|
||||
return Err(anyhow!("No media files found for batch token: {}", batch_token));
|
||||
}
|
||||
|
||||
let media_files = result.media_files
|
||||
.into_iter()
|
||||
.map(|file| EditedImage {
|
||||
media_token: file.token,
|
||||
cdn_url: file.media_links.cdn_url,
|
||||
maybe_thumbnail_template: file.media_links.maybe_thumbnail_template,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(ImageEditCompleteEvent {
|
||||
edited_images: media_files,
|
||||
maybe_frontend_subscriber_id: task.frontend_subscriber_id.clone(),
|
||||
maybe_frontend_subscriber_payload: task.frontend_subscriber_payload.clone(),
|
||||
})
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod maybe_handle_inpainting_complete_event;
|
||||
pub mod maybe_send_background_removal_complete_event;
|
||||
+39
-6
@@ -33,6 +33,9 @@ use storyteller_client::jobs::list_session_jobs::{list_session_jobs, States};
|
||||
use storyteller_client::media_files::upload_image_media_file_from_file::upload_image_media_file_from_file;
|
||||
use tauri::AppHandle;
|
||||
use tempdir::TempDir;
|
||||
use artcraft_api_defs::jobs::list_session_jobs::ListSessionJobsItem;
|
||||
use storyteller_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use crate::services::storyteller::threads::events::maybe_handle_inpainting_complete_event::maybe_handle_inpainting_complete_event;
|
||||
|
||||
pub async fn storyteller_task_polling_thread(
|
||||
app_handle: AppHandle,
|
||||
@@ -140,16 +143,14 @@ async fn polling_loop(
|
||||
continue; // If anything breaks with queries, don't spam events.
|
||||
}
|
||||
|
||||
let result = maybe_send_background_removal_complete_event(
|
||||
send_additional_events(
|
||||
&app_handle,
|
||||
&task,
|
||||
&app_env_configs,
|
||||
creds.as_ref(),
|
||||
&job,
|
||||
&task,
|
||||
).await;
|
||||
|
||||
if let Err(err) = result {
|
||||
error!("Failed to send background removal complete event: {:?}", err);
|
||||
}
|
||||
|
||||
let service = to_generation_service_provider(task.provider);
|
||||
let action = to_generation_action(task.task_type);
|
||||
|
||||
@@ -166,3 +167,35 @@ async fn polling_loop(
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_additional_events(
|
||||
app_handle: &AppHandle,
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
creds: Option<&StorytellerCredentialSet>,
|
||||
job: &ListSessionJobsItem,
|
||||
task: &Task
|
||||
) {
|
||||
info!("Attempting to dispatch events for completed Storyteller : {:?}", task);
|
||||
|
||||
let result = maybe_handle_inpainting_complete_event(
|
||||
app_handle,
|
||||
app_env_configs,
|
||||
creds,
|
||||
task,
|
||||
job,
|
||||
).await;
|
||||
|
||||
if let Err(err) = result {
|
||||
error!("Failed to send image inpainting complete event: {:?}", err);
|
||||
}
|
||||
|
||||
let result = maybe_send_background_removal_complete_event(
|
||||
app_handle,
|
||||
task,
|
||||
job,
|
||||
).await;
|
||||
|
||||
if let Err(err) = result {
|
||||
error!("Failed to send background removal complete event: {:?}", err);
|
||||
}
|
||||
}
|
||||
+20
-10
@@ -1,19 +1,27 @@
|
||||
use anyhow::anyhow;
|
||||
use sqlx::MySqlPool;
|
||||
use sqlx::{Executor, MySql};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use enums::by_table::generic_inference_jobs::inference_result_type::InferenceResultType;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
// TODO: Maybe in the future we set a batch_token instead of media token
|
||||
|
||||
pub async fn mark_fal_generic_inference_job_successfully_done(
|
||||
pool: &MySqlPool,
|
||||
job_token: &InferenceJobToken,
|
||||
media_file_token: MediaFileToken,
|
||||
pub struct MarkJobArgs<'e, 'c, E>
|
||||
where E: 'e + Executor<'c, Database = MySql>
|
||||
{
|
||||
pub job_token: &'e InferenceJobToken,
|
||||
pub media_file_token: &'e MediaFileToken,
|
||||
pub maybe_batch_token: Option<&'e BatchGenerationToken>,
|
||||
pub mysql_executor: E,
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
pub async fn mark_fal_generic_inference_job_successfully_done<'e, 'c : 'e, E>(
|
||||
args: MarkJobArgs<'e, 'c, E>
|
||||
) -> AnyhowResult<()>
|
||||
where E: 'e + Executor<'c, Database = MySql>
|
||||
{
|
||||
const STATUS : JobStatusPlus = JobStatusPlus::CompleteSuccess;
|
||||
const RESULT_TYPE : InferenceResultType = InferenceResultType::MediaFile;
|
||||
@@ -25,6 +33,7 @@ SET
|
||||
status = ?,
|
||||
on_success_result_entity_type = ?,
|
||||
on_success_result_entity_token = ?,
|
||||
on_success_result_batch_token = ?,
|
||||
failure_reason = NULL,
|
||||
internal_debugging_failure_reason = NULL,
|
||||
success_execution_millis = NULL,
|
||||
@@ -35,10 +44,11 @@ WHERE token = ?
|
||||
"#,
|
||||
STATUS.to_str(),
|
||||
RESULT_TYPE.to_str(),
|
||||
media_file_token,
|
||||
job_token,
|
||||
args.media_file_token,
|
||||
args.maybe_batch_token.map(|t| t.as_str()),
|
||||
args.job_token,
|
||||
)
|
||||
.execute(pool)
|
||||
.execute(args.mysql_executor)
|
||||
.await;
|
||||
|
||||
match query_result {
|
||||
|
||||
+7
@@ -11,6 +11,7 @@ use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::traits::mysql_from_row::MySqlFromRow;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::traits::mysql_token_from_row::MySqlTokenFromRow;
|
||||
@@ -102,6 +103,7 @@ SELECT
|
||||
|
||||
jobs.on_success_result_entity_type as maybe_result_entity_type,
|
||||
jobs.on_success_result_entity_token as maybe_result_entity_token,
|
||||
jobs.on_success_result_batch_token as maybe_result_batch_token,
|
||||
|
||||
model_weights.title as maybe_model_weights_title,
|
||||
tts_models.title as maybe_tts_model_title,
|
||||
@@ -230,6 +232,9 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
|
||||
ResultDetails {
|
||||
entity_type: entity_type.to_string(),
|
||||
entity_token: entity_token.to_string(),
|
||||
maybe_batch_token: record.maybe_result_batch_token
|
||||
.as_ref()
|
||||
.map(|token| BatchGenerationToken::new_from_str(token)),
|
||||
public_bucket_location_or_hash: public_bucket_hash.to_string(),
|
||||
maybe_media_file_public_bucket_prefix: record.maybe_media_file_public_bucket_prefix.clone(),
|
||||
maybe_media_file_public_bucket_extension: record.maybe_media_file_public_bucket_extension.clone(),
|
||||
@@ -293,6 +298,7 @@ struct RawGenericInferenceJobStatus {
|
||||
|
||||
pub maybe_result_entity_type: Option<String>,
|
||||
pub maybe_result_entity_token: Option<String>,
|
||||
pub maybe_result_batch_token: Option<String>,
|
||||
|
||||
pub maybe_model_weights_title: Option<String>,
|
||||
pub maybe_tts_model_title: Option<String>,
|
||||
@@ -349,6 +355,7 @@ impl FromRow<'_, MySqlRow> for RawGenericInferenceJobStatus {
|
||||
maybe_inference_args: row.try_get("maybe_inference_args")?,
|
||||
maybe_result_entity_type: row.try_get("maybe_result_entity_type")?,
|
||||
maybe_result_entity_token: row.try_get("maybe_result_entity_token")?,
|
||||
maybe_result_batch_token: row.try_get("maybe_result_batch_token")?,
|
||||
maybe_model_weights_title: row.try_get("maybe_model_weights_title")?,
|
||||
maybe_tts_model_title: row.try_get("maybe_tts_model_title")?,
|
||||
maybe_voice_conversion_model_title: row.try_get("maybe_voice_conversion_model_title")?,
|
||||
|
||||
+6
@@ -11,6 +11,7 @@ use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
@@ -61,6 +62,7 @@ SELECT
|
||||
|
||||
jobs.on_success_result_entity_type as maybe_result_entity_type,
|
||||
jobs.on_success_result_entity_token as maybe_result_entity_token,
|
||||
jobs.on_success_result_batch_token as maybe_result_batch_token,
|
||||
|
||||
model_weights.title as maybe_model_weights_title,
|
||||
tts_models.title as maybe_tts_model_title,
|
||||
@@ -206,6 +208,9 @@ fn raw_record_to_public_result(record: RawGenericInferenceJobStatus) -> GenericI
|
||||
ResultDetails {
|
||||
entity_type: entity_type.to_string(),
|
||||
entity_token: entity_token.to_string(),
|
||||
maybe_batch_token: record.maybe_result_batch_token
|
||||
.as_ref()
|
||||
.map(|token| BatchGenerationToken::new_from_str(token)),
|
||||
public_bucket_location_or_hash: public_bucket_hash.to_string(),
|
||||
maybe_media_file_public_bucket_prefix: record.maybe_media_file_public_bucket_prefix.clone(),
|
||||
maybe_media_file_public_bucket_extension: record.maybe_media_file_public_bucket_extension.clone(),
|
||||
@@ -267,6 +272,7 @@ struct RawGenericInferenceJobStatus {
|
||||
|
||||
pub maybe_result_entity_type: Option<String>,
|
||||
pub maybe_result_entity_token: Option<String>,
|
||||
pub maybe_result_batch_token: Option<String>,
|
||||
|
||||
pub maybe_model_weights_title: Option<String>,
|
||||
pub maybe_tts_model_title: Option<String>,
|
||||
|
||||
@@ -6,6 +6,7 @@ use enums::by_table::generic_inference_jobs::inference_job_product_category::Inf
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
@@ -67,6 +68,8 @@ pub struct ResultDetails {
|
||||
pub entity_type: String,
|
||||
pub entity_token: String,
|
||||
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
/// The bucket storage hash (for vc and media_files) or full path (for tts)
|
||||
pub public_bucket_location_or_hash: String,
|
||||
pub maybe_media_file_public_bucket_prefix: Option<String>,
|
||||
|
||||
+7
@@ -13,6 +13,7 @@ use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::traits::mysql_from_row::MySqlFromRow;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
use tokens::traits::mysql_token_from_row::MySqlTokenFromRow;
|
||||
@@ -168,6 +169,7 @@ SELECT
|
||||
|
||||
jobs.on_success_result_entity_type as maybe_result_entity_type,
|
||||
jobs.on_success_result_entity_token as maybe_result_entity_token,
|
||||
jobs.on_success_result_batch_token as maybe_result_batch_token,
|
||||
|
||||
model_weights.title as maybe_model_weights_title,
|
||||
tts_models.title as maybe_tts_model_title,
|
||||
@@ -296,6 +298,9 @@ fn raw_records_to_public_result(records: Vec<RawGenericInferenceJobStatus>) -> V
|
||||
ResultDetails {
|
||||
entity_type: entity_type.to_string(),
|
||||
entity_token: entity_token.to_string(),
|
||||
maybe_batch_token: record.maybe_result_batch_token
|
||||
.as_ref()
|
||||
.map(|token| BatchGenerationToken::new_from_str(token)),
|
||||
public_bucket_location_or_hash: public_bucket_hash.to_string(),
|
||||
maybe_media_file_public_bucket_prefix: record.maybe_media_file_public_bucket_prefix.clone(),
|
||||
maybe_media_file_public_bucket_extension: record.maybe_media_file_public_bucket_extension.clone(),
|
||||
@@ -359,6 +364,7 @@ struct RawGenericInferenceJobStatus {
|
||||
|
||||
pub maybe_result_entity_type: Option<String>,
|
||||
pub maybe_result_entity_token: Option<String>,
|
||||
pub maybe_result_batch_token: Option<String>,
|
||||
|
||||
pub maybe_model_weights_title: Option<String>,
|
||||
pub maybe_tts_model_title: Option<String>,
|
||||
@@ -415,6 +421,7 @@ impl FromRow<'_, MySqlRow> for RawGenericInferenceJobStatus {
|
||||
maybe_inference_args: row.try_get("maybe_inference_args")?,
|
||||
maybe_result_entity_type: row.try_get("maybe_result_entity_type")?,
|
||||
maybe_result_entity_token: row.try_get("maybe_result_entity_token")?,
|
||||
maybe_result_batch_token: row.try_get("maybe_result_batch_token")?,
|
||||
maybe_model_weights_title: row.try_get("maybe_model_weights_title")?,
|
||||
maybe_tts_model_title: row.try_get("maybe_tts_model_title")?,
|
||||
maybe_voice_conversion_model_title: row.try_get("maybe_voice_conversion_model_title")?,
|
||||
|
||||
+10
-3
@@ -10,6 +10,7 @@ use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use sqlx::MySqlPool;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
@@ -67,7 +68,7 @@ pub struct MediaFileInsertBuilder {
|
||||
// maybe_prompt_token: Option<&'a PromptToken>,
|
||||
|
||||
// // If batch generated, this is the batch token.
|
||||
// maybe_batch_token: Option<&'a BatchGenerationToken>,
|
||||
maybe_batch_generation_token: Option<BatchGenerationToken>,
|
||||
|
||||
// Storage details
|
||||
public_bucket_directory_hash: Option<MediaFileBucketPath>, // NB: Non-nullable field(s)
|
||||
@@ -100,6 +101,7 @@ impl MediaFileInsertBuilder {
|
||||
maybe_frame_height: None,
|
||||
checksum_sha2: None,
|
||||
maybe_engine_category: None,
|
||||
maybe_batch_generation_token: None,
|
||||
public_bucket_directory_hash: None,
|
||||
maybe_prompt_token: None,
|
||||
}
|
||||
@@ -228,7 +230,12 @@ impl MediaFileInsertBuilder {
|
||||
// TODO: maybe_text_transcript
|
||||
// TODO: maybe_scene_source_media_file_token
|
||||
// TODO: maybe_prompt_token
|
||||
// TODO: maybe_batch_token
|
||||
|
||||
pub fn maybe_batch_generation_token(mut self, maybe_batch_token: Option<&BatchGenerationToken>) -> Self {
|
||||
self.maybe_batch_generation_token = maybe_batch_token
|
||||
.map(|token| token.clone());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn public_bucket_directory_hash(mut self, public_bucket_directory_hash: &MediaFileBucketPath) -> Self {
|
||||
self.public_bucket_directory_hash = Some(public_bucket_directory_hash.clone());
|
||||
@@ -281,7 +288,7 @@ impl MediaFileInsertBuilder {
|
||||
maybe_text_transcript: None, // TODO
|
||||
maybe_scene_source_media_file_token: None, // TODO
|
||||
maybe_prompt_token: self.maybe_prompt_token.as_ref(),
|
||||
maybe_batch_token: None, // TODO
|
||||
maybe_batch_token: self.maybe_batch_generation_token.as_ref(),
|
||||
public_bucket_directory_hash: bucket_path.get_object_hash(),
|
||||
maybe_public_bucket_prefix: bucket_path.get_optional_prefix(),
|
||||
maybe_public_bucket_extension: bucket_path.get_optional_extension(),
|
||||
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::AnyhowResult;
|
||||
use log::warn;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{MySql, MySqlPool};
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::helpers::boolean_converters::i8_to_bool;
|
||||
use crate::payloads::prompt_args::prompt_inner_payload::PromptInnerPayload;
|
||||
use crate::utils::transactor::Transactor;
|
||||
|
||||
pub struct MediaFileBatch {
|
||||
pub media_files: Vec<MediaFile>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct MediaFile {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub media_class: MediaFileClass,
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
pub maybe_mime_type: Option<String>,
|
||||
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
pub maybe_title: Option<String>,
|
||||
pub maybe_text_transcript: Option<String>,
|
||||
|
||||
pub maybe_origin_filename: Option<String>,
|
||||
|
||||
pub maybe_duration_millis: Option<u64>,
|
||||
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub maybe_creator_username: Option<String>,
|
||||
pub maybe_creator_display_name: Option<String>,
|
||||
pub maybe_creator_gravatar_hash: Option<String>,
|
||||
|
||||
/// This should not be exposed for GET endpoints, but is useful for permission checking.
|
||||
pub maybe_creator_anonymous_visitor_token: Option<AnonymousVisitorTrackingToken>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub is_user_upload: bool,
|
||||
pub is_intermediate_system_file: bool,
|
||||
|
||||
pub maybe_prompt_token: Option<PromptToken>,
|
||||
pub maybe_prompt_args: Option<PromptInnerPayload>,
|
||||
|
||||
pub maybe_file_cover_image_public_bucket_hash: Option<String>,
|
||||
pub maybe_file_cover_image_public_bucket_prefix: Option<String>,
|
||||
pub maybe_file_cover_image_public_bucket_extension: Option<String>,
|
||||
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
|
||||
// pub maybe_moderator_fields: Option<MediaFileModeratorFields>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MediaFileRaw {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub media_class: MediaFileClass,
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
pub maybe_mime_type: Option<String>,
|
||||
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
pub maybe_title: Option<String>,
|
||||
pub maybe_text_transcript: Option<String>,
|
||||
|
||||
pub maybe_origin_filename: Option<String>,
|
||||
|
||||
pub maybe_duration_millis: Option<i32>,
|
||||
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub maybe_creator_username: Option<String>,
|
||||
pub maybe_creator_display_name: Option<String>,
|
||||
pub maybe_creator_gravatar_hash: Option<String>,
|
||||
|
||||
/// This should not be exposed for GET endpoints, but is useful for permission checking.
|
||||
pub maybe_creator_anonymous_visitor_token: Option<AnonymousVisitorTrackingToken>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub is_user_upload: i8,
|
||||
pub is_intermediate_system_file: i8,
|
||||
|
||||
pub maybe_prompt_token: Option<PromptToken>,
|
||||
pub maybe_other_prompt_args: Option<String>,
|
||||
|
||||
pub maybe_file_cover_image_public_bucket_hash: Option<String>,
|
||||
pub maybe_file_cover_image_public_bucket_prefix: Option<String>,
|
||||
pub maybe_file_cover_image_public_bucket_extension: Option<String>,
|
||||
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn list_batch_generated_redux_media_files(
|
||||
batch_token: &BatchGenerationToken,
|
||||
can_see_deleted: bool,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> AnyhowResult<MediaFileBatch> {
|
||||
list_batch_generated_redux_media_files_with_transactor(
|
||||
batch_token,
|
||||
can_see_deleted,
|
||||
Transactor::for_pool(mysql_pool)
|
||||
).await
|
||||
}
|
||||
|
||||
pub async fn list_batch_generated_redux_media_files_with_connection(
|
||||
batch_token: &BatchGenerationToken,
|
||||
can_see_deleted: bool,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> AnyhowResult<MediaFileBatch> {
|
||||
list_batch_generated_redux_media_files_with_transactor(
|
||||
batch_token,
|
||||
can_see_deleted,
|
||||
Transactor::for_connection(mysql_connection)
|
||||
).await
|
||||
}
|
||||
|
||||
pub async fn list_batch_generated_redux_media_files_with_transactor(
|
||||
batch_token: &BatchGenerationToken,
|
||||
can_see_deleted: bool,
|
||||
transactor: Transactor<'_, '_>,
|
||||
) -> AnyhowResult<MediaFileBatch> {
|
||||
|
||||
let records = if can_see_deleted {
|
||||
select_including_deleted(batch_token, transactor).await?
|
||||
} else {
|
||||
select_without_deleted(batch_token, transactor).await?
|
||||
};
|
||||
|
||||
let records = records
|
||||
.into_iter()
|
||||
.map(|record| {
|
||||
MediaFile {
|
||||
token: record.token,
|
||||
media_type: record.media_type,
|
||||
media_class: record.media_class,
|
||||
maybe_mime_type: record.maybe_mime_type,
|
||||
maybe_batch_token: record.maybe_batch_token,
|
||||
maybe_title: record.maybe_title,
|
||||
maybe_text_transcript: record.maybe_text_transcript,
|
||||
maybe_origin_filename: record.maybe_origin_filename,
|
||||
maybe_duration_millis: record.maybe_duration_millis.map(|d| d as u64),
|
||||
maybe_creator_user_token: record.maybe_creator_user_token,
|
||||
maybe_creator_username: record.maybe_creator_username,
|
||||
maybe_creator_display_name: record.maybe_creator_display_name,
|
||||
maybe_creator_gravatar_hash: record.maybe_creator_gravatar_hash,
|
||||
maybe_creator_anonymous_visitor_token: record.maybe_creator_anonymous_visitor_token,
|
||||
creator_set_visibility: record.creator_set_visibility,
|
||||
is_user_upload: i8_to_bool(record.is_user_upload),
|
||||
is_intermediate_system_file: i8_to_bool(record.is_intermediate_system_file),
|
||||
maybe_prompt_token: record.maybe_prompt_token,
|
||||
maybe_prompt_args: record.maybe_other_prompt_args
|
||||
.as_deref()
|
||||
.map(|args| PromptInnerPayload::from_json(args))
|
||||
.transpose()
|
||||
.ok() // NB: Fail open
|
||||
.flatten(),
|
||||
maybe_file_cover_image_public_bucket_hash: record.maybe_file_cover_image_public_bucket_hash,
|
||||
maybe_file_cover_image_public_bucket_prefix: record.maybe_file_cover_image_public_bucket_prefix,
|
||||
maybe_file_cover_image_public_bucket_extension: record.maybe_file_cover_image_public_bucket_extension,
|
||||
public_bucket_directory_hash: record.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: record.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: record.maybe_public_bucket_extension,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(MediaFileBatch {
|
||||
media_files: records,
|
||||
})
|
||||
}
|
||||
|
||||
async fn select_including_deleted(
|
||||
batch_token: &BatchGenerationToken,
|
||||
transactor: Transactor<'_, '_>,
|
||||
) -> Result<Vec<MediaFileRaw>, sqlx::Error> {
|
||||
let query = sqlx::query_as!(
|
||||
MediaFileRaw,
|
||||
r#"
|
||||
SELECT
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.maybe_mime_type,
|
||||
|
||||
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
users.username as maybe_creator_username,
|
||||
users.display_name as maybe_creator_display_name,
|
||||
users.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,
|
||||
|
||||
m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,
|
||||
|
||||
m.maybe_title,
|
||||
m.maybe_text_transcript,
|
||||
|
||||
m.maybe_origin_filename,
|
||||
|
||||
m.maybe_duration_millis,
|
||||
|
||||
m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,
|
||||
prompts.maybe_other_args as maybe_other_prompt_args,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.is_user_upload,
|
||||
m.is_intermediate_system_file,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m
|
||||
LEFT OUTER JOIN users
|
||||
ON m.maybe_creator_user_token = users.token
|
||||
LEFT OUTER JOIN media_files as media_file_cover_image
|
||||
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
|
||||
LEFT OUTER JOIN prompts
|
||||
ON prompts.token = m.maybe_prompt_token
|
||||
WHERE
|
||||
m.maybe_batch_token = ?
|
||||
"#,
|
||||
batch_token
|
||||
);
|
||||
|
||||
let maybe_results = match transactor {
|
||||
Transactor::Pool { pool } => {
|
||||
query.fetch_all(pool).await
|
||||
},
|
||||
Transactor::Connection { connection } => {
|
||||
query.fetch_all(connection).await
|
||||
},
|
||||
Transactor::Transaction { transaction } => {
|
||||
query.fetch_all(&mut **transaction).await
|
||||
},
|
||||
};
|
||||
|
||||
match maybe_results {
|
||||
Err(err) => {
|
||||
match err {
|
||||
sqlx::Error::RowNotFound => {
|
||||
Ok(Vec::new())
|
||||
},
|
||||
_ => {
|
||||
warn!("list ip bans db error: {:?}", err);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(results) => Ok(results),
|
||||
}
|
||||
}
|
||||
|
||||
async fn select_without_deleted(
|
||||
batch_token: &BatchGenerationToken,
|
||||
transactor: Transactor<'_, '_>,
|
||||
) -> Result<Vec<MediaFileRaw>, sqlx::Error> {
|
||||
let query = sqlx::query_as!(
|
||||
MediaFileRaw,
|
||||
r#"
|
||||
SELECT
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.maybe_mime_type,
|
||||
|
||||
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
users.username as maybe_creator_username,
|
||||
users.display_name as maybe_creator_display_name,
|
||||
users.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
m.maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken`,
|
||||
|
||||
m.maybe_batch_token as `maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken`,
|
||||
|
||||
m.maybe_title,
|
||||
m.maybe_text_transcript,
|
||||
|
||||
m.maybe_origin_filename,
|
||||
|
||||
m.maybe_duration_millis,
|
||||
|
||||
m.maybe_prompt_token as `maybe_prompt_token: tokens::tokens::prompts::PromptToken`,
|
||||
prompts.maybe_other_args as maybe_other_prompt_args,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.is_user_upload,
|
||||
m.is_intermediate_system_file,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m
|
||||
LEFT OUTER JOIN users
|
||||
ON m.maybe_creator_user_token = users.token
|
||||
LEFT OUTER JOIN media_files as media_file_cover_image
|
||||
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
|
||||
LEFT OUTER JOIN prompts
|
||||
ON prompts.token = m.maybe_prompt_token
|
||||
WHERE
|
||||
m.maybe_batch_token = ?
|
||||
AND m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
"#,
|
||||
batch_token
|
||||
);
|
||||
|
||||
let maybe_results = match transactor {
|
||||
Transactor::Pool { pool } => {
|
||||
query.fetch_all(pool).await
|
||||
},
|
||||
Transactor::Connection { connection } => {
|
||||
query.fetch_all(connection).await
|
||||
},
|
||||
Transactor::Transaction { transaction } => {
|
||||
query.fetch_all(&mut **transaction).await
|
||||
},
|
||||
};
|
||||
|
||||
match maybe_results {
|
||||
Err(err) => {
|
||||
match err {
|
||||
sqlx::Error::RowNotFound => {
|
||||
Ok(Vec::new())
|
||||
},
|
||||
_ => {
|
||||
warn!("list ip bans db error: {:?}", err);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(results) => Ok(results),
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod list_batch_generated_redux_media_files;
|
||||
pub mod list_featured_media_files;
|
||||
pub mod list_media_files;
|
||||
pub mod list_media_files_by_batch_token;
|
||||
|
||||
@@ -21,6 +21,7 @@ workspace-build-acceleration.workspace = true
|
||||
|
||||
# Internal
|
||||
enums.workspace = true
|
||||
enums_public.workspace = true
|
||||
tokens.workspace = true
|
||||
|
||||
# External
|
||||
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
use serde_derive::Deserialize;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
// TODO(bt,2025-08-20): Replace the storyteller-web version of this.
|
||||
// All it needs is the impl ported into a builder.
|
||||
|
||||
/// Cover image links can only be jpg, png, etc. No videos.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CoverImageLinks {
|
||||
/// Primary link to the cover image via the CDN.
|
||||
pub cdn_url: Url,
|
||||
+14
-11
@@ -1,19 +1,22 @@
|
||||
use serde_derive::{Deserialize};
|
||||
use url::Url;
|
||||
use crate::shared_response_types::cover_image_links::CoverImageLinks;
|
||||
use crate::common::responses::cover_image_links::CoverImageLinks;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
// TODO(bt,2025-08-20): Replace the storyteller-web version of this.
|
||||
// We can't drop the old version just yet due to the `impl` (easy)
|
||||
// and a few differing fields.
|
||||
|
||||
/// Everything we need to create a cover image.
|
||||
/// Cover images are small descriptive images that can be set for any media file.
|
||||
/// If a cover image is set, this is the path to the asset.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MediaFileCoverImageDetails {
|
||||
// /// (DEPRECATED) URL path to the media file
|
||||
// #[deprecated(note="This field doesn't point to the full URL. Use media_links instead to leverage the CDN.")]
|
||||
// pub maybe_cover_image_public_bucket_path: Option<String>,
|
||||
// /// (DEPRECATED) URL path to the media file
|
||||
// #[deprecated(note="This field doesn't point to the full URL. Use media_links instead to leverage the CDN.")]
|
||||
// pub maybe_cover_image_public_bucket_path: Option<String>,
|
||||
|
||||
// /// (DEPRECATED) Full URL to the media file
|
||||
// #[deprecated(note="This points to the bucket. Use media_links instead to leverage the CDN.")]
|
||||
// pub maybe_cover_image_public_bucket_url: Option<Url>,
|
||||
// /// (DEPRECATED) Full URL to the media file
|
||||
// #[deprecated(note="This points to the bucket. Use media_links instead to leverage the CDN.")]
|
||||
// pub maybe_cover_image_public_bucket_url: Option<Url>,
|
||||
|
||||
// NB(bt,2024-09-19): I accidentally rolled this field out to production.
|
||||
// I don't think this field is in use, but maybe ...
|
||||
@@ -33,7 +36,7 @@ pub struct MediaFileCoverImageDetails {
|
||||
|
||||
/// The default cover is composed of an image and color pair that are
|
||||
/// predefined by the frontend.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MediaFileDefaultCover {
|
||||
pub image_index: u8,
|
||||
pub color_index: u8,
|
||||
@@ -1,2 +1,6 @@
|
||||
pub mod cover_image_links;
|
||||
pub mod job_details;
|
||||
pub mod media_links;
|
||||
pub mod media_file_cover_image_details;
|
||||
pub mod media_links;
|
||||
pub mod simple_entity_stats;
|
||||
pub mod user_details_light;
|
||||
+4
-2
@@ -1,7 +1,9 @@
|
||||
use serde_derive::Deserialize;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
// TODO(bt,2025-08-20): Replace the storyteller-web version of this.
|
||||
|
||||
/// Simple stats that can be attached to any entity
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SimpleEntityStats {
|
||||
/// Number of positive ratings (or "likes") for this item
|
||||
pub positive_rating_count: u32,
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
use serde_derive::Deserialize;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
/// Everything we need to refer to a user on the public web interface.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct UserDetailsLight {
|
||||
/// The token for the user
|
||||
pub user_token: UserToken,
|
||||
@@ -26,7 +26,7 @@ pub struct UserDetailsLight {
|
||||
// In the future, we'll also support user-uploaded avatars that we store on our servers.
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct UserDefaultAvatarInfo {
|
||||
pub image_index: u8,
|
||||
pub color_index: u8,
|
||||
@@ -9,6 +9,7 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
|
||||
pub const LIST_SESSION_JOBS_URL_PATH: &str = "/v1/jobs/session";
|
||||
|
||||
@@ -111,6 +112,9 @@ pub struct ListSessionStatusDetailsResponse {
|
||||
pub struct ListSessionResultDetailsResponse {
|
||||
pub entity_type: String,
|
||||
pub entity_token: String,
|
||||
|
||||
/// If generated as part of a batch, the batch token.
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
/// (DEPRECATED) URL path to the media file
|
||||
#[deprecated(note="This field doesn't point to the full URL. Use media_links instead to leverage the CDN.")]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod common;
|
||||
pub mod generate;
|
||||
pub mod jobs;
|
||||
pub mod media_file;
|
||||
pub mod prompts;
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
use crate::common::responses::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::common::responses::media_links::MediaLinks;
|
||||
use crate::common::responses::user_details_light::UserDetailsLight;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const LIST_BATCH_GENERATED_REDUX_MEDIA_FILES_URL_PATH: &str = "/v1/media_files/batch_gen_redux";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ListBatchGeneratedReduxMediaFilesPathInfo {
|
||||
pub token: BatchGenerationToken,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ListBatchGeneratedReduxMediaFilesSuccessResponse {
|
||||
pub success: bool,
|
||||
pub media_files: Vec<BatchGeneratedReduxMediaFileInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct BatchGeneratedReduxMediaFileInfo {
|
||||
/// Primary key identifier
|
||||
pub token: MediaFileToken,
|
||||
|
||||
/// The coarse-grained class of media file: image, video, etc.
|
||||
pub media_class: MediaFileClass,
|
||||
|
||||
/// Type of media will dictate which fields are populated and what
|
||||
/// the frontend should display (e.g. video player vs audio player).
|
||||
/// This is closer in meaning to a "mime type".
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
/// If the file was generated as part of a batch, this is the token for the batch.
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
/// Rich CDN links to the media, including thumbnails, previews, and more.
|
||||
pub media_links: MediaLinks,
|
||||
|
||||
/// Information about the cover image. Many media files do not require a cover image,
|
||||
/// e.g. image files, video files with thumbnails, audio files, etc.
|
||||
/// 3D files require them.
|
||||
pub cover_image: MediaFileCoverImageDetails,
|
||||
|
||||
/// User info.
|
||||
pub maybe_creator_user: Option<UserDetailsLight>,
|
||||
|
||||
/// File visibility.
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
/// The foreign key to the prompt used to generate the media, if applicable.
|
||||
pub maybe_prompt_token: Option<PromptToken>,
|
||||
|
||||
/// The name or title of the media file (optional)
|
||||
pub maybe_title: Option<String>,
|
||||
|
||||
/// The original filename for uploaded files, if they were provided.
|
||||
/// In the future we'll provide our own internal optional filenames.
|
||||
pub maybe_original_filename: Option<String>,
|
||||
|
||||
/// Duration for audio and video files, if available.
|
||||
/// Measured in milliseconds.
|
||||
pub maybe_duration_millis: Option<u64>,
|
||||
|
||||
/// Created timestamp
|
||||
pub created_at: DateTime<Utc>,
|
||||
|
||||
/// Updated timestamp
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod list_batch_generated_media_files;
|
||||
@@ -11,6 +11,7 @@ use utoipa::ToSchema;
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TaskType {
|
||||
ImageGeneration,
|
||||
ImageInpaintEdit,
|
||||
VideoGeneration,
|
||||
ObjectGeneration,
|
||||
BackgroundRemoval,
|
||||
@@ -26,6 +27,7 @@ impl TaskType {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ImageGeneration => "image_generation",
|
||||
Self::ImageInpaintEdit => "image_inpaint_edit",
|
||||
Self::VideoGeneration => "video_generation",
|
||||
Self::ObjectGeneration => "object_generation",
|
||||
Self::BackgroundRemoval => "background_removal",
|
||||
@@ -35,6 +37,7 @@ impl TaskType {
|
||||
pub fn from_str(job_status: &str) -> Result<Self, String> {
|
||||
match job_status {
|
||||
"image_generation" => Ok(Self::ImageGeneration),
|
||||
"image_inpaint_edit" => Ok(Self::ImageInpaintEdit),
|
||||
"video_generation" => Ok(Self::VideoGeneration),
|
||||
"object_generation" => Ok(Self::ObjectGeneration),
|
||||
"background_removal" => Ok(Self::BackgroundRemoval),
|
||||
@@ -47,6 +50,7 @@ impl TaskType {
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::ImageGeneration,
|
||||
Self::ImageInpaintEdit,
|
||||
Self::VideoGeneration,
|
||||
Self::ObjectGeneration,
|
||||
Self::BackgroundRemoval,
|
||||
@@ -65,6 +69,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(TaskType::ImageGeneration, "image_generation");
|
||||
assert_serialization(TaskType::ImageInpaintEdit, "image_inpaint_edit");
|
||||
assert_serialization(TaskType::VideoGeneration, "video_generation");
|
||||
assert_serialization(TaskType::ObjectGeneration, "object_generation");
|
||||
assert_serialization(TaskType::BackgroundRemoval, "background_removal");
|
||||
@@ -73,6 +78,7 @@ mod tests {
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(TaskType::ImageGeneration.to_str(), "image_generation");
|
||||
assert_eq!(TaskType::ImageInpaintEdit.to_str(), "image_inpaint_edit");
|
||||
assert_eq!(TaskType::VideoGeneration.to_str(), "video_generation");
|
||||
assert_eq!(TaskType::ObjectGeneration.to_str(), "object_generation");
|
||||
assert_eq!(TaskType::BackgroundRemoval.to_str(), "background_removal");
|
||||
@@ -81,6 +87,7 @@ mod tests {
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(TaskType::from_str("image_generation").unwrap(), TaskType::ImageGeneration);
|
||||
assert_eq!(TaskType::from_str("image_inpaint_edit").unwrap(), TaskType::ImageInpaintEdit);
|
||||
assert_eq!(TaskType::from_str("video_generation").unwrap(), TaskType::VideoGeneration);
|
||||
assert_eq!(TaskType::from_str("object_generation").unwrap(), TaskType::ObjectGeneration);
|
||||
assert_eq!(TaskType::from_str("background_removal").unwrap(), TaskType::BackgroundRemoval);
|
||||
@@ -89,8 +96,9 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = TaskType::all_variants();
|
||||
assert_eq!(variants.len(), 4);
|
||||
assert_eq!(variants.len(), 5);
|
||||
assert_eq!(variants.pop_first(), Some(TaskType::ImageGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(TaskType::ImageInpaintEdit));
|
||||
assert_eq!(variants.pop_first(), Some(TaskType::VideoGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(TaskType::ObjectGeneration));
|
||||
assert_eq!(variants.pop_first(), Some(TaskType::BackgroundRemoval));
|
||||
|
||||
@@ -14,6 +14,8 @@ use utoipa::ToSchema;
|
||||
pub enum TauriCommandCaller {
|
||||
/// The 2D canvas
|
||||
Canvas,
|
||||
/// The inpainting editor
|
||||
ImageEditor,
|
||||
}
|
||||
|
||||
impl_enum_display_and_debug_using_to_str!(TauriCommandCaller);
|
||||
@@ -26,12 +28,14 @@ impl TauriCommandCaller {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Canvas => "canvas",
|
||||
Self::ImageEditor => "image_editor",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(job_status: &str) -> Result<Self, String> {
|
||||
match job_status {
|
||||
"canvas" => Ok(Self::Canvas),
|
||||
"image_editor" => Ok(Self::ImageEditor),
|
||||
_ => Err(format!("invalid tauri_command_caller: {:?}", job_status)),
|
||||
}
|
||||
}
|
||||
@@ -41,6 +45,7 @@ impl TauriCommandCaller {
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::Canvas,
|
||||
Self::ImageEditor,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -56,23 +61,27 @@ mod tests {
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(TauriCommandCaller::Canvas, "canvas");
|
||||
assert_serialization(TauriCommandCaller::ImageEditor, "image_editor");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(TauriCommandCaller::Canvas.to_str(), "canvas");
|
||||
assert_eq!(TauriCommandCaller::ImageEditor.to_str(), "image_editor");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(TauriCommandCaller::from_str("canvas").unwrap(), TauriCommandCaller::Canvas);
|
||||
assert_eq!(TauriCommandCaller::from_str("image_editor").unwrap(), TauriCommandCaller::ImageEditor);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = TauriCommandCaller::all_variants();
|
||||
assert_eq!(variants.len(), 1);
|
||||
assert_eq!(variants.len(), 2);
|
||||
assert_eq!(variants.pop_first(), Some(TauriCommandCaller::Canvas));
|
||||
assert_eq!(variants.pop_first(), Some(TauriCommandCaller::ImageEditor));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,11 @@ pub enum TauriEventName {
|
||||
#[serde(rename = "canvas_bg_removed_event")]
|
||||
CanvasBgRemovedEvent,
|
||||
|
||||
/// Special event:
|
||||
/// Image edit is complete
|
||||
#[serde(rename = "image_edit_complete_event")]
|
||||
ImageEditCompleteEvent,
|
||||
|
||||
/// Special event:
|
||||
/// Refresh account states
|
||||
#[serde(rename = "refresh_account_state_event")]
|
||||
@@ -73,6 +78,7 @@ impl TauriEventName {
|
||||
Self::GenerationCompleteEvent => "generation-complete-event",
|
||||
Self::GenerationFailedEvent => "generation-failed-event",
|
||||
Self::CanvasBgRemovedEvent => "canvas_bg_removed_event",
|
||||
Self::ImageEditCompleteEvent => "image_edit_complete_event",
|
||||
Self::RefreshAccountStateEvent => "refresh_account_state_event",
|
||||
Self::ShowProviderLoginModalEvent => "show_provider_login_modal_event",
|
||||
Self::FlashUserInputErrorEvent => "flash_user_input_error_event",
|
||||
@@ -86,6 +92,7 @@ impl TauriEventName {
|
||||
"generation-complete-event" => Ok(Self::GenerationCompleteEvent),
|
||||
"generation-failed-event" => Ok(Self::GenerationFailedEvent),
|
||||
"canvas_bg_removed_event" => Ok(Self::CanvasBgRemovedEvent),
|
||||
"image_edit_complete_event" => Ok(Self::ImageEditCompleteEvent),
|
||||
"refresh_account_state_event" => Ok(Self::RefreshAccountStateEvent),
|
||||
"show_provider_login_modal_event" => Ok(Self::ShowProviderLoginModalEvent),
|
||||
"flash_user_input_error_event" => Ok(Self::FlashUserInputErrorEvent),
|
||||
@@ -102,6 +109,7 @@ impl TauriEventName {
|
||||
Self::GenerationCompleteEvent,
|
||||
Self::GenerationFailedEvent,
|
||||
Self::CanvasBgRemovedEvent,
|
||||
Self::ImageEditCompleteEvent,
|
||||
Self::RefreshAccountStateEvent,
|
||||
Self::ShowProviderLoginModalEvent,
|
||||
Self::FlashUserInputErrorEvent,
|
||||
@@ -124,6 +132,7 @@ mod tests {
|
||||
assert_serialization(TauriEventName::GenerationCompleteEvent, "generation-complete-event");
|
||||
assert_serialization(TauriEventName::GenerationFailedEvent, "generation-failed-event");
|
||||
assert_serialization(TauriEventName::CanvasBgRemovedEvent, "canvas_bg_removed_event");
|
||||
assert_serialization(TauriEventName::ImageEditCompleteEvent, "image_edit_complete_event");
|
||||
assert_serialization(TauriEventName::RefreshAccountStateEvent, "refresh_account_state_event");
|
||||
assert_serialization(TauriEventName::ShowProviderLoginModalEvent, "show_provider_login_modal_event");
|
||||
assert_serialization(TauriEventName::FlashUserInputErrorEvent, "flash_user_input_error_event");
|
||||
@@ -136,6 +145,7 @@ mod tests {
|
||||
assert_eq!(TauriEventName::GenerationCompleteEvent.to_str(), "generation-complete-event");
|
||||
assert_eq!(TauriEventName::GenerationFailedEvent.to_str(), "generation-failed-event");
|
||||
assert_eq!(TauriEventName::CanvasBgRemovedEvent.to_str(), "canvas_bg_removed_event");
|
||||
assert_eq!(TauriEventName::ImageEditCompleteEvent.to_str(), "image_edit_complete_event");
|
||||
assert_eq!(TauriEventName::RefreshAccountStateEvent.to_str(), "refresh_account_state_event");
|
||||
assert_eq!(TauriEventName::ShowProviderLoginModalEvent.to_str(), "show_provider_login_modal_event");
|
||||
assert_eq!(TauriEventName::FlashUserInputErrorEvent.to_str(), "flash_user_input_error_event");
|
||||
@@ -148,6 +158,7 @@ mod tests {
|
||||
assert_eq!(TauriEventName::from_str("generation-complete-event").unwrap(), TauriEventName::GenerationCompleteEvent);
|
||||
assert_eq!(TauriEventName::from_str("generation-failed-event").unwrap(), TauriEventName::GenerationFailedEvent);
|
||||
assert_eq!(TauriEventName::from_str("canvas_bg_removed_event").unwrap(), TauriEventName::CanvasBgRemovedEvent);
|
||||
assert_eq!(TauriEventName::from_str("image_edit_complete_event").unwrap(), TauriEventName::ImageEditCompleteEvent);
|
||||
assert_eq!(TauriEventName::from_str("refresh_account_state_event").unwrap(), TauriEventName::RefreshAccountStateEvent);
|
||||
assert_eq!(TauriEventName::from_str("show_provider_login_modal_event").unwrap(), TauriEventName::ShowProviderLoginModalEvent);
|
||||
assert_eq!(TauriEventName::from_str("flash_user_input_error_event").unwrap(), TauriEventName::FlashUserInputErrorEvent);
|
||||
@@ -156,12 +167,13 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = TauriEventName::all_variants();
|
||||
assert_eq!(variants.len(), 8);
|
||||
assert_eq!(variants.len(), 9);
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationEnqueueSuccessEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationEnqueueFailureEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationCompleteEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::GenerationFailedEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::CanvasBgRemovedEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::ImageEditCompleteEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::RefreshAccountStateEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::ShowProviderLoginModalEvent));
|
||||
assert_eq!(variants.pop_first(), Some(TauriEventName::FlashUserInputErrorEvent));
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use artcraft_api_defs::common::responses::cover_image_links::CoverImageLinks;
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
|
||||
// TODO(bt,2024-09-18): Consolidate thumbnail builder code and vars
|
||||
// TODO(bt,2024-09-05): Worth reducing the quality at all?
|
||||
const QUALITY : u8 = 95;
|
||||
|
||||
pub struct CoverImageLinksBuilder {}
|
||||
|
||||
impl CoverImageLinksBuilder {
|
||||
|
||||
pub fn from_media_path(
|
||||
domain: MediaDomain,
|
||||
bucket_path: &MediaFileBucketPath,
|
||||
) -> Option<CoverImageLinks> {
|
||||
let rooted_path = bucket_path.get_full_object_path_str();
|
||||
Self::from_rooted_path(domain, rooted_path)
|
||||
}
|
||||
|
||||
pub fn from_maybe_media_path(
|
||||
domain: MediaDomain,
|
||||
bucket_path: Option<&MediaFileBucketPath>,
|
||||
) -> Option<CoverImageLinks> {
|
||||
match bucket_path {
|
||||
None => None,
|
||||
Some(bucket_path) => Self::from_media_path(domain, bucket_path),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_parts(
|
||||
domain: MediaDomain,
|
||||
maybe_cover_image_public_bucket_path: Option<&str>,
|
||||
maybe_cover_image_public_bucket_prefix: Option<&str>,
|
||||
maybe_cover_image_public_bucket_extension: Option<&str>,
|
||||
) -> Option<CoverImageLinks> {
|
||||
let bucket_path= match maybe_cover_image_public_bucket_path {
|
||||
None => return None,
|
||||
Some(path) => path,
|
||||
};
|
||||
let bucket_path = MediaFileBucketPath::from_object_hash(
|
||||
bucket_path,
|
||||
maybe_cover_image_public_bucket_prefix,
|
||||
maybe_cover_image_public_bucket_extension
|
||||
);
|
||||
Self::from_media_path(domain, &bucket_path)
|
||||
}
|
||||
|
||||
pub fn from_rooted_path(
|
||||
domain: MediaDomain,
|
||||
rooted_path: &str,
|
||||
) -> Option<CoverImageLinks> {
|
||||
if !rooted_path.ends_with(".jpg")
|
||||
&& !rooted_path.ends_with(".png")
|
||||
&& !rooted_path.ends_with(".gif") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut cdn_url = domain.new_cdn_url();
|
||||
cdn_url.set_path(rooted_path);
|
||||
|
||||
Some(CoverImageLinks {
|
||||
cdn_url,
|
||||
thumbnail_template: thumbnail_template(domain, rooted_path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn thumbnail_template(media_domain: MediaDomain, rooted_path: &str) -> String {
|
||||
let host = media_domain.cdn_url_str();
|
||||
format!("{host}/cdn-cgi/image/width={{WIDTH}},quality={QUALITY}{rooted_path}")
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
use crate::http_server::common_responses::media::cover_image_links_builder::CoverImageLinksBuilder;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::web_utils::bucket_urls::bucket_url_from_media_path::bucket_url_from_media_path;
|
||||
use artcraft_api_defs::common::responses::media_file_cover_image_details::{MediaFileCoverImageDetails, MediaFileDefaultCover};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// There are currently 25 cover images numbered 0 to 24 (0-indexed).
|
||||
/// The original dataset was numbered 1 - 25, but I renamed 25 to 0.
|
||||
const NUMBER_OF_IMAGES : u64 = 25;
|
||||
const NUMBER_OF_IMAGES_SALT_OFFSET : u8 = 5;
|
||||
|
||||
const NUMBER_OF_COLORS : u64 = 8;
|
||||
const NUMBER_OF_COLORS_SALT_OFFSET : u8 = 1;
|
||||
|
||||
|
||||
pub struct MediaFileCoverImageDetailsBuilder {}
|
||||
|
||||
pub struct MediaFileDefaultCoverBuilder {}
|
||||
|
||||
impl MediaFileCoverImageDetailsBuilder {
|
||||
/// Typical constructor
|
||||
pub fn from_token(token: &MediaFileToken) -> MediaFileCoverImageDetails {
|
||||
Self::from_token_str(token.as_str())
|
||||
}
|
||||
|
||||
/// For non-media file tokens (eg. emulated TTS results)
|
||||
pub fn from_token_str(token: &str) -> MediaFileCoverImageDetails {
|
||||
MediaFileCoverImageDetails {
|
||||
// NB: The code using this builder does not populate or read these legacy fields:
|
||||
// maybe_cover_image_public_bucket_path: None,
|
||||
// maybe_cover_image_public_bucket_url: None,
|
||||
// maybe_media_links: None,
|
||||
maybe_links: None,
|
||||
default_cover: MediaFileDefaultCoverBuilder::from_token_str(token),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_optional_db_fields(
|
||||
token: &MediaFileToken,
|
||||
domain: MediaDomain,
|
||||
maybe_cover_image_public_bucket_path: Option<&str>,
|
||||
maybe_cover_image_public_bucket_prefix: Option<&str>,
|
||||
maybe_cover_image_public_bucket_extension: Option<&str>,
|
||||
) -> MediaFileCoverImageDetails {
|
||||
Self::from_optional_db_str_fields(
|
||||
token.as_str(),
|
||||
domain,
|
||||
maybe_cover_image_public_bucket_path,
|
||||
maybe_cover_image_public_bucket_prefix,
|
||||
maybe_cover_image_public_bucket_extension
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_optional_db_str_fields(
|
||||
token: &str,
|
||||
domain: MediaDomain,
|
||||
maybe_cover_image_public_bucket_path: Option<&str>,
|
||||
maybe_cover_image_public_bucket_prefix: Option<&str>,
|
||||
maybe_cover_image_public_bucket_extension: Option<&str>,
|
||||
) -> MediaFileCoverImageDetails {
|
||||
let maybe_bucket_path = maybe_cover_image_public_bucket_path
|
||||
.map(|hash| MediaFileBucketPath::from_object_hash(
|
||||
hash,
|
||||
maybe_cover_image_public_bucket_prefix,
|
||||
maybe_cover_image_public_bucket_extension
|
||||
));
|
||||
|
||||
let maybe_cover_image_public_bucket_path = maybe_bucket_path
|
||||
.as_ref()
|
||||
.map(|bucket_path| bucket_path
|
||||
.get_full_object_path_str()
|
||||
.to_string());
|
||||
|
||||
// NB: Fail construction open.
|
||||
let maybe_cover_image_public_bucket_url = maybe_bucket_path
|
||||
.as_ref()
|
||||
.map(|bucket_path| bucket_url_from_media_path(bucket_path).ok())
|
||||
.flatten();
|
||||
|
||||
let maybe_links = CoverImageLinksBuilder::from_maybe_media_path(
|
||||
domain, maybe_bucket_path.as_ref());
|
||||
|
||||
// let maybe_media_links = maybe_bucket_path
|
||||
// .map(|path| MediaLinks::from_media_path(domain, &path));
|
||||
|
||||
MediaFileCoverImageDetails {
|
||||
// NB: The code using this builder does not populate or read these legacy fields:
|
||||
// maybe_cover_image_public_bucket_path,
|
||||
// maybe_cover_image_public_bucket_url,
|
||||
// maybe_media_links,
|
||||
maybe_links,
|
||||
default_cover: MediaFileDefaultCoverBuilder::from_token_str(token),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaFileDefaultCoverBuilder {
|
||||
/// Typical constructor
|
||||
pub fn from_token(token: &MediaFileToken) -> MediaFileDefaultCover {
|
||||
Self::from_token_str(token.as_str())
|
||||
}
|
||||
|
||||
/// For non-media file tokens (eg. emulated TTS results)
|
||||
pub fn from_token_str(token: &str) -> MediaFileDefaultCover {
|
||||
MediaFileDefaultCover {
|
||||
image_index: hash(token, NUMBER_OF_IMAGES, NUMBER_OF_IMAGES_SALT_OFFSET),
|
||||
color_index: hash(token, NUMBER_OF_COLORS, NUMBER_OF_COLORS_SALT_OFFSET),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(token: &str, max_number: u64, salt: u8) -> u8 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
|
||||
token.hash(&mut hasher);
|
||||
salt.hash(&mut hasher);
|
||||
|
||||
let hash = hasher.finish();
|
||||
|
||||
let index= hash % max_number;
|
||||
index as u8
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details_builder::MediaFileDefaultCoverBuilder;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let token = MediaFileToken::new_from_str("foo");
|
||||
let cover = MediaFileDefaultCoverBuilder::from_token(&token);
|
||||
assert_eq!(cover.color_index, 5);
|
||||
assert_eq!(cover.image_index, 2);
|
||||
|
||||
let token = MediaFileToken::new_from_str("bar");
|
||||
let cover = MediaFileDefaultCoverBuilder::from_token(&token);
|
||||
assert_eq!(cover.color_index, 5);
|
||||
assert_eq!(cover.image_index, 3);
|
||||
|
||||
let token = MediaFileToken::new_from_str("asdf");
|
||||
let cover = MediaFileDefaultCoverBuilder::from_token(&token);
|
||||
assert_eq!(cover.color_index, 0);
|
||||
assert_eq!(cover.image_index, 23);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
pub mod cdn_link;
|
||||
pub mod cover_image_links;
|
||||
pub mod cover_image_links_builder;
|
||||
pub mod media_domain;
|
||||
pub mod media_file_cover_image_details;
|
||||
pub mod media_file_cover_image_details_builder;
|
||||
pub mod media_links_builder;
|
||||
pub mod weights_cover_image_details;
|
||||
@@ -9,3 +9,4 @@ pub mod simple_response;
|
||||
pub mod tag_info;
|
||||
pub mod user_avatars;
|
||||
pub mod user_details_lite;
|
||||
pub mod user_details_lite_builder;
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
use artcraft_api_defs::common::responses::user_details_light::{UserDefaultAvatarInfo, UserDetailsLight};
|
||||
use tokens::tokens::users::UserToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::http_server::common_responses::user_avatars::default_avatar_color_from_username::default_avatar_color_from_username;
|
||||
use crate::http_server::common_responses::user_avatars::default_avatar_from_username::default_avatar_from_username;
|
||||
|
||||
pub struct UserDetailsLightBuilder {}
|
||||
|
||||
impl UserDetailsLightBuilder {
|
||||
|
||||
pub fn from_db_fields(
|
||||
user_token: &UserToken,
|
||||
username: &str,
|
||||
display_name: &str,
|
||||
gravatar_hash: &str,
|
||||
) -> UserDetailsLight {
|
||||
UserDetailsLight{
|
||||
default_avatar: UserDefaultAvatarInfoBuilder::from_username(&username),
|
||||
user_token: user_token.clone(),
|
||||
username: username.to_string(),
|
||||
display_name: display_name.to_string(),
|
||||
gravatar_hash: gravatar_hash.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_optional_db_fields(
|
||||
maybe_user_token: Option<&UserToken>,
|
||||
maybe_username: Option<&str>,
|
||||
maybe_display_name: Option<&str>,
|
||||
maybe_gravatar_hash: Option<&str>,
|
||||
) -> Option<UserDetailsLight> {
|
||||
Self::from_optional_db_fields_owned(
|
||||
maybe_user_token.map(|u| u.clone()),
|
||||
maybe_username.map(|s|s.to_string()),
|
||||
maybe_display_name.map(|s|s.to_string()),
|
||||
maybe_gravatar_hash.map(|s|s.to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_optional_db_fields_owned(
|
||||
maybe_user_token: Option<UserToken>,
|
||||
maybe_username: Option<String>,
|
||||
maybe_display_name: Option<String>,
|
||||
maybe_gravatar_hash: Option<String>,
|
||||
) -> Option<UserDetailsLight> {
|
||||
match (maybe_user_token, maybe_username, maybe_display_name, maybe_gravatar_hash) {
|
||||
(Some(user_token), Some(username), Some(display_name), Some(gravatar_hash)) => {
|
||||
Some(UserDetailsLight {
|
||||
default_avatar: UserDefaultAvatarInfoBuilder::from_username(&username),
|
||||
user_token,
|
||||
username,
|
||||
display_name,
|
||||
gravatar_hash,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserDefaultAvatarInfoBuilder {}
|
||||
|
||||
impl UserDefaultAvatarInfoBuilder {
|
||||
/// Default avatars are based on username, not user token.
|
||||
/// NB(bt,2023-08-23): I think the thinking here was that we'd always have the
|
||||
/// username on hand and that it was more entropic.
|
||||
pub fn from_username(username: &str) -> UserDefaultAvatarInfo {
|
||||
UserDefaultAvatarInfo {
|
||||
image_index: default_avatar_from_username(username),
|
||||
color_index: default_avatar_color_from_username(username),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::http_server::common_responses::user_details_lite_builder::UserDetailsLightBuilder;
|
||||
|
||||
#[test]
|
||||
fn test_from_optional_db_fields() {
|
||||
let user_token = UserToken::new_from_str("token");
|
||||
let username = "username";
|
||||
let display_name = "display_name";
|
||||
let gravatar_hash= "adsf";
|
||||
|
||||
let user_details = UserDetailsLightBuilder::from_optional_db_fields(
|
||||
Some(&user_token),
|
||||
Some(username),
|
||||
Some(display_name),
|
||||
Some(gravatar_hash)
|
||||
);
|
||||
|
||||
let user_details = user_details.expect("Should not be optional.");
|
||||
|
||||
assert_eq!(user_details.user_token, user_token);
|
||||
assert_eq!(user_details.username, username);
|
||||
assert_eq!(user_details.display_name, display_name);
|
||||
assert_eq!(user_details.gravatar_hash, gravatar_hash);
|
||||
assert_eq!(user_details.default_avatar.color_index, 5);
|
||||
assert_eq!(user_details.default_avatar.image_index, 16);
|
||||
}
|
||||
}
|
||||
+1
@@ -358,6 +358,7 @@ fn db_record_to_response_payload(
|
||||
ListSessionResultDetailsResponse {
|
||||
entity_type: result_details.entity_type,
|
||||
entity_token: result_details.entity_token,
|
||||
maybe_batch_token: result_details.maybe_batch_token,
|
||||
media_links: MediaLinksBuilder::from_rooted_path(media_domain, &public_bucket_media_path),
|
||||
maybe_public_bucket_media_path: Some(public_bucket_media_path),
|
||||
maybe_successfully_completed_at: result_details.maybe_successfully_completed_at,
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details_builder::MediaFileCoverImageDetailsBuilder;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::common_responses::user_details_lite_builder::UserDetailsLightBuilder;
|
||||
use crate::http_server::endpoints::media_files::get::get_media_file_handler::GetMediaFilePathInfo;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::bucket_urls::bucket_url_string_from_media_path::bucket_url_string_from_media_path;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web_lab::extract::Query;
|
||||
use artcraft_api_defs::common::responses::media_links::MediaLinks;
|
||||
use artcraft_api_defs::common::responses::simple_entity_stats::SimpleEntityStats;
|
||||
use artcraft_api_defs::media_file::list_batch_generated_media_files::{BatchGeneratedReduxMediaFileInfo, ListBatchGeneratedReduxMediaFilesPathInfo, ListBatchGeneratedReduxMediaFilesSuccessResponse};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType;
|
||||
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_subtype::MediaFileSubtype;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::by_table::model_weights::weights_category::WeightsCategory;
|
||||
use enums::by_table::model_weights::weights_types::WeightsType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use enums_public::by_table::model_weights::public_weights_types::PublicWeightsType;
|
||||
use log::warn;
|
||||
use mysql_queries::queries::media_files::get::batch_get_media_files::batch_get_media_files;
|
||||
use mysql_queries::queries::media_files::list::list_batch_generated_redux_media_files::list_batch_generated_redux_media_files_with_connection;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
/// List media files generated as part of the same batch (redux endpoint)
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Media Files",
|
||||
path = "/v1/media_files/batch_gen_redux/{token}",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = ListBatchGeneratedReduxMediaFilesSuccessResponse),
|
||||
),
|
||||
params(
|
||||
("request" = ListBatchGeneratedReduxMediaFilesPathInfo, description = "Payload for Request"),
|
||||
)
|
||||
)]
|
||||
pub async fn list_batch_generated_redux_media_files_handler(
|
||||
http_request: HttpRequest,
|
||||
path: Path<ListBatchGeneratedReduxMediaFilesPathInfo>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<ListBatchGeneratedReduxMediaFilesSuccessResponse>, CommonWebError> {
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let mut show_deleted_results = false;
|
||||
let mut is_moderator = false;
|
||||
|
||||
if let Some(user_session) = maybe_user_session {
|
||||
// NB: Moderators can see deleted results.
|
||||
// Original creators cannot see them (unless they're moderators!)
|
||||
show_deleted_results = user_session.can_delete_other_users_tts_results;
|
||||
// Moderators get to see all the fields.
|
||||
is_moderator = user_session.can_delete_other_users_tts_results
|
||||
|| user_session.can_edit_other_users_tts_models;
|
||||
}
|
||||
|
||||
// NB(bt,2024-03-24): I'm sorry, this is gross. We're not respecting sorting, input ordering,
|
||||
// de-duplication, (if we swap types), etc. Gotta go fast.
|
||||
let result = list_batch_generated_redux_media_files_with_connection(
|
||||
&path.token,
|
||||
show_deleted_results,
|
||||
&mut mysql_connection,
|
||||
).await;
|
||||
|
||||
let media_files = match result {
|
||||
Ok(batch) => batch.media_files,
|
||||
Err(e) => {
|
||||
warn!("query error: {:?}", e);
|
||||
return Err(CommonWebError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let media_domain = get_media_domain(&http_request);
|
||||
|
||||
let media_files = media_files.into_iter()
|
||||
.map(|result| {
|
||||
let public_bucket_path = MediaFileBucketPath::from_object_hash(
|
||||
&result.public_bucket_directory_hash,
|
||||
result.maybe_public_bucket_prefix.as_deref(),
|
||||
result.maybe_public_bucket_extension.as_deref());
|
||||
|
||||
BatchGeneratedReduxMediaFileInfo {
|
||||
token: result.token.clone(),
|
||||
media_class: result.media_class,
|
||||
media_type: result.media_type,
|
||||
maybe_batch_token: result.maybe_batch_token,
|
||||
media_links: MediaLinksBuilder::from_media_path(media_domain, &public_bucket_path),
|
||||
cover_image: MediaFileCoverImageDetailsBuilder::from_optional_db_fields(
|
||||
&result.token,
|
||||
media_domain,
|
||||
result.maybe_file_cover_image_public_bucket_hash.as_deref(),
|
||||
result.maybe_file_cover_image_public_bucket_prefix.as_deref(),
|
||||
result.maybe_file_cover_image_public_bucket_extension.as_deref(),
|
||||
),
|
||||
maybe_title: result.maybe_title,
|
||||
maybe_creator_user: UserDetailsLightBuilder::from_optional_db_fields_owned(
|
||||
result.maybe_creator_user_token,
|
||||
result.maybe_creator_username,
|
||||
result.maybe_creator_display_name,
|
||||
result.maybe_creator_gravatar_hash,
|
||||
),
|
||||
creator_set_visibility: result.creator_set_visibility,
|
||||
maybe_prompt_token: result.maybe_prompt_token,
|
||||
maybe_original_filename: result.maybe_origin_filename,
|
||||
maybe_duration_millis: result.maybe_duration_millis,
|
||||
created_at: result.created_at,
|
||||
updated_at: result.updated_at,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Json(ListBatchGeneratedReduxMediaFilesSuccessResponse {
|
||||
success: true,
|
||||
media_files,
|
||||
}))
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod list_batch_generated_redux_media_files_handler;
|
||||
pub mod list_featured_media_files_handler;
|
||||
pub mod list_media_files_by_batch_token_handler;
|
||||
pub mod list_media_files_for_user_handler;
|
||||
|
||||
+11
-8
@@ -15,7 +15,7 @@ use http_server_common::response::response_success_helpers::SimpleGenericJsonSuc
|
||||
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
|
||||
use log::{info, warn};
|
||||
use mysql_queries::queries::generic_inference::fal::get_inference_job_by_fal_id::get_inference_job_by_fal_id;
|
||||
use mysql_queries::queries::generic_inference::fal::mark_fal_generic_inference_job_successfully_done::mark_fal_generic_inference_job_successfully_done;
|
||||
use mysql_queries::queries::generic_inference::fal::mark_fal_generic_inference_job_successfully_done::{mark_fal_generic_inference_job_successfully_done, MarkJobArgs};
|
||||
use serde_json::Value;
|
||||
use utoipa::ToSchema;
|
||||
// 1. tauri --> hit endpoint to enqueue
|
||||
@@ -95,7 +95,7 @@ impl From<anyhow::Error> for FalWebhookError {
|
||||
/// Fal webhook
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Webooks",
|
||||
tag = "Webhooks",
|
||||
path = "/v1/webhooks/fal",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = SimpleGenericJsonSuccess),
|
||||
@@ -143,6 +143,7 @@ pub async fn fal_webhook_handler(
|
||||
info!("Fal webhook job record: {:?}", job);
|
||||
|
||||
let mut maybe_media_token = None;
|
||||
let mut maybe_batch_token = None;
|
||||
|
||||
if let Some(payload_obj) = payload.as_object() {
|
||||
if payload_obj.contains_key("image") {
|
||||
@@ -150,7 +151,7 @@ pub async fn fal_webhook_handler(
|
||||
let token = handle_image_payload(payload_obj, &job, &server_state).await?;
|
||||
maybe_media_token = Some(token);
|
||||
} else if payload_obj.contains_key("images") {
|
||||
maybe_media_token = handle_images_payload(payload_obj, &job, &server_state).await?;
|
||||
(maybe_media_token, maybe_batch_token) = handle_images_payload(payload_obj, &job, &server_state).await?;
|
||||
} else if payload_obj.contains_key("video") {
|
||||
info!("Handling video payload for job: {:?}", job.job_token);
|
||||
let token = handle_video_payload(payload_obj, &job, &server_state).await?;
|
||||
@@ -165,11 +166,13 @@ pub async fn fal_webhook_handler(
|
||||
if let Some(media_token) = maybe_media_token {
|
||||
info!("Media file token: {:?}", media_token);
|
||||
// TODO: Update job metadata.
|
||||
mark_fal_generic_inference_job_successfully_done(
|
||||
&server_state.mysql_pool,
|
||||
&job.job_token,
|
||||
media_token,
|
||||
).await.map_err(|err| {
|
||||
mark_fal_generic_inference_job_successfully_done(MarkJobArgs {
|
||||
job_token: &job.job_token,
|
||||
media_file_token: &media_token,
|
||||
maybe_batch_token: maybe_batch_token.as_ref(),
|
||||
mysql_executor: &server_state.mysql_pool,
|
||||
phantom: Default::default(),
|
||||
}).await.map_err(|err| {
|
||||
warn!("Error marking job as successfully done: {:?}", err);
|
||||
FalWebhookError::ServerError
|
||||
})?;
|
||||
|
||||
+28
-8
@@ -15,6 +15,7 @@ use mimetypes::mimetype_info::mimetype_info::MimetypeInfo;
|
||||
use mysql_queries::queries::generic_inference::fal::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 tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
const PREFIX : Option<&str> = Some("artcraft_");
|
||||
@@ -33,8 +34,8 @@ pub async fn handle_images_payload(
|
||||
payload: &Map<String, Value>,
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
) -> AnyhowResult<Option<MediaFileToken>> {
|
||||
|
||||
) -> AnyhowResult<(Option<MediaFileToken>, Option<BatchGenerationToken>)> {
|
||||
|
||||
let images_value = payload.get("images")
|
||||
.ok_or_else(|| anyhow!("no `images` key in payload"))?;
|
||||
|
||||
@@ -43,22 +44,31 @@ pub async fn handle_images_payload(
|
||||
let images: Vec<FalWebhookImage> = serde_json::from_value(images_value.clone())?;
|
||||
|
||||
let mut maybe_media_token = None;
|
||||
|
||||
|
||||
// NB: We are not going to create `batch_generations` table records. We don't need them.
|
||||
// The foreign key in `media_files` is enough to look up the rest of the batch.
|
||||
let mut maybe_batch_token = None;
|
||||
|
||||
if images.len() > 1 {
|
||||
maybe_batch_token = Some(BatchGenerationToken::generate());
|
||||
}
|
||||
|
||||
for image in images {
|
||||
let media_token = upload_image(job, server_state, &image).await?;
|
||||
let media_token = upload_image(job, server_state, &image, maybe_batch_token.as_ref()).await?;
|
||||
|
||||
if maybe_media_token.is_none() {
|
||||
maybe_media_token = Some(media_token); // Set the first media token
|
||||
}
|
||||
}
|
||||
|
||||
Ok(maybe_media_token)
|
||||
Ok((maybe_media_token, maybe_batch_token))
|
||||
}
|
||||
|
||||
async fn upload_image(
|
||||
job: &FalJobDetails,
|
||||
server_state: &ServerState,
|
||||
image: &FalWebhookImage,
|
||||
maybe_batch_token: Option<&BatchGenerationToken>,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
let image_url = image.url
|
||||
.as_deref()
|
||||
@@ -98,6 +108,7 @@ async fn upload_image(
|
||||
image,
|
||||
&file_bytes,
|
||||
mimetype_info,
|
||||
maybe_batch_token,
|
||||
).await
|
||||
}
|
||||
_ => {
|
||||
@@ -107,6 +118,7 @@ async fn upload_image(
|
||||
image,
|
||||
&file_bytes,
|
||||
mimetype_info,
|
||||
maybe_batch_token,
|
||||
).await
|
||||
}
|
||||
}
|
||||
@@ -118,6 +130,7 @@ async fn upload_image_bytes(
|
||||
image: &FalWebhookImage,
|
||||
file_bytes: &[u8],
|
||||
mimetype_info: MimetypeInfo,
|
||||
maybe_batch_token: Option<&BatchGenerationToken>,
|
||||
) -> AnyhowResult<MediaFileToken> {
|
||||
|
||||
let mime_type = mimetype_info.mime_type();
|
||||
@@ -145,7 +158,7 @@ async fn upload_image_bytes(
|
||||
&mime_type)
|
||||
.await?;
|
||||
|
||||
let media_token = MediaFileInsertBuilder::new()
|
||||
let mut query_builder = 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)
|
||||
@@ -159,11 +172,18 @@ async fn upload_image_bytes(
|
||||
.frame_width(image_info.width())
|
||||
.frame_height(image_info.height())
|
||||
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
|
||||
.checksum_sha2(&file_hash)
|
||||
.checksum_sha2(&file_hash);
|
||||
|
||||
if let Some(batch_token) = maybe_batch_token {
|
||||
query_builder = query_builder.maybe_batch_generation_token(Some(batch_token));
|
||||
}
|
||||
|
||||
let media_token = query_builder
|
||||
.insert_pool(&server_state.mysql_pool)
|
||||
.await?;
|
||||
|
||||
info!("Image media file uploaded with token: {}", media_token);
|
||||
info!("Image media file uploaded with token: {} ; possible batch token: {:?}",
|
||||
media_token, maybe_batch_token);
|
||||
|
||||
Ok(media_token)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use crate::http_server::endpoints::media_files::edit::set_media_file_cover_image
|
||||
use crate::http_server::endpoints::media_files::edit::update_media_file_handler::update_media_file_handler;
|
||||
use crate::http_server::endpoints::media_files::get::batch_get_media_files_handler::batch_get_media_files_handler;
|
||||
use crate::http_server::endpoints::media_files::get::get_media_file_handler::get_media_file_handler;
|
||||
use crate::http_server::endpoints::media_files::list::list_batch_generated_redux_media_files_handler::list_batch_generated_redux_media_files_handler;
|
||||
use crate::http_server::endpoints::media_files::list::list_featured_media_files_handler::list_featured_media_files_handler;
|
||||
use crate::http_server::endpoints::media_files::list::list_media_files_by_batch_token_handler::list_media_files_by_batch_token_handler;
|
||||
use crate::http_server::endpoints::media_files::list::list_media_files_for_user_handler::list_media_files_for_user_handler;
|
||||
@@ -76,6 +77,10 @@ pub fn add_media_file_routes<T, B> (app: App<T>) -> App<T>
|
||||
.route(web::get().to(batch_get_media_files_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/batch_gen_redux/{token}")
|
||||
.route(web::get().to(list_batch_generated_redux_media_files_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/list")
|
||||
.route(web::get().to(list_media_files_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
useModelSelectorStore,
|
||||
} from "@storyteller/ui-model-selector";
|
||||
import { ModelInfo } from "@storyteller/model-list";
|
||||
import { useImageEditCompleteEvent } from "@storyteller/tauri-events";
|
||||
|
||||
|
||||
const PAGE_ID: ModelPage = ModelPage.ImageEditor;
|
||||
|
||||
@@ -54,6 +56,17 @@ const PageEdit = () => {
|
||||
|
||||
// Use the Zustand store
|
||||
const store = useEditStore();
|
||||
|
||||
useImageEditCompleteEvent(async (event) => {
|
||||
console.log("Image edit complete:", event);
|
||||
|
||||
// TODO: Logic to show edit history
|
||||
for (const editedImage of event.edited_images) {
|
||||
console.log("Edited image:", editedImage.media_token);
|
||||
console.log("Edited image url:", editedImage.cdn_url);
|
||||
console.log("Edited image thumbnail template:", editedImage.maybe_thumbnail_template);
|
||||
}
|
||||
});
|
||||
|
||||
// Pass store actions directly as callbacks
|
||||
useDeleteHotkeys({ onDelete: store.deleteSelectedItems });
|
||||
@@ -273,6 +286,7 @@ const PageEdit = () => {
|
||||
mask_image_raw_bytes: arrayBuffer,
|
||||
prompt: prompt,
|
||||
image_count: generationCount,
|
||||
frontend_caller: "image_editor",
|
||||
});
|
||||
|
||||
setIsEnqueuing(false);
|
||||
|
||||
@@ -17,6 +17,12 @@ export interface EnqueueImageInpaintRequest {
|
||||
|
||||
// Number of images to generate.
|
||||
image_count?: number;
|
||||
|
||||
// TODO: Actual enum.
|
||||
frontend_caller?: string;
|
||||
|
||||
// Optional frontend state to return later.
|
||||
frontend_subscriber_id?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +34,8 @@ interface RawEnqueueImageInpaintRequest {
|
||||
mask_image_raw_bytes?: Uint8Array;
|
||||
prompt?: string;
|
||||
image_count?: number;
|
||||
frontend_caller?: string;
|
||||
frontend_subscriber_id?: string;
|
||||
}
|
||||
|
||||
export enum EnqueueImageInpaintErrorType {
|
||||
@@ -88,6 +96,14 @@ export const EnqueueImageInpaint = async (request: EnqueueImageInpaintRequest) :
|
||||
mutableRequest.image_count = request.image_count;
|
||||
}
|
||||
|
||||
if (!!request.frontend_caller) {
|
||||
mutableRequest.frontend_caller = request.frontend_caller;
|
||||
}
|
||||
|
||||
if (!!request.frontend_subscriber_id) {
|
||||
mutableRequest.frontend_subscriber_id = request.frontend_subscriber_id;
|
||||
}
|
||||
|
||||
const result = await invoke("enqueue_image_inpaint_command", {
|
||||
request: mutableRequest,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ export * from './lib/common/BasicEventWrapper';
|
||||
export * from './lib/events/FlashUserInputErrorEvent';
|
||||
export * from './lib/events/RefreshAccountStateEvent';
|
||||
export * from './lib/events/ShowProviderLoginModalEvent';
|
||||
export * from './lib/events/functional/ImageEditCompleteEvent';
|
||||
export * from './lib/events/generation/useGenerationCompleteEvent';
|
||||
export * from './lib/events/generation/useGenerationEnqueueFailureEvent';
|
||||
export * from './lib/events/generation/useGenerationEnqueueSuccessEvent';
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { listen, UnlistenFn } from '@tauri-apps/api/event';
|
||||
import { BasicEventWrapper } from '../../common/BasicEventWrapper';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const EVENT_NAME : string = 'image_edit_complete_event';
|
||||
|
||||
export interface ImageEditCompleteEvent {
|
||||
edited_images: EditedImage[],
|
||||
maybe_frontend_subscriber_id?: string,
|
||||
maybe_frontend_subscriber_payload?: string,
|
||||
}
|
||||
|
||||
export interface EditedImage {
|
||||
media_token: string,
|
||||
cdn_url: string,
|
||||
maybe_thumbnail_template?: string,
|
||||
}
|
||||
|
||||
export const useImageEditCompleteEvent = (asyncCallback: (event: ImageEditCompleteEvent) => Promise<void>) => {
|
||||
useEffect(() => {
|
||||
let isUnmounted = false;
|
||||
let unlisten: Promise<UnlistenFn>;
|
||||
|
||||
const setup = async () => {
|
||||
unlisten = listen<BasicEventWrapper<ImageEditCompleteEvent>>(EVENT_NAME, async (wrappedEvent) => {
|
||||
await asyncCallback(wrappedEvent.payload.data);
|
||||
});
|
||||
|
||||
if (isUnmounted) {
|
||||
unlisten.then(f => f()); // Unsubscribe if unmounted early.
|
||||
}
|
||||
};
|
||||
|
||||
setup();
|
||||
|
||||
return () => {
|
||||
isUnmounted = true;
|
||||
unlisten.then(f => f());
|
||||
};
|
||||
|
||||
}, []);
|
||||
}
|
||||
Reference in new issue
Block a user