mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-01 00:05:54 -04:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed8923986a | ||
|
|
c5071d4f2e | ||
|
|
57c77cbbc2 | ||
|
|
98c4fad729 | ||
|
|
f50bfa9ee7 | ||
|
|
d90d446643 | ||
|
|
c4d188a600 | ||
|
|
1d5d63fda1 | ||
|
|
6af5cc7d0b | ||
|
|
a7cd9f3b2f |
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\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.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\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 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.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.maybe_project_type = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id > ?\nORDER BY m.id ASC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "11cc7b91730784e9e8c8f073aeaf51a1f1660584e379b9a5be3aea370d84f692"
|
||||
}
|
||||
+12
@@ -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 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"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\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.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\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 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.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.maybe_project_type = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id < ?\nORDER BY m.id DESC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "6c7adbc8171bc3fdfb6109193ffb0a55413fa0942746d65037c2b1d39b361bd0"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+43
-54
@@ -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_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 ",
|
||||
"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 ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -55,16 +55,6 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_mime_type",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -74,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -84,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -94,7 +84,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -104,7 +94,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -114,7 +104,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -124,7 +114,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -134,7 +124,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -144,7 +134,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -154,7 +144,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -164,7 +154,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "maybe_text_transcript",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -174,7 +164,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "maybe_origin_filename",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -184,7 +174,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "maybe_duration_millis",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -194,7 +184,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -204,7 +194,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "maybe_other_prompt_args",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -214,7 +204,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -224,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -234,7 +224,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -244,7 +234,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -254,7 +244,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "is_user_upload",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -264,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "is_intermediate_system_file",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -274,7 +264,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 26,
|
||||
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -284,7 +274,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 27,
|
||||
"name": "maybe_model_weights_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -294,7 +284,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 28,
|
||||
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -304,7 +294,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 29,
|
||||
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -314,7 +304,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 30,
|
||||
"name": "maybe_model_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -324,7 +314,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 31,
|
||||
"name": "maybe_model_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -334,7 +324,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 32,
|
||||
"name": "maybe_model_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -344,7 +334,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 33,
|
||||
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -354,7 +344,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"ordinal": 34,
|
||||
"name": "maybe_model_weight_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -364,7 +354,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"ordinal": 35,
|
||||
"name": "maybe_model_weight_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -374,7 +364,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 37,
|
||||
"ordinal": 36,
|
||||
"name": "maybe_model_weight_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -384,7 +374,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 38,
|
||||
"ordinal": 37,
|
||||
"name": "extra_file_modification_info",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -394,7 +384,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 39,
|
||||
"ordinal": 38,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -404,7 +394,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 40,
|
||||
"ordinal": 39,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -414,7 +404,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 41,
|
||||
"ordinal": 40,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -424,7 +414,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 42,
|
||||
"ordinal": 41,
|
||||
"name": "maybe_ratings_positive_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -434,7 +424,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 43,
|
||||
"ordinal": 42,
|
||||
"name": "maybe_ratings_negative_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -444,7 +434,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 44,
|
||||
"ordinal": 43,
|
||||
"name": "maybe_bookmark_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -454,7 +444,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 45,
|
||||
"ordinal": 44,
|
||||
"name": "is_featured",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
@@ -464,7 +454,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 46,
|
||||
"ordinal": 45,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -474,7 +464,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 47,
|
||||
"ordinal": 46,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -511,7 +501,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -538,5 +527,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f6c37066ab3e508050e81cf3bea38cc708b05c5c59ba1c322dcfb775e4b9e7a6"
|
||||
"hash": "784ca68c6cb61d481b605bfe6ee3248a5b44499038c71b57fc9ea6c4722d170f"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\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.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\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 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.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id < ?\nORDER BY m.id DESC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "97e4bc3ab037126e3f4e213cdb7535c2ad233809fbeb6607c34fb02ec45cfe47"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+43
-54
@@ -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_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 ",
|
||||
"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 ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -55,16 +55,6 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "maybe_media_subtype: enums::by_table::media_files::media_file_subtype::MediaFileSubtype",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_mime_type",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -74,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -84,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -94,7 +84,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -104,7 +94,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -114,7 +104,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_anonymous_visitor_token: tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -124,7 +114,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "maybe_batch_token: tokens::tokens::batch_generations::BatchGenerationToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -134,7 +124,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "maybe_style_transfer_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -144,7 +134,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "maybe_scene_source_media_file_token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -154,7 +144,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -164,7 +154,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "maybe_text_transcript",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -174,7 +164,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "maybe_origin_filename",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -184,7 +174,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "maybe_duration_millis",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -194,7 +184,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "maybe_prompt_token: tokens::tokens::prompts::PromptToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -204,7 +194,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "maybe_other_prompt_args",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -214,7 +204,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -224,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -234,7 +224,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -244,7 +234,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -254,7 +244,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "is_user_upload",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -264,7 +254,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "is_intermediate_system_file",
|
||||
"type_info": {
|
||||
"type": "Tiny",
|
||||
@@ -274,7 +264,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 26,
|
||||
"name": "maybe_model_weights_token: tokens::tokens::model_weights::ModelWeightToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -284,7 +274,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 27,
|
||||
"name": "maybe_model_weights_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -294,7 +284,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 28,
|
||||
"name": "maybe_model_weights_type: enums::by_table::model_weights::weights_types::WeightsType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -304,7 +294,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 29,
|
||||
"name": "maybe_model_weights_category: enums::by_table::model_weights::weights_category::WeightsCategory",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -314,7 +304,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 30,
|
||||
"name": "maybe_model_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -324,7 +314,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 31,
|
||||
"name": "maybe_model_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -334,7 +324,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 32,
|
||||
"name": "maybe_model_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -344,7 +334,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 33,
|
||||
"name": "maybe_model_weight_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -354,7 +344,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"ordinal": 34,
|
||||
"name": "maybe_model_weight_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -364,7 +354,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"ordinal": 35,
|
||||
"name": "maybe_model_weight_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -374,7 +364,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 37,
|
||||
"ordinal": 36,
|
||||
"name": "maybe_model_weight_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
@@ -384,7 +374,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 38,
|
||||
"ordinal": 37,
|
||||
"name": "extra_file_modification_info",
|
||||
"type_info": {
|
||||
"type": "Blob",
|
||||
@@ -394,7 +384,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 39,
|
||||
"ordinal": 38,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -404,7 +394,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 40,
|
||||
"ordinal": 39,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -414,7 +404,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 41,
|
||||
"ordinal": 40,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
@@ -424,7 +414,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 42,
|
||||
"ordinal": 41,
|
||||
"name": "maybe_ratings_positive_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -434,7 +424,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 43,
|
||||
"ordinal": 42,
|
||||
"name": "maybe_ratings_negative_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -444,7 +434,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 44,
|
||||
"ordinal": 43,
|
||||
"name": "maybe_bookmark_count",
|
||||
"type_info": {
|
||||
"type": "Long",
|
||||
@@ -454,7 +444,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 45,
|
||||
"ordinal": 44,
|
||||
"name": "is_featured",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
@@ -464,7 +454,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 46,
|
||||
"ordinal": 45,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -474,7 +464,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 47,
|
||||
"ordinal": 46,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
@@ -511,7 +501,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -538,5 +527,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "57fa22e6cd4e82b801605c96eac21babd78df3df361b3be566993b78a46efef2"
|
||||
"hash": "ad217c26087b2ebed6f2c9f79b16ead63727107dfce3366f3c1d12bc9d565c2c"
|
||||
}
|
||||
-12
@@ -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 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"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n m.id as `id: u64`,\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.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\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 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.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id > ?\nORDER BY m.id ASC\nLIMIT ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id: u64",
|
||||
"type_info": {
|
||||
"type": "LongLong",
|
||||
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
|
||||
"char_set": 63,
|
||||
"max_size": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "token: tokens::tokens::media_files::MediaFileToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "public_bucket_directory_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "maybe_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "maybe_creator_username",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "maybe_creator_display_name",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "maybe_creator_gravatar_hash",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "maybe_file_cover_image_public_bucket_hash",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "maybe_file_cover_image_public_bucket_prefix",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "maybe_file_cover_image_public_bucket_extension",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "creator_set_visibility: enums::common::visibility::Visibility",
|
||||
"type_info": {
|
||||
"type": "String",
|
||||
"flags": "NOT_NULL | BINARY | ENUM",
|
||||
"char_set": 224,
|
||||
"max_size": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "maybe_title",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "created_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "updated_at",
|
||||
"type_info": {
|
||||
"type": "Timestamp",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
|
||||
"char_set": 63,
|
||||
"max_size": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d8f4ce4e1026c71a7be8fd87f00f6f8858c401701da1ca3316eed1ed971d4bb2"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN has_watermark BOOLEAN NOT NULL DEFAULT FALSE
|
||||
AFTER checksum_sha2;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- 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;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- 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);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_is_in_aws,
|
||||
DROP COLUMN is_in_aws;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
-- 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;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN is_not_in_gcp;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
-- 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);
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_nsfw_status,
|
||||
DROP COLUMN nsfw_status;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN is_batch_generated BOOLEAN NOT NULL DEFAULT FALSE
|
||||
AFTER maybe_origin_filename;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN is_batch_generated;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN maybe_media_subtype VARCHAR(32) DEFAULT NULL
|
||||
AFTER media_class;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP COLUMN maybe_media_subtype;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_maybe_project_type,
|
||||
DROP COLUMN maybe_project_type;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
-- 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,12 +99,6 @@ 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,
|
||||
|
||||
@@ -138,9 +132,6 @@ 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:
|
||||
@@ -166,20 +157,19 @@ 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 video types.
|
||||
-- * 'image' for a variety of image types.
|
||||
-- * 'video' for a variety of video types.
|
||||
-- * 'dimensional' for a variety of 3d 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)
|
||||
media_class VARCHAR(16) NOT NULL DEFAULT "unknown",
|
||||
|
||||
-- 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,
|
||||
-- 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,
|
||||
|
||||
-- The file's mime type.
|
||||
maybe_mime_type VARCHAR(32) DEFAULT NULL,
|
||||
@@ -227,10 +217,6 @@ 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.
|
||||
@@ -259,13 +245,6 @@ 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
|
||||
@@ -351,13 +330,12 @@ 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),
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub mod cover_image_links;
|
||||
pub mod job_details;
|
||||
pub mod media_file_cover_image_details;
|
||||
pub mod media_links;
|
||||
pub mod pagination_cursors;
|
||||
pub mod simple_entity_stats;
|
||||
pub mod simple_generic_json_success;
|
||||
pub mod user_details_light;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
// TODO(bt,2026-07-09): Replace the storyteller-web version of this.
|
||||
|
||||
/// Pagination by cursors
|
||||
/// These types of pagination are for "infinite scrolling", which do not reveal a number of pages.
|
||||
/// This is good so that investors and competitors cannot reveal how many database records we have.
|
||||
/// This is typically used for "discovery" type pages, not user profiles.
|
||||
#[derive(Serialize, Deserialize, ToSchema, Debug)]
|
||||
pub struct PaginationCursors {
|
||||
/// The "next" cursor.
|
||||
/// This is an opaque (typically even encrypted) handle.
|
||||
pub maybe_next: Option<String>,
|
||||
|
||||
/// The "previous" cursor.
|
||||
/// This is an opaque (typically even encrypted) handle.
|
||||
pub maybe_previous: Option<String>,
|
||||
|
||||
/// Details whether we're walking forward or backward.
|
||||
pub cursor_is_reversed: bool,
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
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 tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::common::responses::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::common::responses::media_links::MediaLinks;
|
||||
use crate::common::responses::pagination_cursors::PaginationCursors;
|
||||
use crate::common::responses::user_details_light::UserDetailsLight;
|
||||
|
||||
pub const LIST_SESSION_PROJECT_MEDIA_FILES_URL_PATH: &str = "/v1/media_files/project/list";
|
||||
|
||||
/// Query string parameters for listing the session user's project files.
|
||||
#[derive(Deserialize, IntoParams)]
|
||||
pub struct ListSessionProjectMediaFilesQueryParams {
|
||||
pub sort_ascending: Option<bool>,
|
||||
pub page_size: Option<usize>,
|
||||
pub cursor: Option<String>,
|
||||
pub cursor_is_reversed: Option<bool>,
|
||||
|
||||
/// Optional filter on the specific project document type.
|
||||
///
|
||||
/// Usage:
|
||||
/// - `?filter_project_type=scene_3d`
|
||||
/// - `?filter_project_type=mood_board`
|
||||
/// - `?filter_project_type=workflow`
|
||||
/// - `?filter_project_type=video_timeline`
|
||||
pub filter_project_type: Option<MediaFileProjectType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ListSessionProjectMediaFilesSuccessResponse {
|
||||
pub success: bool,
|
||||
pub results: Vec<ProjectMediaFileInfo>,
|
||||
pub pagination: PaginationCursors,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ProjectMediaFileInfo {
|
||||
/// Primary key identifier
|
||||
pub token: MediaFileToken,
|
||||
|
||||
/// The coarse-grained class of media file.
|
||||
/// Always `project` for this endpoint.
|
||||
pub media_class: MediaFileClass,
|
||||
|
||||
/// The specific kind of project document: 3D scene, mood board, etc.
|
||||
pub project_type: MediaFileProjectType,
|
||||
|
||||
/// Type of media will dictate which fields are populated and what
|
||||
/// the frontend should display (eg. video player vs audio player).
|
||||
/// This is closer in meaning to a "mime type".
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
/// Rich CDN links to the media, including thumbnails, previews, and more.
|
||||
pub media_links: MediaLinks,
|
||||
|
||||
/// Information about the cover image. Many media files do not require a cover image,
|
||||
/// e.g. image files, video files with thumbnails, audio files, etc.
|
||||
/// 3D files require them.
|
||||
pub cover_image: MediaFileCoverImageDetails,
|
||||
|
||||
/// User info
|
||||
pub maybe_creator_user: Option<UserDetailsLight>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
/// The name or title of the media file (optional)
|
||||
pub maybe_title: Option<String>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod delete_media_file;
|
||||
pub mod list_batch_generated_media_files;
|
||||
pub mod list_batch_generated_media_files;
|
||||
pub mod list_session_project_media_files;
|
||||
+1
-1
@@ -2,7 +2,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Response body for the omni-gen video cost estimate endpoint.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct OmniGenVideoCostResponse {
|
||||
pub success: bool,
|
||||
|
||||
|
||||
-5
@@ -76,11 +76,6 @@ 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>,
|
||||
|
||||
@@ -24,6 +24,10 @@ pub enum RouterSplatModel {
|
||||
|
||||
#[serde(rename = "marble_1p1_plus")]
|
||||
Marble1p1Plus,
|
||||
|
||||
/// TripoSplat: single-image to 3D Gaussian splat (via Fal).
|
||||
#[serde(rename = "triposplat")]
|
||||
TripoSplat,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -40,6 +44,7 @@ mod tests {
|
||||
assert_serde_round_trip(RouterSplatModel::Marble1p0Draft, "marble_1p0_draft");
|
||||
assert_serde_round_trip(RouterSplatModel::Marble1p1, "marble_1p1");
|
||||
assert_serde_round_trip(RouterSplatModel::Marble1p1Plus, "marble_1p1_plus");
|
||||
assert_serde_round_trip(RouterSplatModel::TripoSplat, "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -53,6 +58,7 @@ mod tests {
|
||||
(RouterSplatModel::Marble1p0Draft, CommonSplatModel::Marble1p0Draft),
|
||||
(RouterSplatModel::Marble1p1, CommonSplatModel::Marble1p1),
|
||||
(RouterSplatModel::Marble1p1Plus, CommonSplatModel::Marble1p1Plus),
|
||||
(RouterSplatModel::TripoSplat, CommonSplatModel::TripoSplat),
|
||||
];
|
||||
for (router_model, expected_common) in cases {
|
||||
let json = serde_json::to_string(&router_model).unwrap();
|
||||
|
||||
+5
@@ -8,6 +8,8 @@ use crate::generate::generate_splat::providers::artcraft::marble_1p0::build::bui
|
||||
use crate::generate::generate_splat::providers::artcraft::marble_1p0_draft::build::build_artcraft_marble_1p0_draft;
|
||||
use crate::generate::generate_splat::providers::artcraft::marble_1p1::build::build_artcraft_marble_1p1;
|
||||
use crate::generate::generate_splat::providers::artcraft::marble_1p1_plus::build::build_artcraft_marble_1p1_plus;
|
||||
use crate::generate::generate_splat::providers::artcraft::triposplat::build::build_artcraft_triposplat;
|
||||
use crate::generate::generate_splat::providers::fal::triposplat::build::build_fal_triposplat;
|
||||
use crate::generate::generate_splat::providers::worldlabs::marble_1p0::build::build_worldlabs_marble_1p0;
|
||||
use crate::generate::generate_splat::providers::worldlabs::marble_1p0_draft::build::build_worldlabs_marble_1p0_draft;
|
||||
use crate::generate::generate_splat::providers::worldlabs::marble_1p1::build::build_worldlabs_marble_1p1;
|
||||
@@ -79,6 +81,9 @@ impl GenerateSplatRequestBuilder {
|
||||
(RouterProvider::Artcraft, RouterSplatModel::Marble1p0Draft | RouterSplatModel::Marble0p1Mini) => build_artcraft_marble_1p0_draft(self),
|
||||
(RouterProvider::Artcraft, RouterSplatModel::Marble1p1) => build_artcraft_marble_1p1(self),
|
||||
(RouterProvider::Artcraft, RouterSplatModel::Marble1p1Plus) => build_artcraft_marble_1p1_plus(self),
|
||||
(RouterProvider::Artcraft, RouterSplatModel::TripoSplat) => build_artcraft_triposplat(self),
|
||||
// Fal
|
||||
(RouterProvider::Fal, RouterSplatModel::TripoSplat) => build_fal_triposplat(self),
|
||||
// World Labs
|
||||
(RouterProvider::WorldLabs, RouterSplatModel::Marble1p0 | RouterSplatModel::Marble0p1Plus) => build_worldlabs_marble_1p0(self),
|
||||
(RouterProvider::WorldLabs, RouterSplatModel::Marble1p0Draft | RouterSplatModel::Marble0p1Mini) => build_worldlabs_marble_1p0_draft(self),
|
||||
|
||||
+31
@@ -1,3 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -13,9 +16,30 @@ pub struct WorldLabsSplatResponsePayload {
|
||||
pub done: bool,
|
||||
}
|
||||
|
||||
/// Fal's queue/webhook submission receipt for a splat generation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalSplatResponsePayload {
|
||||
/// Fal's ID for the queued request. Present on queue submissions and on
|
||||
/// most webhook submissions.
|
||||
pub request_id: Option<String>,
|
||||
|
||||
/// Gateway request ID (webhook submissions).
|
||||
pub gateway_request_id: Option<String>,
|
||||
|
||||
/// Queue-mode status polling URL.
|
||||
pub maybe_status_url: Option<String>,
|
||||
|
||||
/// Queue-mode response URL.
|
||||
pub maybe_response_url: Option<String>,
|
||||
|
||||
/// The outbound request we sent, for debug logging.
|
||||
pub maybe_outbound_request: Option<Arc<dyn Debug + Send + Sync>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GenerateSplatResponse {
|
||||
Artcraft(ArtcraftSplatResponsePayload),
|
||||
Fal(FalSplatResponsePayload),
|
||||
WorldLabs(WorldLabsSplatResponsePayload),
|
||||
}
|
||||
|
||||
@@ -27,6 +51,13 @@ impl GenerateSplatResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fal_payload(&self) -> Option<FalSplatResponsePayload> {
|
||||
match self {
|
||||
Self::Fal(p) => Some(p.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_worldlabs_payload(&self) -> Option<WorldLabsSplatResponsePayload> {
|
||||
match self {
|
||||
Self::WorldLabs(p) => Some(p.clone()),
|
||||
|
||||
+1
@@ -6,3 +6,4 @@ pub mod marble_1p0;
|
||||
pub mod marble_1p0_draft;
|
||||
pub mod marble_1p1;
|
||||
pub mod marble_1p1_plus;
|
||||
pub mod triposplat;
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
use enums::common::generation::common_splat_model::CommonSplatModel as CommonSplatModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_splat::generate_splat_request_builder::GenerateSplatRequestBuilder;
|
||||
use crate::generate::generate_splat::providers::artcraft::build_common::build_artcraft_omni_splat_request;
|
||||
use crate::generate::generate_splat::providers::artcraft::triposplat::request::ArtcraftTripoSplatRequestState;
|
||||
use crate::generate::generate_splat::splat_generation_draft_or_request::SplatGenerationDraftOrRequest;
|
||||
use crate::generate::generate_splat::splat_generation_request::SplatGenerationRequest;
|
||||
|
||||
pub fn build_artcraft_triposplat(builder: GenerateSplatRequestBuilder) -> Result<SplatGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let request = build_artcraft_omni_splat_request(builder, CommonSplatModelEnum::TripoSplat)?;
|
||||
let state = ArtcraftTripoSplatRequestState { request };
|
||||
Ok(SplatGenerationDraftOrRequest::Request(SplatGenerationRequest::ArtcraftTripoSplat(state)))
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
use crate::generate::generate_splat::providers::artcraft::cost_common::artcraft_splat_cost_estimate;
|
||||
use crate::generate::generate_splat::providers::artcraft::triposplat::request::ArtcraftTripoSplatRequestState;
|
||||
use crate::generate::generate_splat::splat_generation_cost_estimate::SplatGenerationCostEstimate;
|
||||
|
||||
const FLAT_COST_IN_USD_CENTS: u64 = 7;
|
||||
|
||||
/// TripoSplat via Artcraft is flat priced: it takes a single input image and
|
||||
/// no option affects the price.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftTripoSplatCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl ArtcraftTripoSplatCostState {
|
||||
pub fn from_request(_request: &ArtcraftTripoSplatRequestState) -> Self {
|
||||
Self { cost_in_usd_cents: FLAT_COST_IN_USD_CENTS }
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> SplatGenerationCostEstimate {
|
||||
artcraft_splat_cost_estimate(self.cost_in_usd_cents)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::api::router_splat_model::RouterSplatModel;
|
||||
use crate::generate::generate_splat::generate_splat_request_builder::GenerateSplatRequestBuilder;
|
||||
|
||||
#[test]
|
||||
fn flat_seven_cents() {
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
model: RouterSplatModel::TripoSplat,
|
||||
provider: RouterProvider::Artcraft,
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_test123".to_string()),
|
||||
])),
|
||||
..Default::default()
|
||||
};
|
||||
let cost = builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present");
|
||||
assert_eq!(cost, 7);
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_splat_cost_and_generate_request::OmniGenSplatCostAndGenerateRequest;
|
||||
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_splat::generate_splat_response::GenerateSplatResponse;
|
||||
use crate::generate::generate_splat::providers::artcraft::request_common::send_artcraft_omni_splat_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArtcraftTripoSplatRequestState {
|
||||
pub request: OmniGenSplatCostAndGenerateRequest,
|
||||
}
|
||||
|
||||
impl ArtcraftTripoSplatRequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateSplatResponse, ArtcraftRouterError> {
|
||||
send_artcraft_omni_splat_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
pub(crate) mod request_common;
|
||||
pub mod triposplat;
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use fal_client::requests::traits::fal_endpoint_trait::FalEndpoint;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_splat::generate_splat_response::{
|
||||
FalSplatResponsePayload, GenerateSplatResponse,
|
||||
};
|
||||
|
||||
/// Send a splat request to fal using the client's dispatch mode (webhook or
|
||||
/// queue). Shared by all Fal splat request states.
|
||||
pub(crate) async fn send_fal_splat_request<R>(
|
||||
request: &R,
|
||||
client: &RouterFalClient,
|
||||
) -> Result<GenerateSplatResponse, ArtcraftRouterError>
|
||||
where
|
||||
R: FalEndpoint + Clone + Debug + Send + Sync + 'static,
|
||||
{
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
|
||||
let payload = if let Some(webhook_url) = &client.webhook_url {
|
||||
let response = request.send_webhook_request(&client.api_key, webhook_url).await?;
|
||||
FalSplatResponsePayload {
|
||||
request_id: response.request_id,
|
||||
gateway_request_id: response.gateway_request_id,
|
||||
maybe_status_url: None,
|
||||
maybe_response_url: None,
|
||||
maybe_outbound_request: Some(outbound),
|
||||
}
|
||||
} else {
|
||||
let response = request.send_queue_request(&client.api_key).await?;
|
||||
FalSplatResponsePayload {
|
||||
request_id: Some(response.request_id),
|
||||
gateway_request_id: None,
|
||||
maybe_status_url: Some(response.status_url),
|
||||
maybe_response_url: Some(response.response_url),
|
||||
maybe_outbound_request: Some(outbound),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(GenerateSplatResponse::Fal(payload))
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
use fal_client::requests::api::splat::image::triposplat_image_to_splat::api::{
|
||||
TripoSplatImageToSplatRequest, TripoSplatOutputFormat,
|
||||
};
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_mesh::providers::reject_unsupported::reject_unsupported_option;
|
||||
use crate::generate::generate_splat::generate_splat_request_builder::GenerateSplatRequestBuilder;
|
||||
use crate::generate::generate_splat::providers::fal::triposplat::request::FalTripoSplatRequestState;
|
||||
use crate::generate::generate_splat::splat_generation_draft_or_request::SplatGenerationDraftOrRequest;
|
||||
use crate::generate::generate_splat::splat_generation_request::SplatGenerationRequest;
|
||||
|
||||
/// TripoSplat takes exactly one input image and nothing else. Prompts,
|
||||
/// videos, panorama and recaption flags are unsupported: rejected under
|
||||
/// `ErrorOut`, dropped otherwise.
|
||||
pub fn build_fal_triposplat(builder: GenerateSplatRequestBuilder) -> Result<SplatGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
let state = build_fal_triposplat_state(builder)?;
|
||||
Ok(SplatGenerationDraftOrRequest::Request(SplatGenerationRequest::FalTripoSplat(state)))
|
||||
}
|
||||
|
||||
pub(crate) fn build_fal_triposplat_state(
|
||||
mut builder: GenerateSplatRequestBuilder,
|
||||
) -> Result<FalTripoSplatRequestState, ArtcraftRouterError> {
|
||||
let strategy = builder.request_mismatch_mitigation_strategy;
|
||||
|
||||
reject_unsupported_option("prompt", builder.prompt.as_ref(), strategy)?;
|
||||
reject_unsupported_option("reference_video", builder.reference_video.as_ref(), strategy)?;
|
||||
if builder.is_panoramic == Some(true) {
|
||||
reject_unsupported_option("is_panoramic", builder.is_panoramic.as_ref(), strategy)?;
|
||||
}
|
||||
reject_unsupported_option("disable_recaption", builder.disable_recaption.as_ref(), strategy)?;
|
||||
|
||||
let image_url = plan_single_image_url(builder.reference_images.take(), strategy)?
|
||||
.ok_or_else(|| {
|
||||
ArtcraftRouterError::InvalidInput(
|
||||
"TripoSplat requires an input image".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let request = TripoSplatImageToSplatRequest {
|
||||
image_url,
|
||||
num_gaussians: None,
|
||||
num_inference_steps: None,
|
||||
guidance_scale: None,
|
||||
// Always request PLY output — it's the format our pipeline stores.
|
||||
output_format: Some(TripoSplatOutputFormat::Ply),
|
||||
seed: None,
|
||||
enable_safety_checker: None,
|
||||
};
|
||||
Ok(FalTripoSplatRequestState { request })
|
||||
}
|
||||
|
||||
/// Resolve the single input image from `reference_images`. More than one
|
||||
/// image rejects under `ErrorOut`; the other strategies keep the first.
|
||||
/// Fal only takes URLs; media file tokens are rejected.
|
||||
fn plan_single_image_url(
|
||||
reference_images: Option<ImageListRef>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<String>, ArtcraftRouterError> {
|
||||
let urls = match reference_images {
|
||||
None => return Ok(None),
|
||||
Some(ImageListRef::Urls(urls)) => urls,
|
||||
Some(ImageListRef::MediaFileTokens(tokens)) if tokens.is_empty() => return Ok(None),
|
||||
Some(ImageListRef::MediaFileTokens(_)) => {
|
||||
return Err(ArtcraftRouterError::Client(ClientError::FalOnlySupportsUrls));
|
||||
}
|
||||
};
|
||||
|
||||
if urls.len() > 1 {
|
||||
if let RequestMismatchMitigationStrategy::ErrorOut = strategy {
|
||||
return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "reference_images",
|
||||
value: format!("Expected at most 1 image, got {}", urls.len()),
|
||||
}));
|
||||
}
|
||||
}
|
||||
Ok(urls.into_iter().next())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::api::router_splat_model::RouterSplatModel;
|
||||
use crate::api::video_ref::VideoRef;
|
||||
|
||||
use super::*;
|
||||
|
||||
const IMAGE_URL: &str = "https://example.com/object.png";
|
||||
|
||||
mod input_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn single_image_builds() {
|
||||
let state = build_fal_triposplat_state(image_builder()).expect("build");
|
||||
assert_eq!(state.request.image_url, IMAGE_URL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_format_is_always_ply() {
|
||||
let state = build_fal_triposplat_state(image_builder()).expect("build");
|
||||
assert_eq!(state.request.output_format, Some(TripoSplatOutputFormat::Ply));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_image_is_rejected() {
|
||||
let result = build_fal_triposplat_state(base_builder());
|
||||
assert!(matches!(result, Err(ArtcraftRouterError::InvalidInput(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn media_tokens_are_rejected() {
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
reference_images: Some(ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_test123".to_string()),
|
||||
])),
|
||||
..base_builder()
|
||||
};
|
||||
assert!(matches!(
|
||||
build_fal_triposplat_state(builder),
|
||||
Err(ArtcraftRouterError::Client(ClientError::FalOnlySupportsUrls))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_images_error_out_under_error_out() {
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
reference_images: Some(ImageListRef::Urls(vec![
|
||||
IMAGE_URL.to_string(),
|
||||
"https://example.com/second.png".to_string(),
|
||||
])),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..base_builder()
|
||||
};
|
||||
assert!(build_fal_triposplat_state(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_images_keep_the_first_under_lenient_strategies() {
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
reference_images: Some(ImageListRef::Urls(vec![
|
||||
IMAGE_URL.to_string(),
|
||||
"https://example.com/second.png".to_string(),
|
||||
])),
|
||||
..base_builder()
|
||||
};
|
||||
let state = build_fal_triposplat_state(builder).expect("build");
|
||||
assert_eq!(state.request.image_url, IMAGE_URL);
|
||||
}
|
||||
}
|
||||
|
||||
mod unsupported_option_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unsupported_options_error_out_under_error_out() {
|
||||
let cases = [
|
||||
GenerateSplatRequestBuilder {
|
||||
prompt: Some("a cozy cabin".to_string()),
|
||||
..strict_image_builder()
|
||||
},
|
||||
GenerateSplatRequestBuilder {
|
||||
reference_video: Some(VideoRef::Url("https://example.com/v.mp4".to_string())),
|
||||
..strict_image_builder()
|
||||
},
|
||||
GenerateSplatRequestBuilder {
|
||||
is_panoramic: Some(true),
|
||||
..strict_image_builder()
|
||||
},
|
||||
GenerateSplatRequestBuilder {
|
||||
disable_recaption: Some(true),
|
||||
..strict_image_builder()
|
||||
},
|
||||
];
|
||||
for builder in cases {
|
||||
assert!(build_fal_triposplat_state(builder).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_options_are_dropped_under_lenient_strategies() {
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
prompt: Some("a cozy cabin".to_string()),
|
||||
is_panoramic: Some(true),
|
||||
disable_recaption: Some(true),
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_triposplat_state(builder).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_panoramic_false_is_not_a_mismatch() {
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
is_panoramic: Some(false),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
};
|
||||
assert!(build_fal_triposplat_state(builder).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_builder() -> GenerateSplatRequestBuilder {
|
||||
GenerateSplatRequestBuilder {
|
||||
model: RouterSplatModel::TripoSplat,
|
||||
provider: RouterProvider::Fal,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn image_builder() -> GenerateSplatRequestBuilder {
|
||||
GenerateSplatRequestBuilder {
|
||||
reference_images: Some(ImageListRef::Urls(vec![IMAGE_URL.to_string()])),
|
||||
..base_builder()
|
||||
}
|
||||
}
|
||||
|
||||
fn strict_image_builder() -> GenerateSplatRequestBuilder {
|
||||
GenerateSplatRequestBuilder {
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..image_builder()
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
|
||||
use crate::generate::generate_splat::providers::fal::triposplat::request::FalTripoSplatRequestState;
|
||||
use crate::generate::generate_splat::splat_generation_cost_estimate::SplatGenerationCostEstimate;
|
||||
|
||||
/// Cost math is owned by fal_client's `FalRequestCostCalculator` — the
|
||||
/// router state just forwards the result so router cost ≡ fal_client cost
|
||||
/// by construction. TripoSplat is flat 5¢.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalTripoSplatCostState {
|
||||
pub cost_in_usd_cents: u64,
|
||||
}
|
||||
|
||||
impl FalTripoSplatCostState {
|
||||
pub fn from_request(request: &FalTripoSplatRequestState) -> Self {
|
||||
Self {
|
||||
cost_in_usd_cents: request.request.calculate_cost_in_cents(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> SplatGenerationCostEstimate {
|
||||
SplatGenerationCostEstimate {
|
||||
cost_in_credits: Some(self.cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(self.cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::api::router_splat_model::RouterSplatModel;
|
||||
use crate::generate::generate_splat::generate_splat_request_builder::GenerateSplatRequestBuilder;
|
||||
|
||||
#[test]
|
||||
fn flat_five_cents() {
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
model: RouterSplatModel::TripoSplat,
|
||||
provider: RouterProvider::Fal,
|
||||
reference_images: Some(ImageListRef::Urls(vec![
|
||||
"https://example.com/object.png".to_string(),
|
||||
])),
|
||||
..Default::default()
|
||||
};
|
||||
let cost = builder.build2()
|
||||
.expect("build should succeed")
|
||||
.estimate_cost()
|
||||
.expect("estimate should succeed")
|
||||
.cost_in_usd_cents
|
||||
.expect("cost should be present");
|
||||
assert_eq!(cost, 5);
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
use fal_client::requests::api::splat::image::triposplat_image_to_splat::api::TripoSplatImageToSplatRequest;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_splat::generate_splat_response::GenerateSplatResponse;
|
||||
use crate::generate::generate_splat::providers::fal::request_common::send_fal_splat_request;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FalTripoSplatRequestState {
|
||||
/// Final materialized request; ready to fire.
|
||||
pub request: TripoSplatImageToSplatRequest,
|
||||
}
|
||||
|
||||
impl FalTripoSplatRequestState {
|
||||
pub async fn send(&self, client: &RouterFalClient) -> Result<GenerateSplatResponse, ArtcraftRouterError> {
|
||||
send_fal_splat_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_data::web::image_urls::ERNEST_SCARED_STUPID_IMAGE_URL;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::api::router_splat_model::RouterSplatModel;
|
||||
use crate::generate::generate_splat::generate_splat_request_builder::GenerateSplatRequestBuilder;
|
||||
use crate::generate::generate_splat::generate_splat_response::GenerateSplatResponse;
|
||||
use crate::generate::generate_splat::splat_generation_draft_or_request::SplatGenerationDraftOrRequest;
|
||||
use crate::test_helpers::get_fal_client;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // requires real API key, incurs cost
|
||||
async fn send_triposplat_image_to_splat() {
|
||||
let client = get_fal_client();
|
||||
|
||||
let builder = GenerateSplatRequestBuilder {
|
||||
model: RouterSplatModel::TripoSplat,
|
||||
provider: RouterProvider::Fal,
|
||||
reference_images: Some(ImageListRef::Urls(vec![
|
||||
ERNEST_SCARED_STUPID_IMAGE_URL.to_string(),
|
||||
])),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
SplatGenerationDraftOrRequest::Request(r) => r,
|
||||
_ => panic!("expected Request variant (Fal splat skips draft)"),
|
||||
};
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
println!("response: {:?}", response);
|
||||
|
||||
let payload = match response {
|
||||
GenerateSplatResponse::Fal(p) => p,
|
||||
other => panic!("expected Fal payload, got {other:?}"),
|
||||
};
|
||||
assert!(payload.request_id.is_some() || payload.gateway_request_id.is_some());
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
}
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
pub mod artcraft;
|
||||
pub mod fal;
|
||||
pub mod worldlabs;
|
||||
|
||||
+18
@@ -10,6 +10,10 @@ use crate::generate::generate_splat::providers::artcraft::marble_1p1::cost::Artc
|
||||
use crate::generate::generate_splat::providers::artcraft::marble_1p1::request::ArtcraftMarble1p1RequestState;
|
||||
use crate::generate::generate_splat::providers::artcraft::marble_1p1_plus::cost::ArtcraftMarble1p1PlusCostState;
|
||||
use crate::generate::generate_splat::providers::artcraft::marble_1p1_plus::request::ArtcraftMarble1p1PlusRequestState;
|
||||
use crate::generate::generate_splat::providers::artcraft::triposplat::cost::ArtcraftTripoSplatCostState;
|
||||
use crate::generate::generate_splat::providers::artcraft::triposplat::request::ArtcraftTripoSplatRequestState;
|
||||
use crate::generate::generate_splat::providers::fal::triposplat::cost::FalTripoSplatCostState;
|
||||
use crate::generate::generate_splat::providers::fal::triposplat::request::FalTripoSplatRequestState;
|
||||
use crate::generate::generate_splat::providers::worldlabs::marble_1p0::cost::WorldLabsMarble1p0CostState;
|
||||
use crate::generate::generate_splat::providers::worldlabs::marble_1p0::request::WorldLabsMarble1p0RequestState;
|
||||
use crate::generate::generate_splat::providers::worldlabs::marble_1p0_draft::cost::WorldLabsMarble1p0DraftModelCostState;
|
||||
@@ -26,6 +30,8 @@ pub enum SplatGenerationRequest {
|
||||
ArtcraftMarble1p0Draft(ArtcraftMarble1p0DraftRequestState),
|
||||
ArtcraftMarble1p1(ArtcraftMarble1p1RequestState),
|
||||
ArtcraftMarble1p1Plus(ArtcraftMarble1p1PlusRequestState),
|
||||
ArtcraftTripoSplat(ArtcraftTripoSplatRequestState),
|
||||
FalTripoSplat(FalTripoSplatRequestState),
|
||||
WorldLabsMarble1p0(WorldLabsMarble1p0RequestState),
|
||||
WorldLabsMarble1p0Draft(WorldLabsMarble1p0DraftModelRequestState),
|
||||
WorldLabsMarble1p1(WorldLabsMarble1p1RequestState),
|
||||
@@ -40,6 +46,8 @@ impl SplatGenerationRequest {
|
||||
Self::ArtcraftMarble1p0Draft(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftMarble1p1(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftMarble1p1Plus(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftTripoSplat(_) => RouterProvider::Artcraft,
|
||||
Self::FalTripoSplat(_) => RouterProvider::Fal,
|
||||
Self::WorldLabsMarble1p0(_) => RouterProvider::WorldLabs,
|
||||
Self::WorldLabsMarble1p0Draft(_) => RouterProvider::WorldLabs,
|
||||
Self::WorldLabsMarble1p1(_) => RouterProvider::WorldLabs,
|
||||
@@ -54,6 +62,8 @@ impl SplatGenerationRequest {
|
||||
SplatGenerationRequest::ArtcraftMarble1p0Draft(request) => Ok(ArtcraftMarble1p0DraftCostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::ArtcraftMarble1p1(request) => Ok(ArtcraftMarble1p1CostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::ArtcraftMarble1p1Plus(request) => Ok(ArtcraftMarble1p1PlusCostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::ArtcraftTripoSplat(request) => Ok(ArtcraftTripoSplatCostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::FalTripoSplat(request) => Ok(FalTripoSplatCostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::WorldLabsMarble1p0(request) => Ok(WorldLabsMarble1p0CostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::WorldLabsMarble1p0Draft(request) => Ok(WorldLabsMarble1p0DraftModelCostState::from_request(request).estimate_cost()),
|
||||
SplatGenerationRequest::WorldLabsMarble1p1(request) => Ok(WorldLabsMarble1p1CostState::from_request(request).estimate_cost()),
|
||||
@@ -81,6 +91,14 @@ impl SplatGenerationRequest {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
SplatGenerationRequest::ArtcraftTripoSplat(request) => {
|
||||
let client_ref = client.get_artcraft_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
SplatGenerationRequest::FalTripoSplat(request) => {
|
||||
let client_ref = client.get_fal_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
SplatGenerationRequest::WorldLabsMarble1p0(request) => {
|
||||
let client_ref = client.get_worldlabs_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
|
||||
+13
-18
@@ -1,7 +1,6 @@
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::providers::artcraft::build_common::{
|
||||
build_artcraft_omni_video_request, SupportedResolutions, UltraWideSupport,
|
||||
@@ -27,17 +26,10 @@ pub fn build_artcraft_grok_imagine_video_1p5(
|
||||
UltraWideSupport::Unsupported,
|
||||
)?;
|
||||
|
||||
// xAI's v1.5 model rejects text-to-video at the server. Bounce here rather
|
||||
// than spending an upstream call to learn the same thing.
|
||||
if request.start_frame_image_media_token.is_none()
|
||||
&& request.reference_image_media_tokens.as_ref().map_or(true, |v| v.is_empty())
|
||||
{
|
||||
return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "image_inputs",
|
||||
value: "text-to-video isn't supported by grok-imagine-video-1.5; supply a start_frame or at least one reference image".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
// NB: xAI's v1.5 model rejects text-to-video at the server, but that is
|
||||
// NOT enforced here — the cost path builds image-less requests to price
|
||||
// them while the user is still composing. The generate endpoints reject
|
||||
// image-less v1.5 requests at the handler layer (validate_when_image_required).
|
||||
let state = ArtcraftGrokImagineVideo1p5RequestState { request };
|
||||
|
||||
Ok(VideoGenerationDraftOrRequest::Request(
|
||||
@@ -129,6 +121,15 @@ mod tests {
|
||||
mod media_token_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn builds_without_any_image_inputs() {
|
||||
// The cost path prices image-less requests while the user is still
|
||||
// composing; the generate endpoints enforce the image requirement.
|
||||
let req = unwrap_request(make_builder(|_| {}));
|
||||
assert!(req.request.start_frame_image_media_token.is_none());
|
||||
assert!(req.request.reference_image_media_tokens.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_frame_token_passed_through() {
|
||||
let token = MediaFileToken::new("mf_start".to_string());
|
||||
@@ -180,12 +181,6 @@ mod tests {
|
||||
fn make_builder(f: impl FnOnce(&mut GenerateVideoRequestBuilder)) -> GenerateVideoRequestBuilder {
|
||||
let mut builder = base_builder();
|
||||
f(&mut builder);
|
||||
// v1.5 requires an input image; tests that don't explicitly exercise the
|
||||
// image-handling paths get a default MediaFileToken start_frame so they
|
||||
// sail past the no-image guard in `build()`.
|
||||
if builder.start_frame.is_none() && builder.reference_images.is_none() {
|
||||
builder.start_frame = Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_default".to_string())));
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
|
||||
+37
-24
@@ -9,7 +9,6 @@ use grok_api_client::api::types::video_types::video_model::VideoModel as GrokVid
|
||||
use grok_api_client::api::types::video_types::video_resolution::VideoResolution as GrokResolution;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
use crate::generate::generate_video::providers::artcraft::grok_imagine_video_1p5::request::ArtcraftGrokImagineVideo1p5RequestState;
|
||||
|
||||
@@ -65,16 +64,10 @@ impl ArtcraftGrokImagineVideo1p5CostState {
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> Result<VideoGenerationCostEstimate, ArtcraftRouterError> {
|
||||
// Defense in depth: `build()` already enforces this, but a state
|
||||
// constructed by hand must not get a quote for an operation xAI will
|
||||
// reject.
|
||||
if self.input_image_count == 0 {
|
||||
return Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "image_inputs",
|
||||
value: "text-to-video isn't supported by grok-imagine-video-1.5; supply a start_frame or at least one reference image".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
// NB: Image-less requests DO get a quote even though xAI's v1.5 rejects
|
||||
// text-to-video — the cost path prices requests the user is still
|
||||
// composing. The image requirement is enforced at send time (request.rs)
|
||||
// and by the generate endpoints.
|
||||
let base = self.base_cost_in_cents_for_batch();
|
||||
// 5% markup, ceil so the user is always charged enough to cover the
|
||||
// upstream grok cost regardless of rounding.
|
||||
@@ -204,24 +197,23 @@ mod tests {
|
||||
assert_eq!(state.estimate_cost().expect("estimate_cost").cost_in_usd_cents.unwrap(), 150);
|
||||
}
|
||||
|
||||
/// v1.5 doesn't support text-to-video. The cost calculator must refuse
|
||||
/// to quote an image-less request even if a state is constructed
|
||||
/// directly.
|
||||
/// v1.5 doesn't support text-to-video at generation time, but the cost
|
||||
/// calculator still quotes an image-less request — the cost UI prices
|
||||
/// requests before the user has attached an image. The requirement is
|
||||
/// enforced at send time and by the generate endpoints.
|
||||
#[test]
|
||||
fn estimate_cost_rejects_text_only_request() {
|
||||
fn estimate_cost_quotes_text_only_request() {
|
||||
let state = ArtcraftGrokImagineVideo1p5CostState {
|
||||
resolution: CommonResolution::SevenTwentyP,
|
||||
duration_seconds: 5,
|
||||
batch_count: 1,
|
||||
input_image_count: 0,
|
||||
};
|
||||
let err = state.estimate_cost().expect_err("text-only should be rejected");
|
||||
match err {
|
||||
ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { field, .. }) => {
|
||||
assert_eq!(field, "image_inputs");
|
||||
}
|
||||
other => panic!("expected Client(ModelDoesNotSupportOption), got {:?}", other),
|
||||
}
|
||||
let base = state.base_cost_in_cents_for_batch();
|
||||
let want = base.saturating_mul(MARKUP_NUMERATOR).div_ceil(MARKUP_DENOMINATOR);
|
||||
let got = state.estimate_cost().expect("estimate_cost").cost_in_usd_cents.unwrap();
|
||||
assert_eq!(got, want);
|
||||
assert!(got > 0);
|
||||
}
|
||||
|
||||
// (resolution, duration, image_count, batch) — every case has at least
|
||||
@@ -326,6 +318,28 @@ mod tests {
|
||||
.cost_in_usd_cents.unwrap();
|
||||
assert!(p1080 > p720, "1080p ({p1080}) should cost more than 720p ({p720})");
|
||||
}
|
||||
|
||||
/// Regression: an image-less v1.5 request must get a quote through the
|
||||
/// full public builder path (build2 → estimate_cost). The cost UI polls
|
||||
/// for a price before the user has attached an image; the image
|
||||
/// requirement is enforced at send time and by the generate endpoints,
|
||||
/// NOT here.
|
||||
#[test]
|
||||
fn image_less_request_gets_a_quote_through_the_builder() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::GrokImagineVideo1p5,
|
||||
provider: RouterProvider::Artcraft,
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
..Default::default()
|
||||
};
|
||||
let cost = builder.build2()
|
||||
.expect("build2 should succeed without an image")
|
||||
.estimate_cost()
|
||||
.expect("estimate_cost should succeed without an image");
|
||||
assert!(cost.cost_in_usd_cents.unwrap() > 0);
|
||||
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────
|
||||
@@ -390,8 +404,7 @@ mod tests {
|
||||
resolution,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
// v1.5 requires an input image — the no-image guard in build()
|
||||
// rejects T2V.
|
||||
// These cases price the image-to-video tier (one input image).
|
||||
start_frame: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_default".to_string()))),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
+57
-2
@@ -13,8 +13,11 @@ pub struct ArtcraftGrokImagineVideo1p5RequestState {
|
||||
|
||||
impl ArtcraftGrokImagineVideo1p5RequestState {
|
||||
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
// Defense in depth: `build()` already enforces this. Bouncing here costs
|
||||
// nothing and avoids an upstream call we know will fail.
|
||||
// xAI's v1.5 model rejects text-to-video at the server. This is the
|
||||
// GENERATION-time guard — `build()` and `estimate_cost()` deliberately
|
||||
// allow image-less states so the cost path can quote a request the user
|
||||
// is still composing. Bouncing here costs nothing and avoids an upstream
|
||||
// call we know will fail.
|
||||
if self.request.start_frame_image_media_token.is_none()
|
||||
&& self.request.reference_image_media_tokens.as_ref().map_or(true, |v| v.is_empty())
|
||||
{
|
||||
@@ -27,3 +30,55 @@ impl ArtcraftGrokImagineVideo1p5RequestState {
|
||||
send_artcraft_omni_video_request(&self.request, client).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
|
||||
use artcraft_client::utils::api_host::ApiHost;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Generation must never reach xAI with an image-less v1.5 request. The
|
||||
/// build/cost layers allow that state (the cost path quotes it), so the
|
||||
/// send-time guard is the router's last line of defense.
|
||||
#[tokio::test]
|
||||
async fn send_rejects_image_less_request_before_any_network_call() {
|
||||
let state = ArtcraftGrokImagineVideo1p5RequestState {
|
||||
request: OmniGenVideoCostAndGenerateRequest {
|
||||
idempotency_token: Some("a1b2c3d4-e5f6-7890-abcd-ef1234567890".to_string()),
|
||||
model: Some(CommonVideoModelEnum::GrokImagineVideo1p5),
|
||||
prompt: Some("a corgi running through a field".to_string()),
|
||||
negative_prompt: None,
|
||||
start_frame_image_media_token: None,
|
||||
end_frame_image_media_token: None,
|
||||
reference_image_media_tokens: None,
|
||||
reference_video_media_tokens: None,
|
||||
reference_audio_media_tokens: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
generate_audio: None,
|
||||
},
|
||||
};
|
||||
|
||||
// The guard fires before the client is used, so an unroutable localhost
|
||||
// client with empty credentials proves no network call happens.
|
||||
let client = RouterArtcraftClient::new(
|
||||
ApiHost::Localhost { port: 1 },
|
||||
StorytellerCredentialSet::empty(),
|
||||
);
|
||||
|
||||
let err = state.send(&client).await.expect_err("image-less send should be rejected");
|
||||
match err {
|
||||
ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { field, .. }) => {
|
||||
assert_eq!(field, "image_inputs");
|
||||
}
|
||||
other => panic!("expected Client(ModelDoesNotSupportOption), got {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod audio;
|
||||
pub mod image;
|
||||
pub mod mesh;
|
||||
pub mod splat;
|
||||
pub mod video;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod triposplat_image_to_splat;
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
use crate::error::fal_error_plus::FalErrorPlus;
|
||||
use crate::requests::api::splat::image::triposplat_image_to_splat::raw_request::{
|
||||
TripoSplatImageToSplatInput, TripoSplatImageToSplatOutput,
|
||||
};
|
||||
use crate::requests::traits::fal_endpoint_trait::FalEndpoint;
|
||||
|
||||
/// TripoSplat image-to-Gaussian-splat: reconstructs a 3D Gaussian splat from
|
||||
/// a single input image.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TripoSplatImageToSplatRequest {
|
||||
/// URL of the input image (required).
|
||||
pub image_url: String,
|
||||
|
||||
/// Number of Gaussians to generate. Range 32768-262144;
|
||||
/// fal's default is 262144 when `None`.
|
||||
pub num_gaussians: Option<u32>,
|
||||
|
||||
/// Number of inference steps. Range 1-50; fal's default is 20 when `None`.
|
||||
pub num_inference_steps: Option<u32>,
|
||||
|
||||
/// Guidance scale. Range 0-10; fal's default is 3 when `None`.
|
||||
pub guidance_scale: Option<f32>,
|
||||
|
||||
/// Output file format. fal's default is `Ply` when `None`.
|
||||
pub output_format: Option<TripoSplatOutputFormat>,
|
||||
|
||||
/// Seed for reproducibility. fal picks a random seed when `None`.
|
||||
pub seed: Option<i64>,
|
||||
|
||||
/// fal's default is true when `None`.
|
||||
pub enable_safety_checker: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TripoSplatOutputFormat {
|
||||
Ply,
|
||||
Splat,
|
||||
}
|
||||
|
||||
impl TripoSplatOutputFormat {
|
||||
pub(crate) fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Ply => "ply",
|
||||
Self::Splat => "splat",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FalEndpoint for TripoSplatImageToSplatRequest {
|
||||
const ENDPOINT: &str = "tripo3d/triposplat";
|
||||
|
||||
type RawRequest = TripoSplatImageToSplatInput;
|
||||
type RawResponse = TripoSplatImageToSplatOutput;
|
||||
|
||||
fn to_raw_request(&self) -> Result<Self::RawRequest, FalErrorPlus> {
|
||||
Ok(Self::RawRequest {
|
||||
image_url: self.image_url.clone(),
|
||||
num_gaussians: self.num_gaussians,
|
||||
num_inference_steps: self.num_inference_steps,
|
||||
guidance_scale: self.guidance_scale,
|
||||
output_format: self.output_format.map(|f| f.to_str().to_string()),
|
||||
seed: self.seed,
|
||||
enable_safety_checker: self.enable_safety_checker,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::creds::fal_api_key::FalApiKey;
|
||||
use crate::requests::traits::fal_endpoint_trait::FalEndpoint;
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
use test_data::web::image_urls::ERNEST_SCARED_STUPID_IMAGE_URL;
|
||||
|
||||
mod serialization_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn minimal_request_serializes_image_url_only() {
|
||||
let raw = base_request().to_raw_request().expect("to_raw_request");
|
||||
let json = serde_json::to_value(&raw).unwrap();
|
||||
let obj = json.as_object().unwrap();
|
||||
assert_eq!(obj.len(), 1, "only image_url should serialize: {json}");
|
||||
assert_eq!(json["image_url"], "https://example.com/input.png");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_format_wire_values() {
|
||||
assert_eq!(TripoSplatOutputFormat::Ply.to_str(), "ply");
|
||||
assert_eq!(TripoSplatOutputFormat::Splat.to_str(), "splat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_request_serializes_all_fields() {
|
||||
let request = TripoSplatImageToSplatRequest {
|
||||
num_gaussians: Some(131_072),
|
||||
num_inference_steps: Some(30),
|
||||
guidance_scale: Some(5.0),
|
||||
output_format: Some(TripoSplatOutputFormat::Ply),
|
||||
seed: Some(42),
|
||||
enable_safety_checker: Some(false),
|
||||
..base_request()
|
||||
};
|
||||
let raw = request.to_raw_request().expect("to_raw_request");
|
||||
let json = serde_json::to_value(&raw).unwrap();
|
||||
assert_eq!(json["num_gaussians"], 131_072);
|
||||
assert_eq!(json["num_inference_steps"], 30);
|
||||
assert_eq!(json["guidance_scale"], 5.0);
|
||||
assert_eq!(json["output_format"], "ply");
|
||||
assert_eq!(json["seed"], 42);
|
||||
assert_eq!(json["enable_safety_checker"], false);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn base_request() -> TripoSplatImageToSplatRequest {
|
||||
TripoSplatImageToSplatRequest {
|
||||
image_url: "https://example.com/input.png".to_string(),
|
||||
num_gaussians: None,
|
||||
num_inference_steps: None,
|
||||
guidance_scale: None,
|
||||
output_format: None,
|
||||
seed: None,
|
||||
enable_safety_checker: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_image_to_splat_webhook() -> AnyhowResult<()> {
|
||||
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
|
||||
let api_key = FalApiKey::from_str(&secret);
|
||||
let request = TripoSplatImageToSplatRequest {
|
||||
image_url: ERNEST_SCARED_STUPID_IMAGE_URL.to_string(),
|
||||
..base_request()
|
||||
};
|
||||
let result = request.send_webhook_request(&api_key, "https://example.com/webhook").await?;
|
||||
println!("Webhook result: {:?}", result);
|
||||
assert!(result.request_id.is_some() || result.gateway_request_id.is_some());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_image_to_splat_queue() -> AnyhowResult<()> {
|
||||
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
|
||||
let api_key = FalApiKey::from_str(&secret);
|
||||
let request = TripoSplatImageToSplatRequest {
|
||||
image_url: ERNEST_SCARED_STUPID_IMAGE_URL.to_string(),
|
||||
..base_request()
|
||||
};
|
||||
let result = request.send_queue_request(&api_key).await?;
|
||||
println!("Queue result — request_id: {}", result.request_id);
|
||||
assert!(!result.request_id.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
use crate::requests::api::splat::image::triposplat_image_to_splat::api::TripoSplatImageToSplatRequest;
|
||||
use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents};
|
||||
|
||||
impl FalRequestCostCalculator for TripoSplatImageToSplatRequest {
|
||||
fn calculate_cost_in_cents(&self) -> UsdCents {
|
||||
// Pricing (see https://fal.ai/models/tripo3d/triposplat):
|
||||
// "Price: $0.05 per generations"
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::requests::api::splat::image::triposplat_image_to_splat::api::TripoSplatOutputFormat;
|
||||
|
||||
fn base_request() -> TripoSplatImageToSplatRequest {
|
||||
TripoSplatImageToSplatRequest {
|
||||
image_url: "https://example.com/input.png".to_string(),
|
||||
num_gaussians: None,
|
||||
num_inference_steps: None,
|
||||
guidance_scale: None,
|
||||
output_format: None,
|
||||
seed: None,
|
||||
enable_safety_checker: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flat_five_cents() {
|
||||
assert_eq!(base_request().calculate_cost_in_cents(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_do_not_change_cost() {
|
||||
let request = TripoSplatImageToSplatRequest {
|
||||
num_gaussians: Some(32_768),
|
||||
num_inference_steps: Some(50),
|
||||
guidance_scale: Some(10.0),
|
||||
output_format: Some(TripoSplatOutputFormat::Splat),
|
||||
seed: Some(7),
|
||||
enable_safety_checker: Some(false),
|
||||
..base_request()
|
||||
};
|
||||
assert_eq!(request.calculate_cost_in_cents(), 5);
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pub mod api;
|
||||
pub mod cost;
|
||||
pub mod raw_request;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Over-the-wire input shape for `tripo3d/triposplat` (image-to-Gaussian-splat).
|
||||
/// fal's schema: <https://fal.ai/models/tripo3d/triposplat/api>
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct TripoSplatImageToSplatInput {
|
||||
/// URL of the input image.
|
||||
pub image_url: String,
|
||||
|
||||
/// Number of Gaussians to generate. Range 32768-262144.
|
||||
/// fal default: 262144.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub num_gaussians: Option<u32>,
|
||||
|
||||
/// Number of inference steps. Range 1-50. fal default: 20.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub num_inference_steps: Option<u32>,
|
||||
|
||||
/// Guidance scale. Range 0-10. fal default: 3.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub guidance_scale: Option<f32>,
|
||||
|
||||
/// Options: "ply", "splat". fal default: "ply".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub output_format: Option<String>,
|
||||
|
||||
/// Seed for reproducibility. fal default: random.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub seed: Option<i64>,
|
||||
|
||||
/// fal default: true.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enable_safety_checker: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TripoSplatImageToSplatOutput {}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod image;
|
||||
@@ -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::Video,
|
||||
media_file_type: MediaFileType::Mp4,
|
||||
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::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"),
|
||||
(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"),
|
||||
// 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::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"),
|
||||
(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"),
|
||||
];
|
||||
|
||||
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::Video;
|
||||
media_file_type = MediaFileType::Wav;
|
||||
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::Video;
|
||||
media_file_type = MediaFileType::Mp4;
|
||||
maybe_mime_type = mimetype;
|
||||
maybe_public_bucket_prefix = Some("dev_upload_");
|
||||
maybe_public_bucket_extension = Some(".mp4");
|
||||
|
||||
-1
@@ -105,7 +105,6 @@ 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,
|
||||
|
||||
|
||||
-1
@@ -264,7 +264,6 @@ 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
|
||||
|
||||
|
||||
-1
@@ -294,7 +294,6 @@ 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
|
||||
|
||||
|
||||
-1
@@ -254,7 +254,6 @@ 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
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ 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;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
@@ -75,3 +75,87 @@ 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,3 +63,42 @@ 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,3 +49,52 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,3 +37,52 @@ 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ pub (crate) static CUSTOM_INFER: LazyLock<Infer> = LazyLock::new(|| {
|
||||
fn add_custom_types(infer: &mut Infer) {
|
||||
infer.add("model/gltf-binary", "glb", gltf_matcher);
|
||||
infer.add("model/fbx", "fbx", fbx_matcher);
|
||||
infer.add("model/ply", "ply", ply_matcher);
|
||||
}
|
||||
|
||||
fn gltf_matcher(buf: &[u8]) -> bool {
|
||||
@@ -18,6 +19,14 @@ fn gltf_matcher(buf: &[u8]) -> bool {
|
||||
buf.len() >= 4 && buf[0] == 103 && buf[1] == 108 && buf[2] == 84 && buf[3] == 70
|
||||
}
|
||||
|
||||
fn ply_matcher(buf: &[u8]) -> bool {
|
||||
// PLY files (ASCII and binary alike) start with the line "ply".
|
||||
// https://paulbourke.net/dataformats/ply/
|
||||
buf.len() >= 4
|
||||
&& &buf[..3] == b"ply"
|
||||
&& (buf[3] == b'\n' || buf[3] == b'\r')
|
||||
}
|
||||
|
||||
fn fbx_matcher(buf: &[u8]) -> bool {
|
||||
// Binary FBX magic: "Kaydara FBX Binary " (two trailing spaces).
|
||||
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
|
||||
@@ -47,6 +56,19 @@ mod tests {
|
||||
assert_eq!(maybe_type.mime_type(), "model/fbx");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ply_magic() {
|
||||
let bytes = b"ply\nformat binary_little_endian 1.0\n".to_vec();
|
||||
let maybe_type = CUSTOM_INFER.get(&bytes).unwrap();
|
||||
assert_eq!(maybe_type.mime_type(), "model/ply");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ply_prefix_without_newline_is_not_detected() {
|
||||
let bytes = b"plywood texture".to_vec();
|
||||
assert!(CUSTOM_INFER.get(&bytes).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ascii_fbx_is_not_detected() {
|
||||
// ASCII FBX files have no magic bytes.
|
||||
|
||||
@@ -17,6 +17,7 @@ pub enum FileExtension {
|
||||
Obj,
|
||||
Ogg,
|
||||
Opus,
|
||||
Ply,
|
||||
Png,
|
||||
Wav,
|
||||
Webm,
|
||||
@@ -37,6 +38,7 @@ impl FileExtension {
|
||||
"audio/mpeg" => Some(FileExtension::Mp3),
|
||||
"video/mp4" => Some(FileExtension::Mp4),
|
||||
"model/obj" => Some(FileExtension::Obj),
|
||||
"model/ply" => Some(FileExtension::Ply),
|
||||
"audio/ogg" => Some(FileExtension::Ogg),
|
||||
"audio/opus" => Some(FileExtension::Opus),
|
||||
"image/png" => Some(FileExtension::Png),
|
||||
@@ -69,6 +71,7 @@ impl FileExtension {
|
||||
FileExtension::Obj => (".obj", "obj"),
|
||||
FileExtension::Ogg => (".ogg", "ogg"),
|
||||
FileExtension::Opus => (".opus", "opus"),
|
||||
FileExtension::Ply => (".ply", "ply"),
|
||||
FileExtension::Png => (".png", "png"),
|
||||
FileExtension::Wav => (".wav", "wav"),
|
||||
FileExtension::Webm => (".webm", "webm"),
|
||||
|
||||
@@ -6,7 +6,6 @@ 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;
|
||||
@@ -25,7 +24,6 @@ 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>,
|
||||
|
||||
-4
@@ -117,8 +117,6 @@ 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;
|
||||
@@ -172,7 +170,6 @@ pub async fn insert_media_file_generic(
|
||||
maybe_origin_model_token = ?,
|
||||
maybe_origin_filename = ?,
|
||||
|
||||
is_batch_generated = ?,
|
||||
maybe_batch_token = ?,
|
||||
|
||||
maybe_mime_type = ?,
|
||||
@@ -232,7 +229,6 @@ 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,
|
||||
|
||||
+5
-1
@@ -82,6 +82,10 @@ 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#"
|
||||
@@ -129,7 +133,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Video.to_str(),
|
||||
MediaFileType::Video.to_str(), // TODO(bt,2024-04-30): This needs to become "mp4" after a frontend migration
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
product.to_str(),
|
||||
|
||||
+5
-1
@@ -69,6 +69,10 @@ 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#"
|
||||
@@ -116,7 +120,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Audio.to_str(),
|
||||
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
+5
-1
@@ -68,6 +68,10 @@ 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#"
|
||||
@@ -108,7 +112,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Video.to_str(),
|
||||
MediaFileType::Video.to_str(),
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
+7
@@ -8,6 +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_type::MediaFileType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::visibility::Visibility;
|
||||
@@ -33,6 +34,9 @@ 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>,
|
||||
@@ -126,6 +130,7 @@ SET
|
||||
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
maybe_project_type = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
@@ -175,6 +180,8 @@ 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(),
|
||||
|
||||
|
||||
+4
-2
@@ -66,7 +66,9 @@ 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;
|
||||
const MEDIA_TYPE : MediaFileType = MediaFileType::Video;
|
||||
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!(
|
||||
@@ -109,7 +111,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,
|
||||
|
||||
|
||||
+5
-1
@@ -69,6 +69,10 @@ 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#"
|
||||
@@ -116,7 +120,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Audio.to_str(),
|
||||
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
+5
-1
@@ -75,6 +75,10 @@ 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#"
|
||||
@@ -121,7 +125,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Video.to_str(),
|
||||
MediaFileType::Video.to_str(), // TODO(bt,2024-04-30): This needs to become "mp4" after a frontend migration
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
PRODUCT.to_str(),
|
||||
|
||||
+5
-1
@@ -69,6 +69,10 @@ 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#"
|
||||
@@ -116,7 +120,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Audio.to_str(),
|
||||
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
+5
-1
@@ -80,6 +80,10 @@ 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#"
|
||||
@@ -122,7 +126,7 @@ SET
|
||||
result_token.as_str(),
|
||||
|
||||
MediaFileClass::Audio.to_str(),
|
||||
MediaFileType::Audio.to_str(), // TODO(bt,2024-04-30): This needs to become "wav" after a frontend migration
|
||||
media_type.to_str(),
|
||||
|
||||
ORIGIN_CATEGORY.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
+4
-2
@@ -70,7 +70,9 @@ 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;
|
||||
const MEDIA_TYPE : MediaFileType = MediaFileType::Audio;
|
||||
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!(
|
||||
@@ -118,7 +120,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,
|
||||
|
||||
|
||||
+15
@@ -1,5 +1,7 @@
|
||||
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;
|
||||
|
||||
@@ -14,6 +16,13 @@ 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>,
|
||||
@@ -37,6 +46,9 @@ 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 = ?,
|
||||
@@ -48,6 +60,9 @@ 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,
|
||||
|
||||
-6
@@ -12,7 +12,6 @@ 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;
|
||||
@@ -38,7 +37,6 @@ 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.
|
||||
|
||||
@@ -125,7 +123,6 @@ 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.
|
||||
|
||||
@@ -234,7 +231,6 @@ 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,
|
||||
@@ -294,7 +290,6 @@ 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,
|
||||
@@ -390,7 +385,6 @@ 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,7 +8,6 @@ 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;
|
||||
@@ -39,8 +38,6 @@ 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.)
|
||||
@@ -134,8 +131,6 @@ 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>,
|
||||
@@ -263,7 +258,6 @@ 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,
|
||||
@@ -327,8 +321,6 @@ 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`,
|
||||
@@ -450,8 +442,6 @@ 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`,
|
||||
|
||||
-20
@@ -14,7 +14,6 @@ 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;
|
||||
@@ -42,7 +41,6 @@ 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,
|
||||
@@ -59,11 +57,8 @@ 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,
|
||||
@@ -159,7 +154,6 @@ 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),
|
||||
@@ -170,10 +164,8 @@ 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),
|
||||
@@ -222,7 +214,6 @@ SELECT
|
||||
m.maybe_origin_model_token,
|
||||
m.maybe_origin_filename,
|
||||
|
||||
m.is_batch_generated,
|
||||
m.maybe_batch_token,
|
||||
|
||||
m.is_user_upload,
|
||||
@@ -238,11 +229,8 @@ 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,
|
||||
@@ -372,7 +360,6 @@ struct RawRecord {
|
||||
|
||||
pub maybe_origin_filename: Option<String>,
|
||||
|
||||
pub is_batch_generated: i8,
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
pub is_user_upload: i8,
|
||||
@@ -389,11 +376,8 @@ 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,
|
||||
@@ -459,7 +443,6 @@ 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")?,
|
||||
@@ -475,11 +458,8 @@ 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")?,
|
||||
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::{Executor, MySql};
|
||||
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
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 tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
pub struct ProjectMediaFileListPage {
|
||||
pub records: Vec<ProjectMediaFileListItem>,
|
||||
|
||||
/// ID of the first record (for building the "previous" cursor).
|
||||
pub first_id: Option<u64>,
|
||||
|
||||
/// ID of the last record (for building the "next" cursor).
|
||||
pub last_id: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct ProjectMediaFileListItem {
|
||||
pub id: u64,
|
||||
pub token: MediaFileToken,
|
||||
|
||||
pub media_class: MediaFileClass,
|
||||
pub project_type: MediaFileProjectType,
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub maybe_creator_username: Option<String>,
|
||||
pub maybe_creator_display_name: Option<String>,
|
||||
pub maybe_creator_gravatar_hash: Option<String>,
|
||||
|
||||
pub maybe_file_cover_image_public_bucket_hash: Option<String>,
|
||||
pub maybe_file_cover_image_public_bucket_prefix: Option<String>,
|
||||
pub maybe_file_cover_image_public_bucket_extension: Option<String>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub maybe_title: Option<String>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub struct ListSessionProjectMediaFilesArgs<'a, 'c, E>
|
||||
where
|
||||
E: 'a + Executor<'c, Database = MySql>,
|
||||
{
|
||||
pub user_token: &'a UserToken,
|
||||
|
||||
/// Optional filter on the specific project document type.
|
||||
pub maybe_filter_project_type: Option<MediaFileProjectType>,
|
||||
|
||||
pub limit: usize,
|
||||
|
||||
/// Keyset cursor: a `media_files.id` from a previous page.
|
||||
pub maybe_cursor_id: Option<u64>,
|
||||
pub cursor_is_reversed: bool,
|
||||
pub sort_ascending: bool,
|
||||
|
||||
pub mysql_executor: E,
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
|
||||
/// List the session user's project files (`media_class = 'project'`), newest first
|
||||
/// by default, with keyset pagination on `id`.
|
||||
///
|
||||
/// Records with a NULL `maybe_project_type` are excluded so `project_type` is
|
||||
/// always present in the results.
|
||||
pub async fn list_session_project_media_files<'a, 'c: 'a, E>(
|
||||
args: ListSessionProjectMediaFilesArgs<'a, 'c, E>,
|
||||
) -> Result<ProjectMediaFileListPage, sqlx::Error>
|
||||
where
|
||||
E: 'a + Executor<'c, Database = MySql>,
|
||||
{
|
||||
// A reversed cursor walks the opposite direction of the sort, so the effective
|
||||
// scan direction is the XOR of the two flags (same scheme as `list_media_files`).
|
||||
let ascending = args.sort_ascending != args.cursor_is_reversed;
|
||||
|
||||
let limit = args.limit as u64;
|
||||
|
||||
let records = if ascending {
|
||||
// NB: IDs start at 1, so a zero sentinel means "no cursor".
|
||||
let cursor_id = args.maybe_cursor_id.unwrap_or(0);
|
||||
select_page_ascending(
|
||||
args.user_token,
|
||||
args.maybe_filter_project_type,
|
||||
cursor_id,
|
||||
limit,
|
||||
args.mysql_executor,
|
||||
).await?
|
||||
} else {
|
||||
let cursor_id = args.maybe_cursor_id.unwrap_or(u64::MAX);
|
||||
select_page_descending(
|
||||
args.user_token,
|
||||
args.maybe_filter_project_type,
|
||||
cursor_id,
|
||||
limit,
|
||||
args.mysql_executor,
|
||||
).await?
|
||||
};
|
||||
|
||||
let first_id = records.first().map(|record| record.id);
|
||||
let last_id = records.last().map(|record| record.id);
|
||||
|
||||
Ok(ProjectMediaFileListPage {
|
||||
records,
|
||||
first_id,
|
||||
last_id,
|
||||
})
|
||||
}
|
||||
|
||||
// NB: The four queries below are intentionally duplicated so that sqlx can check
|
||||
// them at compile time (no QueryBuilder). Keep the SELECT lists and predicates
|
||||
// in sync; they differ only in the `maybe_project_type = ?` filter, the cursor
|
||||
// comparison direction, and the sort order.
|
||||
|
||||
async fn select_page_ascending<'a, 'c: 'a, E>(
|
||||
user_token: &'a UserToken,
|
||||
maybe_filter_project_type: Option<MediaFileProjectType>,
|
||||
cursor_id: u64,
|
||||
limit: u64,
|
||||
mysql_executor: E,
|
||||
) -> Result<Vec<ProjectMediaFileListItem>, sqlx::Error>
|
||||
where
|
||||
E: 'a + Executor<'c, Database = MySql>,
|
||||
{
|
||||
match maybe_filter_project_type {
|
||||
Some(project_type) => {
|
||||
sqlx::query_as!(
|
||||
ProjectMediaFileListItem,
|
||||
r#"
|
||||
SELECT
|
||||
m.id as `id: u64`,
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
|
||||
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
users.username as maybe_creator_username,
|
||||
users.display_name as maybe_creator_display_name,
|
||||
users.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.maybe_title,
|
||||
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
|
||||
LEFT OUTER JOIN users
|
||||
ON m.maybe_creator_user_token = users.token
|
||||
LEFT OUTER JOIN media_files as media_file_cover_image
|
||||
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
|
||||
WHERE
|
||||
m.maybe_creator_user_token = ?
|
||||
AND m.media_class = 'project'
|
||||
AND m.maybe_project_type IS NOT NULL
|
||||
AND m.maybe_project_type = ?
|
||||
AND m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
AND NOT m.is_intermediate_system_file
|
||||
AND m.id > ?
|
||||
ORDER BY m.id ASC
|
||||
LIMIT ?
|
||||
"#,
|
||||
user_token,
|
||||
project_type.to_str(),
|
||||
cursor_id,
|
||||
limit,
|
||||
)
|
||||
.fetch_all(mysql_executor)
|
||||
.await
|
||||
}
|
||||
None => {
|
||||
sqlx::query_as!(
|
||||
ProjectMediaFileListItem,
|
||||
r#"
|
||||
SELECT
|
||||
m.id as `id: u64`,
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
|
||||
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
users.username as maybe_creator_username,
|
||||
users.display_name as maybe_creator_display_name,
|
||||
users.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.maybe_title,
|
||||
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
|
||||
LEFT OUTER JOIN users
|
||||
ON m.maybe_creator_user_token = users.token
|
||||
LEFT OUTER JOIN media_files as media_file_cover_image
|
||||
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
|
||||
WHERE
|
||||
m.maybe_creator_user_token = ?
|
||||
AND m.media_class = 'project'
|
||||
AND m.maybe_project_type IS NOT NULL
|
||||
AND m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
AND NOT m.is_intermediate_system_file
|
||||
AND m.id > ?
|
||||
ORDER BY m.id ASC
|
||||
LIMIT ?
|
||||
"#,
|
||||
user_token,
|
||||
cursor_id,
|
||||
limit,
|
||||
)
|
||||
.fetch_all(mysql_executor)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn select_page_descending<'a, 'c: 'a, E>(
|
||||
user_token: &'a UserToken,
|
||||
maybe_filter_project_type: Option<MediaFileProjectType>,
|
||||
cursor_id: u64,
|
||||
limit: u64,
|
||||
mysql_executor: E,
|
||||
) -> Result<Vec<ProjectMediaFileListItem>, sqlx::Error>
|
||||
where
|
||||
E: 'a + Executor<'c, Database = MySql>,
|
||||
{
|
||||
match maybe_filter_project_type {
|
||||
Some(project_type) => {
|
||||
sqlx::query_as!(
|
||||
ProjectMediaFileListItem,
|
||||
r#"
|
||||
SELECT
|
||||
m.id as `id: u64`,
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
|
||||
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
users.username as maybe_creator_username,
|
||||
users.display_name as maybe_creator_display_name,
|
||||
users.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.maybe_title,
|
||||
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
|
||||
LEFT OUTER JOIN users
|
||||
ON m.maybe_creator_user_token = users.token
|
||||
LEFT OUTER JOIN media_files as media_file_cover_image
|
||||
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
|
||||
WHERE
|
||||
m.maybe_creator_user_token = ?
|
||||
AND m.media_class = 'project'
|
||||
AND m.maybe_project_type IS NOT NULL
|
||||
AND m.maybe_project_type = ?
|
||||
AND m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
AND NOT m.is_intermediate_system_file
|
||||
AND m.id < ?
|
||||
ORDER BY m.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
user_token,
|
||||
project_type.to_str(),
|
||||
cursor_id,
|
||||
limit,
|
||||
)
|
||||
.fetch_all(mysql_executor)
|
||||
.await
|
||||
}
|
||||
None => {
|
||||
sqlx::query_as!(
|
||||
ProjectMediaFileListItem,
|
||||
r#"
|
||||
SELECT
|
||||
m.id as `id: u64`,
|
||||
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
|
||||
|
||||
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
|
||||
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
|
||||
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
|
||||
|
||||
m.public_bucket_directory_hash,
|
||||
m.maybe_public_bucket_prefix,
|
||||
m.maybe_public_bucket_extension,
|
||||
|
||||
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
|
||||
users.username as maybe_creator_username,
|
||||
users.display_name as maybe_creator_display_name,
|
||||
users.email_gravatar_hash as maybe_creator_gravatar_hash,
|
||||
|
||||
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
|
||||
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
|
||||
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
|
||||
|
||||
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
|
||||
|
||||
m.maybe_title,
|
||||
|
||||
m.created_at,
|
||||
m.updated_at
|
||||
|
||||
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
|
||||
LEFT OUTER JOIN users
|
||||
ON m.maybe_creator_user_token = users.token
|
||||
LEFT OUTER JOIN media_files as media_file_cover_image
|
||||
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
|
||||
WHERE
|
||||
m.maybe_creator_user_token = ?
|
||||
AND m.media_class = 'project'
|
||||
AND m.maybe_project_type IS NOT NULL
|
||||
AND m.user_deleted_at IS NULL
|
||||
AND m.mod_deleted_at IS NULL
|
||||
AND NOT m.is_intermediate_system_file
|
||||
AND m.id < ?
|
||||
ORDER BY m.id DESC
|
||||
LIMIT ?
|
||||
"#,
|
||||
user_token,
|
||||
cursor_id,
|
||||
limit,
|
||||
)
|
||||
.fetch_all(mysql_executor)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,3 +4,4 @@ pub mod list_media_files;
|
||||
pub mod list_media_files_by_batch_token;
|
||||
pub mod list_media_files_for_elastic_search_backfill_using_cursor;
|
||||
pub mod list_media_files_for_user;
|
||||
pub mod list_session_project_media_files;
|
||||
|
||||
+8
-9
@@ -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_subtype::MediaFileSubtype;
|
||||
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 errors::AnyhowResult;
|
||||
@@ -33,6 +33,9 @@ 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>,
|
||||
|
||||
@@ -43,8 +46,6 @@ 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,
|
||||
@@ -108,6 +109,7 @@ SET
|
||||
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
maybe_project_type = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
@@ -115,7 +117,6 @@ SET
|
||||
maybe_engine_category = ?,
|
||||
maybe_animation_type = ?,
|
||||
|
||||
maybe_media_subtype = ?,
|
||||
maybe_mime_type = ?,
|
||||
file_size_bytes = ?,
|
||||
|
||||
@@ -143,6 +144,7 @@ ON DUPLICATE KEY UPDATE
|
||||
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
maybe_project_type = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
@@ -150,7 +152,6 @@ ON DUPLICATE KEY UPDATE
|
||||
maybe_engine_category = ?,
|
||||
maybe_animation_type = ?,
|
||||
|
||||
maybe_media_subtype = ?,
|
||||
maybe_mime_type = ?,
|
||||
file_size_bytes = ?,
|
||||
|
||||
@@ -178,6 +179,7 @@ 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(),
|
||||
@@ -185,8 +187,6 @@ 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,6 +209,7 @@ 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(),
|
||||
@@ -216,8 +217,6 @@ 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,
|
||||
|
||||
|
||||
+10
@@ -171,6 +171,7 @@ impl RawUserBookmarkRecord {
|
||||
MediaFileType::Bvh => Some("3d file".to_string()),
|
||||
MediaFileType::Fbx => Some("3d file".to_string()),
|
||||
MediaFileType::Obj => Some("3d file".to_string()),
|
||||
MediaFileType::Ply => Some("3d splat".to_string()),
|
||||
MediaFileType::Glb => Some("3d file".to_string()),
|
||||
MediaFileType::Gltf => Some("3d file".to_string()),
|
||||
MediaFileType::Spz => Some("3d splat".to_string()),
|
||||
@@ -183,9 +184,18 @@ 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,
|
||||
|
||||
@@ -279,6 +279,8 @@ pub enum InferenceModelType {
|
||||
Marble1p1,
|
||||
#[serde(rename = "marble_1p1_plus")]
|
||||
Marble1p1Plus,
|
||||
#[serde(rename = "triposplat")]
|
||||
TripoSplat,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -411,6 +413,7 @@ impl InferenceModelType {
|
||||
Self::Marble1p0Draft => "marble_1p0_draft",
|
||||
Self::Marble1p1 => "marble_1p1",
|
||||
Self::Marble1p1Plus => "marble_1p1_plus",
|
||||
Self::TripoSplat => "triposplat",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,6 +541,7 @@ impl InferenceModelType {
|
||||
"marble_1p0_draft" => Ok(Self::Marble1p0Draft),
|
||||
"marble_1p1" => Ok(Self::Marble1p1),
|
||||
"marble_1p1_plus" => Ok(Self::Marble1p1Plus),
|
||||
"triposplat" => Ok(Self::TripoSplat),
|
||||
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
@@ -669,6 +673,7 @@ impl InferenceModelType {
|
||||
Self::Marble1p0Draft,
|
||||
Self::Marble1p1,
|
||||
Self::Marble1p1Plus,
|
||||
Self::TripoSplat,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -783,6 +788,7 @@ impl InferenceModelType {
|
||||
CommonModelType::Marble1p0Draft => Self::Marble1p0Draft,
|
||||
CommonModelType::Marble1p1 => Self::Marble1p1,
|
||||
CommonModelType::Marble1p1Plus => Self::Marble1p1Plus,
|
||||
CommonModelType::TripoSplat => Self::TripoSplat,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,6 +924,7 @@ mod tests {
|
||||
assert_serialization(InferenceModelType::Marble1p0Draft, "marble_1p0_draft");
|
||||
assert_serialization(InferenceModelType::Marble1p1, "marble_1p1");
|
||||
assert_serialization(InferenceModelType::Marble1p1Plus, "marble_1p1_plus");
|
||||
assert_serialization(InferenceModelType::TripoSplat, "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1044,6 +1051,7 @@ mod tests {
|
||||
assert_eq!(InferenceModelType::Marble1p0Draft.to_str(), "marble_1p0_draft");
|
||||
assert_eq!(InferenceModelType::Marble1p1.to_str(), "marble_1p1");
|
||||
assert_eq!(InferenceModelType::Marble1p1Plus.to_str(), "marble_1p1_plus");
|
||||
assert_eq!(InferenceModelType::TripoSplat.to_str(), "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1168,6 +1176,7 @@ mod tests {
|
||||
assert_eq!(InferenceModelType::from_str("marble_1p0_draft").unwrap(), InferenceModelType::Marble1p0Draft);
|
||||
assert_eq!(InferenceModelType::from_str("marble_1p1").unwrap(), InferenceModelType::Marble1p1);
|
||||
assert_eq!(InferenceModelType::from_str("marble_1p1_plus").unwrap(), InferenceModelType::Marble1p1Plus);
|
||||
assert_eq!(InferenceModelType::from_str("triposplat").unwrap(), InferenceModelType::TripoSplat);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -27,7 +27,21 @@ 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!
|
||||
@@ -44,6 +58,9 @@ impl MediaFileClass {
|
||||
Self::Image => "image",
|
||||
Self::Video => "video",
|
||||
Self::Dimensional => "dimensional",
|
||||
Self::Mesh => "mesh",
|
||||
Self::Splat => "splat",
|
||||
Self::Project => "project",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +71,9 @@ 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)),
|
||||
}
|
||||
}
|
||||
@@ -67,6 +87,9 @@ impl MediaFileClass {
|
||||
Self::Image,
|
||||
Self::Video,
|
||||
Self::Dimensional,
|
||||
Self::Mesh,
|
||||
Self::Splat,
|
||||
Self::Project,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -86,6 +109,9 @@ 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +125,9 @@ 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]
|
||||
@@ -108,6 +137,9 @@ 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());
|
||||
}
|
||||
}
|
||||
@@ -118,12 +150,15 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileClass::all_variants();
|
||||
assert_eq!(variants.len(), 5);
|
||||
assert_eq!(variants.len(), 8);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user