Compare commits

..
Author SHA1 Message Date
Brandon Thomas fd8fe7f3db fix tests 2026-07-08 16:17:12 -04:00
Brandon Thomas 54b172bf6f test on api client 2026-07-08 16:08:44 -04:00
Brandon Thomas d7868c7da0 don't validate cost estimates 2026-07-08 15:44:56 -04:00
107 changed files with 1131 additions and 1302 deletions
@@ -1,12 +0,0 @@
{
"db_name": "MySQL",
"query": "\n INSERT INTO media_files\n SET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n is_user_upload = ?,\n is_intermediate_system_file = ?,\n\n origin_category = ?, \n origin_product_category = ?, \n maybe_origin_model_type = ?, \n maybe_origin_model_token = ?, \n maybe_origin_filename = ?,\n\n maybe_batch_token = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n maybe_duration_millis = ?,\n maybe_audio_encoding = ?, \n maybe_video_encoding = ?, \n maybe_frame_width = ?, \n maybe_frame_height = ?,\n maybe_prompt_token = ?,\n checksum_sha2 = ?,\n \n maybe_engine_category = ?,\n\n maybe_title = ?,\n maybe_text_transcript = ?,\n\n maybe_scene_source_media_file_token = ?,\n\n public_bucket_directory_hash = ?, \n maybe_public_bucket_prefix = ?, \n maybe_public_bucket_extension = ?, \n\n maybe_creator_user_token = ?, \n maybe_creator_anonymous_visitor_token = ?, \n\n creator_ip_address = ?, \n creator_set_visibility = ?, \n\n maybe_creator_file_synthetic_id = ?, \n maybe_creator_category_synthetic_id = ?,\n\n extra_file_modification_info = ?,\n\n maybe_generation_provider = ?,\n\n platform_type = ?,\n\n maybe_cover_image_media_file_token = ?,\n\n maybe_mod_user_token = ?,\n is_generated_on_prem = ?,\n generated_by_worker = ?,\n generated_by_cluster = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 41
},
"nullable": []
},
"hash": "1c74e9c2dcc9205e2c9fe60a6de0828208e497e68560246e588092bafd03eb59"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_media_subtype = ?,\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n is_user_upload = TRUE,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n\nON DUPLICATE KEY UPDATE\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_media_subtype = ?,\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n is_user_upload = TRUE,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 39
},
"nullable": []
},
"hash": "55706e6ac35be221b9d9c6e8b247812944c61941964bb53815d31dc30d506be8"
}
@@ -1,6 +1,6 @@
{
"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_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\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_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n\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 model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\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 model_weights\n ON m.maybe_origin_model_token = model_weights.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 media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n ",
"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_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\n\n m.maybe_media_subtype as `maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype`,\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_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n\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 model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\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 model_weights\n ON m.maybe_origin_model_token = model_weights.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 media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n ",
"describe": {
"columns": [
{
@@ -55,7 +55,7 @@
},
{
"ordinal": 5,
"name": "maybe_mime_type",
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
"type_info": {
"type": "VarString",
"flags": "BINARY",
@@ -65,6 +65,16 @@
},
{
"ordinal": 6,
"name": "maybe_mime_type",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 7,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -74,7 +84,7 @@
}
},
{
"ordinal": 7,
"ordinal": 8,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
@@ -84,7 +94,7 @@
}
},
{
"ordinal": 8,
"ordinal": 9,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
@@ -94,7 +104,7 @@
}
},
{
"ordinal": 9,
"ordinal": 10,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -104,7 +114,7 @@
}
},
{
"ordinal": 10,
"ordinal": 11,
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
"type_info": {
"type": "VarString",
@@ -114,7 +124,7 @@
}
},
{
"ordinal": 11,
"ordinal": 12,
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
"type_info": {
"type": "VarString",
@@ -124,7 +134,7 @@
}
},
{
"ordinal": 12,
"ordinal": 13,
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -134,7 +144,7 @@
}
},
{
"ordinal": 13,
"ordinal": 14,
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -144,7 +154,7 @@
}
},
{
"ordinal": 14,
"ordinal": 15,
"name": "maybe_title",
"type_info": {
"type": "VarString",
@@ -154,7 +164,7 @@
}
},
{
"ordinal": 15,
"ordinal": 16,
"name": "maybe_text_transcript",
"type_info": {
"type": "Blob",
@@ -164,7 +174,7 @@
}
},
{
"ordinal": 16,
"ordinal": 17,
"name": "maybe_origin_filename",
"type_info": {
"type": "VarString",
@@ -174,7 +184,7 @@
}
},
{
"ordinal": 17,
"ordinal": 18,
"name": "maybe_duration_millis",
"type_info": {
"type": "Long",
@@ -184,7 +194,7 @@
}
},
{
"ordinal": 18,
"ordinal": 19,
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
"type_info": {
"type": "VarString",
@@ -194,7 +204,7 @@
}
},
{
"ordinal": 19,
"ordinal": 20,
"name": "maybe_other_prompt_args",
"type_info": {
"type": "Blob",
@@ -204,7 +214,7 @@
}
},
{
"ordinal": 20,
"ordinal": 21,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -214,7 +224,7 @@
}
},
{
"ordinal": 21,
"ordinal": 22,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -224,7 +234,7 @@
}
},
{
"ordinal": 22,
"ordinal": 23,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -234,7 +244,7 @@
}
},
{
"ordinal": 23,
"ordinal": 24,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
@@ -244,7 +254,7 @@
}
},
{
"ordinal": 24,
"ordinal": 25,
"name": "is_user_upload",
"type_info": {
"type": "Tiny",
@@ -254,7 +264,7 @@
}
},
{
"ordinal": 25,
"ordinal": 26,
"name": "is_intermediate_system_file",
"type_info": {
"type": "Tiny",
@@ -264,7 +274,7 @@
}
},
{
"ordinal": 26,
"ordinal": 27,
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
"type_info": {
"type": "VarString",
@@ -274,7 +284,7 @@
}
},
{
"ordinal": 27,
"ordinal": 28,
"name": "maybe_model_weights_title",
"type_info": {
"type": "VarString",
@@ -284,7 +294,7 @@
}
},
{
"ordinal": 28,
"ordinal": 29,
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
"type_info": {
"type": "VarString",
@@ -294,7 +304,7 @@
}
},
{
"ordinal": 29,
"ordinal": 30,
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
"type_info": {
"type": "VarString",
@@ -304,7 +314,7 @@
}
},
{
"ordinal": 30,
"ordinal": 31,
"name": "maybe_model_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -314,7 +324,7 @@
}
},
{
"ordinal": 31,
"ordinal": 32,
"name": "maybe_model_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -324,7 +334,7 @@
}
},
{
"ordinal": 32,
"ordinal": 33,
"name": "maybe_model_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -334,7 +344,7 @@
}
},
{
"ordinal": 33,
"ordinal": 34,
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -344,7 +354,7 @@
}
},
{
"ordinal": 34,
"ordinal": 35,
"name": "maybe_model_weight_creator_username",
"type_info": {
"type": "VarString",
@@ -354,7 +364,7 @@
}
},
{
"ordinal": 35,
"ordinal": 36,
"name": "maybe_model_weight_creator_display_name",
"type_info": {
"type": "VarString",
@@ -364,7 +374,7 @@
}
},
{
"ordinal": 36,
"ordinal": 37,
"name": "maybe_model_weight_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -374,7 +384,7 @@
}
},
{
"ordinal": 37,
"ordinal": 38,
"name": "extra_file_modification_info",
"type_info": {
"type": "Blob",
@@ -384,7 +394,7 @@
}
},
{
"ordinal": 38,
"ordinal": 39,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
@@ -394,7 +404,7 @@
}
},
{
"ordinal": 39,
"ordinal": 40,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -404,7 +414,7 @@
}
},
{
"ordinal": 40,
"ordinal": 41,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -414,7 +424,7 @@
}
},
{
"ordinal": 41,
"ordinal": 42,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -424,7 +434,7 @@
}
},
{
"ordinal": 42,
"ordinal": 43,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -434,7 +444,7 @@
}
},
{
"ordinal": 43,
"ordinal": 44,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -444,7 +454,7 @@
}
},
{
"ordinal": 44,
"ordinal": 45,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -454,7 +464,7 @@
}
},
{
"ordinal": 45,
"ordinal": 46,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -464,7 +474,7 @@
}
},
{
"ordinal": 46,
"ordinal": 47,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -501,6 +511,7 @@
true,
true,
true,
true,
false,
false,
false,
@@ -527,5 +538,5 @@
false
]
},
"hash": "ad217c26087b2ebed6f2c9f79b16ead63727107dfce3366f3c1d12bc9d565c2c"
"hash": "57fa22e6cd4e82b801605c96eac21babd78df3df361b3be566993b78a46efef2"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_prompt_token = ?,\n maybe_batch_token = ?,\n\n maybe_title = ?,\n maybe_duration_millis = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_scene_source_media_file_token = ?,\n\n is_intermediate_system_file = ?,\n is_user_upload = ?,\n\n maybe_generation_provider = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 27
},
"nullable": []
},
"hash": "7369b2f3fd9e7c1e968dc5501ef9bea7e1610fd7a044a0f9ad8b908dfada2b6a"
}
@@ -1,12 +0,0 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n maybe_project_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_prompt_token = ?,\n maybe_batch_token = ?,\n\n maybe_title = ?,\n maybe_duration_millis = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_scene_source_media_file_token = ?,\n\n is_intermediate_system_file = ?,\n is_user_upload = ?,\n\n maybe_generation_provider = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 28
},
"nullable": []
},
"hash": "a25fe977f51329ed078e2441446ef7ff16683dcad8b81c8d0a0a4540b5e985bc"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\n INSERT INTO media_files\n SET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n is_user_upload = ?,\n is_intermediate_system_file = ?,\n\n origin_category = ?, \n origin_product_category = ?, \n maybe_origin_model_type = ?, \n maybe_origin_model_token = ?, \n maybe_origin_filename = ?,\n\n is_batch_generated = ?,\n maybe_batch_token = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n maybe_duration_millis = ?,\n maybe_audio_encoding = ?, \n maybe_video_encoding = ?, \n maybe_frame_width = ?, \n maybe_frame_height = ?,\n maybe_prompt_token = ?,\n checksum_sha2 = ?,\n \n maybe_engine_category = ?,\n\n maybe_title = ?,\n maybe_text_transcript = ?,\n\n maybe_scene_source_media_file_token = ?,\n\n public_bucket_directory_hash = ?, \n maybe_public_bucket_prefix = ?, \n maybe_public_bucket_extension = ?, \n\n maybe_creator_user_token = ?, \n maybe_creator_anonymous_visitor_token = ?, \n\n creator_ip_address = ?, \n creator_set_visibility = ?, \n\n maybe_creator_file_synthetic_id = ?, \n maybe_creator_category_synthetic_id = ?,\n\n extra_file_modification_info = ?,\n\n maybe_generation_provider = ?,\n\n platform_type = ?,\n\n maybe_cover_image_media_file_token = ?,\n\n maybe_mod_user_token = ?,\n is_generated_on_prem = ?,\n generated_by_worker = ?,\n generated_by_cluster = ?\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 42
},
"nullable": []
},
"hash": "ad2ffbd9f48e52678a5c61817bec043bf1df4033463e5c5a18468c04af391e6a"
}
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\n UPDATE media_files\n SET\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n creator_ip_address = ?\n WHERE token = ?\n LIMIT 1\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 7
},
"nullable": []
},
"hash": "ad311b4aa95a71b1dc37aa09c27850a0e963380c6e7780f47a9fc5a95ab70583"
}
@@ -1,12 +0,0 @@
{
"db_name": "MySQL",
"query": "\n UPDATE media_files\n SET\n media_class = ?,\n maybe_project_type = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n creator_ip_address = ?\n WHERE token = ?\n LIMIT 1\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 9
},
"nullable": []
},
"hash": "e33fa6280a39fff22b2e8c2731b7b6058b4705b5b557f34dba52a6440c22529b"
}
@@ -1,12 +0,0 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n maybe_project_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n is_user_upload = TRUE,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n\nON DUPLICATE KEY UPDATE\n\n media_class = ?,\n media_type = ?,\n maybe_project_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n is_user_upload = TRUE,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 39
},
"nullable": []
},
"hash": "ed190d823c1d0cb5991666a25329578cbca124327bbf84116764f80d8ab4641d"
}
@@ -1,6 +1,6 @@
{
"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_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\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_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\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 model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\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 model_weights\n ON m.maybe_origin_model_token = model_weights.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 media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n ",
"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_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,\n m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,\n\n m.maybe_media_subtype as `maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype`,\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_style_transfer_source_media_file_token as `maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\n m.maybe_scene_source_media_file_token as `maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken`,\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 model_weights.token as `maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken`,\n model_weights.title as maybe_model_weights_title,\n model_weights.weights_type as `maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType`,\n model_weights.weights_category as `maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory`,\n\n model_weight_cover_image.public_bucket_directory_hash as maybe_model_cover_image_public_bucket_hash,\n model_weight_cover_image.maybe_public_bucket_prefix as maybe_model_cover_image_public_bucket_prefix,\n model_weight_cover_image.maybe_public_bucket_extension as maybe_model_cover_image_public_bucket_extension,\n\n model_weight_creator.token as `maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken`,\n model_weight_creator.username as maybe_model_weight_creator_username,\n model_weight_creator.display_name as maybe_model_weight_creator_display_name,\n model_weight_creator.email_gravatar_hash as maybe_model_weight_creator_gravatar_hash,\n\n m.extra_file_modification_info,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n entity_stats.ratings_positive_count as maybe_ratings_positive_count,\n entity_stats.ratings_negative_count as maybe_ratings_negative_count,\n entity_stats.bookmark_count as maybe_bookmark_count,\n\n featured_items.entity_token IS NOT NULL AS is_featured,\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 model_weights\n ON m.maybe_origin_model_token = model_weights.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 media_files as model_weight_cover_image\n ON model_weight_cover_image.token = model_weights.maybe_cover_image_media_file_token\nLEFT OUTER JOIN users as model_weight_creator\n ON model_weight_creator.token = model_weights.creator_user_token\nLEFT OUTER JOIN entity_stats\n ON entity_stats.entity_type = \"media_file\"\n AND entity_stats.entity_token = m.token\nLEFT OUTER JOIN prompts\n ON prompts.token = m.maybe_prompt_token\nLEFT OUTER JOIN featured_items\n ON featured_items.entity_type = 'media_file'\n AND featured_items.entity_token = m.token\n AND featured_items.deleted_at IS NULL\nWHERE\n m.token = ?\n ",
"describe": {
"columns": [
{
@@ -55,7 +55,7 @@
},
{
"ordinal": 5,
"name": "maybe_mime_type",
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
"type_info": {
"type": "VarString",
"flags": "BINARY",
@@ -65,6 +65,16 @@
},
{
"ordinal": 6,
"name": "maybe_mime_type",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 7,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -74,7 +84,7 @@
}
},
{
"ordinal": 7,
"ordinal": 8,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
@@ -84,7 +94,7 @@
}
},
{
"ordinal": 8,
"ordinal": 9,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
@@ -94,7 +104,7 @@
}
},
{
"ordinal": 9,
"ordinal": 10,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -104,7 +114,7 @@
}
},
{
"ordinal": 10,
"ordinal": 11,
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
"type_info": {
"type": "VarString",
@@ -114,7 +124,7 @@
}
},
{
"ordinal": 11,
"ordinal": 12,
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
"type_info": {
"type": "VarString",
@@ -124,7 +134,7 @@
}
},
{
"ordinal": 12,
"ordinal": 13,
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -134,7 +144,7 @@
}
},
{
"ordinal": 13,
"ordinal": 14,
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
@@ -144,7 +154,7 @@
}
},
{
"ordinal": 14,
"ordinal": 15,
"name": "maybe_title",
"type_info": {
"type": "VarString",
@@ -154,7 +164,7 @@
}
},
{
"ordinal": 15,
"ordinal": 16,
"name": "maybe_text_transcript",
"type_info": {
"type": "Blob",
@@ -164,7 +174,7 @@
}
},
{
"ordinal": 16,
"ordinal": 17,
"name": "maybe_origin_filename",
"type_info": {
"type": "VarString",
@@ -174,7 +184,7 @@
}
},
{
"ordinal": 17,
"ordinal": 18,
"name": "maybe_duration_millis",
"type_info": {
"type": "Long",
@@ -184,7 +194,7 @@
}
},
{
"ordinal": 18,
"ordinal": 19,
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
"type_info": {
"type": "VarString",
@@ -194,7 +204,7 @@
}
},
{
"ordinal": 19,
"ordinal": 20,
"name": "maybe_other_prompt_args",
"type_info": {
"type": "Blob",
@@ -204,7 +214,7 @@
}
},
{
"ordinal": 20,
"ordinal": 21,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -214,7 +224,7 @@
}
},
{
"ordinal": 21,
"ordinal": 22,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -224,7 +234,7 @@
}
},
{
"ordinal": 22,
"ordinal": 23,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -234,7 +244,7 @@
}
},
{
"ordinal": 23,
"ordinal": 24,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
@@ -244,7 +254,7 @@
}
},
{
"ordinal": 24,
"ordinal": 25,
"name": "is_user_upload",
"type_info": {
"type": "Tiny",
@@ -254,7 +264,7 @@
}
},
{
"ordinal": 25,
"ordinal": 26,
"name": "is_intermediate_system_file",
"type_info": {
"type": "Tiny",
@@ -264,7 +274,7 @@
}
},
{
"ordinal": 26,
"ordinal": 27,
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
"type_info": {
"type": "VarString",
@@ -274,7 +284,7 @@
}
},
{
"ordinal": 27,
"ordinal": 28,
"name": "maybe_model_weights_title",
"type_info": {
"type": "VarString",
@@ -284,7 +294,7 @@
}
},
{
"ordinal": 28,
"ordinal": 29,
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
"type_info": {
"type": "VarString",
@@ -294,7 +304,7 @@
}
},
{
"ordinal": 29,
"ordinal": 30,
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
"type_info": {
"type": "VarString",
@@ -304,7 +314,7 @@
}
},
{
"ordinal": 30,
"ordinal": 31,
"name": "maybe_model_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
@@ -314,7 +324,7 @@
}
},
{
"ordinal": 31,
"ordinal": 32,
"name": "maybe_model_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -324,7 +334,7 @@
}
},
{
"ordinal": 32,
"ordinal": 33,
"name": "maybe_model_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -334,7 +344,7 @@
}
},
{
"ordinal": 33,
"ordinal": 34,
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
@@ -344,7 +354,7 @@
}
},
{
"ordinal": 34,
"ordinal": 35,
"name": "maybe_model_weight_creator_username",
"type_info": {
"type": "VarString",
@@ -354,7 +364,7 @@
}
},
{
"ordinal": 35,
"ordinal": 36,
"name": "maybe_model_weight_creator_display_name",
"type_info": {
"type": "VarString",
@@ -364,7 +374,7 @@
}
},
{
"ordinal": 36,
"ordinal": 37,
"name": "maybe_model_weight_creator_gravatar_hash",
"type_info": {
"type": "String",
@@ -374,7 +384,7 @@
}
},
{
"ordinal": 37,
"ordinal": 38,
"name": "extra_file_modification_info",
"type_info": {
"type": "Blob",
@@ -384,7 +394,7 @@
}
},
{
"ordinal": 38,
"ordinal": 39,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
@@ -394,7 +404,7 @@
}
},
{
"ordinal": 39,
"ordinal": 40,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
@@ -404,7 +414,7 @@
}
},
{
"ordinal": 40,
"ordinal": 41,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
@@ -414,7 +424,7 @@
}
},
{
"ordinal": 41,
"ordinal": 42,
"name": "maybe_ratings_positive_count",
"type_info": {
"type": "Long",
@@ -424,7 +434,7 @@
}
},
{
"ordinal": 42,
"ordinal": 43,
"name": "maybe_ratings_negative_count",
"type_info": {
"type": "Long",
@@ -434,7 +444,7 @@
}
},
{
"ordinal": 43,
"ordinal": 44,
"name": "maybe_bookmark_count",
"type_info": {
"type": "Long",
@@ -444,7 +454,7 @@
}
},
{
"ordinal": 44,
"ordinal": 45,
"name": "is_featured",
"type_info": {
"type": "LongLong",
@@ -454,7 +464,7 @@
}
},
{
"ordinal": 45,
"ordinal": 46,
"name": "created_at",
"type_info": {
"type": "Timestamp",
@@ -464,7 +474,7 @@
}
},
{
"ordinal": 46,
"ordinal": 47,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
@@ -501,6 +511,7 @@
true,
true,
true,
true,
false,
false,
false,
@@ -527,5 +538,5 @@
false
]
},
"hash": "784ca68c6cb61d481b605bfe6ee3248a5b44499038c71b57fc9ea6c4722d170f"
"hash": "f6c37066ab3e508050e81cf3bea38cc708b05c5c59ba1c322dcfb775e4b9e7a6"
}
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN has_watermark BOOLEAN NOT NULL DEFAULT FALSE
AFTER checksum_sha2;
@@ -1,10 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
-- NB: MySQL 8.0.29+ performs DROP COLUMN with ALGORITHM=INSTANT (metadata-only).
-- On older versions this is an INPLACE table rebuild that still permits
-- concurrent reads and writes. No ALGORITHM clause so the server picks the
-- best available.
ALTER TABLE media_files
DROP COLUMN has_watermark;
@@ -1,10 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN is_in_aws BOOLEAN NOT NULL DEFAULT FALSE
AFTER maybe_public_bucket_extension;
ALTER TABLE media_files
ADD INDEX index_is_in_aws (is_in_aws);
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP INDEX index_is_in_aws,
DROP COLUMN is_in_aws;
@@ -1,9 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
-- NB: anchored to maybe_public_bucket_extension (not is_in_aws) so this works
-- whether or not the is_in_aws drop migration has been reverted yet.
ALTER TABLE media_files
ADD COLUMN is_not_in_gcp BOOLEAN NOT NULL DEFAULT FALSE
AFTER maybe_public_bucket_extension;
@@ -1,6 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP COLUMN is_not_in_gcp;
@@ -1,10 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN nsfw_status VARCHAR(32) NOT NULL DEFAULT 'unknown'
AFTER maybe_scene_source_media_file_token;
ALTER TABLE media_files
ADD INDEX index_nsfw_status (nsfw_status);
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP INDEX index_nsfw_status,
DROP COLUMN nsfw_status;
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN is_batch_generated BOOLEAN NOT NULL DEFAULT FALSE
AFTER maybe_origin_filename;
@@ -1,6 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP COLUMN is_batch_generated;
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
ADD COLUMN maybe_media_subtype VARCHAR(32) DEFAULT NULL
AFTER media_class;
@@ -1,6 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP COLUMN maybe_media_subtype;
@@ -1,7 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE media_files
DROP INDEX index_maybe_project_type,
DROP COLUMN maybe_project_type;
@@ -1,16 +0,0 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
-- Which kind of internal Artcraft project document this media file is
-- ('scene_3d', 'mood_board', 'workflow', 'video_timeline', ...).
-- Only set when media_class = 'project'. The file format lives in media_type;
-- the JSON payload carries its own schema version.
ALTER TABLE media_files
ADD COLUMN maybe_project_type VARCHAR(32) DEFAULT NULL
AFTER media_class;
-- For "list my mood boards"-style filtered queries.
-- The column is all-NULL at creation time, so the index build is cheap.
ALTER TABLE media_files
ADD INDEX index_maybe_project_type (maybe_project_type);
@@ -99,6 +99,12 @@ CREATE TABLE media_files (
-- The original filename of the media (if uploaded by a user)
maybe_origin_filename VARCHAR(255) DEFAULT NULL,
-- TODO: Remove after `maybe_batch_token` gains use.
-- Whether this media file was generated as part of a batch
-- If so, we can look up the batch in a separate query/call to the `batch_generations` table.
-- We won't hold the batch token in this table since it'll be very sparsely populated.
is_batch_generated BOOLEAN NOT NULL DEFAULT FALSE,
-- If the media file is generated as part of a batch, this designates the batch.
maybe_batch_token VARCHAR(32) DEFAULT NULL,
@@ -132,6 +138,9 @@ CREATE TABLE media_files (
-- The token is a media file token.
maybe_scene_source_media_file_token VARCHAR(32) DEFAULT NULL,
-- A flag for NSFW status.
nsfw_status VARCHAR(32) NOT NULL DEFAULT "unknown",
-- ========== MEDIA DETAILS ==========
-- Type of media file:
@@ -157,19 +166,20 @@ CREATE TABLE media_files (
-- This is especially helpful with engine types that map to specific semantics.
-- * 'unknown' (TODO: This is the default until all records are backfilled.)
-- * 'audio' for wav, mp3, etc.
-- * 'image' for a variety of image types.
-- * 'image' for a variety of video types.
-- * 'video' for a variety of video types.
-- * 'dimensional' (DEPRECATED) split into 'mesh', 'splat', and 'project'; read-only until backfilled
-- * 'mesh' for 3d mesh assets (glb, gltf, fbx, obj, pmx, pmd, etc.)
-- * 'splat' for Gaussian splats (spz, etc.)
-- * 'project' for internal Artcraft project documents (see maybe_project_type)
-- * 'dimensional' for a variety of 3d types
media_class VARCHAR(16) NOT NULL DEFAULT "unknown",
-- Which kind of internal Artcraft project document this media file is
-- ('scene_3d', 'mood_board', 'workflow', 'video_timeline', ...).
-- Only set when media_class = 'project'. The file format lives in media_type;
-- the JSON payload carries its own schema version.
maybe_project_type VARCHAR(32) DEFAULT NULL,
-- DEPRECATED / DO NOT USE. THIS FIELD IS NO LONGER RELEVANT.
-- A media file's possible subtype. Typically used for Storyteller Studio.
-- * 'mixamo' for mixamo animations (eg. for BVH, GLB, FBX, etc. files)
-- * 'mocap_net' for mocapnet animations (eg. for BVH files)
-- * 'scene'
-- * 'character'
-- * 'animation'
-- DO NOT USE THE `maybe_media_subtype` FIELD!
maybe_media_subtype VARCHAR(32) DEFAULT NULL,
-- The file's mime type.
maybe_mime_type VARCHAR(32) DEFAULT NULL,
@@ -217,6 +227,10 @@ CREATE TABLE media_files (
-- and produces different lengths output depending on the choice of algorithm.
checksum_sha2 CHAR(64) NOT NULL,
-- Whether the image or video has a visible watermark.
-- If so, we may not need to add a watermark downstream in further processing steps.
has_watermark BOOLEAN NOT NULL DEFAULT FALSE,
-- Tracks the thumbnail algorithm/type used for this media file.
-- NULL = no thumbnail generated yet.
-- 1 = video thumbnail.
@@ -245,6 +259,13 @@ CREATE TABLE media_files (
-- `[{maybe_public_bucket_prefix}]{public_bucket_directory_hash}[{maybe_public_bucket_extension}]`
maybe_public_bucket_extension VARCHAR(16) DEFAULT NULL,
-- This is a migration flag that denotes whether this media file is stored in AWS.
-- If not, it's stored in GCP.
is_in_aws BOOLEAN NOT NULL DEFAULT false,
-- This is a migration flag that denotes that weight is not stored in GCP.
is_not_in_gcp BOOLEAN NOT NULL DEFAULT false,
-- NB: Removed, since this can be derived.
-- The directory this media is uploaded to will be exclusive for this file.
-- Only this given record will live in this bucket, but the directory may include
@@ -330,12 +351,13 @@ CREATE TABLE media_files (
KEY index_is_user_upload (is_user_upload),
KEY index_is_intermediate_system_file (is_intermediate_system_file),
KEY index_maybe_scene_source_media_file_token (maybe_scene_source_media_file_token),
KEY index_nsfw_status (nsfw_status),
KEY index_media_type (media_type),
KEY index_media_class (media_class),
KEY index_maybe_project_type (maybe_project_type),
KEY index_maybe_engine_category (maybe_engine_category),
KEY fk_maybe_prompt_token (maybe_prompt_token),
KEY index_checksum_sha2 (checksum_sha2),
KEY index_is_in_aws (is_in_aws),
KEY fk_maybe_creator_user_token (maybe_creator_user_token),
KEY fk_maybe_mod_user_token (maybe_mod_user_token),
KEY index_creator_ip_address (creator_ip_address),
@@ -0,0 +1,267 @@
{
"title": "[POST] /v1/media_files/write/scene_file",
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/write/scene_file`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/write/scene_file,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
"layout_type": "ordered",
"notify_list": [],
"reflow_type": "fixed",
"widgets": [
{
"definition": {
"type": "group",
"layout_type": "ordered",
"title": "Top-line health",
"show_title": true,
"widgets": [
{
"definition": {
"type": "query_value",
"title": "Requests/sec",
"title_size": "16",
"title_align": "left",
"precision": 2,
"autoscale": true,
"requests": [
{
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}.as_rate()",
"aggregator": "avg"
}
]
},
"layout": {
"x": 0,
"y": 0,
"width": 3,
"height": 2
}
},
{
"definition": {
"type": "query_value",
"title": "5xx error rate (%)",
"title_size": "16",
"title_align": "left",
"precision": 2,
"autoscale": false,
"custom_unit": "%",
"requests": [
{
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}.as_count()) * 100",
"aggregator": "avg",
"conditional_formats": [
{
"comparator": ">",
"value": 5.0,
"palette": "white_on_red"
},
{
"comparator": ">",
"value": 1.0,
"palette": "white_on_yellow"
},
{
"comparator": "<=",
"value": 1.0,
"palette": "white_on_green"
}
]
}
]
},
"layout": {
"x": 3,
"y": 0,
"width": 3,
"height": 2
}
},
{
"definition": {
"type": "query_value",
"title": "Latency p95 (ms)",
"title_size": "16",
"title_align": "left",
"precision": 0,
"autoscale": true,
"custom_unit": "ms",
"requests": [
{
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
"aggregator": "avg",
"conditional_formats": [
{
"comparator": ">",
"value": 1000,
"palette": "white_on_red"
},
{
"comparator": ">",
"value": 250,
"palette": "white_on_yellow"
},
{
"comparator": "<=",
"value": 250,
"palette": "white_on_green"
}
]
}
]
},
"layout": {
"x": 6,
"y": 0,
"width": 3,
"height": 2
}
},
{
"definition": {
"type": "query_value",
"title": "Latency p99 (ms)",
"title_size": "16",
"title_align": "left",
"precision": 0,
"autoscale": true,
"custom_unit": "ms",
"requests": [
{
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
"aggregator": "avg"
}
]
},
"layout": {
"x": 9,
"y": 0,
"width": 3,
"height": 2
}
}
]
},
"layout": {
"x": 0,
"y": 0,
"width": 12,
"height": 3
}
},
{
"definition": {
"type": "group",
"layout_type": "ordered",
"title": "Trends",
"show_title": true,
"widgets": [
{
"definition": {
"type": "timeseries",
"title": "Requests/sec by status class",
"title_size": "16",
"title_align": "left",
"show_legend": true,
"legend_layout": "horizontal",
"requests": [
{
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post} by {status_class}.as_rate()",
"display_type": "bars",
"style": {
"palette": "semantic",
"line_type": "solid",
"line_width": "normal"
}
}
],
"yaxis": {
"scale": "linear",
"include_zero": true
}
},
"layout": {
"x": 0,
"y": 0,
"width": 6,
"height": 4
}
},
{
"definition": {
"type": "timeseries",
"title": "Latency percentiles",
"title_size": "16",
"title_align": "left",
"show_legend": true,
"legend_layout": "horizontal",
"requests": [
{
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
"display_type": "line",
"style": {
"palette": "cool",
"line_type": "solid",
"line_width": "normal"
}
},
{
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
"display_type": "line",
"style": {
"palette": "warm",
"line_type": "solid",
"line_width": "normal"
}
},
{
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
"display_type": "line",
"style": {
"palette": "warm",
"line_type": "dashed",
"line_width": "normal"
}
}
],
"yaxis": {
"scale": "linear",
"include_zero": true,
"label": "ms"
}
},
"layout": {
"x": 6,
"y": 0,
"width": 6,
"height": 4
}
}
]
},
"layout": {
"x": 0,
"y": 3,
"width": 12,
"height": 5
}
},
{
"definition": {
"type": "toplist",
"title": "Status code distribution",
"title_size": "16",
"title_align": "left",
"requests": [
{
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
}
]
},
"layout": {
"x": 0,
"y": 8,
"width": 12,
"height": 4
}
}
],
"id": "c8z-t2z-nk4"
}
@@ -76,6 +76,11 @@ pub struct MediaFileInfo {
/// this describes the animation regime used or supported.
pub maybe_animation_type: Option<MediaFileAnimationType>,
// /// If the media file has a subtype, we'll report it.
// /// This is mostly used for Bevy engine files.
// #[deprecated(note="This was for the Bevy engine. Do not use.")]
// pub maybe_media_subtype: Option<MediaFileSubtype>,
// /// Extension for the engine to load over remote:// URLs.
// #[deprecated(note="This was for the Bevy engine. Do not use.")]
// pub maybe_engine_extension: Option<String>,
@@ -26,7 +26,7 @@ pub async fn seed_test_videos(mysql_pool: &Pool<MySql>, user_token: UserToken) -
let media1 = InsertArgs {
pool: &mysql_pool,
maybe_use_apriori_media_token: Some(&model_weight_token1),
media_file_type: MediaFileType::Mp4,
media_file_type: MediaFileType::Video,
maybe_mime_type: Some(&*metadata1.mimetype),
file_size_bytes: metadata1.file_size_bytes,
sha256_checksum: &metadata1.sha256_checksum,
@@ -29,16 +29,16 @@ pub async fn seed_media_files(mysql_pool: &Pool<MySql>, maybe_bucket_clients: Op
// Delete the records if you want to re-seed.
let media_files = [
// Audio
(HANASHI_USER_TOKEN, "m_21jxt406aattq2cna1xtgf5m4mjyyr", MediaFileType::Wav, "media/audio/voice-samples/biden.wav"),
(HANASHI_USER_TOKEN, "m_cxzabpxf88h10rshqraksxbqmhs41h", MediaFileType::Wav, "media/audio/voice-samples/brandon1.wav"),
(HANASHI_USER_TOKEN, "m_sq2vhrrmmbr079afk21543aa2jg53g", MediaFileType::Wav, "media/audio/voice-samples/brandon2.wav"),
(HANASHI_USER_TOKEN, "m_s61a8g3jnddr3xdg3gctsjxxk9jwja", MediaFileType::Wav, "media/audio/voice-samples/elon.wav"),
(HANASHI_USER_TOKEN, "m_e59fh75g76pvebx4qe4r4aymd93q30", MediaFileType::Wav, "media/audio/voice-samples/goku.wav"),
(HANASHI_USER_TOKEN, "m_ttq0ym8ht4v0z87zqz27fhadp0743c", MediaFileType::Wav, "media/audio/voice-samples/hillary.wav"),
(HANASHI_USER_TOKEN, "m_300bv6f141hv553fjf287p5gb76k37", MediaFileType::Wav, "media/audio/voice-samples/mario_impersonator.wav"),
(HANASHI_USER_TOKEN, "m_rd1ey2jem8a37r3vna2h9mht6zevs5", MediaFileType::Wav, "media/audio/voice-samples/rick.wav"),
(HANASHI_USER_TOKEN, "m_8n5epqfvs2vh60khap0s3ezem4epkc", MediaFileType::Wav, "media/audio/voice-samples/trump.wav"),
(HANASHI_USER_TOKEN, "m_g6dpwcpq51e6kvmpbbkdvgxfan9k1k", MediaFileType::Wav, "media/audio/voice-samples/trump_tts.wav"),
(HANASHI_USER_TOKEN, "m_21jxt406aattq2cna1xtgf5m4mjyyr", MediaFileType::Audio, "media/audio/voice-samples/biden.wav"),
(HANASHI_USER_TOKEN, "m_cxzabpxf88h10rshqraksxbqmhs41h", MediaFileType::Audio, "media/audio/voice-samples/brandon1.wav"),
(HANASHI_USER_TOKEN, "m_sq2vhrrmmbr079afk21543aa2jg53g", MediaFileType::Audio, "media/audio/voice-samples/brandon2.wav"),
(HANASHI_USER_TOKEN, "m_s61a8g3jnddr3xdg3gctsjxxk9jwja", MediaFileType::Audio, "media/audio/voice-samples/elon.wav"),
(HANASHI_USER_TOKEN, "m_e59fh75g76pvebx4qe4r4aymd93q30", MediaFileType::Audio, "media/audio/voice-samples/goku.wav"),
(HANASHI_USER_TOKEN, "m_ttq0ym8ht4v0z87zqz27fhadp0743c", MediaFileType::Audio, "media/audio/voice-samples/hillary.wav"),
(HANASHI_USER_TOKEN, "m_300bv6f141hv553fjf287p5gb76k37", MediaFileType::Audio, "media/audio/voice-samples/mario_impersonator.wav"),
(HANASHI_USER_TOKEN, "m_rd1ey2jem8a37r3vna2h9mht6zevs5", MediaFileType::Audio, "media/audio/voice-samples/rick.wav"),
(HANASHI_USER_TOKEN, "m_8n5epqfvs2vh60khap0s3ezem4epkc", MediaFileType::Audio, "media/audio/voice-samples/trump.wav"),
(HANASHI_USER_TOKEN, "m_g6dpwcpq51e6kvmpbbkdvgxfan9k1k", MediaFileType::Audio, "media/audio/voice-samples/trump_tts.wav"),
// Images
(HANASHI_USER_TOKEN, "m_cn5t6ywm9k1ge9qzzxst5krc5tm08m", MediaFileType::Jpg, "media/image/face-animator-inputs/bill.jpg"),
(HANASHI_USER_TOKEN, "m_nf9xebrr8hd1bxkfy7src5k8nv2dzy", MediaFileType::Jpg, "media/image/face-animator-inputs/elon.jpg"),
@@ -48,18 +48,18 @@ pub async fn seed_media_files(mysql_pool: &Pool<MySql>, maybe_bucket_clients: Op
(HANASHI_USER_TOKEN, "m_we7mnzbd76g4yn19dm6m53er0pjvew", MediaFileType::Png, "media/image/face-animator-inputs/san.png"),
(HANASHI_USER_TOKEN, "m_wahwz92nxxfg0x42dw9fdrhwq30yk0", MediaFileType::Jpg, "media/image/face-animator-inputs/zelda.jpg"),
// Video
(HANASHI_USER_TOKEN, "m_gedarcfdkxx3zv8zz2wmwsnc1r8jbp", MediaFileType::Mp4, "media/video/storyteller-studio-renders/girl-rotating-room.mp4"),
(HANASHI_USER_TOKEN, "m_3s74yyd4jwrjdp17yx1694n29jv5gr", MediaFileType::Mp4, "media/video/storyteller-studio-renders/hanashi-run.mp4"),
(HANASHI_USER_TOKEN, "m_p6d64gm1wawz25bmdahfded3366b9f", MediaFileType::Mp4, "media/video/storyteller-studio-renders/mocap-kiss.mp4"),
(HANASHI_USER_TOKEN, "m_szp2c77z6je965k44vc22xn9432a88", MediaFileType::Mp4, "media/video/storyteller-studio-renders/rotating-mocap-girl.mp4"),
(HANASHI_USER_TOKEN, "m_mdwspz81zymw9hvmb1d3jm830t25xe", MediaFileType::Mp4, "media/video/storyteller-studio-renders/rotating-mocap-m.mp4"),
(HANASHI_USER_TOKEN, "m_prcs6xkm8phs5y4a2dfwemwjy6n6z3", MediaFileType::Mp4, "media/video/storyteller-studio-renders/rotating-run-girl.mp4"),
(HANASHI_USER_TOKEN, "m_5j889dzw08vn9pbrscx1sncff43ekp", MediaFileType::Mp4, "media/video/storyteller-studio-renders/rotating-run-jww.mp4"),
(HANASHI_USER_TOKEN, "m_fwm2w6wbssez274ayz6x8ta0h1a9qp", MediaFileType::Mp4, "media/video/storyteller-studio-renders/rotating-run-pascal.mp4"),
(HANASHI_USER_TOKEN, "m_yqqkrt2srep6a35xg78114jn9epwg1", MediaFileType::Mp4, "media/video/storyteller-studio-renders/rotating-run-pop.mp4"),
(HANASHI_USER_TOKEN, "m_m4r043kez14pm6vaamsecpz8fafh7g", MediaFileType::Mp4, "media/video/video-style-transfer/man-explaining.mp4"),
(HANASHI_USER_TOKEN, "m_81gq3hygqvrahg6ncxbtcpjap4f9nv", MediaFileType::Mp4, "media/video/video-style-transfer/woman-gradient-background.mp4"),
(HANASHI_USER_TOKEN, "m_dp9ykj3yasjx69g2fx2bpd26wwjz0f", MediaFileType::Mp4, "media/video/video-style-transfer/woman-red-hair.mp4"),
(HANASHI_USER_TOKEN, "m_gedarcfdkxx3zv8zz2wmwsnc1r8jbp", MediaFileType::Video, "media/video/storyteller-studio-renders/girl-rotating-room.mp4"),
(HANASHI_USER_TOKEN, "m_3s74yyd4jwrjdp17yx1694n29jv5gr", MediaFileType::Video, "media/video/storyteller-studio-renders/hanashi-run.mp4"),
(HANASHI_USER_TOKEN, "m_p6d64gm1wawz25bmdahfded3366b9f", MediaFileType::Video, "media/video/storyteller-studio-renders/mocap-kiss.mp4"),
(HANASHI_USER_TOKEN, "m_szp2c77z6je965k44vc22xn9432a88", MediaFileType::Video, "media/video/storyteller-studio-renders/rotating-mocap-girl.mp4"),
(HANASHI_USER_TOKEN, "m_mdwspz81zymw9hvmb1d3jm830t25xe", MediaFileType::Video, "media/video/storyteller-studio-renders/rotating-mocap-m.mp4"),
(HANASHI_USER_TOKEN, "m_prcs6xkm8phs5y4a2dfwemwjy6n6z3", MediaFileType::Video, "media/video/storyteller-studio-renders/rotating-run-girl.mp4"),
(HANASHI_USER_TOKEN, "m_5j889dzw08vn9pbrscx1sncff43ekp", MediaFileType::Video, "media/video/storyteller-studio-renders/rotating-run-jww.mp4"),
(HANASHI_USER_TOKEN, "m_fwm2w6wbssez274ayz6x8ta0h1a9qp", MediaFileType::Video, "media/video/storyteller-studio-renders/rotating-run-pascal.mp4"),
(HANASHI_USER_TOKEN, "m_yqqkrt2srep6a35xg78114jn9epwg1", MediaFileType::Video, "media/video/storyteller-studio-renders/rotating-run-pop.mp4"),
(HANASHI_USER_TOKEN, "m_m4r043kez14pm6vaamsecpz8fafh7g", MediaFileType::Video, "media/video/video-style-transfer/man-explaining.mp4"),
(HANASHI_USER_TOKEN, "m_81gq3hygqvrahg6ncxbtcpjap4f9nv", MediaFileType::Video, "media/video/video-style-transfer/woman-gradient-background.mp4"),
(HANASHI_USER_TOKEN, "m_dp9ykj3yasjx69g2fx2bpd26wwjz0f", MediaFileType::Video, "media/video/video-style-transfer/woman-red-hair.mp4"),
];
let seed_tool_data_root = get_seed_tool_data_root();
@@ -73,14 +73,14 @@ pub async fn main() -> AnyhowResult<()> {
match mimetype {
Some("audio/wav") |
Some("audio/x-wav") => {
media_file_type = MediaFileType::Wav;
media_file_type = MediaFileType::Video;
maybe_mime_type = Some("audio/wav");
maybe_public_bucket_prefix = Some("dev_upload_");
maybe_public_bucket_extension = Some(".wav");
}
Some("video/mp4") => {
media_file_type = MediaFileType::Mp4;
media_file_type = MediaFileType::Video;
maybe_mime_type = mimetype;
maybe_public_bucket_prefix = Some("dev_upload_");
maybe_public_bucket_extension = Some(".mp4");
@@ -105,6 +105,7 @@ async fn create_document_from_record(elasticsearch: &Elasticsearch, record: Medi
media_class: record.media_class,
media_type: record.media_type,
maybe_media_subtype: record.maybe_media_subtype,
maybe_engine_category: record.maybe_engine_category,
maybe_animation_type: record.maybe_animation_type,
@@ -264,6 +264,7 @@ async fn upload_grok_video(
// TODO: media_files.maybe_generation_provider
// TODO: media_files.maybe_origin_model_type
// TODO: media_files.maybe_origin_model_token (sref?)
// TODO: media_files.is_batch_generated
// TODO: media_files.maybe_batch_token
// TODO: media_files.is_user_upload
@@ -294,6 +294,7 @@ async fn upload_midjourney_batch(
// TODO: media_files.maybe_generation_provider
// TODO: media_files.maybe_origin_model_type
// TODO: media_files.maybe_origin_model_token (sref?)
// TODO: media_files.is_batch_generated
// TODO: media_files.maybe_batch_token
// TODO: media_files.is_user_upload
@@ -254,6 +254,7 @@ async fn upload_spz_splat(
// TODO: media_files.maybe_generation_provider
// TODO: media_files.maybe_origin_model_type
// TODO: media_files.maybe_origin_model_token (sref?)
// TODO: media_files.is_batch_generated
// TODO: media_files.maybe_batch_token
// TODO: media_files.is_user_upload
-3
View File
@@ -59,9 +59,6 @@ mod init;
mod logging;
pub (crate) mod types;
#[cfg(test)]
mod tests_common;
/// Re-export of env_logger
pub mod env_logger {
pub use env_logger::Builder;
-28
View File
@@ -1,28 +0,0 @@
//! Test utilities shared across easyenv test modules.
#![cfg(test)]
use std::env;
pub(crate) struct EnvVarGuard {
name: &'static str,
}
impl EnvVarGuard {
pub(crate) fn set(name: &'static str, value: &str) -> Self {
env::remove_var(name);
env::set_var(name, value);
Self { name }
}
pub(crate) fn unset(name: &'static str) -> Self {
env::remove_var(name);
Self { name }
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
env::remove_var(self.name);
}
}
-84
View File
@@ -75,87 +75,3 @@ fn get_env_bool_internal(env_name: &str) -> Result<Option<bool>, EnvError> {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests_common::EnvVarGuard;
#[test]
fn literals_round_trip_through_optional_required_and_or_default() {
let _g = EnvVarGuard::set("BOOLEAN_TEST_TRUE_LOWER", "true");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_TRUE_LOWER"), Some(true));
assert_eq!(get_env_bool_required("BOOLEAN_TEST_TRUE_LOWER").unwrap(), true);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_TRUE_LOWER", false), true);
let _g = EnvVarGuard::set("BOOLEAN_TEST_TRUE_UPPER", "TRUE");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_TRUE_UPPER"), Some(true));
assert_eq!(get_env_bool_required("BOOLEAN_TEST_TRUE_UPPER").unwrap(), true);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_TRUE_UPPER", false), true);
let _g = EnvVarGuard::set("BOOLEAN_TEST_FALSE_LOWER", "false");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_FALSE_LOWER"), Some(false));
assert_eq!(get_env_bool_required("BOOLEAN_TEST_FALSE_LOWER").unwrap(), false);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_FALSE_LOWER", true), false);
let _g = EnvVarGuard::set("BOOLEAN_TEST_FALSE_UPPER", "FALSE");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_FALSE_UPPER"), Some(false));
assert_eq!(get_env_bool_required("BOOLEAN_TEST_FALSE_UPPER").unwrap(), false);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_FALSE_UPPER", true), false);
}
#[test]
fn missing_returns_none_required_error_and_default() {
let _g = EnvVarGuard::unset("BOOLEAN_TEST_MISSING");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_MISSING"), None);
assert!(matches!(get_env_bool_required("BOOLEAN_TEST_MISSING"), Err(EnvError::RequiredNotPresent { .. })));
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_MISSING", true), true);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_MISSING", false), false);
}
#[test]
fn yes_is_unparseable() {
let _g = EnvVarGuard::set("BOOLEAN_TEST_YES", "yes");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_YES"), None);
assert!(matches!(get_env_bool_required("BOOLEAN_TEST_YES"), Err(EnvError::ParseError { .. })));
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_YES", true), true);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_YES", false), false);
}
#[test]
fn one_is_not_treated_as_true() {
let _g = EnvVarGuard::set("BOOLEAN_TEST_ONE", "1");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_ONE"), None);
assert!(matches!(get_env_bool_required("BOOLEAN_TEST_ONE"), Err(EnvError::ParseError { .. })));
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_ONE", true), true);
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_ONE", false), false);
}
#[test]
fn empty_string_is_unparseable() {
let _g = EnvVarGuard::set("BOOLEAN_TEST_EMPTY", "");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_EMPTY"), None);
assert!(matches!(get_env_bool_required("BOOLEAN_TEST_EMPTY"), Err(EnvError::ParseError { .. })));
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_EMPTY", true), true);
}
#[test]
fn mixed_case_true_is_unparseable() {
let _g = EnvVarGuard::set("BOOLEAN_TEST_MIXED_CASE", "TrUe");
assert_eq!(get_env_bool_optional("BOOLEAN_TEST_MIXED_CASE"), None);
assert!(matches!(get_env_bool_required("BOOLEAN_TEST_MIXED_CASE"), Err(EnvError::ParseError { .. })));
assert_eq!(get_env_bool_or_default("BOOLEAN_TEST_MIXED_CASE", false), false);
}
#[test]
fn required_not_present_carries_env_var_name() {
let _g = EnvVarGuard::unset("BOOLEAN_TEST_REQUIRED_NAME");
match get_env_bool_required("BOOLEAN_TEST_REQUIRED_NAME") {
Err(EnvError::RequiredNotPresent { name }) => {
assert_eq!(name, "BOOLEAN_TEST_REQUIRED_NAME");
},
other => panic!("unexpected result: {:?}", other),
}
}
}
-39
View File
@@ -63,42 +63,3 @@ fn get_env_duration_seconds_internal(env_name: &str) -> Result<Option<Duration>,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests_common::EnvVarGuard;
#[test]
fn zero_seconds_round_trips() {
let _g = EnvVarGuard::set("DURATION_TEST_ZERO", "0");
assert_eq!(get_env_duration_seconds_optional("DURATION_TEST_ZERO"), Some(Duration::from_secs(0)));
assert_eq!(get_env_duration_seconds_required("DURATION_TEST_ZERO").unwrap(), Duration::from_secs(0));
assert_eq!(get_env_duration_seconds_or_default("DURATION_TEST_ZERO", Duration::from_secs(99)), Duration::from_secs(0));
}
#[test]
fn u64_max_seconds_round_trips() {
let _g = EnvVarGuard::set("DURATION_TEST_U64_MAX", &u64::MAX.to_string());
assert_eq!(get_env_duration_seconds_optional("DURATION_TEST_U64_MAX"), Some(Duration::from_secs(u64::MAX)));
assert_eq!(get_env_duration_seconds_required("DURATION_TEST_U64_MAX").unwrap(), Duration::from_secs(u64::MAX));
assert_eq!(get_env_duration_seconds_or_default("DURATION_TEST_U64_MAX", Duration::from_secs(1)), Duration::from_secs(u64::MAX));
}
#[test]
fn negative_is_unparseable() {
let _g = EnvVarGuard::set("DURATION_TEST_NEGATIVE", "-1");
assert_eq!(get_env_duration_seconds_optional("DURATION_TEST_NEGATIVE"), None);
assert!(matches!(get_env_duration_seconds_required("DURATION_TEST_NEGATIVE"), Err(EnvError::ParseError { .. })));
assert_eq!(get_env_duration_seconds_or_default("DURATION_TEST_NEGATIVE", Duration::from_secs(5)), Duration::from_secs(5));
}
#[test]
fn missing_returns_none_required_error_and_default() {
let _g = EnvVarGuard::unset("DURATION_TEST_MISSING");
assert_eq!(get_env_duration_seconds_optional("DURATION_TEST_MISSING"), None);
assert!(matches!(get_env_duration_seconds_required("DURATION_TEST_MISSING"), Err(EnvError::RequiredNotPresent { .. })));
assert_eq!(get_env_duration_seconds_or_default("DURATION_TEST_MISSING", Duration::from_secs(42)), Duration::from_secs(42));
}
}
-49
View File
@@ -49,52 +49,3 @@ pub fn try_get_env_num_optional<T>(env_name: &str) -> Result<Option<T>, EnvError
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests_common::EnvVarGuard;
#[test]
fn parses_i32() {
let _g = EnvVarGuard::set("NUM_TEST_I32", "-42");
assert_eq!(get_env_num("NUM_TEST_I32", 0i32).unwrap(), -42);
assert_eq!(try_get_env_num_optional::<i32>("NUM_TEST_I32").unwrap(), Some(-42));
}
#[test]
fn parses_u64() {
let _g = EnvVarGuard::set("NUM_TEST_U64", "18446744073709551615");
assert_eq!(get_env_num("NUM_TEST_U64", 0u64).unwrap(), u64::MAX);
assert_eq!(try_get_env_num_optional::<u64>("NUM_TEST_U64").unwrap(), Some(u64::MAX));
}
#[test]
fn parses_f64() {
let _g = EnvVarGuard::set("NUM_TEST_F64", "3.14");
assert_eq!(get_env_num("NUM_TEST_F64", 0.0f64).unwrap(), 3.14);
assert_eq!(try_get_env_num_optional::<f64>("NUM_TEST_F64").unwrap(), Some(3.14));
}
#[test]
fn u8_overflow_returns_parse_error() {
let _g = EnvVarGuard::set("NUM_TEST_U8_OVERFLOW", "256");
assert!(get_env_num("NUM_TEST_U8_OVERFLOW", 0u8).is_err());
assert!(try_get_env_num_optional::<u8>("NUM_TEST_U8_OVERFLOW").is_err());
}
#[test]
fn missing_returns_default_and_none() {
let _g = EnvVarGuard::unset("NUM_TEST_MISSING");
assert_eq!(get_env_num("NUM_TEST_MISSING", 99i32).unwrap(), 99);
assert_eq!(try_get_env_num_optional::<i32>("NUM_TEST_MISSING").unwrap(), None);
}
#[test]
fn unparseable_returns_parse_error() {
let _g = EnvVarGuard::set("NUM_TEST_UNPARSEABLE", "not_a_number");
assert!(get_env_num("NUM_TEST_UNPARSEABLE", 0i32).is_err());
assert!(try_get_env_num_optional::<i32>("NUM_TEST_UNPARSEABLE").is_err());
}
}
-49
View File
@@ -37,52 +37,3 @@ pub fn get_env_string_required(env_name: &str) -> Result<String, EnvError> {
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests_common::EnvVarGuard;
#[test]
fn returns_empty_string_when_set_to_empty() {
let _g = EnvVarGuard::set("STRING_TEST_EMPTY", "");
assert_eq!(get_env_string_optional("STRING_TEST_EMPTY"), Some("".to_string()));
assert_eq!(get_env_string_required("STRING_TEST_EMPTY").unwrap(), "");
assert_eq!(get_env_string_or_default("STRING_TEST_EMPTY", "default"), "");
}
#[test]
fn preserves_unicode() {
let _g = EnvVarGuard::set("STRING_TEST_UNICODE", "café🎨");
assert_eq!(get_env_string_optional("STRING_TEST_UNICODE"), Some("café🎨".to_string()));
assert_eq!(get_env_string_required("STRING_TEST_UNICODE").unwrap(), "café🎨");
assert_eq!(get_env_string_or_default("STRING_TEST_UNICODE", "default"), "café🎨");
}
#[test]
fn preserves_whitespace() {
let _g = EnvVarGuard::set("STRING_TEST_WHITESPACE", " hello \t");
assert_eq!(get_env_string_optional("STRING_TEST_WHITESPACE"), Some(" hello \t".to_string()));
assert_eq!(get_env_string_required("STRING_TEST_WHITESPACE").unwrap(), " hello \t");
assert_eq!(get_env_string_or_default("STRING_TEST_WHITESPACE", "default"), " hello \t");
}
#[test]
fn missing_returns_none_required_error_and_default() {
let _g = EnvVarGuard::unset("STRING_TEST_MISSING");
assert_eq!(get_env_string_optional("STRING_TEST_MISSING"), None);
assert!(matches!(get_env_string_required("STRING_TEST_MISSING"), Err(EnvError::RequiredNotPresent { .. })));
assert_eq!(get_env_string_or_default("STRING_TEST_MISSING", "fallback"), "fallback");
}
#[test]
fn required_not_present_carries_env_var_name() {
let _g = EnvVarGuard::unset("STRING_TEST_REQUIRED_NAME");
match get_env_string_required("STRING_TEST_REQUIRED_NAME") {
Err(EnvError::RequiredNotPresent { name }) => {
assert_eq!(name, "STRING_TEST_REQUIRED_NAME");
},
other => panic!("unexpected result: {:?}", other),
}
}
}
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
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::common::visibility::Visibility;
use tokens::tokens::media_files::MediaFileToken;
@@ -24,6 +25,7 @@ pub struct MediaFileDocument {
pub media_class: MediaFileClass,
pub media_type: MediaFileType,
pub maybe_media_subtype: Option<MediaFileSubtype>,
pub maybe_engine_category: Option<MediaFileEngineCategory>,
pub maybe_mime_type: Option<String>,
@@ -117,6 +117,8 @@ pub async fn insert_media_file_generic(
let mut maybe_creator_file_synthetic_id : Option<u64> = None;
let mut maybe_creator_category_synthetic_id : Option<u64> = None;
let is_batch_generated = args.maybe_batch_token.is_some();
let mut maybe_generation_provider_str = None;
let mut is_intermediate_system_file = args.is_intermediate_system_file;
let mut is_user_upload = args.is_user_upload;
@@ -170,6 +172,7 @@ pub async fn insert_media_file_generic(
maybe_origin_model_token = ?,
maybe_origin_filename = ?,
is_batch_generated = ?,
maybe_batch_token = ?,
maybe_mime_type = ?,
@@ -229,6 +232,7 @@ pub async fn insert_media_file_generic(
args.maybe_origin_model_token.map(|t| t.to_string()),
args.maybe_origin_filename,
is_batch_generated,
args.maybe_batch_token.map(|t| t.as_str()),
args.maybe_mime_type,
@@ -82,10 +82,6 @@ pub async fn insert_media_file_from_comfy_ui(args: InsertArgs<'_>) -> AnyhowResu
let maybe_model_type = args.maybe_model_type.map(|m| m.to_str());
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Video); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -133,7 +129,7 @@ SET
result_token.as_str(),
MediaFileClass::Video.to_str(),
media_type.to_str(),
MediaFileType::Video.to_str(), // TODO(bt,2024-04-30): This needs to become "mp4" after a frontend migration
ORIGIN_CATEGORY.to_str(),
product.to_str(),
@@ -69,10 +69,6 @@ pub async fn insert_media_file_from_f5_tts(
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::TextToSpeech;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Audio); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -120,7 +116,7 @@ SET
result_token.as_str(),
MediaFileClass::Audio.to_str(),
media_type.to_str(),
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -68,10 +68,6 @@ pub async fn insert_media_file_from_face_animation(
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::FaceAnimator;
const ORIGIN_MODEL_TYPE : MediaFileOriginModelType = MediaFileOriginModelType::SadTalker;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Video); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -112,7 +108,7 @@ SET
result_token.as_str(),
MediaFileClass::Video.to_str(),
media_type.to_str(),
MediaFileType::Video.to_str(),
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -8,7 +8,6 @@ use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::generation_provider::GenerationProvider;
use enums::common::visibility::Visibility;
@@ -34,9 +33,6 @@ pub struct InsertMediaFileFromUploadArgs<'a> {
pub media_file_type: MediaFileType,
pub maybe_media_class: Option<MediaFileClass>,
/// Only set for internal Artcraft project documents (`media_class = 'project'`).
pub maybe_project_type: Option<MediaFileProjectType>,
pub upload_type: UploadType,
pub maybe_engine_category: Option<MediaFileEngineCategory>,
@@ -130,7 +126,6 @@ SET
media_class = ?,
media_type = ?,
maybe_project_type = ?,
origin_category = ?,
origin_product_category = ?,
@@ -180,8 +175,6 @@ SET
args.media_file_type.to_str(),
args.maybe_project_type.map(|project_type| project_type.to_str()),
origin_category.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -66,9 +66,7 @@ pub async fn insert_media_file_from_rerender(
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::VideoFilter;
const ORIGIN_MODEL_TYPE : MediaFileOriginModelType = MediaFileOriginModelType::Rerender;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Video); // Coarse fallback for unrecognized mimes
const MEDIA_TYPE : MediaFileType = MediaFileType::Video;
let record_id = {
let query_result = sqlx::query!(
@@ -111,7 +109,7 @@ SET
ORIGIN_PRODUCT_CATEGORY.to_str(),
ORIGIN_MODEL_TYPE.to_str(),
media_type.to_str(),
MEDIA_TYPE.to_str(),
args.maybe_mime_type,
args.file_size_bytes,
@@ -69,10 +69,6 @@ pub async fn insert_media_file_from_seed_vc(
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::TextToSpeech;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Audio); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -120,7 +116,7 @@ SET
result_token.as_str(),
MediaFileClass::Audio.to_str(),
media_type.to_str(),
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -75,10 +75,6 @@ pub async fn insert_media_file_from_studio_gen2(args: InsertArgs<'_>) -> AnyhowR
const MODEL_TYPE : MediaFileOriginModelType = MediaFileOriginModelType::StorytellerStudio;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Video); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -125,7 +121,7 @@ SET
result_token.as_str(),
MediaFileClass::Video.to_str(),
media_type.to_str(),
MediaFileType::Video.to_str(), // TODO(bt,2024-04-30): This needs to become "mp4" after a frontend migration
ORIGIN_CATEGORY.to_str(),
PRODUCT.to_str(),
@@ -69,10 +69,6 @@ pub async fn insert_media_file_from_tacotron2(
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::TextToSpeech;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Audio); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -120,7 +116,7 @@ SET
result_token.as_str(),
MediaFileClass::Audio.to_str(),
media_type.to_str(),
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -80,10 +80,6 @@ pub async fn insert_media_file_from_voice_conversion(
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::VoiceConversion;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Audio); // Coarse fallback for unrecognized mimes
let record_id = {
let query_result = sqlx::query!(
r#"
@@ -126,7 +122,7 @@ SET
result_token.as_str(),
MediaFileClass::Audio.to_str(),
media_type.to_str(),
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
ORIGIN_CATEGORY.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -70,9 +70,7 @@ pub async fn insert_media_file_from_zero_shot(
// Since fill this out apart of the funtion this seems like the only thing to change.
const ORIGIN_CATEGORY : MediaFileOriginCategory = MediaFileOriginCategory::Inference;
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::ZeroShotVoice;
let media_type = args.maybe_mime_type
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Audio); // Coarse fallback for unrecognized mimes
const MEDIA_TYPE : MediaFileType = MediaFileType::Audio;
let record_id = {
let query_result = sqlx::query!(
@@ -120,7 +118,7 @@ pub async fn insert_media_file_from_zero_shot(
args.job.maybe_raw_inference_text,
media_type.to_str(),
MEDIA_TYPE.to_str(),
args.maybe_mime_type,
args.file_size_bytes,
@@ -1,7 +1,5 @@
use sqlx::MySqlPool;
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use errors::AnyhowResult;
use tokens::tokens::media_files::MediaFileToken;
@@ -16,13 +14,6 @@ use tokens::tokens::media_files::MediaFileToken;
pub struct UpdateArgs<'a> {
pub media_file_token: &'a MediaFileToken,
/// The class the record should hold after the update. Also backfills legacy
/// records that were written before the current classification scheme.
pub media_class: MediaFileClass,
/// Only set for internal Artcraft project documents (`media_class = 'project'`).
pub maybe_project_type: Option<MediaFileProjectType>,
pub public_bucket_directory_hash: &'a str,
pub maybe_public_bucket_prefix: Option<&'a str>,
pub maybe_public_bucket_extension: Option<&'a str>,
@@ -46,9 +37,6 @@ pub async fn updated_media_file_stored_cloud_contents(args: UpdateArgs<'_>) -> A
r#"
UPDATE media_files
SET
media_class = ?,
maybe_project_type = ?,
public_bucket_directory_hash = ?,
maybe_public_bucket_prefix = ?,
maybe_public_bucket_extension = ?,
@@ -60,9 +48,6 @@ pub async fn updated_media_file_stored_cloud_contents(args: UpdateArgs<'_>) -> A
WHERE token = ?
LIMIT 1
"#,
args.media_class.to_str(),
args.maybe_project_type.map(|project_type| project_type.to_str()),
args.public_bucket_directory_hash,
args.maybe_public_bucket_prefix,
args.maybe_public_bucket_extension,
@@ -12,6 +12,7 @@ use enums::by_table::media_files::media_file_animation_type::MediaFileAnimationT
use enums::traits::mysql_from_row::MySqlFromRow as _;
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;
@@ -37,6 +38,7 @@ pub struct MediaFile {
pub maybe_engine_category: Option<MediaFileEngineCategory>,
pub maybe_animation_type: Option<MediaFileAnimationType>,
pub maybe_media_subtype: Option<MediaFileSubtype>,
// TODO: Bucket hash bits.
@@ -123,6 +125,7 @@ pub struct MediaFileRaw {
pub maybe_engine_category: Option<MediaFileEngineCategory>,
pub maybe_animation_type: Option<MediaFileAnimationType>,
pub maybe_media_subtype: Option<MediaFileSubtype>,
// TODO: Bucket hash bits.
@@ -231,6 +234,7 @@ pub async fn batch_get_media_files(
media_type: record.media_type,
maybe_engine_category: record.maybe_engine_category,
maybe_animation_type: record.maybe_animation_type,
maybe_media_subtype: record.maybe_media_subtype,
maybe_batch_token: record.maybe_batch_token,
maybe_title: record.maybe_title,
maybe_text_transcript: record.maybe_text_transcript,
@@ -290,6 +294,7 @@ SELECT
m.maybe_engine_category,
m.maybe_animation_type,
m.maybe_media_subtype,
users.token as maybe_creator_user_token,
users.username as maybe_creator_username,
@@ -385,6 +390,7 @@ impl FromRow<'_, MySqlRow> for MediaFileRaw {
maybe_engine_category: MediaFileEngineCategory::try_from_mysql_row_nullable(row, "maybe_engine_category")?,
maybe_animation_type: MediaFileAnimationType::try_from_mysql_row_nullable(row, "maybe_animation_type")?,
maybe_media_subtype: MediaFileSubtype::try_from_mysql_row_nullable(row, "maybe_media_subtype")?,
maybe_batch_token: BatchGenerationToken::try_from_mysql_row_nullable(row, "maybe_batch_token")?,
@@ -8,6 +8,7 @@ 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;
@@ -38,6 +39,8 @@ pub struct MediaFile {
pub maybe_engine_category: Option<MediaFileEngineCategory>,
pub maybe_animation_type: Option<MediaFileAnimationType>,
pub maybe_media_subtype: Option<MediaFileSubtype>,
pub maybe_mime_type: Option<String>,
// TODO: Other media details (file size, dimensions, duration, etc.)
@@ -131,6 +134,8 @@ pub struct MediaFileRaw {
pub maybe_engine_category: Option<MediaFileEngineCategory>,
pub maybe_animation_type: Option<MediaFileAnimationType>,
pub maybe_media_subtype: Option<MediaFileSubtype>,
pub maybe_mime_type: Option<String>,
pub maybe_batch_token: Option<BatchGenerationToken>,
@@ -258,6 +263,7 @@ pub async fn get_media_file_with_transactor(
maybe_engine_category: record.maybe_engine_category,
maybe_animation_type: record.maybe_animation_type,
media_class: record.media_class,
maybe_media_subtype: record.maybe_media_subtype,
maybe_mime_type: record.maybe_mime_type,
maybe_batch_token: record.maybe_batch_token,
maybe_style_transfer_source_media_file_token: record.maybe_style_transfer_source_media_file_token,
@@ -321,6 +327,8 @@ SELECT
m.maybe_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,
m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,
m.maybe_media_subtype as `maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype`,
m.maybe_mime_type,
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
@@ -442,6 +450,8 @@ SELECT
m.maybe_engine_category as `maybe_engine_category: enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory`,
m.maybe_animation_type as `maybe_animation_type: enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType`,
m.maybe_media_subtype as `maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype`,
m.maybe_mime_type,
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
@@ -14,6 +14,7 @@ use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCat
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
use enums::by_table::media_files::media_file_subtype::MediaFileSubtype;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use errors::AnyhowResult;
@@ -41,6 +42,7 @@ pub struct MediaFileForElasticsearchRecord {
pub maybe_origin_filename: Option<String>,
pub is_batch_generated: bool,
pub maybe_batch_token: Option<BatchGenerationToken>,
pub is_user_upload: bool,
@@ -57,8 +59,11 @@ pub struct MediaFileForElasticsearchRecord {
pub maybe_style_transfer_source_media_file_token: Option<MediaFileToken>,
pub maybe_scene_source_media_file_token: Option<MediaFileToken>,
pub nsfw_status: String,
pub media_type: MediaFileType,
pub media_class: MediaFileClass,
pub maybe_media_subtype: Option<MediaFileSubtype>,
pub maybe_mime_type: Option<String>,
pub file_size_bytes: u64,
@@ -154,6 +159,7 @@ pub async fn list_media_files_for_elastic_search_backfill_using_cursor(
maybe_origin_model_type: record.maybe_origin_model_type,
maybe_origin_model_token: record.maybe_origin_model_token,
maybe_origin_filename: record.maybe_origin_filename,
is_batch_generated: i8_to_bool(record.is_batch_generated),
maybe_batch_token: record.maybe_batch_token,
is_user_upload: i8_to_bool(record.is_user_upload),
is_intermediate_system_file: i8_to_bool(record.is_intermediate_system_file),
@@ -164,8 +170,10 @@ pub async fn list_media_files_for_elastic_search_backfill_using_cursor(
maybe_cover_image_public_bucket_extension: record.maybe_cover_image_public_bucket_extension,
maybe_style_transfer_source_media_file_token: record.maybe_style_transfer_source_media_file_token,
maybe_scene_source_media_file_token: record.maybe_scene_source_media_file_token,
nsfw_status: record.nsfw_status,
media_type: record.media_type,
media_class: record.media_class,
maybe_media_subtype: record.maybe_media_subtype,
maybe_mime_type: record.maybe_mime_type,
file_size_bytes: record.file_size_bytes as u64,
maybe_duration_millis: record.maybe_duration_millis.map(|d| d as u64),
@@ -214,6 +222,7 @@ SELECT
m.maybe_origin_model_token,
m.maybe_origin_filename,
m.is_batch_generated,
m.maybe_batch_token,
m.is_user_upload,
@@ -229,8 +238,11 @@ SELECT
m.maybe_style_transfer_source_media_file_token,
m.maybe_scene_source_media_file_token,
m.nsfw_status,
m.media_type,
m.media_class,
m.maybe_media_subtype,
m.maybe_mime_type,
m.file_size_bytes,
@@ -360,6 +372,7 @@ struct RawRecord {
pub maybe_origin_filename: Option<String>,
pub is_batch_generated: i8,
pub maybe_batch_token: Option<BatchGenerationToken>,
pub is_user_upload: i8,
@@ -376,8 +389,11 @@ struct RawRecord {
pub maybe_style_transfer_source_media_file_token: Option<MediaFileToken>,
pub maybe_scene_source_media_file_token: Option<MediaFileToken>,
pub nsfw_status: String,
pub media_type: MediaFileType,
pub media_class: MediaFileClass,
pub maybe_media_subtype: Option<MediaFileSubtype>,
pub maybe_mime_type: Option<String>,
pub file_size_bytes: i32,
@@ -443,6 +459,7 @@ impl FromRow<'_, MySqlRow> for RawRecord {
maybe_origin_model_token: row.try_get("maybe_origin_model_token")?,
maybe_origin_filename: row.try_get("maybe_origin_filename")?,
is_batch_generated: row.try_get("is_batch_generated")?,
maybe_batch_token: BatchGenerationToken::try_from_mysql_row_nullable(row, "maybe_batch_token")?,
is_user_upload: row.try_get("is_user_upload")?,
@@ -458,8 +475,11 @@ impl FromRow<'_, MySqlRow> for RawRecord {
maybe_style_transfer_source_media_file_token: row.try_get("maybe_style_transfer_source_media_file_token")?,
maybe_scene_source_media_file_token: row.try_get("maybe_scene_source_media_file_token")?,
nsfw_status: row.try_get("nsfw_status")?,
media_type: MediaFileType::try_from_mysql_row(row, "media_type")?,
media_class: MediaFileClass::try_from_mysql_row(row, "media_class")?,
maybe_media_subtype: MediaFileSubtype::try_from_mysql_row_nullable(row, "maybe_media_subtype")?,
maybe_mime_type: row.try_get("maybe_mime_type")?,
file_size_bytes: row.try_get("file_size_bytes")?,
@@ -8,7 +8,7 @@ use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_subtype::MediaFileSubtype;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use errors::AnyhowResult;
@@ -33,9 +33,6 @@ pub struct UpsertMediaFileFromUploadArgs<'a> {
pub maybe_media_class: Option<MediaFileClass>,
pub media_file_type: MediaFileType,
/// Only set for internal Artcraft project documents (`media_class = 'project'`).
pub maybe_project_type: Option<MediaFileProjectType>,
pub maybe_engine_category: Option<MediaFileEngineCategory>,
pub maybe_animation_type: Option<MediaFileAnimationType>,
@@ -46,6 +43,8 @@ pub struct UpsertMediaFileFromUploadArgs<'a> {
pub upload_type: UploadType,
pub maybe_media_subtype: Option<MediaFileSubtype>,
pub maybe_mime_type: Option<&'a str>,
pub file_size_bytes: u64,
pub duration_millis: u64,
@@ -109,7 +108,6 @@ SET
media_class = ?,
media_type = ?,
maybe_project_type = ?,
origin_category = ?,
origin_product_category = ?,
@@ -117,6 +115,7 @@ SET
maybe_engine_category = ?,
maybe_animation_type = ?,
maybe_media_subtype = ?,
maybe_mime_type = ?,
file_size_bytes = ?,
@@ -144,7 +143,6 @@ ON DUPLICATE KEY UPDATE
media_class = ?,
media_type = ?,
maybe_project_type = ?,
origin_category = ?,
origin_product_category = ?,
@@ -152,6 +150,7 @@ ON DUPLICATE KEY UPDATE
maybe_engine_category = ?,
maybe_animation_type = ?,
maybe_media_subtype = ?,
maybe_mime_type = ?,
file_size_bytes = ?,
@@ -179,7 +178,6 @@ ON DUPLICATE KEY UPDATE
media_class.to_str(),
args.media_file_type.to_str(),
args.maybe_project_type.map(|project_type| project_type.to_str()),
origin_category.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -187,6 +185,8 @@ ON DUPLICATE KEY UPDATE
args.maybe_engine_category.map(|s| s.to_str()),
args.maybe_animation_type.map(|s| s.to_str()),
args.maybe_media_subtype.map(|s| s.to_str()),
args.maybe_mime_type,
args.file_size_bytes,
@@ -209,7 +209,6 @@ ON DUPLICATE KEY UPDATE
media_class.to_str(),
args.media_file_type.to_str(),
args.maybe_project_type.map(|project_type| project_type.to_str()),
origin_category.to_str(),
ORIGIN_PRODUCT_CATEGORY.to_str(),
@@ -217,6 +216,8 @@ ON DUPLICATE KEY UPDATE
args.maybe_engine_category.map(|s| s.to_str()),
args.maybe_animation_type.map(|s| s.to_str()),
args.maybe_media_subtype.map(|s| s.to_str()),
args.maybe_mime_type,
args.file_size_bytes,
@@ -183,18 +183,9 @@ impl RawUserBookmarkRecord {
MediaFileType::Jpg => Some("image media file".to_string()),
MediaFileType::Png => Some("image media file".to_string()),
MediaFileType::Gif => Some("image media file".to_string()),
MediaFileType::Webp => Some("image media file".to_string()),
MediaFileType::Mp4 => Some("video media file".to_string()),
MediaFileType::Webm => Some("video media file".to_string()),
MediaFileType::Mov => Some("video media file".to_string()),
MediaFileType::Wav => Some("audio media file".to_string()),
MediaFileType::Mp3 => Some("audio media file".to_string()),
MediaFileType::Opus => Some("audio media file".to_string()),
MediaFileType::Ogg => Some("audio media file".to_string()),
MediaFileType::Aac => Some("audio media file".to_string()),
MediaFileType::M4a => Some("audio media file".to_string()),
MediaFileType::Flac => Some("audio media file".to_string()),
MediaFileType::Json => Some("project file".to_string()),
}),
UserBookmarkEntityType::VoiceConversionModel => self.maybe_descriptive_text_voice_conversion_model_title,
UserBookmarkEntityType::ZsVoice => self.maybe_descriptive_text_zs_voice_title,
@@ -27,21 +27,7 @@ pub enum MediaFileClass {
Video,
/// 3D engine data: glb, gltf, etc.
/// Being split into `Mesh`, `Splat`, and `Project`. Do not write new records
/// with this value; it remains readable until existing rows are backfilled.
#[deprecated(note = "Split into Mesh, Splat, and Project. Read-only until backfilled.")]
Dimensional,
/// 3D mesh assets: glb, gltf, fbx, obj, pmx, pmd, etc.
Mesh,
/// Gaussian splats: spz, etc.
Splat,
/// Internal Artcraft project documents (JSON): 3D editor scenes, mood boards,
/// graph workflows, video editor timelines, etc.
/// The specific kind is stored in `maybe_project_type`.
Project,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
@@ -58,9 +44,6 @@ impl MediaFileClass {
Self::Image => "image",
Self::Video => "video",
Self::Dimensional => "dimensional",
Self::Mesh => "mesh",
Self::Splat => "splat",
Self::Project => "project",
}
}
@@ -71,9 +54,6 @@ impl MediaFileClass {
"image" => Ok(Self::Image),
"video" => Ok(Self::Video),
"dimensional" => Ok(Self::Dimensional),
"mesh" => Ok(Self::Mesh),
"splat" => Ok(Self::Splat),
"project" => Ok(Self::Project),
_ => Err(format!("invalid value: {:?}", value)),
}
}
@@ -87,9 +67,6 @@ impl MediaFileClass {
Self::Image,
Self::Video,
Self::Dimensional,
Self::Mesh,
Self::Splat,
Self::Project,
])
}
}
@@ -109,9 +86,6 @@ mod tests {
assert_serialization(MediaFileClass::Image, "image");
assert_serialization(MediaFileClass::Video, "video");
assert_serialization(MediaFileClass::Dimensional, "dimensional");
assert_serialization(MediaFileClass::Mesh, "mesh");
assert_serialization(MediaFileClass::Splat, "splat");
assert_serialization(MediaFileClass::Project, "project");
}
}
@@ -125,9 +99,6 @@ mod tests {
assert_eq!(MediaFileClass::Image.to_str(), "image");
assert_eq!(MediaFileClass::Video.to_str(), "video");
assert_eq!(MediaFileClass::Dimensional.to_str(), "dimensional");
assert_eq!(MediaFileClass::Mesh.to_str(), "mesh");
assert_eq!(MediaFileClass::Splat.to_str(), "splat");
assert_eq!(MediaFileClass::Project.to_str(), "project");
}
#[test]
@@ -137,9 +108,6 @@ mod tests {
assert_eq!(MediaFileClass::from_str("image").unwrap(), MediaFileClass::Image);
assert_eq!(MediaFileClass::from_str("video").unwrap(), MediaFileClass::Video);
assert_eq!(MediaFileClass::from_str("dimensional").unwrap(), MediaFileClass::Dimensional);
assert_eq!(MediaFileClass::from_str("mesh").unwrap(), MediaFileClass::Mesh);
assert_eq!(MediaFileClass::from_str("splat").unwrap(), MediaFileClass::Splat);
assert_eq!(MediaFileClass::from_str("project").unwrap(), MediaFileClass::Project);
assert!(MediaFileClass::from_str("foo").is_err());
}
}
@@ -150,15 +118,12 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = MediaFileClass::all_variants();
assert_eq!(variants.len(), 8);
assert_eq!(variants.len(), 5);
assert_eq!(variants.pop_first(), Some(MediaFileClass::Unknown));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Audio));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Image));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Video));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Dimensional));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Mesh));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Splat));
assert_eq!(variants.pop_first(), Some(MediaFileClass::Project));
assert_eq!(variants.pop_first(), None);
}
}
@@ -1,155 +0,0 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in the nullable `maybe_project_type`
/// VARCHAR(32) field.
///
/// Identifies which kind of internal Artcraft project document a media file is.
/// Only set when `media_class = 'project'`. The file format lives in
/// `media_type` (`json` / legacy `scene_json`); the JSON payload carries its
/// own schema version.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileProjectType {
/// 3D editor scene descriptor: objects with locations, skyboxes, animations,
/// etc. — a full editable 3D scene.
/// NB: serde's snake_case renames this to "scene3d" (no underscore before
/// the digit), so the rename must be explicit.
#[serde(rename = "scene_3d")]
Scene3d,
/// Mood board: a collection of images on a lightweight graph.
MoodBoard,
/// Graph workflow: a node-graph pipeline of image and video generation steps.
Workflow,
/// Video editor project: a nonlinear video editing timeline.
VideoTimeline,
}
impl_enum_display_and_debug_using_to_str!(MediaFileProjectType);
impl_mysql_enum_coders!(MediaFileProjectType);
impl_mysql_from_row!(MediaFileProjectType);
impl MediaFileProjectType {
pub const fn to_str(&self) -> &'static str {
match self {
Self::Scene3d => "scene_3d",
Self::MoodBoard => "mood_board",
Self::Workflow => "workflow",
Self::VideoTimeline => "video_timeline",
}
}
pub fn from_str(value: &str) -> Result<Self, String> {
match value {
"scene_3d" => Ok(Self::Scene3d),
"mood_board" => Ok(Self::MoodBoard),
"workflow" => Ok(Self::Workflow),
"video_timeline" => Ok(Self::VideoTimeline),
_ => Err(format!("invalid value: {:?}", value)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
// NB: BTreeSet is sorted
BTreeSet::from([
Self::Scene3d,
Self::MoodBoard,
Self::Workflow,
Self::VideoTimeline,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_project_type::MediaFileProjectType;
use crate::test_helpers::assert_serialization;
mod serde {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(MediaFileProjectType::Scene3d, "scene_3d");
assert_serialization(MediaFileProjectType::MoodBoard, "mood_board");
assert_serialization(MediaFileProjectType::Workflow, "workflow");
assert_serialization(MediaFileProjectType::VideoTimeline, "video_timeline");
}
}
mod impl_methods {
use super::*;
#[test]
fn to_str() {
assert_eq!(MediaFileProjectType::Scene3d.to_str(), "scene_3d");
assert_eq!(MediaFileProjectType::MoodBoard.to_str(), "mood_board");
assert_eq!(MediaFileProjectType::Workflow.to_str(), "workflow");
assert_eq!(MediaFileProjectType::VideoTimeline.to_str(), "video_timeline");
}
#[test]
fn from_str() {
assert_eq!(MediaFileProjectType::from_str("scene_3d").unwrap(), MediaFileProjectType::Scene3d);
assert_eq!(MediaFileProjectType::from_str("mood_board").unwrap(), MediaFileProjectType::MoodBoard);
assert_eq!(MediaFileProjectType::from_str("workflow").unwrap(), MediaFileProjectType::Workflow);
assert_eq!(MediaFileProjectType::from_str("video_timeline").unwrap(), MediaFileProjectType::VideoTimeline);
assert!(MediaFileProjectType::from_str("foo").is_err());
}
}
mod manual_variant_checks {
use super::*;
#[test]
fn all_variants() {
let mut variants = MediaFileProjectType::all_variants();
assert_eq!(variants.len(), 4);
assert_eq!(variants.pop_first(), Some(MediaFileProjectType::Scene3d));
assert_eq!(variants.pop_first(), Some(MediaFileProjectType::MoodBoard));
assert_eq!(variants.pop_first(), Some(MediaFileProjectType::Workflow));
assert_eq!(variants.pop_first(), Some(MediaFileProjectType::VideoTimeline));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(MediaFileProjectType::all_variants().len(), MediaFileProjectType::iter().len());
}
#[test]
fn round_trip() {
for variant in MediaFileProjectType::all_variants() {
assert_eq!(variant, MediaFileProjectType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, MediaFileProjectType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, MediaFileProjectType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
for variant in MediaFileProjectType::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
}
}
}
}
@@ -6,8 +6,6 @@ use strum::EnumCount;
use strum::EnumIter;
use utoipa::ToSchema;
use crate::by_table::media_files::media_file_class::MediaFileClass;
/// Used in the `media_files` table in a `VARCHAR(16)` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
@@ -92,36 +90,6 @@ pub enum MediaFileType {
/// Mp3 audio
Mp3,
// ========================= LESS SUPPORT FOR THE FILES BELOW THIS LINE ========================= //
/// Webp images
Webp,
/// Webm videos
Webm,
/// QuickTime videos
Mov,
/// Opus audio
Opus,
/// Ogg audio
Ogg,
/// AAC audio
Aac,
/// M4a audio
M4a,
/// FLAC audio
Flac,
/// Generic JSON documents (eg. internal project files).
/// NB: 3D scene files predate this and use `scene_json` instead.
Json,
}
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
@@ -142,56 +110,7 @@ impl MediaFileType {
pub fn is_jpg_or_png_or_legacy_image(&self) -> bool {
matches!(self, Self::Jpg | Self::Png | Self::Image)
}
/// Any image format, including the legacy coarse `Image` type.
pub fn is_image(&self) -> bool {
matches!(self, Self::Image | Self::Jpg | Self::Png | Self::Gif | Self::Webp)
}
/// The broad `media_class` this file format belongs to.
pub fn to_media_class(&self) -> MediaFileClass {
match self {
Self::Audio
| Self::Wav
| Self::Mp3
| Self::Opus
| Self::Ogg
| Self::Aac
| Self::M4a
| Self::Flac => MediaFileClass::Audio,
Self::Image
| Self::Jpg
| Self::Png
| Self::Gif
| Self::Webp => MediaFileClass::Image,
Self::Video
| Self::Mp4
| Self::Webm
| Self::Mov => MediaFileClass::Video,
Self::Fbx
| Self::Obj
| Self::Glb
| Self::Gltf
| Self::Pmd
| Self::Pmx => MediaFileClass::Mesh,
Self::Spz => MediaFileClass::Splat,
// Animation and auxiliary data formats with no dedicated class yet.
#[allow(deprecated)]
Self::Bvh
| Self::Vmd
| Self::Csv => MediaFileClass::Dimensional,
Self::SceneRon
| Self::SceneJson
| Self::Json => MediaFileClass::Project,
}
}
/// Returns the `MediaFileType` if the mime type matches one of the known types.
/// This is not exhaustive.
/// (NB: In general we shouldn't keep much application logic in the `enums` crate,
@@ -200,80 +119,19 @@ impl MediaFileType {
match mime_type {
"image/jpeg" => Some(Self::Jpg),
"image/png" => Some(Self::Png),
"image/gif" => Some(Self::Gif),
"image/webp" => Some(Self::Webp),
"video/mp4" => Some(Self::Mp4),
"video/webm" => Some(Self::Webm),
"video/quicktime" => Some(Self::Mov),
"audio/wav" => Some(Self::Wav),
"audio/x-wav" => Some(Self::Wav),
"audio/wave" => Some(Self::Wav),
"audio/mpeg" => Some(Self::Mp3),
"audio/mp3" => Some(Self::Mp3),
"audio/opus" => Some(Self::Opus),
"audio/ogg" => Some(Self::Ogg),
"audio/aac" => Some(Self::Aac),
"audio/m4a" => Some(Self::M4a),
"audio/x-m4a" => Some(Self::M4a),
"audio/mp4" => Some(Self::M4a),
"audio/flac" => Some(Self::Flac),
"audio/x-flac" => Some(Self::Flac),
"model/gltf-binary" => Some(Self::Glb),
"model/gltf+json" => Some(Self::Gltf),
"model/fbx" => Some(Self::Fbx),
"application/fbx" => Some(Self::Fbx),
"model/obj" => Some(Self::Obj),
//"audio/wav" => Some(Self::Wav), // NB: Not sure if this is correct
//"audio/mpeg" => Some(Self::Mp3), // NB: Not sure if this is correct
//"image/gif" => Some(Self::Gif), // NB: Not sure if this is correct
//"model/gltf+json" => Some(Self::Gltf), // NB: Not sure if this is correct
_ => None,
}
}
/// Tries the input as a filename first, then as a bare extension.
/// Useful as a fallback when `try_from_mime_type` fails.
pub fn try_from_filename_or_extension(filename_or_extension: &str) -> Option<Self> {
Self::try_from_filename(filename_or_extension)
.or_else(|| Self::try_from_extension(filename_or_extension))
}
/// Returns the `MediaFileType` for a filename by examining its extension.
pub fn try_from_filename(filename: &str) -> Option<Self> {
let (_, extension) = filename.trim().rsplit_once('.')?;
Self::try_from_extension(extension)
}
/// Returns the `MediaFileType` for a known file extension.
/// Accepts extensions with or without a leading dot, in any letter case.
pub fn try_from_extension(extension: &str) -> Option<Self> {
let extension = extension.trim().trim_start_matches('.').to_ascii_lowercase();
match extension.as_str() {
"jpg" | "jpeg" => Some(Self::Jpg),
"png" => Some(Self::Png),
"gif" => Some(Self::Gif),
"webp" => Some(Self::Webp),
"mp4" => Some(Self::Mp4),
"webm" => Some(Self::Webm),
"mov" => Some(Self::Mov),
"wav" => Some(Self::Wav),
"mp3" => Some(Self::Mp3),
"opus" => Some(Self::Opus),
"ogg" => Some(Self::Ogg),
"aac" => Some(Self::Aac),
"m4a" => Some(Self::M4a),
"flac" => Some(Self::Flac),
"bvh" => Some(Self::Bvh),
"fbx" => Some(Self::Fbx),
"obj" => Some(Self::Obj),
"glb" => Some(Self::Glb),
"gltf" => Some(Self::Gltf),
"spz" => Some(Self::Spz),
"pmd" => Some(Self::Pmd),
"vmd" => Some(Self::Vmd),
"pmx" => Some(Self::Pmx),
"csv" => Some(Self::Csv),
"json" => Some(Self::Json),
_ => None,
}
}
pub fn to_str(&self) -> &'static str {
match self {
Self::Audio => "audio",
@@ -297,15 +155,6 @@ impl MediaFileType {
Self::Mp4 => "mp4",
Self::Wav => "wav",
Self::Mp3 => "mp3",
Self::Webp => "webp",
Self::Webm => "webm",
Self::Mov => "mov",
Self::Opus => "opus",
Self::Ogg => "ogg",
Self::Aac => "aac",
Self::M4a => "m4a",
Self::Flac => "flac",
Self::Json => "json",
}
}
@@ -332,15 +181,6 @@ impl MediaFileType {
"mp4" => Ok(Self::Mp4),
"wav" => Ok(Self::Wav),
"mp3" => Ok(Self::Mp3),
"webp" => Ok(Self::Webp),
"webm" => Ok(Self::Webm),
"mov" => Ok(Self::Mov),
"opus" => Ok(Self::Opus),
"ogg" => Ok(Self::Ogg),
"aac" => Ok(Self::Aac),
"m4a" => Ok(Self::M4a),
"flac" => Ok(Self::Flac),
"json" => Ok(Self::Json),
_ => Err(format!("invalid value: {:?}", value)),
}
}
@@ -370,22 +210,12 @@ impl MediaFileType {
Self::Mp4,
Self::Wav,
Self::Mp3,
Self::Webp,
Self::Webm,
Self::Mov,
Self::Opus,
Self::Ogg,
Self::Aac,
Self::M4a,
Self::Flac,
Self::Json,
])
}
}
#[cfg(test)]
mod tests {
use crate::by_table::media_files::media_file_class::MediaFileClass;
use crate::by_table::media_files::media_file_type::MediaFileType;
use crate::test_helpers::assert_serialization;
@@ -438,15 +268,6 @@ mod tests {
assert_serialization(MediaFileType::Mp4, "mp4");
assert_serialization(MediaFileType::Wav, "wav");
assert_serialization(MediaFileType::Mp3, "mp3");
assert_serialization(MediaFileType::Webp, "webp");
assert_serialization(MediaFileType::Webm, "webm");
assert_serialization(MediaFileType::Mov, "mov");
assert_serialization(MediaFileType::Opus, "opus");
assert_serialization(MediaFileType::Ogg, "ogg");
assert_serialization(MediaFileType::Aac, "aac");
assert_serialization(MediaFileType::M4a, "m4a");
assert_serialization(MediaFileType::Flac, "flac");
assert_serialization(MediaFileType::Json, "json");
}
}
@@ -475,15 +296,6 @@ mod tests {
assert_eq!(MediaFileType::Mp4.to_str(), "mp4");
assert_eq!(MediaFileType::Wav.to_str(), "wav");
assert_eq!(MediaFileType::Mp3.to_str(), "mp3");
assert_eq!(MediaFileType::Webp.to_str(), "webp");
assert_eq!(MediaFileType::Webm.to_str(), "webm");
assert_eq!(MediaFileType::Mov.to_str(), "mov");
assert_eq!(MediaFileType::Opus.to_str(), "opus");
assert_eq!(MediaFileType::Ogg.to_str(), "ogg");
assert_eq!(MediaFileType::Aac.to_str(), "aac");
assert_eq!(MediaFileType::M4a.to_str(), "m4a");
assert_eq!(MediaFileType::Flac.to_str(), "flac");
assert_eq!(MediaFileType::Json.to_str(), "json");
}
#[test]
@@ -508,185 +320,8 @@ mod tests {
assert_eq!(MediaFileType::from_str("mp4").unwrap(), MediaFileType::Mp4);
assert_eq!(MediaFileType::from_str("wav").unwrap(), MediaFileType::Wav);
assert_eq!(MediaFileType::from_str("mp3").unwrap(), MediaFileType::Mp3);
assert_eq!(MediaFileType::from_str("webp").unwrap(), MediaFileType::Webp);
assert_eq!(MediaFileType::from_str("webm").unwrap(), MediaFileType::Webm);
assert_eq!(MediaFileType::from_str("mov").unwrap(), MediaFileType::Mov);
assert_eq!(MediaFileType::from_str("opus").unwrap(), MediaFileType::Opus);
assert_eq!(MediaFileType::from_str("ogg").unwrap(), MediaFileType::Ogg);
assert_eq!(MediaFileType::from_str("aac").unwrap(), MediaFileType::Aac);
assert_eq!(MediaFileType::from_str("m4a").unwrap(), MediaFileType::M4a);
assert_eq!(MediaFileType::from_str("flac").unwrap(), MediaFileType::Flac);
assert_eq!(MediaFileType::from_str("json").unwrap(), MediaFileType::Json);
assert!(MediaFileType::from_str("foo").is_err());
}
#[test]
fn to_media_class() {
assert_eq!(MediaFileType::Audio.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Image.to_media_class(), MediaFileClass::Image);
assert_eq!(MediaFileType::Video.to_media_class(), MediaFileClass::Video);
assert_eq!(MediaFileType::Bvh.to_media_class(), MediaFileClass::Dimensional);
assert_eq!(MediaFileType::Fbx.to_media_class(), MediaFileClass::Mesh);
assert_eq!(MediaFileType::Obj.to_media_class(), MediaFileClass::Mesh);
assert_eq!(MediaFileType::Glb.to_media_class(), MediaFileClass::Mesh);
assert_eq!(MediaFileType::Gltf.to_media_class(), MediaFileClass::Mesh);
assert_eq!(MediaFileType::Spz.to_media_class(), MediaFileClass::Splat);
assert_eq!(MediaFileType::SceneRon.to_media_class(), MediaFileClass::Project);
assert_eq!(MediaFileType::SceneJson.to_media_class(), MediaFileClass::Project);
assert_eq!(MediaFileType::Pmd.to_media_class(), MediaFileClass::Mesh);
assert_eq!(MediaFileType::Vmd.to_media_class(), MediaFileClass::Dimensional);
assert_eq!(MediaFileType::Pmx.to_media_class(), MediaFileClass::Mesh);
assert_eq!(MediaFileType::Csv.to_media_class(), MediaFileClass::Dimensional);
assert_eq!(MediaFileType::Jpg.to_media_class(), MediaFileClass::Image);
assert_eq!(MediaFileType::Png.to_media_class(), MediaFileClass::Image);
assert_eq!(MediaFileType::Gif.to_media_class(), MediaFileClass::Image);
assert_eq!(MediaFileType::Webp.to_media_class(), MediaFileClass::Image);
assert_eq!(MediaFileType::Mp4.to_media_class(), MediaFileClass::Video);
assert_eq!(MediaFileType::Webm.to_media_class(), MediaFileClass::Video);
assert_eq!(MediaFileType::Mov.to_media_class(), MediaFileClass::Video);
assert_eq!(MediaFileType::Wav.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Mp3.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Opus.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Ogg.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Aac.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::M4a.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Flac.to_media_class(), MediaFileClass::Audio);
assert_eq!(MediaFileType::Json.to_media_class(), MediaFileClass::Project);
}
}
mod format_detection {
use super::*;
#[test]
fn try_from_mime_type() {
assert_eq!(MediaFileType::try_from_mime_type("image/jpeg"), Some(MediaFileType::Jpg));
assert_eq!(MediaFileType::try_from_mime_type("image/png"), Some(MediaFileType::Png));
assert_eq!(MediaFileType::try_from_mime_type("image/gif"), Some(MediaFileType::Gif));
assert_eq!(MediaFileType::try_from_mime_type("image/webp"), Some(MediaFileType::Webp));
assert_eq!(MediaFileType::try_from_mime_type("video/mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_mime_type("video/webm"), Some(MediaFileType::Webm));
assert_eq!(MediaFileType::try_from_mime_type("video/quicktime"), Some(MediaFileType::Mov));
assert_eq!(MediaFileType::try_from_mime_type("audio/wav"), Some(MediaFileType::Wav));
assert_eq!(MediaFileType::try_from_mime_type("audio/x-wav"), Some(MediaFileType::Wav));
assert_eq!(MediaFileType::try_from_mime_type("audio/wave"), Some(MediaFileType::Wav));
assert_eq!(MediaFileType::try_from_mime_type("audio/mpeg"), Some(MediaFileType::Mp3));
assert_eq!(MediaFileType::try_from_mime_type("audio/mp3"), Some(MediaFileType::Mp3));
assert_eq!(MediaFileType::try_from_mime_type("audio/opus"), Some(MediaFileType::Opus));
assert_eq!(MediaFileType::try_from_mime_type("audio/ogg"), Some(MediaFileType::Ogg));
assert_eq!(MediaFileType::try_from_mime_type("audio/aac"), Some(MediaFileType::Aac));
assert_eq!(MediaFileType::try_from_mime_type("audio/m4a"), Some(MediaFileType::M4a));
assert_eq!(MediaFileType::try_from_mime_type("audio/x-m4a"), Some(MediaFileType::M4a));
assert_eq!(MediaFileType::try_from_mime_type("audio/mp4"), Some(MediaFileType::M4a));
assert_eq!(MediaFileType::try_from_mime_type("audio/flac"), Some(MediaFileType::Flac));
assert_eq!(MediaFileType::try_from_mime_type("audio/x-flac"), Some(MediaFileType::Flac));
assert_eq!(MediaFileType::try_from_mime_type("model/gltf-binary"), Some(MediaFileType::Glb));
assert_eq!(MediaFileType::try_from_mime_type("model/gltf+json"), Some(MediaFileType::Gltf));
assert_eq!(MediaFileType::try_from_mime_type("model/fbx"), Some(MediaFileType::Fbx));
assert_eq!(MediaFileType::try_from_mime_type("application/fbx"), Some(MediaFileType::Fbx));
assert_eq!(MediaFileType::try_from_mime_type("model/obj"), Some(MediaFileType::Obj));
}
#[test]
fn try_from_mime_type_unknown_mimes() {
// NB: application/json is deliberately unmapped — scene JSON, mood boards,
// and other project files can't be told apart by mime alone.
assert_eq!(MediaFileType::try_from_mime_type("application/json"), None);
// NB: application/gzip is the mime for spz splats, but gzip isn't
// necessarily a splat, so it stays unmapped.
assert_eq!(MediaFileType::try_from_mime_type("application/gzip"), None);
assert_eq!(MediaFileType::try_from_mime_type("application/octet-stream"), None);
assert_eq!(MediaFileType::try_from_mime_type("text/plain"), None);
assert_eq!(MediaFileType::try_from_mime_type(""), None);
assert_eq!(MediaFileType::try_from_mime_type("IMAGE/PNG"), None);
}
#[test]
fn try_from_extension() {
assert_eq!(MediaFileType::try_from_extension("jpg"), Some(MediaFileType::Jpg));
assert_eq!(MediaFileType::try_from_extension("jpeg"), Some(MediaFileType::Jpg));
assert_eq!(MediaFileType::try_from_extension("png"), Some(MediaFileType::Png));
assert_eq!(MediaFileType::try_from_extension("gif"), Some(MediaFileType::Gif));
assert_eq!(MediaFileType::try_from_extension("webp"), Some(MediaFileType::Webp));
assert_eq!(MediaFileType::try_from_extension("mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_extension("webm"), Some(MediaFileType::Webm));
assert_eq!(MediaFileType::try_from_extension("mov"), Some(MediaFileType::Mov));
assert_eq!(MediaFileType::try_from_extension("wav"), Some(MediaFileType::Wav));
assert_eq!(MediaFileType::try_from_extension("mp3"), Some(MediaFileType::Mp3));
assert_eq!(MediaFileType::try_from_extension("opus"), Some(MediaFileType::Opus));
assert_eq!(MediaFileType::try_from_extension("ogg"), Some(MediaFileType::Ogg));
assert_eq!(MediaFileType::try_from_extension("aac"), Some(MediaFileType::Aac));
assert_eq!(MediaFileType::try_from_extension("m4a"), Some(MediaFileType::M4a));
assert_eq!(MediaFileType::try_from_extension("flac"), Some(MediaFileType::Flac));
assert_eq!(MediaFileType::try_from_extension("bvh"), Some(MediaFileType::Bvh));
assert_eq!(MediaFileType::try_from_extension("fbx"), Some(MediaFileType::Fbx));
assert_eq!(MediaFileType::try_from_extension("obj"), Some(MediaFileType::Obj));
assert_eq!(MediaFileType::try_from_extension("glb"), Some(MediaFileType::Glb));
assert_eq!(MediaFileType::try_from_extension("gltf"), Some(MediaFileType::Gltf));
assert_eq!(MediaFileType::try_from_extension("spz"), Some(MediaFileType::Spz));
assert_eq!(MediaFileType::try_from_extension("pmd"), Some(MediaFileType::Pmd));
assert_eq!(MediaFileType::try_from_extension("vmd"), Some(MediaFileType::Vmd));
assert_eq!(MediaFileType::try_from_extension("pmx"), Some(MediaFileType::Pmx));
assert_eq!(MediaFileType::try_from_extension("csv"), Some(MediaFileType::Csv));
assert_eq!(MediaFileType::try_from_extension("json"), Some(MediaFileType::Json));
}
#[test]
fn try_from_extension_normalization() {
// Leading dot
assert_eq!(MediaFileType::try_from_extension(".mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_extension(".spz"), Some(MediaFileType::Spz));
// Case insensitivity
assert_eq!(MediaFileType::try_from_extension("JPG"), Some(MediaFileType::Jpg));
assert_eq!(MediaFileType::try_from_extension(".WebP"), Some(MediaFileType::Webp));
// Whitespace
assert_eq!(MediaFileType::try_from_extension(" png "), Some(MediaFileType::Png));
assert_eq!(MediaFileType::try_from_extension(" .glb "), Some(MediaFileType::Glb));
}
#[test]
fn try_from_extension_unknown_extensions() {
assert_eq!(MediaFileType::try_from_extension("ron"), None);
assert_eq!(MediaFileType::try_from_extension("exe"), None);
assert_eq!(MediaFileType::try_from_extension("scene_json"), None);
assert_eq!(MediaFileType::try_from_extension("audio"), None);
assert_eq!(MediaFileType::try_from_extension(""), None);
assert_eq!(MediaFileType::try_from_extension("."), None);
}
#[test]
fn try_from_filename() {
assert_eq!(MediaFileType::try_from_filename("photo.jpg"), Some(MediaFileType::Jpg));
assert_eq!(MediaFileType::try_from_filename("PHOTO.JPEG"), Some(MediaFileType::Jpg));
assert_eq!(MediaFileType::try_from_filename("clip.final.mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_filename("model.GLB"), Some(MediaFileType::Glb));
assert_eq!(MediaFileType::try_from_filename("scene.spz"), Some(MediaFileType::Spz));
assert_eq!(MediaFileType::try_from_filename("data.json"), Some(MediaFileType::Json));
assert_eq!(MediaFileType::try_from_filename(" padded.png "), Some(MediaFileType::Png));
// Failures
assert_eq!(MediaFileType::try_from_filename("archive.tar.gz"), None);
assert_eq!(MediaFileType::try_from_filename("noextension"), None);
assert_eq!(MediaFileType::try_from_filename("file."), None);
assert_eq!(MediaFileType::try_from_filename(".gitignore"), None);
assert_eq!(MediaFileType::try_from_filename(""), None);
// NB: a bare extension is not a filename (no dot separator)
assert_eq!(MediaFileType::try_from_filename("mp4"), None);
}
#[test]
fn try_from_filename_or_extension() {
// Filenames
assert_eq!(MediaFileType::try_from_filename_or_extension("movie.mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_filename_or_extension("track.FLAC"), Some(MediaFileType::Flac));
// Bare extensions, with and without a leading dot
assert_eq!(MediaFileType::try_from_filename_or_extension("mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_filename_or_extension(".mp4"), Some(MediaFileType::Mp4));
assert_eq!(MediaFileType::try_from_filename_or_extension("webp"), Some(MediaFileType::Webp));
// Failures
assert_eq!(MediaFileType::try_from_filename_or_extension("movie.xyz"), None);
assert_eq!(MediaFileType::try_from_filename_or_extension("unknown"), None);
assert_eq!(MediaFileType::try_from_filename_or_extension(""), None);
}
}
mod manual_variant_checks {
@@ -695,7 +330,7 @@ mod tests {
#[test]
fn all_variants() {
let mut variants = MediaFileType::all_variants();
assert_eq!(variants.len(), 30);
assert_eq!(variants.len(), 21);
assert_eq!(variants.pop_first(), Some(MediaFileType::Audio));
assert_eq!(variants.pop_first(), Some(MediaFileType::Image));
assert_eq!(variants.pop_first(), Some(MediaFileType::Video));
@@ -717,15 +352,6 @@ mod tests {
assert_eq!(variants.pop_first(), Some(MediaFileType::Mp4));
assert_eq!(variants.pop_first(), Some(MediaFileType::Wav));
assert_eq!(variants.pop_first(), Some(MediaFileType::Mp3));
assert_eq!(variants.pop_first(), Some(MediaFileType::Webp));
assert_eq!(variants.pop_first(), Some(MediaFileType::Webm));
assert_eq!(variants.pop_first(), Some(MediaFileType::Mov));
assert_eq!(variants.pop_first(), Some(MediaFileType::Opus));
assert_eq!(variants.pop_first(), Some(MediaFileType::Ogg));
assert_eq!(variants.pop_first(), Some(MediaFileType::Aac));
assert_eq!(variants.pop_first(), Some(MediaFileType::M4a));
assert_eq!(variants.pop_first(), Some(MediaFileType::Flac));
assert_eq!(variants.pop_first(), Some(MediaFileType::Json));
assert_eq!(variants.pop_first(), None);
}
}
@@ -4,6 +4,5 @@ pub mod media_file_engine_category;
pub mod media_file_origin_category;
pub mod media_file_origin_model_type;
pub mod media_file_origin_product_category;
pub mod media_file_project_type;
pub mod media_file_subtype;
pub mod media_file_type;
@@ -126,6 +126,7 @@ async fn create_document_from_record(elasticsearch: &Elasticsearch, record: Medi
media_class: record.media_class,
media_type: record.media_type,
maybe_media_subtype: record.maybe_media_subtype,
maybe_engine_category: record.maybe_engine_category,
maybe_animation_type: record.maybe_animation_type,
@@ -187,7 +187,7 @@ pub async fn process_job(args: FbxToGltfJobArgs<'_>) -> Result<JobSuccessResult,
let (inference_result_token, id) = insert_media_file_generic_from_job(InsertFromJobArgs {
pool: &args.job_dependencies.db.mysql_pool,
job: &job,
media_class: MediaFileClass::Mesh,
media_class: MediaFileClass::Dimensional,
media_type: upload_details.media_type,
origin_category: MediaFileOriginCategory::Processed,
origin_product_category: MediaFileOriginProductCategory::Mocap,
@@ -251,8 +251,13 @@ pub async fn process_job_inference(
}
};
let media_type = MediaFileType::try_from_mime_type(metadata.mimetype.as_ref())
.unwrap_or(MediaFileType::Image); // Coarse fallback for unrecognized mimes
let media_type = match metadata.mimetype.as_ref() {
"image/png" => MediaFileType::Png,
"image/jpeg" => MediaFileType::Jpg,
"image/gif" => MediaFileType::Gif,
"image/webp" => MediaFileType::Image, // Fallback
_ => MediaFileType::Image, // Fallback
};
// extra_file_modification_info: todo!(), // JSON ENCODED STRUCT
let (media_file_token, _id) = insert_media_file_generic_from_job(InsertFromJobArgs {
@@ -257,9 +257,7 @@ pub async fn process_job(args: BvhToWorkflowJobArgs<'_>) -> Result<JobSuccessRes
pool: &args.job_dependencies.db.mysql_pool,
job: &job,
media_class: MediaFileClass::Video,
media_type: maybe_mimetype.as_deref()
.and_then(MediaFileType::try_from_mime_type)
.unwrap_or(MediaFileType::Video), // Coarse fallback for unrecognized mimes
media_type: MediaFileType::Video,
origin_category: MediaFileOriginCategory::Processed,
origin_product_category: MediaFileOriginProductCategory::Mocap,
maybe_origin_model_type: None,
@@ -122,7 +122,7 @@ pub async fn process_successful_job(
.maybe_generation_provider(Some(GenerationProvider::Artcraft))
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
.maybe_platform_type(job.maybe_platform_type)
.media_file_class(MediaFileClass::Splat)
.media_file_class(MediaFileClass::Dimensional)
.media_file_origin_category(MediaFileOriginCategory::Inference)
.media_file_origin_product_category(MediaFileOriginProductCategory::WorldGeneration)
.media_file_type(MediaFileType::Spz)
@@ -84,6 +84,7 @@ use crate::http_server::endpoints::omni_api::upload::omni_upload_image_media_fil
use crate::http_server::endpoints::omni_api::upload::omni_upload_video_media_file_handler::*;
use crate::http_server::endpoints::media_files::upsert_upload::write_engine_asset::write_engine_asset_media_file_handler::*;
use crate::http_server::endpoints::media_files::upsert_upload::write_error::MediaFileWriteError;
use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::*;
use crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::*;
use artcraft_api_defs::generate::video::edit::beeble_switchx_edit_video::*;
use artcraft_api_defs::moderation::debug_logs::debug_log_entry::*;
@@ -258,6 +259,7 @@ use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
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, weights_types::WeightsType};
use enums::by_table::prompt_context_items::prompt_context_semantic_type::PromptContextSemanticType;
@@ -482,6 +484,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
crate::http_server::endpoints::media_files::upload::upload_video_new::upload_new_video_media_file_handler::upload_new_video_media_file_handler,
crate::http_server::endpoints::media_files::upload::upload_video_old::upload_video_media_file_handler::upload_video_media_file_handler,
crate::http_server::endpoints::media_files::upsert_upload::write_engine_asset::write_engine_asset_media_file_handler::write_engine_asset_media_file_handler,
crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::write_scene_file_media_file_handler,
crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::enqueue_gptsovits_model_download_handler,
crate::http_server::endpoints::moderation::user_feature_flags::moderator_edit_user_feature_flags_handler::moderator_edit_user_feature_flags_handler,
crate::http_server::endpoints::moderation::user_feature_flags::moderator_list_all_available_user_feature_flags_handler::moderator_list_all_available_user_feature_flags_handler,
@@ -707,6 +710,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
MediaFileEngineCategory,
MediaFileOriginCategory,
MediaFileOriginProductCategory,
MediaFileSubtype,
MediaFileType,
CommonModelClass,
CommonModelType,
@@ -1209,6 +1213,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
Weight,
WeightsData,
WriteEngineAssetMediaSuccessResponse,
WriteSceneFileMediaSuccessResponse,
// Cost Estimate types
EstimateImageCostRequest,
@@ -9,7 +9,6 @@ use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
@@ -166,8 +165,7 @@ pub async fn create_scene_handler(
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Project),
maybe_project_type: Some(MediaFileProjectType::Scene3d),
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type: MediaFileType::SceneRon,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -177,9 +177,26 @@ pub async fn process_upload_media_file(
// do not have any open issues filed. They may simply be too old:
// - .wma (audio/x-ms-wma)
// - .avi (video/x-msvideo)
// NB: mimetypes are gated by the allow-list check above; this only maps
// the already-permitted set to concrete media types.
media_file_type = MediaFileType::try_from_mime_type(mimetype);
media_file_type = match mimetype {
// Audio
"audio/aac" /* .aac */ => Some(MediaFileType::Audio),
"audio/m4a" /* .m4a */ => Some(MediaFileType::Audio),
"audio/mpeg" /* .mp3 */ => Some(MediaFileType::Audio),
"audio/ogg" /* .ogg */ => Some(MediaFileType::Audio),
"audio/opus" /* .opus */ => Some(MediaFileType::Audio),
"audio/x-flac" /* .flac */ => Some(MediaFileType::Audio),
"audio/x-wav" /* .wav */ => Some(MediaFileType::Audio),
// Image — `webp` has no dedicated variant yet
"image/gif" /* .gif */ => Some(MediaFileType::Gif),
"image/jpeg" /* .jpg */ => Some(MediaFileType::Jpg),
"image/png" /* .png */ => Some(MediaFileType::Png),
"image/webp" /* .webp */ => Some(MediaFileType::Image), // TODO: Replace.
// Video
"video/mp4" /* .mp4 */ => Some(MediaFileType::Video),
"video/quicktime" /* .mov */ => Some(MediaFileType::Video),
"video/webm" /* .webm */ => Some(MediaFileType::Video),
_ => None,
};
let do_audio_decode = match mimetype {
// TODO: Revisit when Safari can send us this metadata consistently
@@ -308,7 +325,6 @@ pub async fn process_upload_media_file(
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: None,
maybe_project_type: None,
media_file_type,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -236,15 +236,16 @@ pub async fn upload_snapshot_media_file_handler(
MediaFileUploadError::ServerError
})?;
let maybe_upload_filename = form.snapshot.file_name.as_deref();
let media_file_type = MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Image); // Coarse fallback for unrecognized files
let media_file_type = match mimetype.as_str() {
"image/jpeg" => MediaFileType::Jpg,
"image/png" => MediaFileType::Png,
"image/gif" => MediaFileType::Gif,
"image/webp" => MediaFileType::Image, // Fallback
_ => MediaFileType::Image, // Fallback
};
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Image),
maybe_project_type: None,
media_file_type,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -129,9 +129,9 @@ pub async fn set_media_file_cover_image_handler(
//let can_use_image = media_file.creator_set_visibility == Visibility::Public
// && media_file.media_type == MediaFileType::Image;
let can_use_image = media_file.media_type.is_image();
let can_use_image = media_file.media_type == MediaFileType::Image;
if !can_use_image {
if !can_use_image {
return Err(CommonWebError::BadInputWithSimpleMessage("Invalid media file token.".to_string()));
}
@@ -10,6 +10,7 @@ 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;
@@ -78,6 +79,11 @@ pub struct BatchMediaFileInfo {
/// this describes the animation regime used or supported.
pub maybe_animation_type: Option<MediaFileAnimationType>,
/// If the media file has a subtype, we'll report it.
/// This is mostly used for Bevy engine files.
#[deprecated(note="This was for the Bevy engine. Do not use.")]
pub maybe_media_subtype: Option<MediaFileSubtype>,
/// Extension for the engine to load over remote:// URLs.
#[deprecated(note="This was for the Bevy engine. Do not use.")]
pub maybe_engine_extension: Option<String>,
@@ -283,6 +289,7 @@ pub async fn batch_get_media_files_handler(
media_type: result.media_type,
maybe_engine_category: result.maybe_engine_category,
maybe_animation_type: result.maybe_animation_type,
maybe_media_subtype: result.maybe_media_subtype,
maybe_engine_extension,
maybe_batch_token: result.maybe_batch_token,
media_links: MediaLinksBuilder::from_media_path_and_env(
@@ -19,6 +19,7 @@ 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;
@@ -68,6 +69,11 @@ pub struct MediaFileInfo {
/// this describes the animation regime used or supported.
pub maybe_animation_type: Option<MediaFileAnimationType>,
/// If the media file has a subtype, we'll report it.
/// This is mostly used for Bevy engine files.
#[deprecated(note="This was for the Bevy engine. Do not use.")]
pub maybe_media_subtype: Option<MediaFileSubtype>,
/// Extension for the engine to load over remote:// URLs.
#[deprecated(note="This was for the Bevy engine. Do not use.")]
pub maybe_engine_extension: Option<String>,
@@ -315,6 +321,8 @@ async fn modern_media_file_lookup(
media_type: result.media_type,
maybe_engine_category: result.maybe_engine_category,
maybe_animation_type: result.maybe_animation_type,
// maybe_media_subtype: result.maybe_media_subtype,
maybe_media_subtype: None, // NB(bt,2024-07-25): Stop populating field for consumers.
maybe_engine_extension,
maybe_batch_token: result.maybe_batch_token,
maybe_scene_source_media_file_token: result.maybe_scene_source_media_file_token,
@@ -452,6 +460,7 @@ async fn emulate_media_file_with_legacy_tts_result_lookup(
media_type: MediaFileType::Audio, // NB: Always audio
maybe_engine_category: None,
maybe_animation_type: None,
maybe_media_subtype: None,
maybe_engine_extension: None,
maybe_batch_token: None,
maybe_scene_source_media_file_token: None,
@@ -331,14 +331,9 @@ pub async fn upload_audio_media_file_handler(
UploadType::Filesystem
};
let maybe_upload_filename = form.file.file_name.as_deref();
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Audio),
maybe_project_type: None,
media_file_type: MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Audio), // Coarse fallback for unrecognized files
media_file_type: MediaFileType::Audio,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
creator_ip_address: &ip_address,
@@ -7,7 +7,7 @@ use log::{error, info, warn};
use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
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 hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
@@ -204,11 +204,7 @@ pub async fn upload_engine_asset_media_file_handler(
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(media_file_type.to_media_class()),
maybe_project_type: match media_file_type {
MediaFileType::SceneRon | MediaFileType::SceneJson => Some(MediaFileProjectType::Scene3d),
_ => None,
},
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -204,11 +204,26 @@ pub async fn process_upload_media_file(
// do not have any open issues filed. They may simply be too old:
// - .wma (audio/x-ms-wma)
// - .avi (video/x-msvideo)
// NB: mimetypes are gated by the `allowed_mimetypes` check above; this only
// maps the already-permitted set to concrete media types.
let maybe_upload_filename = upload_media_request.file_name.as_deref();
media_file_type = MediaFileType::try_from_mime_type(mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension));
media_file_type = match mimetype {
// Audio
"audio/aac" /* .aac */ => Some(MediaFileType::Audio),
"audio/m4a" /* .m4a */ => Some(MediaFileType::Audio),
"audio/mpeg" /* .mp3 */ => Some(MediaFileType::Audio),
"audio/ogg" /* .ogg */ => Some(MediaFileType::Audio),
"audio/opus" /* .opus */ => Some(MediaFileType::Audio),
"audio/x-flac" /* .flac */ => Some(MediaFileType::Audio),
"audio/x-wav" /* .wav */ => Some(MediaFileType::Audio),
// Image — `webp` has no dedicated variant yet
"image/gif" /* .gif */ => Some(MediaFileType::Gif),
"image/jpeg" /* .jpg */ => Some(MediaFileType::Jpg),
"image/png" /* .png */ => Some(MediaFileType::Png),
"image/webp" /* .webp */ => Some(MediaFileType::Image),
// Video
"video/mp4" /* .mp4 */ => Some(MediaFileType::Video),
"video/quicktime" /* .mov */ => Some(MediaFileType::Video),
"video/webm" /* .webm */ => Some(MediaFileType::Video),
_ => None,
};
let do_audio_decode = match mimetype {
// TODO: Revisit when Safari can send us this metadata consistently
@@ -298,7 +313,29 @@ pub async fn process_upload_media_file(
},
};
let media_file_class = media_file_type.to_media_class();
let media_file_class = match media_file_type {
MediaFileType::Audio => MediaFileClass::Audio,
MediaFileType::Image => MediaFileClass::Image,
MediaFileType::Video => MediaFileClass::Video,
MediaFileType::Bvh => MediaFileClass::Dimensional,
MediaFileType::Fbx => MediaFileClass::Dimensional,
MediaFileType::Obj => MediaFileClass::Dimensional,
MediaFileType::Glb => MediaFileClass::Dimensional,
MediaFileType::Gltf => MediaFileClass::Dimensional,
MediaFileType::Spz => MediaFileClass::Dimensional,
MediaFileType::SceneRon => MediaFileClass::Dimensional,
MediaFileType::SceneJson => MediaFileClass::Dimensional,
MediaFileType::Pmd => MediaFileClass::Dimensional,
MediaFileType::Vmd => MediaFileClass::Dimensional,
MediaFileType::Csv => MediaFileClass::Dimensional,
MediaFileType::Pmx => MediaFileClass::Dimensional,
MediaFileType::Jpg => MediaFileClass::Image,
MediaFileType::Png => MediaFileClass::Image,
MediaFileType::Gif => MediaFileClass::Image,
MediaFileType::Mp4 => MediaFileClass::Video,
MediaFileType::Wav => MediaFileClass::Audio,
MediaFileType::Mp3 => MediaFileClass::Audio,
};
let upload_type = match upload_media_request.media_source {
MediaFileUploadSource::Unknown => UploadType::Filesystem,
@@ -299,15 +299,16 @@ pub async fn upload_image_media_file_handler(
UploadType::Filesystem
};
let maybe_upload_filename = form.file.file_name.as_deref();
let media_file_type = MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Image); // Coarse fallback for unrecognized files
let media_file_type = match mimetype.as_str() {
"image/jpeg" => MediaFileType::Jpg,
"image/png" => MediaFileType::Png,
"image/gif" => MediaFileType::Gif,
"image/webp" => MediaFileType::Image, // Fallback
_ => MediaFileType::Image, // fallback
};
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Image),
maybe_project_type: None,
media_file_type,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -210,7 +210,6 @@ pub async fn upload_new_engine_asset_media_file_handler(
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(file_info.media_class),
maybe_project_type: None,
media_file_type: file_info.media_type,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -390,7 +389,11 @@ fn validate_and_process_form(
}
};
let media_class = media_type.to_media_class();
let media_class = match media_type {
MediaFileType::Jpg | MediaFileType::Png | MediaFileType::Gif => MediaFileClass::Image,
MediaFileType::Mp4 => MediaFileClass::Video,
_ => MediaFileClass::Dimensional,
};
Ok(FileInfo {
media_class,
@@ -13,7 +13,6 @@ use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
@@ -225,8 +224,7 @@ pub async fn upload_new_scene_media_file_handler(
})?;
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Project),
maybe_project_type: Some(MediaFileProjectType::Scene3d),
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type: MediaFileType::SceneJson,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -256,8 +256,7 @@ pub async fn upload_pmx_media_file_handler(
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Mesh),
maybe_project_type: None,
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type: MediaFileType::Pmx,
maybe_creator_user_token: Some(user_session.get_user_token()),
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -11,8 +11,6 @@ use log::{error, info, warn};
use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::common::visibility::Visibility;
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
use http_server_common::request::get_request_ip::get_request_ip;
@@ -245,8 +243,6 @@ pub async fn upload_saved_scene_media_file_handler(
updated_media_file_stored_cloud_contents(UpdateArgs {
media_file_token: &path.token,
media_class: MediaFileClass::Project,
maybe_project_type: Some(MediaFileProjectType::Scene3d),
public_bucket_directory_hash: public_upload_path.get_object_hash(),
maybe_public_bucket_prefix: PREFIX,
maybe_public_bucket_extension: Some(SUFFIX),
@@ -13,7 +13,6 @@ use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
@@ -218,8 +217,7 @@ pub async fn upload_scene_snapshot_media_file_handler(
})?;
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Project),
maybe_project_type: Some(MediaFileProjectType::Scene3d),
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type: MediaFileType::SceneJson,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
@@ -253,7 +253,7 @@ pub async fn upload_spz_media_file_handler(
.and_then(|text| try_parse_generation_provider(text.as_ref()));
let media_token = MediaFileInsertBuilder::new()
.media_file_class(MediaFileClass::Splat)
.media_file_class(MediaFileClass::Dimensional)
.media_file_type(MediaFileType::Spz)
.maybe_creator_user(maybe_user_token)
.maybe_creator_anonymous_visitor(maybe_avt_token.as_ref())
@@ -504,14 +504,9 @@ pub async fn upload_new_video_media_file_handler(
UploadType::Filesystem
};
let maybe_upload_filename = form.file.file_name.as_deref();
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Video),
maybe_project_type: None,
media_file_type: MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Video), // Coarse fallback for unrecognized files
media_file_type: MediaFileType::Video,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
creator_ip_address: &ip_address,
@@ -221,9 +221,7 @@ pub async fn upload_video_media_file_handler(
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Video),
maybe_project_type: None,
media_file_type: MediaFileType::try_from_mime_type(mimetype)
.unwrap_or(MediaFileType::Video), // Coarse fallback for unrecognized mimes
media_file_type: MediaFileType::Video,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
creator_ip_address: &ip_address,
@@ -1,2 +1,3 @@
pub mod write_engine_asset;
pub mod write_error;
pub mod write_error;
pub mod write_scene_file;
@@ -7,7 +7,7 @@ use log::{error, info, warn};
use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
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 hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
@@ -227,14 +227,11 @@ pub async fn write_engine_asset_media_file_handler(
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = upsert_media_file_from_file_upload(UpsertMediaFileFromUploadArgs {
maybe_media_file_token: upload_media_request.media_file_token.as_ref(),
maybe_media_class: Some(media_file_type.to_media_class()),
maybe_project_type: match media_file_type {
MediaFileType::SceneRon | MediaFileType::SceneJson => Some(MediaFileProjectType::Scene3d),
_ => None,
},
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type,
maybe_engine_category: upload_media_request.maybe_engine_category,
maybe_animation_type: upload_media_request.maybe_animation_type,
maybe_media_subtype: None,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
creator_ip_address: &ip_address,
@@ -0,0 +1,94 @@
use actix_multipart::Multipart;
use actix_web::web::BytesMut;
use futures::TryStreamExt;
use log::warn;
use tokens::tokens::media_files::MediaFileToken;
use crate::http_server::endpoints::media_files::upsert_upload::write_error::MediaFileWriteError;
use crate::http_server::web_utils::read_multipart_field_bytes::{checked_read_multipart_bytes, read_multipart_field_as_text};
pub struct MediaFileUploadData {
pub uuid_idempotency_token: Option<String>,
pub file_name: Option<String>,
pub file_bytes: Option<BytesMut>,
pub media_file_token: Option<MediaFileToken>,
// Optional: title of the scene (media_files' maybe_title)
pub maybe_title: Option<String>,
// Optional: visibility
pub maybe_visibility: Option<String>,
}
/// Pull common parts out of multipart media HTTP requests, typically for handling file uploads.
pub async fn drain_multipart_request(mut multipart_payload: Multipart) -> Result<MediaFileUploadData, MediaFileWriteError> {
let mut uuid_idempotency_token = None;
let mut file_bytes = None;
let mut file_name = None;
let mut media_file_token = None;
let mut title = None;
let mut visibility = None;
while let Ok(Some(mut field)) = multipart_payload.try_next().await {
let mut field_name = None;
let mut field_filename = None;
if let Some(content_disposition) = field.content_disposition() {
field_name = content_disposition.get_name()
.map(|s| s.to_string());
field_filename = content_disposition.get_filename() // NB: Only used for the file bytes.
.map(|s| s.to_string());
}
match field_name.as_deref() {
Some("uuid_idempotency_token") => {
uuid_idempotency_token = read_multipart_field_as_text(&mut field).await
.map_err(|err| {
warn!("Error reading idempotency token: {:?}", &err);
MediaFileWriteError::BadInput("Error reading idempotency token".to_string())
})?;
},
Some("file") => {
file_name = field_filename.clone();
file_bytes = checked_read_multipart_bytes(&mut field).await
.map_err(|err| {
warn!("Error reading audio upload: {:?}", &err);
MediaFileWriteError::BadInput("Error reading file bytes".to_string())
})?;
},
Some("media_file_token") => {
media_file_token = read_multipart_field_as_text(&mut field).await
.map_err(|err| {
warn!("Error reading source: {:?}", &err);
MediaFileWriteError::BadInput("Error reading media_file_token".to_string())
})?
.map(|field| MediaFileToken::new_from_str(&field));
},
Some("title") => {
title = read_multipart_field_as_text(&mut field).await
.map_err(|err| {
warn!("Error reading title: {:}", &err);
MediaFileWriteError::BadInput("Error reading title".to_string())
})?;
},
Some("visibility") => {
visibility = read_multipart_field_as_text(&mut field).await
.map_err(|err| {
warn!("Error reading visibility: {:}", &err);
MediaFileWriteError::BadInput("Error reading visibility".to_string())
})?;
},
_ => continue,
}
}
Ok(MediaFileUploadData {
uuid_idempotency_token,
file_name,
file_bytes,
media_file_token,
maybe_title: title,
maybe_visibility: visibility,
})
}
@@ -0,0 +1,2 @@
pub mod drain_multipart_request;
pub mod write_scene_file_media_file_handler;
@@ -0,0 +1,256 @@
use std::path::PathBuf;
use std::sync::Arc;
use actix_multipart::Multipart;
use actix_web::{web, HttpRequest, HttpResponse};
use log::{error, info, warn};
use utoipa::ToSchema;
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use hashing::sha256::sha256_hash_bytes::sha256_hash_bytes;
use http_server_common::request::get_request_ip::get_request_ip;
use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token;
use mysql_queries::queries::media_files::get::get_media_file::get_media_file;
use mysql_queries::queries::media_files::upsert::upsert_media_file_from_file_upload::{upsert_media_file_from_file_upload, UploadType, UpsertMediaFileFromUploadArgs};
use tokens::tokens::media_files::MediaFileToken;
use crate::http_server::endpoints::media_files::upsert_upload::write_error::MediaFileWriteError;
use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::drain_multipart_request::drain_multipart_request;
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
use crate::state::server_state::ServerState;
use crate::util::check_creator_tokens::{check_creator_tokens, CheckCreatorTokenArgs, CheckCreatorTokenResult};
// Unlike the "upload" endpoints, which are pure inserts, these endpoints are *upserts*.
#[derive(Serialize, ToSchema)]
pub struct WriteSceneFileMediaSuccessResponse {
pub success: bool,
pub media_file_token: MediaFileToken,
}
/// DEPRECATED: Use the "new scene" and "saved scene" endpoints instead.
#[deprecated]
#[utoipa::path(
post,
tag = "Media Files [Deprecated]",
path = "/v1/media_files/write/scene_file",
responses(
(status = 200, description = "Success Update", body = WriteSceneFileMediaSuccessResponse),
(status = 400, description = "Bad input", body = MediaFileWriteError),
(status = 401, description = "Not authorized", body = MediaFileWriteError),
(status = 429, description = "Too many requests", body = MediaFileWriteError),
(status = 500, description = "Server error", body = MediaFileWriteError),
),
params(
("request" = (), description = "Ask Brandon. This is form-multipart."),
)
)]
pub async fn write_scene_file_media_file_handler(
http_request: HttpRequest,
server_state: web::Data<Arc<ServerState>>,
mut multipart_payload: Multipart,
) -> Result<HttpResponse, MediaFileWriteError> {
let mut mysql_connection = server_state.mysql_pool
.acquire()
.await
.map_err(|err| {
error!("MySql pool error: {:?}", err);
MediaFileWriteError::ServerError
})?;
// ==================== READ SESSION ==================== //
let maybe_user_session = server_state
.session_checker
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
.await
.map_err(|e| {
error!("Session checker error: {:?}", e);
MediaFileWriteError::ServerError
})?;
let maybe_user_token = maybe_user_session
.as_ref()
.map(|session| session.get_user_token());
let maybe_avt_token = server_state
.avt_cookie_manager
.get_avt_token_from_request(&http_request);
// ==================== BANNED USERS ==================== //
if let Some(ref user) = maybe_user_session {
if user.is_banned {
return Err(MediaFileWriteError::NotAuthorizedVerbose("user is banned".to_string()));
}
}
// ==================== RATE LIMIT ==================== //
let rate_limiter = match maybe_user_session {
None => &server_state.redis_rate_limiters.file_upload_logged_out,
Some(ref _session) => &server_state.redis_rate_limiters.file_upload_logged_in,
};
if let Err(_err) = rate_limiter.rate_limit_request(&http_request).await {
return Err(MediaFileWriteError::RateLimited);
}
// ==================== READ MULTIPART REQUEST ==================== //
let upload_media_request = drain_multipart_request(multipart_payload).await?;
// ==================== MAKE SURE USER OWNS FILE ==================== //
if let Some(media_file_token) = upload_media_request.media_file_token.as_ref() {
// TODO(bt,2024-03-26): Don't use the mysql_pool, use the mysql_connection.
let media_file =
get_media_file(media_file_token, false, &server_state.mysql_pool)
.await
.map_err(|err| {
error!("Error getting media file: {:?}", err);
MediaFileWriteError::ServerError
})?
.ok_or_else(|| MediaFileWriteError::NotFoundVerbose("media file not found with that token".to_string()))?;
let creator_check = check_creator_tokens(CheckCreatorTokenArgs {
maybe_creator_user_token: media_file.maybe_creator_user_token.as_ref(),
maybe_current_request_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: media_file.maybe_creator_anonymous_visitor_token.as_ref(),
maybe_current_request_anonymous_visitor_token: maybe_avt_token.as_ref(),
});
match creator_check {
CheckCreatorTokenResult::UserTokenMatch => {} // Allowed
CheckCreatorTokenResult::NoUserAnonymousVisitorTokenMatch => {} // Allowed
CheckCreatorTokenResult::InsufficientInformation => {} // TODO(bt,2024-03-28): Temporary fallthrough. This should be a 401.
CheckCreatorTokenResult::UserTokenMismatch => return Err(MediaFileWriteError::NotAuthorizedVerbose(
"user tokens do not match".to_string())),
CheckCreatorTokenResult::NoUserAnonymousVisitorTokenMismatch => return Err(MediaFileWriteError::NotAuthorizedVerbose(
"anonymous visitor tokens do not match".to_string())),
}
}
// ==================== HANDLE IDEMPOTENCY ==================== //
// TODO(bt, 2024-02-26): This should be a transaction.
let uuid_idempotency_token = upload_media_request.uuid_idempotency_token
.ok_or(MediaFileWriteError::BadInput("no uuid".to_string()))?;
if let Err(reason) = validate_idempotency_token_format(&uuid_idempotency_token) {
return Err(MediaFileWriteError::BadInput(reason));
}
insert_idempotency_token(&uuid_idempotency_token, &mut *mysql_connection)
.await
.map_err(|err| {
error!("Error inserting idempotency token: {:?}", err);
MediaFileWriteError::BadInput("invalid idempotency token".to_string())
})?;
// ==================== UPLOAD METADATA ==================== //
let creator_set_visibility = maybe_user_session
.as_ref()
.map(|user_session| user_session.preferred_tts_result_visibility) // TODO: We need a new type of visibility control.
.unwrap_or(Visibility::default());
// ==================== USER DATA ==================== //
let ip_address = get_request_ip(&http_request);
// ==================== FILE DATA ==================== //
let file_bytes = match upload_media_request.file_bytes {
None => return Err(MediaFileWriteError::BadInput("missing file contents".to_string())),
Some(bytes) => bytes,
};
let maybe_filename = upload_media_request.file_name
.as_deref()
.map(|filename| PathBuf::from(filename));
let maybe_file_extension = maybe_filename
.as_ref()
.and_then(|filename| filename.extension())
.and_then(|ext| ext.to_str());
let file_size_bytes = file_bytes.len();
let hash = sha256_hash_bytes(&file_bytes)
.map_err(|io_error| {
error!("Problem hashing bytes: {:?}", io_error);
MediaFileWriteError::ServerError
})?;
// ==================== UPLOAD AND SAVE ==================== //
// TODO(bt,2024-03-26): At first I thought we should map these to the existing file paths on upsert,
// but now I'm thinking we can just lead cruft in the bucket and clean it later. We don't have the
// benefit of restoring old versions (if we mapped to existing paths but had a versioning scheme),
// but we can move fast.
const MIMETYPE: &str = "application/json";
const PREFIX : Option<&str> = Some("upload_");
const SUFFIX: &str = ".json";
let public_upload_path = MediaFileBucketPath::generate_new(PREFIX, Some(SUFFIX));
info!("Uploading media to bucket path: {}", public_upload_path.get_full_object_path_str());
server_state.public_bucket_client.upload_file_with_content_type(
public_upload_path.get_full_object_path_str(),
file_bytes.as_ref(),
MIMETYPE)
.await
.map_err(|e| {
warn!("Upload media bytes to bucket error: {:?}", e);
MediaFileWriteError::ServerError
})?;
// TODO(bt, 2024-02-22): This should be a transaction.
let (token, record_id) = upsert_media_file_from_file_upload(UpsertMediaFileFromUploadArgs {
maybe_media_file_token: upload_media_request.media_file_token.as_ref(),
maybe_media_class: Some(MediaFileClass::Dimensional),
media_file_type: MediaFileType::SceneJson,
maybe_engine_category: Some(MediaFileEngineCategory::Scene),
maybe_animation_type: None,
maybe_media_subtype: None,
maybe_creator_user_token: maybe_user_token,
maybe_creator_anonymous_visitor_token: maybe_avt_token.as_ref(),
creator_ip_address: &ip_address,
creator_set_visibility,
upload_type: UploadType::Filesystem,
maybe_mime_type: Some(MIMETYPE),
file_size_bytes: file_size_bytes as u64,
duration_millis: 0,
sha256_checksum: &hash,
public_bucket_directory_hash: public_upload_path.get_object_hash(),
maybe_public_bucket_prefix: PREFIX,
maybe_public_bucket_extension: Some(SUFFIX),
pool: &server_state.mysql_pool,
})
.await
.map_err(|err| {
warn!("New file creation DB error: {:?}", err);
MediaFileWriteError::ServerError
})?;
info!("new media file id: {} token: {:?}", record_id, &token);
let response = WriteSceneFileMediaSuccessResponse {
success: true,
media_file_token: token,
};
let body = serde_json::to_string(&response)
.map_err(|e| MediaFileWriteError::ServerError)?;
return Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body));
}
@@ -110,7 +110,6 @@ pub async fn upload_url_to_media_file(
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(kind.media_file_class()),
maybe_project_type: None,
media_file_type: kind.media_file_type(&mimetype),
maybe_creator_user_token: Some(user_token),
// NB: AVT (anonymous visitor) tokens are a web-session concept; API-key callers have none.
@@ -203,12 +202,15 @@ impl MediaUploadKind {
}
fn media_file_type(&self, mimetype: &str) -> MediaFileType {
MediaFileType::try_from_mime_type(mimetype)
.unwrap_or(match self {
// Coarse fallbacks for unrecognized mimes
MediaUploadKind::Image => MediaFileType::Image,
MediaUploadKind::Video => MediaFileType::Video,
MediaUploadKind::Audio => MediaFileType::Audio,
})
match self {
MediaUploadKind::Image => match mimetype {
"image/jpeg" => MediaFileType::Jpg,
"image/png" => MediaFileType::Png,
"image/gif" => MediaFileType::Gif,
_ => MediaFileType::Image,
},
MediaUploadKind::Video => MediaFileType::Video,
MediaUploadKind::Audio => MediaFileType::Audio,
}
}
}
@@ -275,14 +275,9 @@ pub async fn omni_upload_audio_media_file_handler(
UploadType::Filesystem
};
let maybe_upload_filename = form.file.file_name.as_deref();
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Audio),
maybe_project_type: None,
media_file_type: MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Audio), // Coarse fallback for unrecognized files
media_file_type: MediaFileType::Audio,
maybe_creator_user_token: maybe_user_token,
// NB: AVT (anonymous visitor) tokens are a web-session concept; API-key callers have none.
maybe_creator_anonymous_visitor_token: None,
@@ -259,15 +259,16 @@ pub async fn omni_upload_image_media_file_handler(
UploadType::Filesystem
};
let maybe_upload_filename = form.file.file_name.as_deref();
let media_file_type = MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Image); // Coarse fallback for unrecognized files
let media_file_type = match mimetype.as_str() {
"image/jpeg" => MediaFileType::Jpg,
"image/png" => MediaFileType::Png,
"image/gif" => MediaFileType::Gif,
"image/webp" => MediaFileType::Image, // Fallback
_ => MediaFileType::Image, // fallback
};
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Image),
maybe_project_type: None,
media_file_type,
maybe_creator_user_token: maybe_user_token,
// NB: AVT (anonymous visitor) tokens are a web-session concept; API-key callers have none.
@@ -456,14 +456,9 @@ pub async fn omni_upload_video_media_file_handler(
UploadType::Filesystem
};
let maybe_upload_filename = form.file.file_name.as_deref();
let (token, record_id) = insert_media_file_from_file_upload(InsertMediaFileFromUploadArgs {
maybe_media_class: Some(MediaFileClass::Video),
maybe_project_type: None,
media_file_type: MediaFileType::try_from_mime_type(&mimetype)
.or_else(|| maybe_upload_filename.and_then(MediaFileType::try_from_filename_or_extension))
.unwrap_or(MediaFileType::Video), // Coarse fallback for unrecognized files
media_file_type: MediaFileType::Video,
maybe_creator_user_token: maybe_user_token,
// NB: AVT (anonymous visitor) tokens are a web-session concept; API-key callers have none.
maybe_creator_anonymous_visitor_token: None,
@@ -143,7 +143,7 @@ pub async fn process_model_glb_payload(
.maybe_creator_anonymous_visitor(job.maybe_creator_anonymous_visitor_token.as_ref())
.creator_ip_address(&job.creator_ip_address)
.public_bucket_directory_hash(&public_upload_path)
.media_file_class(MediaFileClass::Mesh)
.media_file_class(MediaFileClass::Dimensional)
.media_file_type(media_file_type)
.media_file_origin_category(MediaFileOriginCategory::Inference)
.maybe_engine_category(Some(MediaFileEngineCategory::Object))
@@ -280,7 +280,7 @@ async fn try_to_attach_thumbnail(
.maybe_generation_provider(Some(GenerationProvider::Artcraft))
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
.maybe_platform_type(job.maybe_platform_type)
.media_file_class(MediaFileClass::Image)
.media_file_class(MediaFileClass::Dimensional)
.media_file_origin_category(MediaFileOriginCategory::Inference)
.media_file_type(media_file_type)
.mime_type(mime_type)
@@ -174,7 +174,7 @@ pub(crate) async fn upload_mesh_file(
.maybe_generation_provider(Some(GenerationProvider::Artcraft))
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
.maybe_platform_type(job.maybe_platform_type)
.media_file_class(MediaFileClass::Mesh)
.media_file_class(MediaFileClass::Dimensional)
.media_file_origin_category(MediaFileOriginCategory::Inference)
.media_file_type(media_file_type)
.mime_type(mime_type)
@@ -134,9 +134,9 @@ pub async fn set_model_weight_cover_image_handler(
//let can_use_image = media_file.creator_set_visibility == Visibility::Public
// && media_file.media_type == MediaFileType::Image;
let can_use_image = media_file.media_type.is_image();
let can_use_image = media_file.media_type == MediaFileType::Image;
if !can_use_image {
if !can_use_image {
return Err(CommonWebError::BadInputWithSimpleMessage("Invalid media file token.".to_string()));
}
@@ -158,7 +158,7 @@ pub async fn update_weight_handler(
let maybe_media_file_type = maybe_media_file.map(|media_file| media_file.media_type);
match maybe_media_file_type {
Some(media_file_type) if media_file_type.is_image() => cover_image = Some(CoverImageOption::SetCoverImage(media_file_token)),
Some(MediaFileType::Image) => cover_image = Some(CoverImageOption::SetCoverImage(media_file_token)),
None => return Err(CommonWebError::BadInputWithSimpleMessage("Media file does not exist".to_string())),
_ => return Err(CommonWebError::BadInputWithSimpleMessage("Media file is the wrong type".to_string())),
}
@@ -34,6 +34,7 @@ use crate::http_server::endpoints::media_files::upload::upload_studio_shot::uplo
use crate::http_server::endpoints::media_files::upload::upload_video_new::upload_new_video_media_file_handler::upload_new_video_media_file_handler;
use crate::http_server::endpoints::media_files::upload::upload_video_old::upload_video_media_file_handler::upload_video_media_file_handler;
use crate::http_server::endpoints::media_files::upsert_upload::write_engine_asset::write_engine_asset_media_file_handler::write_engine_asset_media_file_handler;
use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::write_scene_file_media_file_handler;
pub fn add_media_file_routes<T, B> (app: App<T>) -> App<T>
where
@@ -137,6 +138,10 @@ pub fn add_media_file_routes<T, B> (app: App<T>) -> App<T>
.route(web::post().to(write_engine_asset_media_file_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(web::resource("/write/scene_file")
.route(web::post().to(write_scene_file_media_file_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(web::resource("/upload/new_engine_asset")
.route(web::post().to(upload_new_engine_asset_media_file_handler))
.route(web::head().to(|| HttpResponse::Ok()))

Some files were not shown because too many files have changed in this diff Show More