Compare commits

...
Author SHA1 Message Date
Brandon Thomas 37992cb354 list project media files endpoint 2026-07-09 20:08:29 -04:00
14 changed files with 1531 additions and 1 deletions
@@ -0,0 +1,223 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.maybe_project_type = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id > ?\nORDER BY m.id ASC\nLIMIT ?\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id: u64",
"type_info": {
"type": "LongLong",
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
"char_set": 63,
"max_size": 20
}
},
{
"ordinal": 1,
"name": "token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 2,
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 3,
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
"type_info": {
"type": "VarString",
"flags": "MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 4,
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 5,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 6,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 7,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 8,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 9,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 10,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 11,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 12,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 13,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 14,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 15,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
"flags": "NOT_NULL | BINARY | ENUM",
"char_set": 224,
"max_size": 28
}
},
{
"ordinal": 16,
"name": "maybe_title",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 17,
"name": "created_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
"char_set": 63,
"max_size": 19
}
},
{
"ordinal": 18,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
"char_set": 63,
"max_size": 19
}
}
],
"parameters": {
"Right": 4
},
"nullable": [
false,
false,
false,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
false,
true,
false,
false
]
},
"hash": "11cc7b91730784e9e8c8f073aeaf51a1f1660584e379b9a5be3aea370d84f692"
}
@@ -0,0 +1,223 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.maybe_project_type = ?\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id < ?\nORDER BY m.id DESC\nLIMIT ?\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id: u64",
"type_info": {
"type": "LongLong",
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
"char_set": 63,
"max_size": 20
}
},
{
"ordinal": 1,
"name": "token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 2,
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 3,
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
"type_info": {
"type": "VarString",
"flags": "MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 4,
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 5,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 6,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 7,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 8,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 9,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 10,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 11,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 12,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 13,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 14,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 15,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
"flags": "NOT_NULL | BINARY | ENUM",
"char_set": 224,
"max_size": 28
}
},
{
"ordinal": 16,
"name": "maybe_title",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 17,
"name": "created_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
"char_set": 63,
"max_size": 19
}
},
{
"ordinal": 18,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
"char_set": 63,
"max_size": 19
}
}
],
"parameters": {
"Right": 4
},
"nullable": [
false,
false,
false,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
false,
true,
false,
false
]
},
"hash": "6c7adbc8171bc3fdfb6109193ffb0a55413fa0942746d65037c2b1d39b361bd0"
}
@@ -0,0 +1,223 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id < ?\nORDER BY m.id DESC\nLIMIT ?\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id: u64",
"type_info": {
"type": "LongLong",
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
"char_set": 63,
"max_size": 20
}
},
{
"ordinal": 1,
"name": "token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 2,
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 3,
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
"type_info": {
"type": "VarString",
"flags": "MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 4,
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 5,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 6,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 7,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 8,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 9,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 10,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 11,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 12,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 13,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 14,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 15,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
"flags": "NOT_NULL | BINARY | ENUM",
"char_set": 224,
"max_size": 28
}
},
{
"ordinal": 16,
"name": "maybe_title",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 17,
"name": "created_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
"char_set": 63,
"max_size": 19
}
},
{
"ordinal": 18,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
"char_set": 63,
"max_size": 19
}
}
],
"parameters": {
"Right": 3
},
"nullable": [
false,
false,
false,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
false,
true,
false,
false
]
},
"hash": "97e4bc3ab037126e3f4e213cdb7535c2ad233809fbeb6607c34fb02ec45cfe47"
}
@@ -0,0 +1,223 @@
{
"db_name": "MySQL",
"query": "\nSELECT\n m.id as `id: u64`,\n m.token as `token: tokens::tokens::media_files::MediaFileToken`,\n\n m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,\n m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,\n m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,\n\n m.public_bucket_directory_hash,\n m.maybe_public_bucket_prefix,\n m.maybe_public_bucket_extension,\n\n users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,\n users.username as maybe_creator_username,\n users.display_name as maybe_creator_display_name,\n users.email_gravatar_hash as maybe_creator_gravatar_hash,\n\n media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,\n media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,\n media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,\n\n m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,\n\n m.maybe_title,\n\n m.created_at,\n m.updated_at\n\nFROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)\nLEFT OUTER JOIN users\n ON m.maybe_creator_user_token = users.token\nLEFT OUTER JOIN media_files as media_file_cover_image\n ON media_file_cover_image.token = m.maybe_cover_image_media_file_token\nWHERE\n m.maybe_creator_user_token = ?\n AND m.media_class = 'project'\n AND m.maybe_project_type IS NOT NULL\n AND m.user_deleted_at IS NULL\n AND m.mod_deleted_at IS NULL\n AND NOT m.is_intermediate_system_file\n AND m.id > ?\nORDER BY m.id ASC\nLIMIT ?\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id: u64",
"type_info": {
"type": "LongLong",
"flags": "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT",
"char_set": 63,
"max_size": 20
}
},
{
"ordinal": 1,
"name": "token: tokens::tokens::media_files::MediaFileToken",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 2,
"name": "media_class: enums::by_table::media_files::media_file_class::MediaFileClass",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 3,
"name": "project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType",
"type_info": {
"type": "VarString",
"flags": "MULTIPLE_KEY | BINARY",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 4,
"name": "media_type: enums::by_table::media_files::media_file_type::MediaFileType",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 5,
"name": "public_bucket_directory_hash",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 6,
"name": "maybe_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 7,
"name": "maybe_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 8,
"name": "maybe_creator_user_token: tokens::tokens::users::UserToken",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 9,
"name": "maybe_creator_username",
"type_info": {
"type": "VarString",
"flags": "UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 10,
"name": "maybe_creator_display_name",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 80
}
},
{
"ordinal": 11,
"name": "maybe_creator_gravatar_hash",
"type_info": {
"type": "String",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 12,
"name": "maybe_file_cover_image_public_bucket_hash",
"type_info": {
"type": "VarString",
"flags": "BINARY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 128
}
},
{
"ordinal": 13,
"name": "maybe_file_cover_image_public_bucket_prefix",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 14,
"name": "maybe_file_cover_image_public_bucket_extension",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 64
}
},
{
"ordinal": 15,
"name": "creator_set_visibility: enums::common::visibility::Visibility",
"type_info": {
"type": "String",
"flags": "NOT_NULL | BINARY | ENUM",
"char_set": 224,
"max_size": 28
}
},
{
"ordinal": 16,
"name": "maybe_title",
"type_info": {
"type": "VarString",
"flags": "BINARY",
"char_set": 224,
"max_size": 1020
}
},
{
"ordinal": 17,
"name": "created_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP",
"char_set": 63,
"max_size": 19
}
},
{
"ordinal": 18,
"name": "updated_at",
"type_info": {
"type": "Timestamp",
"flags": "NOT_NULL | MULTIPLE_KEY | BINARY | TIMESTAMP | ON_UPDATE_NOW",
"char_set": 63,
"max_size": 19
}
}
],
"parameters": {
"Right": 3
},
"nullable": [
false,
false,
false,
true,
false,
false,
true,
true,
true,
true,
true,
true,
true,
true,
true,
false,
true,
false,
false
]
},
"hash": "d8f4ce4e1026c71a7be8fd87f00f6f8858c401701da1ca3316eed1ed971d4bb2"
}
@@ -2,6 +2,7 @@ pub mod cover_image_links;
pub mod job_details;
pub mod media_file_cover_image_details;
pub mod media_links;
pub mod pagination_cursors;
pub mod simple_entity_stats;
pub mod simple_generic_json_success;
pub mod user_details_light;
@@ -0,0 +1,22 @@
use serde_derive::{Deserialize, Serialize};
use utoipa::ToSchema;
// TODO(bt,2026-07-09): Replace the storyteller-web version of this.
/// Pagination by cursors
/// These types of pagination are for "infinite scrolling", which do not reveal a number of pages.
/// This is good so that investors and competitors cannot reveal how many database records we have.
/// This is typically used for "discovery" type pages, not user profiles.
#[derive(Serialize, Deserialize, ToSchema, Debug)]
pub struct PaginationCursors {
/// The "next" cursor.
/// This is an opaque (typically even encrypted) handle.
pub maybe_next: Option<String>,
/// The "previous" cursor.
/// This is an opaque (typically even encrypted) handle.
pub maybe_previous: Option<String>,
/// Details whether we're walking forward or backward.
pub cursor_is_reversed: bool,
}
@@ -0,0 +1,78 @@
use chrono::{DateTime, Utc};
use serde_derive::{Deserialize, Serialize};
use utoipa::{IntoParams, ToSchema};
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use tokens::tokens::media_files::MediaFileToken;
use crate::common::responses::media_file_cover_image_details::MediaFileCoverImageDetails;
use crate::common::responses::media_links::MediaLinks;
use crate::common::responses::pagination_cursors::PaginationCursors;
use crate::common::responses::user_details_light::UserDetailsLight;
pub const LIST_SESSION_PROJECT_MEDIA_FILES_URL_PATH: &str = "/v1/media_files/project/list";
/// Query string parameters for listing the session user's project files.
#[derive(Deserialize, IntoParams)]
pub struct ListSessionProjectMediaFilesQueryParams {
pub sort_ascending: Option<bool>,
pub page_size: Option<usize>,
pub cursor: Option<String>,
pub cursor_is_reversed: Option<bool>,
/// Optional filter on the specific project document type.
///
/// Usage:
/// - `?filter_project_type=scene_3d`
/// - `?filter_project_type=mood_board`
/// - `?filter_project_type=workflow`
/// - `?filter_project_type=video_timeline`
pub filter_project_type: Option<MediaFileProjectType>,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct ListSessionProjectMediaFilesSuccessResponse {
pub success: bool,
pub results: Vec<ProjectMediaFileInfo>,
pub pagination: PaginationCursors,
}
#[derive(Serialize, Deserialize, ToSchema)]
pub struct ProjectMediaFileInfo {
/// Primary key identifier
pub token: MediaFileToken,
/// The coarse-grained class of media file.
/// Always `project` for this endpoint.
pub media_class: MediaFileClass,
/// The specific kind of project document: 3D scene, mood board, etc.
pub project_type: MediaFileProjectType,
/// Type of media will dictate which fields are populated and what
/// the frontend should display (eg. video player vs audio player).
/// This is closer in meaning to a "mime type".
pub media_type: MediaFileType,
/// Rich CDN links to the media, including thumbnails, previews, and more.
pub media_links: MediaLinks,
/// Information about the cover image. Many media files do not require a cover image,
/// e.g. image files, video files with thumbnails, audio files, etc.
/// 3D files require them.
pub cover_image: MediaFileCoverImageDetails,
/// User info
pub maybe_creator_user: Option<UserDetailsLight>,
pub creator_set_visibility: Visibility,
/// The name or title of the media file (optional)
pub maybe_title: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
@@ -1,2 +1,3 @@
pub mod delete_media_file;
pub mod list_batch_generated_media_files;
pub mod list_batch_generated_media_files;
pub mod list_session_project_media_files;
@@ -0,0 +1,375 @@
use std::marker::PhantomData;
use chrono::{DateTime, Utc};
use sqlx::{Executor, MySql};
use enums::by_table::media_files::media_file_class::MediaFileClass;
use enums::by_table::media_files::media_file_project_type::MediaFileProjectType;
use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::users::UserToken;
pub struct ProjectMediaFileListPage {
pub records: Vec<ProjectMediaFileListItem>,
/// ID of the first record (for building the "previous" cursor).
pub first_id: Option<u64>,
/// ID of the last record (for building the "next" cursor).
pub last_id: Option<u64>,
}
pub struct ProjectMediaFileListItem {
pub id: u64,
pub token: MediaFileToken,
pub media_class: MediaFileClass,
pub project_type: MediaFileProjectType,
pub media_type: MediaFileType,
pub public_bucket_directory_hash: String,
pub maybe_public_bucket_prefix: Option<String>,
pub maybe_public_bucket_extension: Option<String>,
pub maybe_creator_user_token: Option<UserToken>,
pub maybe_creator_username: Option<String>,
pub maybe_creator_display_name: Option<String>,
pub maybe_creator_gravatar_hash: Option<String>,
pub maybe_file_cover_image_public_bucket_hash: Option<String>,
pub maybe_file_cover_image_public_bucket_prefix: Option<String>,
pub maybe_file_cover_image_public_bucket_extension: Option<String>,
pub creator_set_visibility: Visibility,
pub maybe_title: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
pub struct ListSessionProjectMediaFilesArgs<'a, 'c, E>
where
E: 'a + Executor<'c, Database = MySql>,
{
pub user_token: &'a UserToken,
/// Optional filter on the specific project document type.
pub maybe_filter_project_type: Option<MediaFileProjectType>,
pub limit: usize,
/// Keyset cursor: a `media_files.id` from a previous page.
pub maybe_cursor_id: Option<u64>,
pub cursor_is_reversed: bool,
pub sort_ascending: bool,
pub mysql_executor: E,
pub phantom: PhantomData<&'c E>,
}
/// List the session user's project files (`media_class = 'project'`), newest first
/// by default, with keyset pagination on `id`.
///
/// Records with a NULL `maybe_project_type` are excluded so `project_type` is
/// always present in the results.
pub async fn list_session_project_media_files<'a, 'c: 'a, E>(
args: ListSessionProjectMediaFilesArgs<'a, 'c, E>,
) -> Result<ProjectMediaFileListPage, sqlx::Error>
where
E: 'a + Executor<'c, Database = MySql>,
{
// A reversed cursor walks the opposite direction of the sort, so the effective
// scan direction is the XOR of the two flags (same scheme as `list_media_files`).
let ascending = args.sort_ascending != args.cursor_is_reversed;
let limit = args.limit as u64;
let records = if ascending {
// NB: IDs start at 1, so a zero sentinel means "no cursor".
let cursor_id = args.maybe_cursor_id.unwrap_or(0);
select_page_ascending(
args.user_token,
args.maybe_filter_project_type,
cursor_id,
limit,
args.mysql_executor,
).await?
} else {
let cursor_id = args.maybe_cursor_id.unwrap_or(u64::MAX);
select_page_descending(
args.user_token,
args.maybe_filter_project_type,
cursor_id,
limit,
args.mysql_executor,
).await?
};
let first_id = records.first().map(|record| record.id);
let last_id = records.last().map(|record| record.id);
Ok(ProjectMediaFileListPage {
records,
first_id,
last_id,
})
}
// NB: The four queries below are intentionally duplicated so that sqlx can check
// them at compile time (no QueryBuilder). Keep the SELECT lists and predicates
// in sync; they differ only in the `maybe_project_type = ?` filter, the cursor
// comparison direction, and the sort order.
async fn select_page_ascending<'a, 'c: 'a, E>(
user_token: &'a UserToken,
maybe_filter_project_type: Option<MediaFileProjectType>,
cursor_id: u64,
limit: u64,
mysql_executor: E,
) -> Result<Vec<ProjectMediaFileListItem>, sqlx::Error>
where
E: 'a + Executor<'c, Database = MySql>,
{
match maybe_filter_project_type {
Some(project_type) => {
sqlx::query_as!(
ProjectMediaFileListItem,
r#"
SELECT
m.id as `id: u64`,
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
m.public_bucket_directory_hash,
m.maybe_public_bucket_prefix,
m.maybe_public_bucket_extension,
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
users.username as maybe_creator_username,
users.display_name as maybe_creator_display_name,
users.email_gravatar_hash as maybe_creator_gravatar_hash,
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
m.maybe_title,
m.created_at,
m.updated_at
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
LEFT OUTER JOIN users
ON m.maybe_creator_user_token = users.token
LEFT OUTER JOIN media_files as media_file_cover_image
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
WHERE
m.maybe_creator_user_token = ?
AND m.media_class = 'project'
AND m.maybe_project_type IS NOT NULL
AND m.maybe_project_type = ?
AND m.user_deleted_at IS NULL
AND m.mod_deleted_at IS NULL
AND NOT m.is_intermediate_system_file
AND m.id > ?
ORDER BY m.id ASC
LIMIT ?
"#,
user_token,
project_type.to_str(),
cursor_id,
limit,
)
.fetch_all(mysql_executor)
.await
}
None => {
sqlx::query_as!(
ProjectMediaFileListItem,
r#"
SELECT
m.id as `id: u64`,
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
m.public_bucket_directory_hash,
m.maybe_public_bucket_prefix,
m.maybe_public_bucket_extension,
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
users.username as maybe_creator_username,
users.display_name as maybe_creator_display_name,
users.email_gravatar_hash as maybe_creator_gravatar_hash,
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
m.maybe_title,
m.created_at,
m.updated_at
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
LEFT OUTER JOIN users
ON m.maybe_creator_user_token = users.token
LEFT OUTER JOIN media_files as media_file_cover_image
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
WHERE
m.maybe_creator_user_token = ?
AND m.media_class = 'project'
AND m.maybe_project_type IS NOT NULL
AND m.user_deleted_at IS NULL
AND m.mod_deleted_at IS NULL
AND NOT m.is_intermediate_system_file
AND m.id > ?
ORDER BY m.id ASC
LIMIT ?
"#,
user_token,
cursor_id,
limit,
)
.fetch_all(mysql_executor)
.await
}
}
}
async fn select_page_descending<'a, 'c: 'a, E>(
user_token: &'a UserToken,
maybe_filter_project_type: Option<MediaFileProjectType>,
cursor_id: u64,
limit: u64,
mysql_executor: E,
) -> Result<Vec<ProjectMediaFileListItem>, sqlx::Error>
where
E: 'a + Executor<'c, Database = MySql>,
{
match maybe_filter_project_type {
Some(project_type) => {
sqlx::query_as!(
ProjectMediaFileListItem,
r#"
SELECT
m.id as `id: u64`,
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
m.public_bucket_directory_hash,
m.maybe_public_bucket_prefix,
m.maybe_public_bucket_extension,
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
users.username as maybe_creator_username,
users.display_name as maybe_creator_display_name,
users.email_gravatar_hash as maybe_creator_gravatar_hash,
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
m.maybe_title,
m.created_at,
m.updated_at
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
LEFT OUTER JOIN users
ON m.maybe_creator_user_token = users.token
LEFT OUTER JOIN media_files as media_file_cover_image
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
WHERE
m.maybe_creator_user_token = ?
AND m.media_class = 'project'
AND m.maybe_project_type IS NOT NULL
AND m.maybe_project_type = ?
AND m.user_deleted_at IS NULL
AND m.mod_deleted_at IS NULL
AND NOT m.is_intermediate_system_file
AND m.id < ?
ORDER BY m.id DESC
LIMIT ?
"#,
user_token,
project_type.to_str(),
cursor_id,
limit,
)
.fetch_all(mysql_executor)
.await
}
None => {
sqlx::query_as!(
ProjectMediaFileListItem,
r#"
SELECT
m.id as `id: u64`,
m.token as `token: tokens::tokens::media_files::MediaFileToken`,
m.media_class as `media_class: enums::by_table::media_files::media_file_class::MediaFileClass`,
m.maybe_project_type as `project_type!: enums::by_table::media_files::media_file_project_type::MediaFileProjectType`,
m.media_type as `media_type: enums::by_table::media_files::media_file_type::MediaFileType`,
m.public_bucket_directory_hash,
m.maybe_public_bucket_prefix,
m.maybe_public_bucket_extension,
users.token as `maybe_creator_user_token: tokens::tokens::users::UserToken`,
users.username as maybe_creator_username,
users.display_name as maybe_creator_display_name,
users.email_gravatar_hash as maybe_creator_gravatar_hash,
media_file_cover_image.public_bucket_directory_hash as maybe_file_cover_image_public_bucket_hash,
media_file_cover_image.maybe_public_bucket_prefix as maybe_file_cover_image_public_bucket_prefix,
media_file_cover_image.maybe_public_bucket_extension as maybe_file_cover_image_public_bucket_extension,
m.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`,
m.maybe_title,
m.created_at,
m.updated_at
FROM media_files AS m FORCE INDEX (fk_maybe_creator_user_token)
LEFT OUTER JOIN users
ON m.maybe_creator_user_token = users.token
LEFT OUTER JOIN media_files as media_file_cover_image
ON media_file_cover_image.token = m.maybe_cover_image_media_file_token
WHERE
m.maybe_creator_user_token = ?
AND m.media_class = 'project'
AND m.maybe_project_type IS NOT NULL
AND m.user_deleted_at IS NULL
AND m.mod_deleted_at IS NULL
AND NOT m.is_intermediate_system_file
AND m.id < ?
ORDER BY m.id DESC
LIMIT ?
"#,
user_token,
cursor_id,
limit,
)
.fetch_all(mysql_executor)
.await
}
}
}
@@ -4,3 +4,4 @@ pub mod list_media_files;
pub mod list_media_files_by_batch_token;
pub mod list_media_files_for_elastic_search_backfill_using_cursor;
pub mod list_media_files_for_user;
pub mod list_session_project_media_files;
@@ -332,6 +332,7 @@ 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::*;
// Handler modules with locally-defined types
use crate::http_server::endpoints::moderation::info::moderator_token_info_handler::*;
use artcraft_api_defs::characters::create_character::*;
@@ -466,6 +467,7 @@ 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,
@@ -1078,6 +1080,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
ListPinnedWeightsSuccessResponse,
ListSessionJobsItem,
ListSessionJobsQueryParams,
ListSessionProjectMediaFilesSuccessResponse,
ListSessionJobsSuccessResponse,
ListSessionRequestDetailsResponse,
ListSessionResultDetailsResponse,
@@ -1121,6 +1124,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
ModernInferenceQueueStats,
PinnedMediaFile,
PinnedModelWeightForList,
ProjectMediaFileInfo,
PromptInfo,
RatingRow,
RedeemBetaKeyRequest,
@@ -0,0 +1,150 @@
use std::marker::PhantomData;
use std::sync::Arc;
use actix_web::web::{Json, Query};
use actix_web::{web, HttpRequest};
use log::{error, warn};
use artcraft_api_defs::common::responses::pagination_cursors::PaginationCursors;
use artcraft_api_defs::media_file::list_session_project_media_files::{ListSessionProjectMediaFilesQueryParams, ListSessionProjectMediaFilesSuccessResponse, ProjectMediaFileInfo};
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
use mysql_queries::queries::media_files::list::list_session_project_media_files::{list_session_project_media_files, ListSessionProjectMediaFilesArgs};
use crate::http_server::common_responses::common_web_error::CommonWebError;
use crate::http_server::common_responses::media::media_file_cover_image_details_builder::MediaFileCoverImageDetailsBuilder;
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
use crate::http_server::common_responses::user_details_lite_builder::UserDetailsLightBuilder;
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
use crate::http_server::user_lookup::user_session::require_user_session::require_user_session;
use crate::state::server_state::ServerState;
const DEFAULT_PAGE_SIZE: usize = 100;
const MAX_PAGE_SIZE: usize = 1000;
/// List the session user's project files (`media_class = 'project'`), such as
/// 3D scenes, mood boards, workflows, and video timelines (paginated).
///
/// Use `filter_project_type` to scope the results to a single project type.
#[utoipa::path(
get,
tag = "Media Files",
path = "/v1/media_files/project/list",
params(ListSessionProjectMediaFilesQueryParams),
responses(
(status = 200, description = "List the session user's project files", body = ListSessionProjectMediaFilesSuccessResponse),
(status = 401, description = "Not authorized"),
(status = 500, description = "Server error"),
),
)]
pub async fn list_session_project_media_files_handler(
http_request: HttpRequest,
query: Query<ListSessionProjectMediaFilesQueryParams>,
server_state: web::Data<Arc<ServerState>>,
) -> Result<Json<ListSessionProjectMediaFilesSuccessResponse>, CommonWebError> {
let mut mysql_connection = server_state.mysql_pool
.acquire()
.await
.map_err(|err| {
error!("MySql pool error: {:?}", err);
CommonWebError::from_error(err)
})?;
let user_session = require_user_session(
&http_request,
&server_state.session_checker,
&mut *mysql_connection,
).await?;
let limit = query.page_size
.unwrap_or(DEFAULT_PAGE_SIZE)
.min(MAX_PAGE_SIZE);
let sort_ascending = query.sort_ascending.unwrap_or(false);
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
let maybe_cursor_id = if let Some(cursor) = query.cursor.as_deref() {
Some(server_state.sort_key_crypto.decrypt_id(cursor)?)
} else {
None
};
let results_page = list_session_project_media_files(ListSessionProjectMediaFilesArgs {
user_token: user_session.get_user_token(),
maybe_filter_project_type: query.filter_project_type,
limit,
maybe_cursor_id,
cursor_is_reversed,
sort_ascending,
mysql_executor: &mut *mysql_connection,
phantom: PhantomData,
})
.await
.map_err(|err| {
warn!("List session project media files query error: {:?}", err);
CommonWebError::from_error(err)
})?;
let maybe_next = if let Some(id) = results_page.last_id {
Some(server_state.sort_key_crypto.encrypt_id(id)?)
} else {
None
};
let maybe_previous = if let Some(id) = results_page.first_id {
Some(server_state.sort_key_crypto.encrypt_id(id)?)
} else {
None
};
let media_domain = get_media_domain(&http_request);
let results = results_page.records.into_iter()
.map(|record| {
let public_bucket_path = MediaFileBucketPath::from_object_hash(
&record.public_bucket_directory_hash,
record.maybe_public_bucket_prefix.as_deref(),
record.maybe_public_bucket_extension.as_deref(),
);
ProjectMediaFileInfo {
media_links: MediaLinksBuilder::from_media_path_and_env(
media_domain,
server_state.server_environment,
&public_bucket_path,
),
cover_image: MediaFileCoverImageDetailsBuilder::from_optional_db_fields(
&record.token,
media_domain,
server_state.server_environment,
record.maybe_file_cover_image_public_bucket_hash.as_deref(),
record.maybe_file_cover_image_public_bucket_prefix.as_deref(),
record.maybe_file_cover_image_public_bucket_extension.as_deref(),
),
maybe_creator_user: UserDetailsLightBuilder::from_optional_db_fields_owned(
record.maybe_creator_user_token,
record.maybe_creator_username,
record.maybe_creator_display_name,
record.maybe_creator_gravatar_hash,
),
token: record.token,
media_class: record.media_class,
project_type: record.project_type,
media_type: record.media_type,
creator_set_visibility: record.creator_set_visibility,
maybe_title: record.maybe_title,
created_at: record.created_at,
updated_at: record.updated_at,
}
})
.collect::<Vec<_>>();
Ok(Json(ListSessionProjectMediaFilesSuccessResponse {
success: true,
results,
pagination: PaginationCursors {
maybe_next,
maybe_previous,
cursor_is_reversed,
},
}))
}
@@ -4,3 +4,4 @@ pub mod list_media_files_by_batch_token_handler;
pub mod list_media_files_for_user_handler;
pub mod list_media_files_handler;
pub mod list_pinned_media_files_handler;
pub mod list_session_project_media_files_handler;
@@ -18,6 +18,7 @@ use crate::http_server::endpoints::media_files::list::list_media_files_by_batch_
use crate::http_server::endpoints::media_files::list::list_media_files_for_user_handler::list_media_files_for_user_handler;
use crate::http_server::endpoints::media_files::list::list_media_files_handler::list_media_files_handler;
use crate::http_server::endpoints::media_files::list::list_pinned_media_files_handler::list_pinned_media_files_handler;
use crate::http_server::endpoints::media_files::list::list_session_project_media_files_handler::list_session_project_media_files_handler;
use crate::http_server::endpoints::media_files::search::search_featured_media_files_handler::search_featured_media_files_handler;
use crate::http_server::endpoints::media_files::search::search_session_media_files_handler::search_session_media_files_handler;
use crate::http_server::endpoints::media_files::upload::upload_audio_media_file_handler::upload_audio_media_file_handler;
@@ -101,6 +102,10 @@ pub fn add_media_file_routes<T, B> (app: App<T>) -> App<T>
.route(web::get().to(list_media_files_for_user_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(web::resource("/project/list")
.route(web::get().to(list_session_project_media_files_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(web::resource("/search_featured")
.route(web::get().to(search_featured_media_files_handler))
.route(web::head().to(|| HttpResponse::Ok()))