mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 13:54:53 -05:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acc16c15fb |
-223
@@ -1,223 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n t.token as `token: tokens::tokens::tags::TagToken`,\n t.tag_value\n\nFROM\n tags AS t\nJOIN tag_uses AS u\n ON t.token = u.tag_token\nWHERE\n u.entity_type = ?\n AND u.entity_token = ?\n AND u.maybe_deleted_at IS NULL\nORDER BY t.id DESC\nLIMIT 500\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "token: tokens::tokens::tags::TagToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "tag_value",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 1020
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n 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_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": 37
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3af0a10ab0ed65daba9363605db4ddae9a60a2177369c58046abdee9ad1e91ee"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nDELETE FROM tag_uses\nWHERE entity_type = ?\nAND entity_token = ?\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec"
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
{
|
||||
"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
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_prompt_token = ?,\n maybe_batch_token = ?,\n\n maybe_title = ?,\n maybe_duration_millis = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_scene_source_media_file_token = ?,\n\n is_intermediate_system_file = ?,\n is_user_upload = ?,\n\n maybe_generation_provider = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 27
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7369b2f3fd9e7c1e968dc5501ef9bea7e1610fd7a044a0f9ad8b908dfada2b6a"
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n maybe_creator_user_token as `maybe_creator_user_token: UserToken`,\n maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: AnonymousVisitorTrackingToken`,\n media_type as `media_type: MediaFileType`,\n maybe_project_type as `maybe_project_type: MediaFileProjectType`\nFROM media_files\nWHERE token = ?\n AND user_deleted_at IS NULL\n AND mod_deleted_at IS NULL\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "maybe_creator_user_token: UserToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "maybe_creator_anonymous_visitor_token: AnonymousVisitorTrackingToken",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "media_type: MediaFileType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
|
||||
"char_set": 224,
|
||||
"max_size": 64
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "maybe_project_type: MediaFileProjectType",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
"char_set": 224,
|
||||
"max_size": 128
|
||||
}
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "8dcd4bb064881f245beb39dd246e74ec1a2c47b69d7ce92a0d175f58dc4b9e63"
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
{
|
||||
"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
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n media_class = ?,\n media_type = ?,\n maybe_project_type = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n\n maybe_engine_category = ?,\n maybe_animation_type = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n maybe_prompt_token = ?,\n maybe_batch_token = ?,\n\n maybe_title = ?,\n maybe_duration_millis = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n\n maybe_scene_source_media_file_token = ?,\n\n is_intermediate_system_file = ?,\n is_user_upload = ?,\n\n maybe_generation_provider = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n maybe_origin_model_type = NULL,\n maybe_origin_model_token = NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 28
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a25fe977f51329ed078e2441446ef7ff16683dcad8b81c8d0a0a4540b5e985bc"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nUPDATE media_files\nSET\n media_class = ?,\n media_type = ?,\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 checksum_sha2 = ?,\n\n creator_ip_address = ?\nWHERE token = ?\nLIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 11
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a3a86db1b77af3973bb37751d797a7dc5ed34b0f0fa156845e3b4b805e397f9d"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE media_files\n SET\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n creator_ip_address = ?\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 7
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ad311b4aa95a71b1dc37aa09c27850a0e963380c6e7780f47a9fc5a95ab70583"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO tags\nSET\n token = ?,\n tag_value = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b"
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
{
|
||||
"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
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\n UPDATE media_files\n SET\n media_class = ?,\n maybe_project_type = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n creator_ip_address = ?\n WHERE token = ?\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e33fa6280a39fff22b2e8c2731b7b6058b4705b5b557f34dba52a6440c22529b"
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE media_files
|
||||
DROP INDEX index_maybe_project_type,
|
||||
DROP COLUMN maybe_project_type;
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Which kind of internal Artcraft project document this media file is
|
||||
-- ('scene_3d', 'mood_board', 'workflow', 'video_timeline', ...).
|
||||
-- Only set when media_class = 'project'. The file format lives in media_type;
|
||||
-- the JSON payload carries its own schema version.
|
||||
ALTER TABLE media_files
|
||||
ADD COLUMN maybe_project_type VARCHAR(32) DEFAULT NULL
|
||||
AFTER media_class;
|
||||
|
||||
-- For "list my mood boards"-style filtered queries.
|
||||
-- The column is all-NULL at creation time, so the index build is cheap.
|
||||
ALTER TABLE media_files
|
||||
ADD INDEX index_maybe_project_type (maybe_project_type);
|
||||
@@ -157,20 +157,11 @@ CREATE TABLE media_files (
|
||||
-- This is especially helpful with engine types that map to specific semantics.
|
||||
-- * 'unknown' (TODO: This is the default until all records are backfilled.)
|
||||
-- * 'audio' for wav, mp3, etc.
|
||||
-- * 'image' for a variety of image types.
|
||||
-- * 'image' for a variety of video types.
|
||||
-- * 'video' for a variety of video types.
|
||||
-- * 'dimensional' (DEPRECATED) split into 'mesh', 'splat', and 'project'; read-only until backfilled
|
||||
-- * 'mesh' for 3d mesh assets (glb, gltf, fbx, obj, pmx, pmd, etc.)
|
||||
-- * 'splat' for Gaussian splats (spz, etc.)
|
||||
-- * 'project' for internal Artcraft project documents (see maybe_project_type)
|
||||
-- * 'dimensional' for a variety of 3d types
|
||||
media_class VARCHAR(16) NOT NULL DEFAULT "unknown",
|
||||
|
||||
-- Which kind of internal Artcraft project document this media file is
|
||||
-- ('scene_3d', 'mood_board', 'workflow', 'video_timeline', ...).
|
||||
-- Only set when media_class = 'project'. The file format lives in media_type;
|
||||
-- the JSON payload carries its own schema version.
|
||||
maybe_project_type VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
-- The file's mime type.
|
||||
maybe_mime_type VARCHAR(32) DEFAULT NULL,
|
||||
|
||||
@@ -332,7 +323,6 @@ CREATE TABLE media_files (
|
||||
KEY index_maybe_scene_source_media_file_token (maybe_scene_source_media_file_token),
|
||||
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),
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"title": "[GET] /v1/engine/scene/{token}",
|
||||
"description": "Auto-generated per-endpoint dashboard for `GET /v1/engine/scene/{token}`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/engine/scene/_token,method:get`.\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/engine/scene/_token,method:get}.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/engine/scene/_token,method:get,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,method:get}.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/engine/scene/_token,method:get}",
|
||||
"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/engine/scene/_token,method:get}",
|
||||
"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/engine/scene/_token,method:get} 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/engine/scene/_token,method:get}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/engine/scene/_token,method:get}",
|
||||
"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/engine/scene/_token,method:get} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "24m-c4g-btm"
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"title": "[POST] /v1/engine/create_scene",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/engine/create_scene`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/engine/create_scene,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/engine/create_scene,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/engine/create_scene,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/engine/create_scene,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/engine/create_scene,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/engine/create_scene,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/engine/create_scene,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/engine/create_scene,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/engine/create_scene,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/engine/create_scene,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/engine/create_scene,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "z9c-89r-p57"
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"title": "[POST] /v1/engine/scene/{token}",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/engine/scene/{token}`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,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/engine/scene/_token,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "bxf-r4f-xfs"
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"title": "[POST] /v1/media_files/upload/engine_asset",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/upload/engine_asset`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,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/upload/engine_asset,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "7nm-j6w-9tz"
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"title": "[POST] /v1/media_files/write/engine_asset",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/write/engine_asset`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/write/engine_asset,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/engine_asset,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/engine_asset,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/engine_asset,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/engine_asset,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/engine_asset,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/engine_asset,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/engine_asset,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/engine_asset,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/engine_asset,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/engine_asset,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "xv7-2ca-2c5"
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"title": "[POST] /v1/media_files/write/scene_file",
|
||||
"description": "Auto-generated per-endpoint dashboard for `POST /v1/media_files/write/scene_file`.\n\nFilters all metrics on `service:storyteller-web,route:/v1/media_files/write/scene_file,method:post`.\n\nSource: `script/metrics/generate_endpoint_dashboards.py`. Do not hand-edit; changes will be overwritten on the next regeneration.",
|
||||
"layout_type": "ordered",
|
||||
"notify_list": [],
|
||||
"reflow_type": "fixed",
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Top-line health",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Requests/sec",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": true,
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}.as_rate()",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "5xx error rate (%)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 2,
|
||||
"autoscale": false,
|
||||
"custom_unit": "%",
|
||||
"requests": [
|
||||
{
|
||||
"q": "(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post,status_class:5xx}.as_count() / sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}.as_count()) * 100",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 5.0,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 1.0,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 3,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p95 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 1000,
|
||||
"palette": "white_on_red"
|
||||
},
|
||||
{
|
||||
"comparator": ">",
|
||||
"value": 250,
|
||||
"palette": "white_on_yellow"
|
||||
},
|
||||
{
|
||||
"comparator": "<=",
|
||||
"value": 250,
|
||||
"palette": "white_on_green"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "query_value",
|
||||
"title": "Latency p99 (ms)",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"precision": 0,
|
||||
"autoscale": true,
|
||||
"custom_unit": "ms",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"aggregator": "avg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 9,
|
||||
"y": 0,
|
||||
"width": 3,
|
||||
"height": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 12,
|
||||
"height": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "group",
|
||||
"layout_type": "ordered",
|
||||
"title": "Trends",
|
||||
"show_title": true,
|
||||
"widgets": [
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Requests/sec by status class",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post} by {status_class}.as_rate()",
|
||||
"display_type": "bars",
|
||||
"style": {
|
||||
"palette": "semantic",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "timeseries",
|
||||
"title": "Latency percentiles",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"show_legend": true,
|
||||
"legend_layout": "horizontal",
|
||||
"requests": [
|
||||
{
|
||||
"q": "p50:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "cool",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p95:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "solid",
|
||||
"line_width": "normal"
|
||||
}
|
||||
},
|
||||
{
|
||||
"q": "p99:http.server.request.duration_ms{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post}",
|
||||
"display_type": "line",
|
||||
"style": {
|
||||
"palette": "warm",
|
||||
"line_type": "dashed",
|
||||
"line_width": "normal"
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"scale": "linear",
|
||||
"include_zero": true,
|
||||
"label": "ms"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
"width": 6,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 3,
|
||||
"width": 12,
|
||||
"height": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"definition": {
|
||||
"type": "toplist",
|
||||
"title": "Status code distribution",
|
||||
"title_size": "16",
|
||||
"title_align": "left",
|
||||
"requests": [
|
||||
{
|
||||
"q": "top(sum:http.server.request.count{service:storyteller-web,route:/v1/media_files/write/scene_file,method:post} by {status_code}.as_count(), 25, 'sum', 'desc')"
|
||||
}
|
||||
]
|
||||
},
|
||||
"layout": {
|
||||
"x": 0,
|
||||
"y": 8,
|
||||
"width": 12,
|
||||
"height": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "c8z-t2z-nk4"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
$kind: collection
|
||||
order: 8000
|
||||
@@ -0,0 +1,15 @@
|
||||
$kind: http-request
|
||||
name: Edit Tags
|
||||
url: "{{api_host}}/v1/tags/edit/model_weight/weight_84t9zeafn72wkje1x59f9rm81"
|
||||
method: POST
|
||||
headers:
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Cookie: ""
|
||||
body:
|
||||
type: json
|
||||
content: |-
|
||||
{
|
||||
"tags": "video games, legend of zelda"
|
||||
}
|
||||
order: 1000
|
||||
@@ -0,0 +1,6 @@
|
||||
$kind: http-request
|
||||
url: "{{api_host}}/v1/tags/list/model_weight/weight_84t9zeafn72wkje1x59f9rm81"
|
||||
method: GET
|
||||
headers:
|
||||
Accept: application/json
|
||||
order: 2000
|
||||
@@ -2,7 +2,6 @@ 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
@@ -1,22 +0,0 @@
|
||||
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
@@ -1,78 +0,0 @@
|
||||
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,4 +1,2 @@
|
||||
pub mod delete_media_file;
|
||||
pub mod list_batch_generated_media_files;
|
||||
pub mod list_session_project_media_files;
|
||||
pub mod project;
|
||||
pub mod list_batch_generated_media_files;
|
||||
@@ -1,8 +0,0 @@
|
||||
pub mod upload_new_editor_2d_project;
|
||||
pub mod upload_new_mood_board_project;
|
||||
pub mod upload_new_scene_3d_project;
|
||||
pub mod upload_new_video_timeline_project;
|
||||
pub mod upload_updated_editor_2d_project;
|
||||
pub mod upload_updated_mood_board_project;
|
||||
pub mod upload_updated_scene_3d_project;
|
||||
pub mod upload_updated_video_timeline_project;
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_EDITOR_2D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/editor_2d/new";
|
||||
|
||||
/// Response for saving a new 2D editor document project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewEditor2dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_MOOD_BOARD_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/mood_board/new";
|
||||
|
||||
/// Response for saving a new mood board project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewMoodBoardProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_SCENE_3D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/scene_3d/new";
|
||||
|
||||
/// Response for saving a new 3D scene project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewScene3dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_NEW_VIDEO_TIMELINE_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/video_timeline/new";
|
||||
|
||||
/// Response for saving a new video editor timeline project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadNewVideoTimelineProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token for the newly created project media file. Use it to query the
|
||||
/// project and to save subsequent updates via the update endpoint.
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_EDITOR_2D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/editor_2d/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedEditor2dProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing 2D editor project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedEditor2dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_MOOD_BOARD_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/mood_board/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedMoodBoardProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing mood board project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedMoodBoardProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_SCENE_3D_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/scene_3d/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedScene3dProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing 3D scene project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedScene3dProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub const UPLOAD_UPDATED_VIDEO_TIMELINE_PROJECT_URL_PATH: &str = "/v1/media_files/upload/project/video_timeline/update/{token}";
|
||||
|
||||
/// For the URL PathInfo
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedVideoTimelineProjectPathInfo {
|
||||
pub token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Response for overwriting an existing video editor timeline project.
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct UploadUpdatedVideoTimelineProjectSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// Token of the updated project media file (unchanged by the update).
|
||||
pub media_file_token: MediaFileToken,
|
||||
}
|
||||
@@ -24,10 +24,6 @@ pub enum RouterSplatModel {
|
||||
|
||||
#[serde(rename = "marble_1p1_plus")]
|
||||
Marble1p1Plus,
|
||||
|
||||
/// TripoSplat: single-image to 3D Gaussian splat (via Fal).
|
||||
#[serde(rename = "triposplat")]
|
||||
TripoSplat,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -44,7 +40,6 @@ 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]
|
||||
@@ -58,7 +53,6 @@ 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,8 +8,6 @@ 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;
|
||||
@@ -81,9 +79,6 @@ 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,6 +1,3 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -16,30 +13,9 @@ 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),
|
||||
}
|
||||
|
||||
@@ -51,13 +27,6 @@ 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,4 +6,3 @@ pub mod marble_1p0;
|
||||
pub mod marble_1p0_draft;
|
||||
pub mod marble_1p1;
|
||||
pub mod marble_1p1_plus;
|
||||
pub mod triposplat;
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
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
@@ -1,51 +0,0 @@
|
||||
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
@@ -1,3 +0,0 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
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
@@ -1,2 +0,0 @@
|
||||
pub(crate) mod request_common;
|
||||
pub mod triposplat;
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
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
@@ -1,228 +0,0 @@
|
||||
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
@@ -1,59 +0,0 @@
|
||||
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
@@ -1,3 +0,0 @@
|
||||
pub mod build;
|
||||
pub mod cost;
|
||||
pub mod request;
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
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,3 +1,2 @@
|
||||
pub mod artcraft;
|
||||
pub mod fal;
|
||||
pub mod worldlabs;
|
||||
|
||||
-18
@@ -10,10 +10,6 @@ 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;
|
||||
@@ -30,8 +26,6 @@ pub enum SplatGenerationRequest {
|
||||
ArtcraftMarble1p0Draft(ArtcraftMarble1p0DraftRequestState),
|
||||
ArtcraftMarble1p1(ArtcraftMarble1p1RequestState),
|
||||
ArtcraftMarble1p1Plus(ArtcraftMarble1p1PlusRequestState),
|
||||
ArtcraftTripoSplat(ArtcraftTripoSplatRequestState),
|
||||
FalTripoSplat(FalTripoSplatRequestState),
|
||||
WorldLabsMarble1p0(WorldLabsMarble1p0RequestState),
|
||||
WorldLabsMarble1p0Draft(WorldLabsMarble1p0DraftModelRequestState),
|
||||
WorldLabsMarble1p1(WorldLabsMarble1p1RequestState),
|
||||
@@ -46,8 +40,6 @@ 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,
|
||||
@@ -62,8 +54,6 @@ 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()),
|
||||
@@ -91,14 +81,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod audio;
|
||||
pub mod image;
|
||||
pub mod mesh;
|
||||
pub mod splat;
|
||||
pub mod video;
|
||||
@@ -1 +0,0 @@
|
||||
pub mod triposplat_image_to_splat;
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
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
@@ -1,47 +0,0 @@
|
||||
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
@@ -1,3 +0,0 @@
|
||||
pub mod api;
|
||||
pub mod cost;
|
||||
pub mod raw_request;
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
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 {}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod image;
|
||||
@@ -10,7 +10,6 @@ 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 {
|
||||
@@ -19,14 +18,6 @@ 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/
|
||||
@@ -56,19 +47,6 @@ 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,7 +17,6 @@ pub enum FileExtension {
|
||||
Obj,
|
||||
Ogg,
|
||||
Opus,
|
||||
Ply,
|
||||
Png,
|
||||
Wav,
|
||||
Webm,
|
||||
@@ -38,7 +37,6 @@ 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),
|
||||
@@ -71,7 +69,6 @@ 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"),
|
||||
|
||||
-14
@@ -8,7 +8,6 @@ use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::visibility::Visibility;
|
||||
@@ -26,12 +25,6 @@ pub enum UploadType {
|
||||
Filesystem,
|
||||
DeviceCaptureApi,
|
||||
StorytellerEngine,
|
||||
|
||||
/// Internal project documents (scenes, mood boards, timelines, etc.)
|
||||
/// created by our apps and uploaded by the client. Stored with origin
|
||||
/// `upload`; the project nature is captured by `media_class = 'project'`
|
||||
/// and `maybe_project_type`.
|
||||
ProjectFile,
|
||||
}
|
||||
|
||||
pub struct InsertMediaFileFromUploadArgs<'a> {
|
||||
@@ -40,9 +33,6 @@ pub struct InsertMediaFileFromUploadArgs<'a> {
|
||||
pub media_file_type: MediaFileType,
|
||||
pub maybe_media_class: Option<MediaFileClass>,
|
||||
|
||||
/// Only set for internal Artcraft project documents (`media_class = 'project'`).
|
||||
pub maybe_project_type: Option<MediaFileProjectType>,
|
||||
|
||||
pub upload_type: UploadType,
|
||||
|
||||
pub maybe_engine_category: Option<MediaFileEngineCategory>,
|
||||
@@ -84,7 +74,6 @@ pub async fn insert_media_file_from_file_upload(
|
||||
let mut origin_category = match args.upload_type {
|
||||
UploadType::ThirdPartyInference => MediaFileOriginCategory::ThirdPartyInference,
|
||||
UploadType::Filesystem => MediaFileOriginCategory::Upload,
|
||||
UploadType::ProjectFile => MediaFileOriginCategory::Upload,
|
||||
UploadType::DeviceCaptureApi => MediaFileOriginCategory::DeviceApi,
|
||||
UploadType::StorytellerEngine => MediaFileOriginCategory::StorytellerStudio,
|
||||
};
|
||||
@@ -137,7 +126,6 @@ SET
|
||||
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
maybe_project_type = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
@@ -187,8 +175,6 @@ SET
|
||||
|
||||
args.media_file_type.to_str(),
|
||||
|
||||
args.maybe_project_type.map(|project_type| project_type.to_str()),
|
||||
|
||||
origin_category.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@ pub mod set_media_file_cover_image;
|
||||
pub mod update_media_file_animation_type;
|
||||
pub mod update_media_file_engine_category;
|
||||
pub mod update_media_file_stored_cloud_contents;
|
||||
pub mod update_project_media_file_contents;
|
||||
pub mod update_media_file_visibility;
|
||||
pub mod update_media_file_visibility_transactional;
|
||||
-15
@@ -1,7 +1,5 @@
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -16,13 +14,6 @@ use tokens::tokens::media_files::MediaFileToken;
|
||||
pub struct UpdateArgs<'a> {
|
||||
pub media_file_token: &'a MediaFileToken,
|
||||
|
||||
/// The class the record should hold after the update. Also backfills legacy
|
||||
/// records that were written before the current classification scheme.
|
||||
pub media_class: MediaFileClass,
|
||||
|
||||
/// Only set for internal Artcraft project documents (`media_class = 'project'`).
|
||||
pub maybe_project_type: Option<MediaFileProjectType>,
|
||||
|
||||
pub public_bucket_directory_hash: &'a str,
|
||||
pub maybe_public_bucket_prefix: Option<&'a str>,
|
||||
pub maybe_public_bucket_extension: Option<&'a str>,
|
||||
@@ -46,9 +37,6 @@ pub async fn updated_media_file_stored_cloud_contents(args: UpdateArgs<'_>) -> A
|
||||
r#"
|
||||
UPDATE media_files
|
||||
SET
|
||||
media_class = ?,
|
||||
maybe_project_type = ?,
|
||||
|
||||
public_bucket_directory_hash = ?,
|
||||
maybe_public_bucket_prefix = ?,
|
||||
maybe_public_bucket_extension = ?,
|
||||
@@ -60,9 +48,6 @@ pub async fn updated_media_file_stored_cloud_contents(args: UpdateArgs<'_>) -> A
|
||||
WHERE token = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
args.media_class.to_str(),
|
||||
args.maybe_project_type.map(|project_type| project_type.to_str()),
|
||||
|
||||
args.public_bucket_directory_hash,
|
||||
args.maybe_public_bucket_prefix,
|
||||
args.maybe_public_bucket_extension,
|
||||
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
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 tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
pub struct UpdateProjectMediaFileContentsArgs<'e, 'c, E>
|
||||
where
|
||||
E: 'e + Executor<'c, Database = MySql>,
|
||||
{
|
||||
pub media_file_token: &'e MediaFileToken,
|
||||
|
||||
/// The class/type/project-type the record should hold after the update.
|
||||
/// Also backfills legacy records that predate the `project` class.
|
||||
pub media_class: MediaFileClass,
|
||||
pub media_type: MediaFileType,
|
||||
pub project_type: MediaFileProjectType,
|
||||
|
||||
pub public_bucket_directory_hash: &'e str,
|
||||
pub maybe_public_bucket_prefix: Option<&'e str>,
|
||||
pub maybe_public_bucket_extension: Option<&'e str>,
|
||||
|
||||
pub maybe_mime_type: Option<&'e str>,
|
||||
pub file_size_bytes: u64,
|
||||
pub sha256_checksum: &'e str,
|
||||
|
||||
pub update_ip_address: &'e str,
|
||||
|
||||
pub mysql_executor: E,
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
|
||||
/// Point a project media file record at freshly uploaded bucket contents.
|
||||
pub async fn update_project_media_file_contents<'e, 'c: 'e, E>(
|
||||
args: UpdateProjectMediaFileContentsArgs<'e, 'c, E>,
|
||||
) -> Result<(), sqlx::Error>
|
||||
where
|
||||
E: 'e + Executor<'c, Database = MySql>,
|
||||
{
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE media_files
|
||||
SET
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
maybe_project_type = ?,
|
||||
|
||||
public_bucket_directory_hash = ?,
|
||||
maybe_public_bucket_prefix = ?,
|
||||
maybe_public_bucket_extension = ?,
|
||||
|
||||
maybe_mime_type = ?,
|
||||
file_size_bytes = ?,
|
||||
checksum_sha2 = ?,
|
||||
|
||||
creator_ip_address = ?
|
||||
WHERE token = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
args.media_class.to_str(),
|
||||
args.media_type.to_str(),
|
||||
args.project_type.to_str(),
|
||||
|
||||
args.public_bucket_directory_hash,
|
||||
args.maybe_public_bucket_prefix,
|
||||
args.maybe_public_bucket_extension,
|
||||
|
||||
args.maybe_mime_type,
|
||||
args.file_size_bytes,
|
||||
args.sha256_checksum,
|
||||
|
||||
args.update_ip_address,
|
||||
|
||||
args.media_file_token.as_str(),
|
||||
)
|
||||
.execute(args.mysql_executor)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use sqlx::{Executor, MySql};
|
||||
|
||||
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MediaFileForProjectUpdate {
|
||||
pub maybe_creator_user_token: Option<UserToken>,
|
||||
pub maybe_creator_anonymous_visitor_token: Option<AnonymousVisitorTrackingToken>,
|
||||
pub media_type: MediaFileType,
|
||||
pub maybe_project_type: Option<MediaFileProjectType>,
|
||||
}
|
||||
|
||||
pub struct LookupMediaFileForProjectUpdateArgs<'e, 'c, E>
|
||||
where
|
||||
E: 'e + Executor<'c, Database = MySql>,
|
||||
{
|
||||
pub media_file_token: &'e MediaFileToken,
|
||||
pub mysql_executor: E,
|
||||
pub phantom: PhantomData<&'c E>,
|
||||
}
|
||||
|
||||
/// Minimal lookup of a media file for a project-save (overwrite) request:
|
||||
/// the creator tokens (for ownership checks) and the media/project types
|
||||
/// (to confirm the record is the right kind of project document).
|
||||
/// Excludes soft-deleted rows.
|
||||
pub async fn lookup_media_file_for_project_update<'e, 'c: 'e, E>(
|
||||
args: LookupMediaFileForProjectUpdateArgs<'e, 'c, E>,
|
||||
) -> Result<Option<MediaFileForProjectUpdate>, sqlx::Error>
|
||||
where
|
||||
E: 'e + Executor<'c, Database = MySql>,
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
maybe_creator_user_token as `maybe_creator_user_token: UserToken`,
|
||||
maybe_creator_anonymous_visitor_token as `maybe_creator_anonymous_visitor_token: AnonymousVisitorTrackingToken`,
|
||||
media_type as `media_type: MediaFileType`,
|
||||
maybe_project_type as `maybe_project_type: MediaFileProjectType`
|
||||
FROM media_files
|
||||
WHERE token = ?
|
||||
AND user_deleted_at IS NULL
|
||||
AND mod_deleted_at IS NULL
|
||||
LIMIT 1
|
||||
"#,
|
||||
args.media_file_token.as_str(),
|
||||
)
|
||||
.fetch_optional(args.mysql_executor)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(|record| MediaFileForProjectUpdate {
|
||||
maybe_creator_user_token: record.maybe_creator_user_token,
|
||||
maybe_creator_anonymous_visitor_token: record.maybe_creator_anonymous_visitor_token,
|
||||
media_type: record.media_type,
|
||||
maybe_project_type: record.maybe_project_type,
|
||||
}))
|
||||
}
|
||||
@@ -3,5 +3,4 @@ pub mod batch_get_media_files;
|
||||
pub mod batch_get_media_files_by_tokens;
|
||||
pub mod get_media_file;
|
||||
pub mod get_media_file_for_inference;
|
||||
pub mod lookup_media_file_for_cover_check;
|
||||
pub mod lookup_media_file_for_project_update;
|
||||
pub mod lookup_media_file_for_cover_check;
|
||||
-375
@@ -1,375 +0,0 @@
|
||||
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,4 +4,3 @@ 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;
|
||||
|
||||
@@ -4,3 +4,4 @@ pub mod edit;
|
||||
pub mod get;
|
||||
pub mod thumbnails;
|
||||
pub mod list;
|
||||
pub mod upsert;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod upsert_media_file_from_file_upload;
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
use anyhow::anyhow;
|
||||
use sqlx;
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use enums::by_table::generic_synthetic_ids::id_category::IdCategory;
|
||||
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_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::anonymous_visitor_tracking::AnonymousVisitorTrackingToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
use crate::queries::generic_synthetic_ids::transactional_increment_generic_synthetic_id::transactional_increment_generic_synthetic_id;
|
||||
|
||||
pub enum UploadType {
|
||||
Filesystem,
|
||||
DeviceCaptureApi,
|
||||
StorytellerEngine,
|
||||
}
|
||||
|
||||
pub struct UpsertMediaFileFromUploadArgs<'a> {
|
||||
pub pool: &'a MySqlPool,
|
||||
|
||||
/// If the media file token is present, we're upserting an existing media file.
|
||||
pub maybe_media_file_token: Option<&'a MediaFileToken>,
|
||||
|
||||
pub maybe_media_class: Option<MediaFileClass>,
|
||||
pub media_file_type: MediaFileType,
|
||||
|
||||
pub maybe_engine_category: Option<MediaFileEngineCategory>,
|
||||
pub maybe_animation_type: Option<MediaFileAnimationType>,
|
||||
|
||||
pub maybe_creator_user_token: Option<&'a UserToken>,
|
||||
pub maybe_creator_anonymous_visitor_token: Option<&'a AnonymousVisitorTrackingToken>,
|
||||
pub creator_ip_address: &'a str,
|
||||
pub creator_set_visibility: Visibility,
|
||||
|
||||
pub upload_type: UploadType,
|
||||
|
||||
pub maybe_mime_type: Option<&'a str>,
|
||||
pub file_size_bytes: u64,
|
||||
pub duration_millis: u64,
|
||||
pub sha256_checksum: &'a str,
|
||||
|
||||
// TODO: Media duration.
|
||||
//pub duration_millis: u64,
|
||||
|
||||
pub public_bucket_directory_hash: &'a str,
|
||||
pub maybe_public_bucket_prefix: Option<&'a str>,
|
||||
pub maybe_public_bucket_extension: Option<&'a str>,
|
||||
}
|
||||
|
||||
pub async fn upsert_media_file_from_file_upload(
|
||||
args: UpsertMediaFileFromUploadArgs<'_>
|
||||
) -> AnyhowResult<(MediaFileToken, u64)>
|
||||
{
|
||||
let token = args.maybe_media_file_token
|
||||
.map(|token| token.clone())
|
||||
.unwrap_or_else(|| MediaFileToken::generate());
|
||||
|
||||
let origin_category = match args.upload_type {
|
||||
UploadType::Filesystem => MediaFileOriginCategory::Upload,
|
||||
UploadType::DeviceCaptureApi => MediaFileOriginCategory::DeviceApi,
|
||||
UploadType::StorytellerEngine => MediaFileOriginCategory::StorytellerStudio,
|
||||
};
|
||||
|
||||
let mut maybe_creator_file_synthetic_id : Option<u64> = None;
|
||||
let mut maybe_creator_category_synthetic_id : Option<u64> = None;
|
||||
|
||||
let mut transaction = args.pool.begin().await?;
|
||||
|
||||
if let Some(user_token) = args.maybe_creator_user_token.as_deref() {
|
||||
|
||||
let next_media_file_id = transactional_increment_generic_synthetic_id(
|
||||
&user_token,
|
||||
IdCategory::MediaFile,
|
||||
&mut transaction
|
||||
).await?;
|
||||
|
||||
let next_voice_conversion_id = transactional_increment_generic_synthetic_id(
|
||||
&user_token,
|
||||
IdCategory::FileUpload,
|
||||
&mut transaction
|
||||
).await?;
|
||||
|
||||
maybe_creator_file_synthetic_id = Some(next_media_file_id);
|
||||
maybe_creator_category_synthetic_id = Some(next_voice_conversion_id);
|
||||
}
|
||||
|
||||
const ORIGIN_PRODUCT_CATEGORY : MediaFileOriginProductCategory = MediaFileOriginProductCategory::Unknown;
|
||||
|
||||
let media_class = args.maybe_media_class.unwrap_or(MediaFileClass::Unknown);
|
||||
|
||||
let record_id = {
|
||||
let query_result = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO media_files
|
||||
SET
|
||||
token = ?,
|
||||
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
|
||||
maybe_engine_category = ?,
|
||||
maybe_animation_type = ?,
|
||||
|
||||
maybe_mime_type = ?,
|
||||
file_size_bytes = ?,
|
||||
|
||||
checksum_sha2 = ?,
|
||||
|
||||
public_bucket_directory_hash = ?,
|
||||
maybe_public_bucket_prefix = ?,
|
||||
maybe_public_bucket_extension = ?,
|
||||
|
||||
maybe_creator_user_token = ?,
|
||||
maybe_creator_anonymous_visitor_token = ?,
|
||||
creator_ip_address = ?,
|
||||
|
||||
creator_set_visibility = ?,
|
||||
|
||||
maybe_creator_file_synthetic_id = ?,
|
||||
maybe_creator_category_synthetic_id = ?,
|
||||
|
||||
is_user_upload = TRUE,
|
||||
|
||||
maybe_origin_model_type = NULL,
|
||||
maybe_origin_model_token = NULL
|
||||
|
||||
ON DUPLICATE KEY UPDATE
|
||||
|
||||
media_class = ?,
|
||||
media_type = ?,
|
||||
|
||||
origin_category = ?,
|
||||
origin_product_category = ?,
|
||||
|
||||
maybe_engine_category = ?,
|
||||
maybe_animation_type = ?,
|
||||
|
||||
maybe_mime_type = ?,
|
||||
file_size_bytes = ?,
|
||||
|
||||
checksum_sha2 = ?,
|
||||
|
||||
public_bucket_directory_hash = ?,
|
||||
maybe_public_bucket_prefix = ?,
|
||||
maybe_public_bucket_extension = ?,
|
||||
|
||||
maybe_creator_user_token = ?,
|
||||
maybe_creator_anonymous_visitor_token = ?,
|
||||
creator_ip_address = ?,
|
||||
|
||||
creator_set_visibility = ?,
|
||||
|
||||
maybe_creator_file_synthetic_id = ?,
|
||||
maybe_creator_category_synthetic_id = ?,
|
||||
|
||||
is_user_upload = TRUE,
|
||||
|
||||
maybe_origin_model_type = NULL,
|
||||
maybe_origin_model_token = NULL
|
||||
"#,
|
||||
token.as_str(),
|
||||
|
||||
media_class.to_str(),
|
||||
args.media_file_type.to_str(),
|
||||
|
||||
origin_category.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
args.maybe_engine_category.map(|s| s.to_str()),
|
||||
args.maybe_animation_type.map(|s| s.to_str()),
|
||||
|
||||
args.maybe_mime_type,
|
||||
args.file_size_bytes,
|
||||
|
||||
args.sha256_checksum,
|
||||
|
||||
args.public_bucket_directory_hash,
|
||||
args.maybe_public_bucket_prefix,
|
||||
args.maybe_public_bucket_extension,
|
||||
|
||||
args.maybe_creator_user_token,
|
||||
args.maybe_creator_anonymous_visitor_token,
|
||||
args.creator_ip_address,
|
||||
|
||||
args.creator_set_visibility.to_str(),
|
||||
|
||||
maybe_creator_file_synthetic_id,
|
||||
maybe_creator_category_synthetic_id,
|
||||
|
||||
// Duplicate key
|
||||
|
||||
media_class.to_str(),
|
||||
args.media_file_type.to_str(),
|
||||
|
||||
origin_category.to_str(),
|
||||
ORIGIN_PRODUCT_CATEGORY.to_str(),
|
||||
|
||||
args.maybe_engine_category.map(|s| s.to_str()),
|
||||
args.maybe_animation_type.map(|s| s.to_str()),
|
||||
|
||||
args.maybe_mime_type,
|
||||
args.file_size_bytes,
|
||||
|
||||
args.sha256_checksum,
|
||||
|
||||
args.public_bucket_directory_hash,
|
||||
args.maybe_public_bucket_prefix,
|
||||
args.maybe_public_bucket_extension,
|
||||
|
||||
args.maybe_creator_user_token,
|
||||
args.maybe_creator_anonymous_visitor_token,
|
||||
args.creator_ip_address,
|
||||
|
||||
args.creator_set_visibility.to_str(),
|
||||
|
||||
maybe_creator_file_synthetic_id,
|
||||
maybe_creator_category_synthetic_id,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await;
|
||||
|
||||
let record_id = match query_result {
|
||||
Ok(res) => {
|
||||
res.last_insert_id()
|
||||
},
|
||||
Err(err) => {
|
||||
// TODO: handle better
|
||||
//transaction.rollback().await?;
|
||||
return Err(anyhow!("Mysql error: {:?}", err));
|
||||
}
|
||||
};
|
||||
|
||||
record_id
|
||||
};
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok((token, record_id))
|
||||
}
|
||||
@@ -32,6 +32,7 @@ pub mod prompts;
|
||||
pub mod public_event_feed;
|
||||
pub mod staff_audit_logs;
|
||||
pub mod stats;
|
||||
pub mod tags;
|
||||
pub mod trending_model_analytics;
|
||||
pub mod tts;
|
||||
pub mod twitch;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::errors::database_insert_error::DatabaseInsertError;
|
||||
use crate::utils::transactor::Transactor;
|
||||
use tokens::tokens::tags::TagToken;
|
||||
|
||||
pub async fn create_tag(
|
||||
tag: &str,
|
||||
transactor: Transactor<'_, '_>,
|
||||
) -> Result<TagToken, DatabaseInsertError>
|
||||
{
|
||||
let tag = tag.trim().to_lowercase();
|
||||
let token = TagToken::generate();
|
||||
|
||||
let query = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO tags
|
||||
SET
|
||||
token = ?,
|
||||
tag_value = ?
|
||||
"#,
|
||||
token,
|
||||
tag,
|
||||
);
|
||||
|
||||
let _r = transactor.execute(query).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity;
|
||||
use errors::AnyhowResult;
|
||||
use tokens::tokens::tags::TagToken;
|
||||
|
||||
use crate::utils::transactor::Transactor;
|
||||
|
||||
pub struct EntityTag {
|
||||
pub token: TagToken,
|
||||
pub tag_value: String,
|
||||
}
|
||||
|
||||
pub async fn list_tags_for_entity(
|
||||
tag_entity: TagUseEntity,
|
||||
transactor: Transactor<'_, '_>,
|
||||
) -> AnyhowResult<Vec<EntityTag>> {
|
||||
|
||||
let (entity_type, entity_token) = tag_entity.get_composite_keys();
|
||||
|
||||
let query = sqlx::query_as!(
|
||||
RawTag,
|
||||
r#"
|
||||
SELECT
|
||||
t.token as `token: tokens::tokens::tags::TagToken`,
|
||||
t.tag_value
|
||||
|
||||
FROM
|
||||
tags AS t
|
||||
JOIN tag_uses AS u
|
||||
ON t.token = u.tag_token
|
||||
WHERE
|
||||
u.entity_type = ?
|
||||
AND u.entity_token = ?
|
||||
AND u.maybe_deleted_at IS NULL
|
||||
ORDER BY t.id DESC
|
||||
LIMIT 500
|
||||
"#,
|
||||
entity_type,
|
||||
entity_token
|
||||
);
|
||||
|
||||
let results = match transactor {
|
||||
Transactor::Pool { pool } => {
|
||||
query.fetch_all(pool).await?
|
||||
},
|
||||
Transactor::Connection { connection } => {
|
||||
query.fetch_all(connection).await?
|
||||
},
|
||||
Transactor::Transaction { transaction } => {
|
||||
query.fetch_all(&mut **transaction).await?
|
||||
},
|
||||
};
|
||||
|
||||
Ok(results.into_iter()
|
||||
.map(|record| EntityTag {
|
||||
token: record.token,
|
||||
tag_value: record.tag_value,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
struct RawTag {
|
||||
token: TagToken,
|
||||
tag_value: String,
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod create_tag;
|
||||
pub mod list_tags_for_entity;
|
||||
pub mod select_matching_tags;
|
||||
pub mod update_tags_for_entity;
|
||||
@@ -0,0 +1,78 @@
|
||||
use crate::utils::transactor::Transactor;
|
||||
use errors::AnyhowResult;
|
||||
use sqlx::mysql::MySqlRow;
|
||||
use sqlx::{FromRow, MySql, QueryBuilder, Row};
|
||||
use tokens::tokens::tags::TagToken;
|
||||
|
||||
pub struct MatchingTag {
|
||||
pub token: TagToken,
|
||||
pub tag_value: String,
|
||||
}
|
||||
|
||||
pub async fn select_matching_tags(
|
||||
tag_values: &[String],
|
||||
transactor: Transactor<'_, '_>,
|
||||
) -> AnyhowResult<Vec<MatchingTag>> {
|
||||
if tag_values.is_empty() {
|
||||
return Ok(Vec::new()) // This prevents a SQL error when the IN clause is empty.
|
||||
}
|
||||
|
||||
let tag_values = tag_values.iter()
|
||||
.map(|tag| tag.trim().to_lowercase())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new(
|
||||
r#"
|
||||
SELECT
|
||||
token,
|
||||
tag_value
|
||||
FROM
|
||||
tags
|
||||
WHERE
|
||||
tag_value IN (
|
||||
"#,
|
||||
);
|
||||
|
||||
let mut separated = query_builder.separated(", ");
|
||||
|
||||
for tag_value in tag_values.iter() {
|
||||
separated.push_bind(tag_value);
|
||||
}
|
||||
|
||||
separated.push_unseparated(") ");
|
||||
|
||||
let query = query_builder.build_query_as::<RawTag>();
|
||||
|
||||
let results = match transactor {
|
||||
Transactor::Pool { pool } => {
|
||||
query.fetch_all(pool).await?
|
||||
},
|
||||
Transactor::Connection { connection } => {
|
||||
query.fetch_all(connection).await?
|
||||
},
|
||||
Transactor::Transaction { transaction } => {
|
||||
query.fetch_all(&mut **transaction).await?
|
||||
},
|
||||
};
|
||||
|
||||
Ok(results.into_iter()
|
||||
.map(|record| MatchingTag {
|
||||
token: record.token,
|
||||
tag_value: record.tag_value,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
struct RawTag {
|
||||
token: TagToken,
|
||||
tag_value: String,
|
||||
}
|
||||
|
||||
impl FromRow<'_, MySqlRow> for RawTag {
|
||||
fn from_row(row: &MySqlRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self {
|
||||
token: TagToken::new(row.try_get("token")?),
|
||||
tag_value: row.try_get("tag_value")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity;
|
||||
use errors::AnyhowResult;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{Acquire, Executor, MySql, QueryBuilder, Transaction};
|
||||
use tokens::tokens::tags::TagToken;
|
||||
|
||||
pub async fn update_tags_for_entity(
|
||||
entity: TagUseEntity,
|
||||
tags: &[TagToken],
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> AnyhowResult<()>
|
||||
{
|
||||
if tags.is_empty() {
|
||||
delete_all_query(&entity, mysql_connection).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut transaction = mysql_connection.begin().await?;
|
||||
|
||||
delete_query(&entity, tags, &mut transaction).await?;
|
||||
insert_query(&entity, tags, &mut transaction).await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_query(
|
||||
entity: &TagUseEntity,
|
||||
tags: &[TagToken],
|
||||
transaction: &mut Transaction<'_, MySql>,
|
||||
) -> AnyhowResult<()> {
|
||||
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("DELETE FROM tag_uses ");
|
||||
|
||||
query_builder.push(" WHERE entity_type = ");;
|
||||
query_builder.push_bind(entity.get_entity_type().to_str());
|
||||
|
||||
query_builder.push(" AND entity_token = ");;
|
||||
query_builder.push_bind(entity.get_entity_token_str());
|
||||
|
||||
query_builder.push(" AND tag_token NOT IN ( ");
|
||||
|
||||
let mut separated = query_builder.separated(", ");
|
||||
|
||||
for tag in tags.iter() {
|
||||
separated.push_bind(tag.as_str());
|
||||
}
|
||||
|
||||
separated.push_unseparated(") ");
|
||||
|
||||
let query = query_builder.build();
|
||||
|
||||
transaction.execute(query).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_all_query(
|
||||
entity: &TagUseEntity,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> AnyhowResult<()> {
|
||||
let query = sqlx::query!(
|
||||
r#"
|
||||
DELETE FROM tag_uses
|
||||
WHERE entity_type = ?
|
||||
AND entity_token = ?
|
||||
LIMIT 1
|
||||
"#,
|
||||
entity.get_entity_type().to_str(),
|
||||
entity.get_entity_token_str(),
|
||||
);
|
||||
|
||||
mysql_connection.execute(query).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn insert_query(
|
||||
entity: &TagUseEntity,
|
||||
tags: &[TagToken],
|
||||
transaction: &mut Transaction<'_, MySql>,
|
||||
) -> AnyhowResult<()> {
|
||||
if tags.is_empty() {
|
||||
return Ok(()) // This prevents a SQL error when the IN clause is empty.
|
||||
}
|
||||
|
||||
// NB: Insert ignore will insert non-duplicate rows and silently ignore duplicates
|
||||
let mut query_builder: QueryBuilder<MySql> = QueryBuilder::new("INSERT IGNORE INTO tag_uses ");
|
||||
|
||||
query_builder.push(" (entity_type, entity_token, tag_token) VALUES ");
|
||||
|
||||
for (i, tag) in tags.iter().enumerate() {
|
||||
query_builder.push(" ( ");;
|
||||
query_builder.push_bind(entity.get_entity_type().to_str());
|
||||
query_builder.push(", ");;
|
||||
query_builder.push_bind(entity.get_entity_token_str());
|
||||
query_builder.push(", ");;
|
||||
query_builder.push_bind(tag.as_str());
|
||||
query_builder.push(" ) ");
|
||||
|
||||
if i < tags.len() - 1 {
|
||||
query_builder.push(", ");
|
||||
}
|
||||
}
|
||||
|
||||
let query = query_builder.build();
|
||||
|
||||
transaction.execute(query).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
-4
@@ -171,15 +171,11 @@ 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()),
|
||||
MediaFileType::SceneRon => Some("3d scene".to_string()),
|
||||
MediaFileType::SceneJson => Some("3d scene".to_string()),
|
||||
MediaFileType::MoodJson => Some("mood board".to_string()),
|
||||
MediaFileType::TimelineJson => Some("video timeline".to_string()),
|
||||
MediaFileType::EditorJson => Some("2d editor project".to_string()),
|
||||
MediaFileType::Pmd => Some("polygon data".to_string()),
|
||||
MediaFileType::Vmd => Some("motion data".to_string()),
|
||||
MediaFileType::Pmx => Some("pmx data".to_string()),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod audit_logs;
|
||||
pub mod batch_generations;
|
||||
pub mod featured_items;
|
||||
pub mod tag_uses;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod tag_use_entity;
|
||||
@@ -0,0 +1,40 @@
|
||||
use enums::by_table::tag_uses::tag_use_entity_type::TagUseEntityType;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::model_weights::ModelWeightToken;
|
||||
|
||||
// TODO(bt, 2024-01-16): A ref type with <'a> lifetime of inner data instead of ownership?
|
||||
pub enum TagUseEntity {
|
||||
MediaFile(MediaFileToken),
|
||||
ModelWeight(ModelWeightToken),
|
||||
}
|
||||
|
||||
// TODO(bt, 2024-01-16): Codegen? Make traits for these? Maybe overkill.
|
||||
impl TagUseEntity {
|
||||
pub fn from_entity_type_and_token(entity_type: TagUseEntityType, token: &str) -> Self {
|
||||
match entity_type {
|
||||
TagUseEntityType::ModelWeight => Self::ModelWeight(ModelWeightToken::new_from_str(token)),
|
||||
TagUseEntityType::MediaFile => Self::MediaFile(MediaFileToken::new_from_str(token)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_entity_type(&self) -> TagUseEntityType {
|
||||
match self {
|
||||
Self::MediaFile(_) => TagUseEntityType::MediaFile,
|
||||
Self::ModelWeight(_) => TagUseEntityType::ModelWeight,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_entity_token_str(&self) -> &str {
|
||||
match self {
|
||||
Self::MediaFile(token) => token.as_str(),
|
||||
Self::ModelWeight(token) => token.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_composite_keys(&self) -> (TagUseEntityType, &str) {
|
||||
match self {
|
||||
Self::MediaFile(token) => (TagUseEntityType::MediaFile, token.as_str()),
|
||||
Self::ModelWeight(token) => (TagUseEntityType::ModelWeight, token.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,8 +279,6 @@ 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!
|
||||
@@ -413,7 +411,6 @@ impl InferenceModelType {
|
||||
Self::Marble1p0Draft => "marble_1p0_draft",
|
||||
Self::Marble1p1 => "marble_1p1",
|
||||
Self::Marble1p1Plus => "marble_1p1_plus",
|
||||
Self::TripoSplat => "triposplat",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +538,6 @@ 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)),
|
||||
}
|
||||
@@ -673,7 +669,6 @@ impl InferenceModelType {
|
||||
Self::Marble1p0Draft,
|
||||
Self::Marble1p1,
|
||||
Self::Marble1p1Plus,
|
||||
Self::TripoSplat,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -788,7 +783,6 @@ impl InferenceModelType {
|
||||
CommonModelType::Marble1p0Draft => Self::Marble1p0Draft,
|
||||
CommonModelType::Marble1p1 => Self::Marble1p1,
|
||||
CommonModelType::Marble1p1Plus => Self::Marble1p1Plus,
|
||||
CommonModelType::TripoSplat => Self::TripoSplat,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -924,7 +918,6 @@ 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]
|
||||
@@ -1051,7 +1044,6 @@ 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]
|
||||
@@ -1176,7 +1168,6 @@ 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,21 +27,7 @@ pub enum MediaFileClass {
|
||||
Video,
|
||||
|
||||
/// 3D engine data: glb, gltf, etc.
|
||||
/// Being split into `Mesh`, `Splat`, and `Project`. Do not write new records
|
||||
/// with this value; it remains readable until existing rows are backfilled.
|
||||
#[deprecated(note = "Split into Mesh, Splat, and Project. Read-only until backfilled.")]
|
||||
Dimensional,
|
||||
|
||||
/// 3D mesh assets: glb, gltf, fbx, obj, pmx, pmd, etc.
|
||||
Mesh,
|
||||
|
||||
/// Gaussian splats: spz, etc.
|
||||
Splat,
|
||||
|
||||
/// Internal Artcraft project documents (JSON): 3D editor scenes, mood boards,
|
||||
/// graph workflows, video editor timelines, etc.
|
||||
/// The specific kind is stored in `maybe_project_type`.
|
||||
Project,
|
||||
}
|
||||
|
||||
// TODO(bt, 2022-12-21): This desperately needs MySQL integration tests!
|
||||
@@ -58,9 +44,6 @@ impl MediaFileClass {
|
||||
Self::Image => "image",
|
||||
Self::Video => "video",
|
||||
Self::Dimensional => "dimensional",
|
||||
Self::Mesh => "mesh",
|
||||
Self::Splat => "splat",
|
||||
Self::Project => "project",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +54,6 @@ impl MediaFileClass {
|
||||
"image" => Ok(Self::Image),
|
||||
"video" => Ok(Self::Video),
|
||||
"dimensional" => Ok(Self::Dimensional),
|
||||
"mesh" => Ok(Self::Mesh),
|
||||
"splat" => Ok(Self::Splat),
|
||||
"project" => Ok(Self::Project),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -87,9 +67,6 @@ impl MediaFileClass {
|
||||
Self::Image,
|
||||
Self::Video,
|
||||
Self::Dimensional,
|
||||
Self::Mesh,
|
||||
Self::Splat,
|
||||
Self::Project,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -109,9 +86,6 @@ mod tests {
|
||||
assert_serialization(MediaFileClass::Image, "image");
|
||||
assert_serialization(MediaFileClass::Video, "video");
|
||||
assert_serialization(MediaFileClass::Dimensional, "dimensional");
|
||||
assert_serialization(MediaFileClass::Mesh, "mesh");
|
||||
assert_serialization(MediaFileClass::Splat, "splat");
|
||||
assert_serialization(MediaFileClass::Project, "project");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +99,6 @@ mod tests {
|
||||
assert_eq!(MediaFileClass::Image.to_str(), "image");
|
||||
assert_eq!(MediaFileClass::Video.to_str(), "video");
|
||||
assert_eq!(MediaFileClass::Dimensional.to_str(), "dimensional");
|
||||
assert_eq!(MediaFileClass::Mesh.to_str(), "mesh");
|
||||
assert_eq!(MediaFileClass::Splat.to_str(), "splat");
|
||||
assert_eq!(MediaFileClass::Project.to_str(), "project");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -137,9 +108,6 @@ mod tests {
|
||||
assert_eq!(MediaFileClass::from_str("image").unwrap(), MediaFileClass::Image);
|
||||
assert_eq!(MediaFileClass::from_str("video").unwrap(), MediaFileClass::Video);
|
||||
assert_eq!(MediaFileClass::from_str("dimensional").unwrap(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileClass::from_str("mesh").unwrap(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileClass::from_str("splat").unwrap(), MediaFileClass::Splat);
|
||||
assert_eq!(MediaFileClass::from_str("project").unwrap(), MediaFileClass::Project);
|
||||
assert!(MediaFileClass::from_str("foo").is_err());
|
||||
}
|
||||
}
|
||||
@@ -150,15 +118,12 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileClass::all_variants();
|
||||
assert_eq!(variants.len(), 8);
|
||||
assert_eq!(variants.len(), 5);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Unknown));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Audio));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Image));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Video));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Dimensional));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Mesh));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Splat));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileClass::Project));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `media_files` table in the nullable `maybe_project_type`
|
||||
/// VARCHAR(32) field.
|
||||
///
|
||||
/// Identifies which kind of internal Artcraft project document a media file is.
|
||||
/// Only set when `media_class = 'project'`. The file format lives in
|
||||
/// `media_type` (`json` / legacy `scene_json`); the JSON payload carries its
|
||||
/// own schema version.
|
||||
///
|
||||
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MediaFileProjectType {
|
||||
/// 3D editor scene descriptor: objects with locations, skyboxes, animations,
|
||||
/// etc. — a full editable 3D scene.
|
||||
/// NB: serde's snake_case renames this to "scene3d" (no underscore before
|
||||
/// the digit), so the rename must be explicit.
|
||||
#[serde(rename = "scene_3d")]
|
||||
Scene3d,
|
||||
|
||||
/// Mood board: a collection of images on a lightweight graph.
|
||||
MoodBoard,
|
||||
|
||||
/// Graph workflow: a node-graph pipeline of image and video generation steps.
|
||||
Workflow,
|
||||
|
||||
/// Video editor project: a nonlinear video editing timeline.
|
||||
VideoTimeline,
|
||||
|
||||
/// 2D editor project: a 2D image editing document.
|
||||
/// NB: serde's snake_case renames this to "editor2d" (no underscore before
|
||||
/// the digit), so the rename must be explicit.
|
||||
#[serde(rename = "editor_2d")]
|
||||
Editor2d,
|
||||
}
|
||||
|
||||
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",
|
||||
Self::Editor2d => "editor_2d",
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
"editor_2d" => Ok(Self::Editor2d),
|
||||
_ => 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,
|
||||
Self::Editor2d,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[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");
|
||||
assert_serialization(MediaFileProjectType::Editor2d, "editor_2d");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
assert_eq!(MediaFileProjectType::Editor2d.to_str(), "editor_2d");
|
||||
}
|
||||
|
||||
#[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_eq!(MediaFileProjectType::from_str("editor_2d").unwrap(), MediaFileProjectType::Editor2d);
|
||||
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(), 5);
|
||||
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(), Some(MediaFileProjectType::Editor2d));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,6 @@ pub enum MediaFileType {
|
||||
/// Wavefront OBJ mesh files
|
||||
Obj,
|
||||
|
||||
/// PLY (polygon file format) files; also used for Gaussian splats.
|
||||
Ply,
|
||||
|
||||
/// glTF binary files (for Bevy)
|
||||
Glb,
|
||||
|
||||
@@ -61,15 +58,6 @@ pub enum MediaFileType {
|
||||
/// Alternate scene files.
|
||||
SceneJson,
|
||||
|
||||
/// Mood board project documents (JSON).
|
||||
MoodJson,
|
||||
|
||||
/// Video editor timeline project documents (JSON).
|
||||
TimelineJson,
|
||||
|
||||
/// 2D editor project documents (JSON).
|
||||
EditorJson,
|
||||
|
||||
/// "Polygon Model Data", character data for MikuMikuDance
|
||||
/// See: https://mikumikudance.fandom.com/wiki/MMD:Polygon_Model_Data
|
||||
Pmd,
|
||||
@@ -183,28 +171,21 @@ impl MediaFileType {
|
||||
| Self::Webm
|
||||
| Self::Mov => MediaFileClass::Video,
|
||||
|
||||
Self::Fbx
|
||||
Self::Bvh
|
||||
| Self::Fbx
|
||||
| Self::Obj
|
||||
| Self::Glb
|
||||
| Self::Gltf
|
||||
| Self::Spz
|
||||
| Self::SceneRon
|
||||
| Self::SceneJson
|
||||
| Self::Pmd
|
||||
| Self::Pmx => MediaFileClass::Mesh,
|
||||
|
||||
Self::Spz
|
||||
| Self::Ply => MediaFileClass::Splat,
|
||||
|
||||
// Animation and auxiliary data formats with no dedicated class yet.
|
||||
#[allow(deprecated)]
|
||||
Self::Bvh
|
||||
| Self::Vmd
|
||||
| Self::Pmx
|
||||
| Self::Csv => MediaFileClass::Dimensional,
|
||||
|
||||
Self::SceneRon
|
||||
| Self::SceneJson
|
||||
| Self::MoodJson
|
||||
| Self::TimelineJson
|
||||
| Self::EditorJson
|
||||
| Self::Json => MediaFileClass::Project,
|
||||
// Generic project documents don't have a class yet.
|
||||
Self::Json => MediaFileClass::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +220,6 @@ impl MediaFileType {
|
||||
"model/fbx" => Some(Self::Fbx),
|
||||
"application/fbx" => Some(Self::Fbx),
|
||||
"model/obj" => Some(Self::Obj),
|
||||
"model/ply" => Some(Self::Ply),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -299,15 +279,11 @@ impl MediaFileType {
|
||||
Self::Bvh => "bvh",
|
||||
Self::Fbx => "fbx",
|
||||
Self::Obj => "obj",
|
||||
Self::Ply => "ply",
|
||||
Self::Glb => "glb",
|
||||
Self::Gltf => "gltf",
|
||||
Self::Spz => "spz",
|
||||
Self::SceneRon => "scene_ron",
|
||||
Self::SceneJson => "scene_json",
|
||||
Self::MoodJson => "mood_json",
|
||||
Self::TimelineJson => "timeline_json",
|
||||
Self::EditorJson => "editor_json",
|
||||
Self::Pmd => "pmd",
|
||||
Self::Vmd => "vmd",
|
||||
Self::Pmx => "pmx",
|
||||
@@ -338,15 +314,11 @@ impl MediaFileType {
|
||||
"bvh" => Ok(Self::Bvh),
|
||||
"fbx" => Ok(Self::Fbx),
|
||||
"obj" => Ok(Self::Obj),
|
||||
"ply" => Ok(Self::Ply),
|
||||
"glb" => Ok(Self::Glb),
|
||||
"gltf" => Ok(Self::Gltf),
|
||||
"spz" => Ok(Self::Spz),
|
||||
"scene_ron" => Ok(Self::SceneRon),
|
||||
"scene_json" => Ok(Self::SceneJson),
|
||||
"mood_json" => Ok(Self::MoodJson),
|
||||
"timeline_json" => Ok(Self::TimelineJson),
|
||||
"editor_json" => Ok(Self::EditorJson),
|
||||
"pmd" => Ok(Self::Pmd),
|
||||
"vmd" => Ok(Self::Vmd),
|
||||
"pmx" => Ok(Self::Pmx),
|
||||
@@ -380,15 +352,11 @@ impl MediaFileType {
|
||||
Self::Bvh,
|
||||
Self::Fbx,
|
||||
Self::Obj,
|
||||
Self::Ply,
|
||||
Self::Glb,
|
||||
Self::Gltf,
|
||||
Self::Spz,
|
||||
Self::SceneRon,
|
||||
Self::SceneJson,
|
||||
Self::MoodJson,
|
||||
Self::TimelineJson,
|
||||
Self::EditorJson,
|
||||
Self::Pmd,
|
||||
Self::Vmd,
|
||||
Self::Pmx,
|
||||
@@ -452,15 +420,11 @@ mod tests {
|
||||
assert_serialization(MediaFileType::Bvh, "bvh");
|
||||
assert_serialization(MediaFileType::Fbx, "fbx");
|
||||
assert_serialization(MediaFileType::Obj, "obj");
|
||||
assert_serialization(MediaFileType::Ply, "ply");
|
||||
assert_serialization(MediaFileType::Glb, "glb");
|
||||
assert_serialization(MediaFileType::Gltf, "gltf");
|
||||
assert_serialization(MediaFileType::Spz, "spz");
|
||||
assert_serialization(MediaFileType::SceneRon, "scene_ron");
|
||||
assert_serialization(MediaFileType::SceneJson, "scene_json");
|
||||
assert_serialization(MediaFileType::MoodJson, "mood_json");
|
||||
assert_serialization(MediaFileType::TimelineJson, "timeline_json");
|
||||
assert_serialization(MediaFileType::EditorJson, "editor_json");
|
||||
assert_serialization(MediaFileType::Pmd, "pmd");
|
||||
assert_serialization(MediaFileType::Vmd, "vmd");
|
||||
assert_serialization(MediaFileType::Pmx, "pmx");
|
||||
@@ -498,9 +462,6 @@ mod tests {
|
||||
assert_eq!(MediaFileType::Spz.to_str(), "spz");
|
||||
assert_eq!(MediaFileType::SceneRon.to_str(), "scene_ron");
|
||||
assert_eq!(MediaFileType::SceneJson.to_str(), "scene_json");
|
||||
assert_eq!(MediaFileType::MoodJson.to_str(), "mood_json");
|
||||
assert_eq!(MediaFileType::TimelineJson.to_str(), "timeline_json");
|
||||
assert_eq!(MediaFileType::EditorJson.to_str(), "editor_json");
|
||||
assert_eq!(MediaFileType::Pmd.to_str(), "pmd");
|
||||
assert_eq!(MediaFileType::Vmd.to_str(), "vmd");
|
||||
assert_eq!(MediaFileType::Pmx.to_str(), "pmx");
|
||||
@@ -534,9 +495,6 @@ mod tests {
|
||||
assert_eq!(MediaFileType::from_str("spz").unwrap(), MediaFileType::Spz);
|
||||
assert_eq!(MediaFileType::from_str("scene_ron").unwrap(), MediaFileType::SceneRon);
|
||||
assert_eq!(MediaFileType::from_str("scene_json").unwrap(), MediaFileType::SceneJson);
|
||||
assert_eq!(MediaFileType::from_str("mood_json").unwrap(), MediaFileType::MoodJson);
|
||||
assert_eq!(MediaFileType::from_str("timeline_json").unwrap(), MediaFileType::TimelineJson);
|
||||
assert_eq!(MediaFileType::from_str("editor_json").unwrap(), MediaFileType::EditorJson);
|
||||
assert_eq!(MediaFileType::from_str("pmd").unwrap(), MediaFileType::Pmd);
|
||||
assert_eq!(MediaFileType::from_str("vmd").unwrap(), MediaFileType::Vmd);
|
||||
assert_eq!(MediaFileType::from_str("pmx").unwrap(), MediaFileType::Pmx);
|
||||
@@ -565,20 +523,16 @@ mod tests {
|
||||
assert_eq!(MediaFileType::Image.to_media_class(), MediaFileClass::Image);
|
||||
assert_eq!(MediaFileType::Video.to_media_class(), MediaFileClass::Video);
|
||||
assert_eq!(MediaFileType::Bvh.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Fbx.to_media_class(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileType::Obj.to_media_class(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileType::Glb.to_media_class(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileType::Gltf.to_media_class(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileType::Spz.to_media_class(), MediaFileClass::Splat);
|
||||
assert_eq!(MediaFileType::Ply.to_media_class(), MediaFileClass::Splat);
|
||||
assert_eq!(MediaFileType::SceneRon.to_media_class(), MediaFileClass::Project);
|
||||
assert_eq!(MediaFileType::SceneJson.to_media_class(), MediaFileClass::Project);
|
||||
assert_eq!(MediaFileType::MoodJson.to_media_class(), MediaFileClass::Project);
|
||||
assert_eq!(MediaFileType::TimelineJson.to_media_class(), MediaFileClass::Project);
|
||||
assert_eq!(MediaFileType::EditorJson.to_media_class(), MediaFileClass::Project);
|
||||
assert_eq!(MediaFileType::Pmd.to_media_class(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileType::Fbx.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Obj.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Glb.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Gltf.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Spz.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::SceneRon.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::SceneJson.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Pmd.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Vmd.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Pmx.to_media_class(), MediaFileClass::Mesh);
|
||||
assert_eq!(MediaFileType::Pmx.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Csv.to_media_class(), MediaFileClass::Dimensional);
|
||||
assert_eq!(MediaFileType::Jpg.to_media_class(), MediaFileClass::Image);
|
||||
assert_eq!(MediaFileType::Png.to_media_class(), MediaFileClass::Image);
|
||||
@@ -594,7 +548,7 @@ mod tests {
|
||||
assert_eq!(MediaFileType::Aac.to_media_class(), MediaFileClass::Audio);
|
||||
assert_eq!(MediaFileType::M4a.to_media_class(), MediaFileClass::Audio);
|
||||
assert_eq!(MediaFileType::Flac.to_media_class(), MediaFileClass::Audio);
|
||||
assert_eq!(MediaFileType::Json.to_media_class(), MediaFileClass::Project);
|
||||
assert_eq!(MediaFileType::Json.to_media_class(), MediaFileClass::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -738,22 +692,18 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = MediaFileType::all_variants();
|
||||
assert_eq!(variants.len(), 34);
|
||||
assert_eq!(variants.len(), 30);
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Audio));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Image));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Video));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Bvh));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Fbx));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Obj));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Ply));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Glb));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Gltf));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Spz));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::SceneRon));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::SceneJson));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::MoodJson));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::TimelineJson));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::EditorJson));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Pmd));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Vmd));
|
||||
assert_eq!(variants.pop_first(), Some(MediaFileType::Pmx));
|
||||
|
||||
@@ -4,6 +4,5 @@ pub mod media_file_engine_category;
|
||||
pub mod media_file_origin_category;
|
||||
pub mod media_file_origin_model_type;
|
||||
pub mod media_file_origin_product_category;
|
||||
pub mod media_file_project_type;
|
||||
pub mod media_file_subtype;
|
||||
pub mod media_file_type;
|
||||
@@ -18,6 +18,7 @@ pub mod model_weights;
|
||||
pub mod prompt_context_items;
|
||||
pub mod prompts;
|
||||
pub mod staff_audit_logs;
|
||||
pub mod tag_uses;
|
||||
pub mod trending_model_analytics;
|
||||
pub mod tts_models;
|
||||
pub mod uploaded_video_notes;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod tag_use_entity_type;
|
||||
@@ -0,0 +1,127 @@
|
||||
use std::collections::BTreeSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Used in the `tag_uses` table in a `VARCHAR(32)` field named `entity_type`.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)]
|
||||
pub enum TagUseEntityType {
|
||||
/// Media files
|
||||
#[serde(rename = "media_file")]
|
||||
MediaFile,
|
||||
|
||||
/// Model weights
|
||||
#[serde(rename = "model_weight")]
|
||||
ModelWeight,
|
||||
}
|
||||
|
||||
// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests!
|
||||
impl_enum_display_and_debug_using_to_str!(TagUseEntityType);
|
||||
impl_mysql_enum_coders!(TagUseEntityType);
|
||||
|
||||
/// NB: Legacy API for older code.
|
||||
impl TagUseEntityType {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::MediaFile => "media_file",
|
||||
Self::ModelWeight => "model_weight",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"media_file" => Ok(Self::MediaFile),
|
||||
"model_weight" => Ok(Self::ModelWeight),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
// NB: BTreeSet is sorted
|
||||
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
|
||||
BTreeSet::from([
|
||||
Self::MediaFile,
|
||||
Self::ModelWeight,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test_helpers::assert_serialization;
|
||||
use crate::by_table::tag_uses::tag_use_entity_type::TagUseEntityType;
|
||||
|
||||
mod serde {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(TagUseEntityType::MediaFile, "media_file");
|
||||
assert_serialization(TagUseEntityType::ModelWeight, "model_weight");
|
||||
}
|
||||
}
|
||||
|
||||
mod impl_methods {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_to_str() {
|
||||
assert_eq!(TagUseEntityType::MediaFile.to_str(), "media_file");
|
||||
assert_eq!(TagUseEntityType::ModelWeight.to_str(), "model_weight");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
assert_eq!(TagUseEntityType::from_str("media_file").unwrap(), TagUseEntityType::MediaFile);
|
||||
assert_eq!(TagUseEntityType::from_str("model_weight").unwrap(), TagUseEntityType::ModelWeight);
|
||||
assert!(TagUseEntityType::from_str("foo").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
mod manual_variant_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = TagUseEntityType::all_variants();
|
||||
assert_eq!(variants.len(), 2);
|
||||
assert_eq!(variants.pop_first(), Some(TagUseEntityType::MediaFile));
|
||||
assert_eq!(variants.pop_first(), Some(TagUseEntityType::ModelWeight));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(TagUseEntityType::all_variants().len(), TagUseEntityType::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in TagUseEntityType::all_variants() {
|
||||
assert_eq!(variant, TagUseEntityType::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, TagUseEntityType::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, TagUseEntityType::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
const MAX_LENGTH : usize = 32;
|
||||
for variant in TagUseEntityType::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,8 +248,6 @@ pub enum CommonModelType {
|
||||
Marble1p1,
|
||||
#[serde(rename = "marble_1p1_plus")]
|
||||
Marble1p1Plus,
|
||||
#[serde(rename = "triposplat")]
|
||||
TripoSplat,
|
||||
}
|
||||
|
||||
impl_enum_display_and_debug_using_to_str!(CommonModelType);
|
||||
@@ -366,7 +364,6 @@ impl CommonModelType {
|
||||
Self::Marble1p0Draft => "marble_1p0_draft",
|
||||
Self::Marble1p1 => "marble_1p1",
|
||||
Self::Marble1p1Plus => "marble_1p1_plus",
|
||||
Self::TripoSplat => "triposplat",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +474,6 @@ impl CommonModelType {
|
||||
"marble_1p0_draft" => Ok(Self::Marble1p0Draft),
|
||||
"marble_1p1" => Ok(Self::Marble1p1),
|
||||
"marble_1p1_plus" => Ok(Self::Marble1p1Plus),
|
||||
"triposplat" => Ok(Self::TripoSplat),
|
||||
|
||||
_ => Err(format!("invalid model_type: {:?}", job_status)),
|
||||
}
|
||||
@@ -592,7 +588,6 @@ impl CommonModelType {
|
||||
Self::Marble1p0Draft,
|
||||
Self::Marble1p1,
|
||||
Self::Marble1p1Plus,
|
||||
Self::TripoSplat,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -704,7 +699,6 @@ impl CommonModelType {
|
||||
Self::Marble1p0Draft => CommonModelClass::DimensionalSplat,
|
||||
Self::Marble1p1 => CommonModelClass::DimensionalSplat,
|
||||
Self::Marble1p1Plus => CommonModelClass::DimensionalSplat,
|
||||
Self::TripoSplat => CommonModelClass::DimensionalSplat,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -821,7 +815,6 @@ mod tests {
|
||||
assert_serialization(CommonModelType::Marble1p0Draft, "marble_1p0_draft");
|
||||
assert_serialization(CommonModelType::Marble1p1, "marble_1p1");
|
||||
assert_serialization(CommonModelType::Marble1p1Plus, "marble_1p1_plus");
|
||||
assert_serialization(CommonModelType::TripoSplat, "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -930,7 +923,6 @@ mod tests {
|
||||
assert_eq!(CommonModelType::Marble1p0Draft.to_str(), "marble_1p0_draft");
|
||||
assert_eq!(CommonModelType::Marble1p1.to_str(), "marble_1p1");
|
||||
assert_eq!(CommonModelType::Marble1p1Plus.to_str(), "marble_1p1_plus");
|
||||
assert_eq!(CommonModelType::TripoSplat.to_str(), "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1036,13 +1028,12 @@ mod tests {
|
||||
assert_eq!(CommonModelType::from_str("marble_1p0_draft").unwrap(), CommonModelType::Marble1p0Draft);
|
||||
assert_eq!(CommonModelType::from_str("marble_1p1").unwrap(), CommonModelType::Marble1p1);
|
||||
assert_eq!(CommonModelType::from_str("marble_1p1_plus").unwrap(), CommonModelType::Marble1p1Plus);
|
||||
assert_eq!(CommonModelType::from_str("triposplat").unwrap(), CommonModelType::TripoSplat);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = CommonModelType::all_variants();
|
||||
assert_eq!(variants.len(), 96);
|
||||
assert_eq!(variants.len(), 95);
|
||||
// Image models
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Dev));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Flux1Schnell));
|
||||
@@ -1143,7 +1134,6 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Marble1p0Draft));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Marble1p1));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::Marble1p1Plus));
|
||||
assert_eq!(variants.pop_first(), Some(CommonModelType::TripoSplat));
|
||||
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ pub enum CommonSplatModel {
|
||||
|
||||
#[serde(rename = "marble_1p1_plus")]
|
||||
Marble1p1Plus,
|
||||
|
||||
/// TripoSplat: single-image to 3D Gaussian splat (via Fal).
|
||||
#[serde(rename = "triposplat")]
|
||||
TripoSplat,
|
||||
}
|
||||
|
||||
impl CommonSplatModel {
|
||||
@@ -37,7 +33,6 @@ impl CommonSplatModel {
|
||||
Self::Marble1p0Draft => CommonModelType::Marble1p0Draft,
|
||||
Self::Marble1p1 => CommonModelType::Marble1p1,
|
||||
Self::Marble1p1Plus => CommonModelType::Marble1p1Plus,
|
||||
Self::TripoSplat => CommonModelType::TripoSplat,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +51,6 @@ mod tests {
|
||||
assert_serialization(CommonSplatModel::Marble1p0Draft, "marble_1p0_draft");
|
||||
assert_serialization(CommonSplatModel::Marble1p1, "marble_1p1");
|
||||
assert_serialization(CommonSplatModel::Marble1p1Plus, "marble_1p1_plus");
|
||||
assert_serialization(CommonSplatModel::TripoSplat, "triposplat");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -68,7 +62,6 @@ mod tests {
|
||||
("marble_1p0_draft", CommonSplatModel::Marble1p0Draft),
|
||||
("marble_1p1", CommonSplatModel::Marble1p1),
|
||||
("marble_1p1_plus", CommonSplatModel::Marble1p1Plus),
|
||||
("triposplat", CommonSplatModel::TripoSplat),
|
||||
];
|
||||
for (json_str, expected) in cases {
|
||||
let json = format!("\"{}\"", json_str);
|
||||
@@ -87,7 +80,6 @@ mod tests {
|
||||
CommonSplatModel::Marble1p0Draft,
|
||||
CommonSplatModel::Marble1p1,
|
||||
CommonSplatModel::Marble1p1Plus,
|
||||
CommonSplatModel::TripoSplat,
|
||||
];
|
||||
for variant in all {
|
||||
let json = serde_json::to_string(&variant).unwrap();
|
||||
@@ -105,7 +97,6 @@ mod tests {
|
||||
(CommonSplatModel::Marble1p0Draft, CommonModelType::Marble1p0Draft),
|
||||
(CommonSplatModel::Marble1p1, CommonModelType::Marble1p1),
|
||||
(CommonSplatModel::Marble1p1Plus, CommonModelType::Marble1p1Plus),
|
||||
(CommonSplatModel::TripoSplat, CommonModelType::TripoSplat),
|
||||
];
|
||||
for (splat_model, expected) in models {
|
||||
assert_eq!(splat_model.to_common_model_type(), expected);
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@ pub async fn process_job(args: FbxToGltfJobArgs<'_>) -> Result<JobSuccessResult,
|
||||
let (inference_result_token, id) = insert_media_file_generic_from_job(InsertFromJobArgs {
|
||||
pool: &args.job_dependencies.db.mysql_pool,
|
||||
job: &job,
|
||||
media_class: MediaFileClass::Mesh,
|
||||
media_class: MediaFileClass::Dimensional,
|
||||
media_type: upload_details.media_type,
|
||||
origin_category: MediaFileOriginCategory::Processed,
|
||||
origin_product_category: MediaFileOriginProductCategory::Mocap,
|
||||
|
||||
@@ -122,7 +122,7 @@ pub async fn process_successful_job(
|
||||
.maybe_generation_provider(Some(GenerationProvider::Artcraft))
|
||||
.maybe_prompt_token(job.maybe_prompt_token.as_ref())
|
||||
.maybe_platform_type(job.maybe_platform_type)
|
||||
.media_file_class(MediaFileClass::Splat)
|
||||
.media_file_class(MediaFileClass::Dimensional)
|
||||
.media_file_origin_category(MediaFileOriginCategory::Inference)
|
||||
.media_file_origin_product_category(MediaFileOriginProductCategory::WorldGeneration)
|
||||
.media_file_type(MediaFileType::Spz)
|
||||
|
||||
@@ -28,7 +28,6 @@ fn build_omni_gen_splat_models() -> Vec<OmniGenSplatModelDetails> {
|
||||
marble_model(CommonSplatModel::Marble1p0Draft, "Marble 1.0 Draft"),
|
||||
marble_model(CommonSplatModel::Marble1p1, "Marble 1.1"),
|
||||
marble_model(CommonSplatModel::Marble1p1Plus, "Marble 1.1 Plus"),
|
||||
triposplat_model(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -54,31 +53,12 @@ fn build_omni_gen_splat_model_providers() -> Vec<OmniGenSplatModelProviderDetail
|
||||
model: CommonSplatModel::Marble1p1Plus,
|
||||
overrides: None,
|
||||
},
|
||||
OmniGenSplatProviderModelDetails {
|
||||
model: CommonSplatModel::TripoSplat,
|
||||
overrides: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
providers
|
||||
}
|
||||
|
||||
/// TripoSplat reconstructs an object-scale Gaussian splat from exactly one
|
||||
/// image. No prompt, video, panorama, or recaption toggle.
|
||||
fn triposplat_model() -> OmniGenSplatModelDetails {
|
||||
OmniGenSplatModelDetails {
|
||||
model: CommonSplatModel::TripoSplat,
|
||||
model_creator: Some(ModelCreator::Tripo),
|
||||
full_name: Some("TripoSplat".to_string()),
|
||||
extra_info: Some("Reconstructs a 3D Gaussian splat from a single image".to_string()),
|
||||
extra_info_short: Some("Image to splat".to_string()),
|
||||
image_references_supported: Some(true),
|
||||
image_references_max: Some(1),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// All Marble models share the same capability surface: text prompt, image
|
||||
/// references (up to 4, multi-view), a reference video, 360-degree panorama
|
||||
/// input, and the "disable recaption" toggle.
|
||||
|
||||
@@ -12,8 +12,10 @@ use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
|
||||
use crate::http_server::common_responses::pagination_page::PaginationPage;
|
||||
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
|
||||
use crate::http_server::common_responses::simple_response::SimpleResponse;
|
||||
use crate::http_server::common_responses::tag_info::TagInfo;
|
||||
use crate::http_server::common_responses::user_details_lite::{UserDefaultAvatarInfo, UserDetailsLight};
|
||||
use crate::http_server::deprecated_endpoints::conversion::enqueue_fbx_to_gltf_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::engine::create_scene_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_face_fusion_workflow_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_live_portrait_workflow_handler::*;
|
||||
use crate::http_server::deprecated_endpoints::workflows::enqueue::vst_common::vst_error::*;
|
||||
@@ -64,21 +66,12 @@ use crate::http_server::endpoints::media_files::list::list_pinned_media_files_ha
|
||||
use crate::http_server::endpoints::media_files::search::search_featured_media_files_handler::*;
|
||||
use crate::http_server::endpoints::media_files::search::search_session_media_files_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_audio_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_engine_asset::upload_engine_asset_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_error::MediaFileUploadError;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_generic::upload_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_image_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_new_engine_asset_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_new_scene_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_updated_mood_board_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_new_mood_board_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_updated_video_timeline_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_new_video_timeline_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_updated_editor_2d_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_new_editor_2d_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_updated_scene_3d_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::upload_new_scene_3d_project_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::project::save_new_project::NewProjectMultipartForm;
|
||||
use crate::http_server::endpoints::media_files::upload::project::update_project::UpdateProjectMultipartForm;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_pmx::upload_pmx_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_saved_scene_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_scene_snapshot_media_file_handler::*;
|
||||
@@ -89,6 +82,9 @@ use crate::http_server::endpoints::media_files::upload::upload_video_old::upload
|
||||
use crate::http_server::endpoints::omni_api::upload::omni_upload_audio_media_file_handler::*;
|
||||
use crate::http_server::endpoints::omni_api::upload::omni_upload_image_media_file_handler::*;
|
||||
use crate::http_server::endpoints::omni_api::upload::omni_upload_video_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upsert_upload::write_engine_asset::write_engine_asset_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upsert_upload::write_error::MediaFileWriteError;
|
||||
use crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::*;
|
||||
use crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::*;
|
||||
use artcraft_api_defs::generate::video::edit::beeble_switchx_edit_video::*;
|
||||
use artcraft_api_defs::moderation::debug_logs::debug_log_entry::*;
|
||||
@@ -221,6 +217,8 @@ use billing_component::stripe::http_endpoints::checkout::create::stripe_create_c
|
||||
use crate::http_server::endpoints::billing_fakeyou::list_active_user_subscriptions_handler::*;
|
||||
use crate::http_server::endpoints::service::status_alert_handler::*;
|
||||
use crate::http_server::endpoints::stats::get_unified_queue_stats_handler::*;
|
||||
use crate::http_server::endpoints::tags::list_tags_for_entity_handler::*;
|
||||
use crate::http_server::endpoints::tags::set_tags_for_entity_handler::*;
|
||||
use crate::http_server::endpoints::tts::enqueue_infer_tts_handler::enqueue_infer_tts_handler::*;
|
||||
use crate::http_server::endpoints::user_bookmarks::batch_get_user_bookmarks_handler::*;
|
||||
use crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::*;
|
||||
@@ -335,15 +333,6 @@ use artcraft_api_defs::analytics::log_active_user::*;
|
||||
use artcraft_api_defs::credits::get_session_credits::*;
|
||||
use artcraft_api_defs::subscriptions::get_session_subscription::*;
|
||||
use artcraft_api_defs::media_file::list_batch_generated_media_files::*;
|
||||
use artcraft_api_defs::media_file::list_session_project_media_files::*;
|
||||
use artcraft_api_defs::media_file::project::upload_updated_mood_board_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_new_mood_board_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_updated_video_timeline_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_new_video_timeline_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_updated_editor_2d_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_new_editor_2d_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_updated_scene_3d_project::*;
|
||||
use artcraft_api_defs::media_file::project::upload_new_scene_3d_project::*;
|
||||
// Handler modules with locally-defined types
|
||||
use crate::http_server::endpoints::moderation::info::moderator_token_info_handler::*;
|
||||
use artcraft_api_defs::characters::create_character::*;
|
||||
@@ -423,6 +412,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_json_handler::stripe_create_checkout_session_json_handler,
|
||||
crate::http_server::endpoints::billing_fakeyou::list_active_user_subscriptions_handler::list_active_user_subscriptions_handler,
|
||||
crate::http_server::deprecated_endpoints::conversion::enqueue_fbx_to_gltf_handler::enqueue_fbx_to_gltf_handler,
|
||||
crate::http_server::deprecated_endpoints::engine::create_scene_handler::create_scene_handler,
|
||||
crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_face_fusion_workflow_handler::enqueue_face_fusion_workflow_handler,
|
||||
crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_live_portrait_workflow_handler::enqueue_live_portrait_workflow_handler,
|
||||
crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_studio_workflow_handler::enqueue_studio_workflow_handler,
|
||||
@@ -477,22 +467,14 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
crate::http_server::endpoints::media_files::list::list_media_files_for_user_handler::list_media_files_for_user_handler,
|
||||
crate::http_server::endpoints::media_files::list::list_media_files_handler::list_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::list::list_pinned_media_files_handler::list_pinned_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::list::list_session_project_media_files_handler::list_session_project_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::search::search_featured_media_files_handler::search_featured_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::search::search_session_media_files_handler::search_session_media_files_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_audio_media_file_handler::upload_audio_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_engine_asset::upload_engine_asset_media_file_handler::upload_engine_asset_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_generic::upload_media_file_handler::upload_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_image_media_file_handler::upload_image_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_new_engine_asset_media_file_handler::upload_new_engine_asset_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_new_scene_media_file_handler::upload_new_scene_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_new_mood_board_project_handler::upload_new_mood_board_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_updated_mood_board_project_handler::upload_updated_mood_board_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_new_video_timeline_project_handler::upload_new_video_timeline_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_updated_video_timeline_project_handler::upload_updated_video_timeline_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_new_editor_2d_project_handler::upload_new_editor_2d_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_updated_editor_2d_project_handler::upload_updated_editor_2d_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_new_scene_3d_project_handler::upload_new_scene_3d_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::project::upload_updated_scene_3d_project_handler::upload_updated_scene_3d_project_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_spz_media_file_handler::upload_spz_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_pmx::upload_pmx_media_file_handler::upload_pmx_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_saved_scene_media_file_handler::upload_saved_scene_media_file_handler,
|
||||
@@ -500,6 +482,8 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
crate::http_server::endpoints::media_files::upload::upload_studio_shot::upload_studio_shot_media_file_handler::upload_studio_shot_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_video_new::upload_new_video_media_file_handler::upload_new_video_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upload::upload_video_old::upload_video_media_file_handler::upload_video_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upsert_upload::write_engine_asset::write_engine_asset_media_file_handler::write_engine_asset_media_file_handler,
|
||||
crate::http_server::endpoints::media_files::upsert_upload::write_scene_file::write_scene_file_media_file_handler::write_scene_file_media_file_handler,
|
||||
crate::http_server::endpoints::model_download::enqueue_gptsovits_model_download_handler::enqueue_gptsovits_model_download_handler,
|
||||
crate::http_server::endpoints::moderation::user_feature_flags::moderator_edit_user_feature_flags_handler::moderator_edit_user_feature_flags_handler,
|
||||
crate::http_server::endpoints::moderation::user_feature_flags::moderator_list_all_available_user_feature_flags_handler::moderator_list_all_available_user_feature_flags_handler,
|
||||
@@ -509,6 +493,8 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
crate::http_server::endpoints::prompts::get_prompt_handler::get_prompt_handler,
|
||||
crate::http_server::endpoints::service::status_alert_handler::status_alert_handler,
|
||||
crate::http_server::endpoints::stats::get_unified_queue_stats_handler::get_unified_queue_stats_handler,
|
||||
crate::http_server::endpoints::tags::list_tags_for_entity_handler::list_tags_for_entity_handler,
|
||||
crate::http_server::endpoints::tags::set_tags_for_entity_handler::set_tags_for_entity_handler,
|
||||
crate::http_server::endpoints::tts::enqueue_infer_tts_handler::enqueue_infer_tts_handler::enqueue_infer_tts_handler,
|
||||
crate::http_server::endpoints::user_bookmarks::batch_get_user_bookmarks_handler::batch_get_user_bookmarks_handler,
|
||||
crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::create_user_bookmark_handler,
|
||||
@@ -751,12 +737,15 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
MediaFileModelDetails,
|
||||
MediaFileOriginDetails,
|
||||
MediaFileSocialMetaLight,
|
||||
MediaFileWriteError,
|
||||
MediaFileWriteError,
|
||||
MediaLinks,
|
||||
PaginationCursors,
|
||||
PaginationPage,
|
||||
SimpleEntityStats,
|
||||
SimpleGenericJsonSuccess,
|
||||
SimpleResponse,
|
||||
TagInfo,
|
||||
UserDetailsLight,
|
||||
VideoPreviews,
|
||||
Visibility,
|
||||
@@ -828,6 +817,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
CreateCommentSuccessResponse,
|
||||
CreateFeaturedItemRequest,
|
||||
CreateFeaturedItemSuccessResponse,
|
||||
CreateSceneSuccessResponse,
|
||||
CreateUserBookmarkError,
|
||||
CreateUserBookmarkRequest,
|
||||
CreateUserBookmarkSuccessResponse,
|
||||
@@ -1090,11 +1080,12 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
ListPinnedWeightsSuccessResponse,
|
||||
ListSessionJobsItem,
|
||||
ListSessionJobsQueryParams,
|
||||
ListSessionProjectMediaFilesSuccessResponse,
|
||||
ListSessionJobsSuccessResponse,
|
||||
ListSessionRequestDetailsResponse,
|
||||
ListSessionResultDetailsResponse,
|
||||
ListSessionStatusDetailsResponse,
|
||||
ListTagsForEntityPathInfo,
|
||||
ListTagsForEntitySuccessResponse,
|
||||
ListUserBookmarksForEntityError,
|
||||
ListUserBookmarksForEntityPathInfo,
|
||||
ListUserBookmarksForEntitySuccessResponse,
|
||||
@@ -1130,10 +1121,8 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
ModeratorUserEmailChangeItem,
|
||||
ModeratorUserEmailChangeUserSummary,
|
||||
ModernInferenceQueueStats,
|
||||
NewProjectMultipartForm,
|
||||
PinnedMediaFile,
|
||||
PinnedModelWeightForList,
|
||||
ProjectMediaFileInfo,
|
||||
PromptInfo,
|
||||
RatingRow,
|
||||
RedeemBetaKeyRequest,
|
||||
@@ -1160,6 +1149,9 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
SetModelWeightCoverImagePathInfo,
|
||||
SetModelWeightCoverImageRequest,
|
||||
SetModelWeightCoverImageResponse,
|
||||
SetTagsForEntityPathInfo,
|
||||
SetTagsForEntityRequest,
|
||||
SetTagsForEntitySuccessResponse,
|
||||
SetUserRatingRequest,
|
||||
SetUserRatingResponse,
|
||||
StatusAlertCategory,
|
||||
@@ -1173,6 +1165,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
UpdateWeightRequest,
|
||||
UploadAudioMediaFileForm,
|
||||
UploadAudioMediaFileSuccessResponse,
|
||||
UploadEngineAssetMediaSuccessResponse,
|
||||
UploadImageMediaFileForm,
|
||||
UploadImageMediaFileSuccessResponse,
|
||||
OmniUploadAudioMediaFileForm,
|
||||
@@ -1185,19 +1178,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
UploadMediaSuccessResponse,
|
||||
UploadNewEngineAssetFileForm,
|
||||
UploadNewEngineAssetSuccessResponse,
|
||||
UploadUpdatedEditor2dProjectPathInfo,
|
||||
UpdateProjectMultipartForm,
|
||||
UploadUpdatedEditor2dProjectSuccessResponse,
|
||||
UploadUpdatedMoodBoardProjectPathInfo,
|
||||
UploadUpdatedMoodBoardProjectSuccessResponse,
|
||||
UploadUpdatedScene3dProjectPathInfo,
|
||||
UploadUpdatedScene3dProjectSuccessResponse,
|
||||
UploadUpdatedVideoTimelineProjectPathInfo,
|
||||
UploadUpdatedVideoTimelineProjectSuccessResponse,
|
||||
UploadNewEditor2dProjectSuccessResponse,
|
||||
UploadNewMoodBoardProjectSuccessResponse,
|
||||
UploadNewScene3dProjectSuccessResponse,
|
||||
UploadNewVideoTimelineProjectSuccessResponse,
|
||||
UploadNewSceneMediaFileForm,
|
||||
UploadNewSceneMediaFileSuccessResponse,
|
||||
UploadSpzMediaFileForm,
|
||||
@@ -1230,6 +1210,8 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
VstSuccessResponse,
|
||||
Weight,
|
||||
WeightsData,
|
||||
WriteEngineAssetMediaSuccessResponse,
|
||||
WriteSceneFileMediaSuccessResponse,
|
||||
|
||||
// Cost Estimate types
|
||||
EstimateImageCostRequest,
|
||||
|
||||
@@ -6,6 +6,7 @@ pub mod pagination_cursors;
|
||||
pub mod pagination_page;
|
||||
pub mod simple_entity_stats;
|
||||
pub mod simple_response;
|
||||
pub mod tag_info;
|
||||
pub mod user_avatars;
|
||||
pub mod user_details_lite;
|
||||
pub mod user_details_lite_builder;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user