mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-11 20:32:12 +00:00
change media files api
This commit is contained in:
1 parent
e9f46b1e87
commit
7d7e95fc22
6 files changed
+263
-81
No files matched your search
@@ -6,6 +6,11 @@ use tokens::tokens::folders::FolderToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::users::UserToken;
|
||||
|
||||
// NB: The "folder media file" list-item shape lives in storyteller_web
|
||||
// (see `endpoints/folders/media_files/list_folder_media_files_handler.rs`)
|
||||
// because it embeds `MediaFileCoverImageDetails` and other domain types
|
||||
// that depend on the request's `MediaDomain` + `ServerEnvironment`.
|
||||
|
||||
/// Canonical wire shape for a folder. Used by single-folder GETs, create
|
||||
/// responses, list rows, and subfolder list rows.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
@@ -33,22 +38,3 @@ pub struct FolderInfo {
|
||||
pub is_orphaned: bool,
|
||||
}
|
||||
|
||||
/// Canonical wire shape for a media file inside a folder. Lean enough for
|
||||
/// thumbnail rendering; callers that need richer fields should hit the
|
||||
/// media-file batch-get endpoint.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct FolderMediaFileInfo {
|
||||
pub media_file_token: MediaFileToken,
|
||||
pub added_to_folder_at: DateTime<Utc>,
|
||||
|
||||
pub media_type: String,
|
||||
pub media_class: String,
|
||||
pub maybe_mime_type: Option<String>,
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
pub maybe_frame_width: Option<i32>,
|
||||
pub maybe_frame_height: Option<i32>,
|
||||
pub maybe_duration_millis: Option<i32>,
|
||||
pub maybe_title: Option<String>,
|
||||
}
|
||||
@@ -4,9 +4,12 @@ use utoipa::{IntoParams, ToSchema};
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::folders::common::FolderMediaFileInfo;
|
||||
|
||||
// ── GET /v1/folders/media_files/{folder_token} ──
|
||||
//
|
||||
// NB: `ListFolderMediaFilesSuccessResponse` and `FolderMediaFileListItem`
|
||||
// live in `storyteller_web`'s handler because the wire shape embeds
|
||||
// `MediaFileCoverImageDetails` and `MediaLinks` constructors that depend
|
||||
// on the request's `MediaDomain` + `ServerEnvironment`.
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub struct FolderMediaFilesPathInfo {
|
||||
@@ -19,13 +22,6 @@ pub struct ListFolderMediaFilesQueryParams {
|
||||
pub limit: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListFolderMediaFilesSuccessResponse {
|
||||
pub success: bool,
|
||||
pub media_files: Vec<FolderMediaFileInfo>,
|
||||
pub maybe_cursor: Option<String>,
|
||||
}
|
||||
|
||||
// ── PUT /v1/folders/media_files/{folder_token}/bulk_add ──
|
||||
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
|
||||
+119
-28
@@ -3,29 +3,57 @@ 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_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
|
||||
/// One folder-media-file row + the joined media-file fields the list
|
||||
/// endpoint needs to materialize MediaLinks, MediaFileCoverImageDetails,
|
||||
/// and the rest of the typed response shape.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FolderMediaFileRow {
|
||||
/// The `folder_media_files.id` of the membership row. Use this as the
|
||||
/// cursor for the next page.
|
||||
/// `folder_media_files.id` — used as the pagination cursor.
|
||||
pub membership_id: u64,
|
||||
|
||||
/// When the media file was added to the folder.
|
||||
pub added_to_folder_at: DateTime<Utc>,
|
||||
|
||||
pub media_file_token: MediaFileToken,
|
||||
pub media_type: String,
|
||||
pub media_class: String,
|
||||
pub maybe_mime_type: Option<String>,
|
||||
pub media_class: MediaFileClass,
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
// The media file's own bucket fields (caller uses these to build MediaLinks).
|
||||
pub public_bucket_directory_hash: String,
|
||||
pub maybe_public_bucket_prefix: Option<String>,
|
||||
pub maybe_public_bucket_extension: Option<String>,
|
||||
|
||||
// Cover image's bucket fields, from a LEFT JOIN on
|
||||
// mf.maybe_cover_image_media_file_token. All three are Some-or-None
|
||||
// together (matched / unmatched join row).
|
||||
pub maybe_cover_public_bucket_directory_hash: Option<String>,
|
||||
pub maybe_cover_public_bucket_prefix: Option<String>,
|
||||
pub maybe_cover_public_bucket_extension: Option<String>,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
pub is_user_upload: bool,
|
||||
pub is_intermediate_system_file: bool,
|
||||
|
||||
pub maybe_title: Option<String>,
|
||||
pub maybe_prompt_token: Option<PromptToken>,
|
||||
pub maybe_origin_filename: Option<String>,
|
||||
|
||||
pub maybe_duration_millis: Option<i32>,
|
||||
pub maybe_frame_width: Option<i32>,
|
||||
pub maybe_frame_height: Option<i32>,
|
||||
pub maybe_duration_millis: Option<i32>,
|
||||
pub maybe_title: Option<String>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub struct ListFolderMediaFilesArgs<'e, 'c, E>
|
||||
@@ -40,9 +68,12 @@ where
|
||||
}
|
||||
|
||||
/// Paginated list of media files in the given folder. Joins `media_files`
|
||||
/// and filters out rows that are soft-deleted on the media-file side.
|
||||
/// Most-recently-added first. The caller is expected to have already
|
||||
/// authorized the folder access.
|
||||
/// for the file's own metadata and LEFT JOINs `media_files` again on
|
||||
/// `maybe_cover_image_media_file_token` so the cover-image bucket fields
|
||||
/// come back in a single round-trip. Filters out media files that are
|
||||
/// soft-deleted on the media-file side. Most-recently-added first.
|
||||
///
|
||||
/// The caller is expected to have already authorized the folder access.
|
||||
pub async fn list_folder_media_files<'e, 'c: 'e, E>(
|
||||
args: ListFolderMediaFilesArgs<'e, 'c, E>,
|
||||
) -> Result<Vec<FolderMediaFileRow>, sqlx::Error>
|
||||
@@ -58,20 +89,40 @@ where
|
||||
SELECT
|
||||
fmf.id as `membership_id: u64`,
|
||||
fmf.created_at as `added_to_folder_at: DateTime<Utc>`,
|
||||
|
||||
mf.token as `media_file_token: MediaFileToken`,
|
||||
mf.media_type,
|
||||
mf.media_class,
|
||||
mf.maybe_mime_type,
|
||||
mf.media_class as `media_class: MediaFileClass`,
|
||||
mf.media_type as `media_type: MediaFileType`,
|
||||
|
||||
mf.maybe_batch_token as `maybe_batch_token: BatchGenerationToken`,
|
||||
|
||||
mf.public_bucket_directory_hash,
|
||||
mf.maybe_public_bucket_prefix,
|
||||
mf.maybe_public_bucket_extension,
|
||||
|
||||
cover.public_bucket_directory_hash as `maybe_cover_public_bucket_directory_hash?`,
|
||||
cover.maybe_public_bucket_prefix as `maybe_cover_public_bucket_prefix?`,
|
||||
cover.maybe_public_bucket_extension as `maybe_cover_public_bucket_extension?`,
|
||||
|
||||
mf.creator_set_visibility as `creator_set_visibility: Visibility`,
|
||||
mf.is_user_upload as `is_user_upload: bool`,
|
||||
mf.is_intermediate_system_file as `is_intermediate_system_file: bool`,
|
||||
|
||||
mf.maybe_title,
|
||||
mf.maybe_prompt_token as `maybe_prompt_token: PromptToken`,
|
||||
mf.maybe_origin_filename,
|
||||
|
||||
mf.maybe_duration_millis,
|
||||
mf.maybe_frame_width,
|
||||
mf.maybe_frame_height,
|
||||
mf.maybe_duration_millis,
|
||||
mf.maybe_title
|
||||
|
||||
mf.created_at,
|
||||
mf.updated_at
|
||||
FROM folder_media_files fmf
|
||||
JOIN media_files mf
|
||||
ON mf.token = fmf.media_file_token
|
||||
LEFT JOIN media_files cover
|
||||
ON cover.token = mf.maybe_cover_image_media_file_token
|
||||
WHERE fmf.folder_token = ?
|
||||
AND mf.user_deleted_at IS NULL
|
||||
AND mf.mod_deleted_at IS NULL
|
||||
@@ -90,16 +141,26 @@ LIMIT ?
|
||||
membership_id: r.membership_id,
|
||||
added_to_folder_at: r.added_to_folder_at,
|
||||
media_file_token: r.media_file_token,
|
||||
media_type: r.media_type,
|
||||
media_class: r.media_class,
|
||||
maybe_mime_type: r.maybe_mime_type,
|
||||
media_type: r.media_type,
|
||||
maybe_batch_token: r.maybe_batch_token,
|
||||
public_bucket_directory_hash: r.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: r.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: r.maybe_public_bucket_extension,
|
||||
maybe_cover_public_bucket_directory_hash: r.maybe_cover_public_bucket_directory_hash,
|
||||
maybe_cover_public_bucket_prefix: r.maybe_cover_public_bucket_prefix,
|
||||
maybe_cover_public_bucket_extension: r.maybe_cover_public_bucket_extension,
|
||||
creator_set_visibility: r.creator_set_visibility,
|
||||
is_user_upload: r.is_user_upload,
|
||||
is_intermediate_system_file: r.is_intermediate_system_file,
|
||||
maybe_title: r.maybe_title,
|
||||
maybe_prompt_token: r.maybe_prompt_token,
|
||||
maybe_origin_filename: r.maybe_origin_filename,
|
||||
maybe_duration_millis: r.maybe_duration_millis,
|
||||
maybe_frame_width: r.maybe_frame_width,
|
||||
maybe_frame_height: r.maybe_frame_height,
|
||||
maybe_duration_millis: r.maybe_duration_millis,
|
||||
maybe_title: r.maybe_title,
|
||||
created_at: r.created_at,
|
||||
updated_at: r.updated_at,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
@@ -109,20 +170,40 @@ LIMIT ?
|
||||
SELECT
|
||||
fmf.id as `membership_id: u64`,
|
||||
fmf.created_at as `added_to_folder_at: DateTime<Utc>`,
|
||||
|
||||
mf.token as `media_file_token: MediaFileToken`,
|
||||
mf.media_type,
|
||||
mf.media_class,
|
||||
mf.maybe_mime_type,
|
||||
mf.media_class as `media_class: MediaFileClass`,
|
||||
mf.media_type as `media_type: MediaFileType`,
|
||||
|
||||
mf.maybe_batch_token as `maybe_batch_token: BatchGenerationToken`,
|
||||
|
||||
mf.public_bucket_directory_hash,
|
||||
mf.maybe_public_bucket_prefix,
|
||||
mf.maybe_public_bucket_extension,
|
||||
|
||||
cover.public_bucket_directory_hash as `maybe_cover_public_bucket_directory_hash?`,
|
||||
cover.maybe_public_bucket_prefix as `maybe_cover_public_bucket_prefix?`,
|
||||
cover.maybe_public_bucket_extension as `maybe_cover_public_bucket_extension?`,
|
||||
|
||||
mf.creator_set_visibility as `creator_set_visibility: Visibility`,
|
||||
mf.is_user_upload as `is_user_upload: bool`,
|
||||
mf.is_intermediate_system_file as `is_intermediate_system_file: bool`,
|
||||
|
||||
mf.maybe_title,
|
||||
mf.maybe_prompt_token as `maybe_prompt_token: PromptToken`,
|
||||
mf.maybe_origin_filename,
|
||||
|
||||
mf.maybe_duration_millis,
|
||||
mf.maybe_frame_width,
|
||||
mf.maybe_frame_height,
|
||||
mf.maybe_duration_millis,
|
||||
mf.maybe_title
|
||||
|
||||
mf.created_at,
|
||||
mf.updated_at
|
||||
FROM folder_media_files fmf
|
||||
JOIN media_files mf
|
||||
ON mf.token = fmf.media_file_token
|
||||
LEFT JOIN media_files cover
|
||||
ON cover.token = mf.maybe_cover_image_media_file_token
|
||||
WHERE fmf.folder_token = ?
|
||||
AND mf.user_deleted_at IS NULL
|
||||
AND mf.mod_deleted_at IS NULL
|
||||
@@ -139,16 +220,26 @@ LIMIT ?
|
||||
membership_id: r.membership_id,
|
||||
added_to_folder_at: r.added_to_folder_at,
|
||||
media_file_token: r.media_file_token,
|
||||
media_type: r.media_type,
|
||||
media_class: r.media_class,
|
||||
maybe_mime_type: r.maybe_mime_type,
|
||||
media_type: r.media_type,
|
||||
maybe_batch_token: r.maybe_batch_token,
|
||||
public_bucket_directory_hash: r.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: r.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: r.maybe_public_bucket_extension,
|
||||
maybe_cover_public_bucket_directory_hash: r.maybe_cover_public_bucket_directory_hash,
|
||||
maybe_cover_public_bucket_prefix: r.maybe_cover_public_bucket_prefix,
|
||||
maybe_cover_public_bucket_extension: r.maybe_cover_public_bucket_extension,
|
||||
creator_set_visibility: r.creator_set_visibility,
|
||||
is_user_upload: r.is_user_upload,
|
||||
is_intermediate_system_file: r.is_intermediate_system_file,
|
||||
maybe_title: r.maybe_title,
|
||||
maybe_prompt_token: r.maybe_prompt_token,
|
||||
maybe_origin_filename: r.maybe_origin_filename,
|
||||
maybe_duration_millis: r.maybe_duration_millis,
|
||||
maybe_frame_width: r.maybe_frame_width,
|
||||
maybe_frame_height: r.maybe_frame_height,
|
||||
maybe_duration_millis: r.maybe_duration_millis,
|
||||
maybe_title: r.maybe_title,
|
||||
created_at: r.created_at,
|
||||
updated_at: r.updated_at,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
@@ -1246,7 +1246,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
|
||||
// Folders
|
||||
FolderInfo,
|
||||
FolderMediaFileInfo,
|
||||
FolderMediaFileListItem,
|
||||
FolderPathInfo,
|
||||
FolderMediaFilesPathInfo,
|
||||
SubfolderPathInfo,
|
||||
|
||||
+1
-19
@@ -1,6 +1,5 @@
|
||||
use artcraft_api_defs::folders::common::{FolderInfo, FolderMediaFileInfo};
|
||||
use artcraft_api_defs::folders::common::FolderInfo;
|
||||
use mysql_queries::queries::folders::folder::folder_row::FolderRow;
|
||||
use mysql_queries::queries::folders::media_files::list_folder_media_files::FolderMediaFileRow;
|
||||
|
||||
pub fn folder_row_to_info(row: FolderRow) -> FolderInfo {
|
||||
FolderInfo {
|
||||
@@ -20,20 +19,3 @@ pub fn folder_row_to_info(row: FolderRow) -> FolderInfo {
|
||||
is_orphaned: row.is_orphaned,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folder_media_file_row_to_info(row: FolderMediaFileRow) -> FolderMediaFileInfo {
|
||||
FolderMediaFileInfo {
|
||||
media_file_token: row.media_file_token,
|
||||
added_to_folder_at: row.added_to_folder_at,
|
||||
media_type: row.media_type,
|
||||
media_class: row.media_class,
|
||||
maybe_mime_type: row.maybe_mime_type,
|
||||
public_bucket_directory_hash: row.public_bucket_directory_hash,
|
||||
maybe_public_bucket_prefix: row.maybe_public_bucket_prefix,
|
||||
maybe_public_bucket_extension: row.maybe_public_bucket_extension,
|
||||
maybe_frame_width: row.maybe_frame_width,
|
||||
maybe_frame_height: row.maybe_frame_height,
|
||||
maybe_duration_millis: row.maybe_duration_millis,
|
||||
maybe_title: row.maybe_title,
|
||||
}
|
||||
}
|
||||
+132
-5
@@ -3,21 +3,35 @@ use std::sync::Arc;
|
||||
|
||||
use actix_web::web::{Json, Path, Query};
|
||||
use actix_web::{web, HttpRequest};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::warn;
|
||||
use serde_derive::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use artcraft_api_defs::common::responses::media_links::MediaLinks;
|
||||
use artcraft_api_defs::folders::media_files::{
|
||||
FolderMediaFilesPathInfo, ListFolderMediaFilesQueryParams, ListFolderMediaFilesSuccessResponse,
|
||||
FolderMediaFilesPathInfo, ListFolderMediaFilesQueryParams,
|
||||
};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use mysql_queries::queries::folders::folder::get_folder_for_owner::{
|
||||
get_folder_for_owner, GetFolderForOwnerArgs,
|
||||
};
|
||||
use mysql_queries::queries::folders::media_files::list_folder_media_files::{
|
||||
list_folder_media_files, ListFolderMediaFilesArgs,
|
||||
list_folder_media_files, FolderMediaFileRow, ListFolderMediaFilesArgs,
|
||||
};
|
||||
use tokens::tokens::batch_generations::BatchGenerationToken;
|
||||
use tokens::tokens::folders::FolderToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::endpoints::folders::folder::folder_info_conversion::folder_media_file_row_to_info;
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::web_utils::user_session::require_user_session_using_connection::require_user_session_using_connection;
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
@@ -25,6 +39,65 @@ const CURSOR_NAME: &str = "folder_mf";
|
||||
const DEFAULT_LIMIT: u32 = 100;
|
||||
const MAX_LIMIT: u32 = 1000;
|
||||
|
||||
// ── Response shape ──
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListFolderMediaFilesSuccessResponse {
|
||||
pub success: bool,
|
||||
pub media_files: Vec<FolderMediaFileListItem>,
|
||||
pub maybe_cursor: Option<String>,
|
||||
}
|
||||
|
||||
/// One media file as it appears inside a folder. Lean version of the
|
||||
/// "by batch" / "by user" list-item shape — keeps the fields that matter
|
||||
/// for folder browsing and drops the bucket-path noise.
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct FolderMediaFileListItem {
|
||||
pub token: MediaFileToken,
|
||||
|
||||
/// When the media file was added to the folder.
|
||||
pub added_to_folder_at: DateTime<Utc>,
|
||||
|
||||
/// Coarse-grained class (image / video / audio / dimensional).
|
||||
pub media_class: MediaFileClass,
|
||||
|
||||
/// Specific format (jpg, png, mp4, etc.) — closer to a MIME type.
|
||||
pub media_type: MediaFileType,
|
||||
|
||||
/// If this file was generated as part of a batch, the batch token —
|
||||
/// useful for showing "siblings" in the UI.
|
||||
pub maybe_batch_token: Option<BatchGenerationToken>,
|
||||
|
||||
/// Rich CDN links to the media itself (full URL, thumbnail template,
|
||||
/// video previews when applicable).
|
||||
pub media_links: MediaLinks,
|
||||
|
||||
/// Cover image details. For files that don't carry their own cover
|
||||
/// image, this still gives the deterministic default-cover spec
|
||||
/// (image_index + color_index) the frontend uses for placeholders.
|
||||
pub cover_image: MediaFileCoverImageDetails,
|
||||
|
||||
pub creator_set_visibility: Visibility,
|
||||
pub is_user_upload: bool,
|
||||
pub is_intermediate_system_file: bool,
|
||||
|
||||
pub maybe_title: Option<String>,
|
||||
pub maybe_prompt_token: Option<PromptToken>,
|
||||
pub maybe_origin_filename: Option<String>,
|
||||
|
||||
/// Duration for audio and video files, if available. Milliseconds.
|
||||
pub maybe_duration_millis: Option<u64>,
|
||||
|
||||
/// Original pixel width / height for image and video files when known.
|
||||
pub maybe_frame_width: Option<i32>,
|
||||
pub maybe_frame_height: Option<i32>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
// ── Handler ──
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
tag = "Folders (Media File Management)",
|
||||
@@ -65,7 +138,7 @@ pub async fn list_folder_media_files_handler(
|
||||
warn!("Folder lookup failed: {:?}", err);
|
||||
CommonWebError::from_error(err)
|
||||
})?;
|
||||
|
||||
|
||||
if folder.is_none() {
|
||||
return Err(CommonWebError::NotFound);
|
||||
}
|
||||
@@ -103,7 +176,12 @@ pub async fn list_folder_media_files_handler(
|
||||
CommonWebError::server_error_with_message("Failed to encode cursor")
|
||||
})?;
|
||||
|
||||
let media_files = rows.into_iter().map(folder_media_file_row_to_info).collect();
|
||||
let media_domain = get_media_domain(&http_request);
|
||||
let server_environment = server_state.server_environment;
|
||||
|
||||
let media_files = rows.into_iter()
|
||||
.map(|row| folder_media_file_row_to_list_item(row, media_domain, server_environment))
|
||||
.collect();
|
||||
|
||||
Ok(Json(ListFolderMediaFilesSuccessResponse {
|
||||
success: true,
|
||||
@@ -111,3 +189,52 @@ pub async fn list_folder_media_files_handler(
|
||||
maybe_cursor,
|
||||
}))
|
||||
}
|
||||
|
||||
fn folder_media_file_row_to_list_item(
|
||||
row: FolderMediaFileRow,
|
||||
media_domain: MediaDomain,
|
||||
server_environment: server_environment::ServerEnvironment,
|
||||
) -> FolderMediaFileListItem {
|
||||
let bucket_path = MediaFileBucketPath::from_object_hash(
|
||||
&row.public_bucket_directory_hash,
|
||||
row.maybe_public_bucket_prefix.as_deref(),
|
||||
row.maybe_public_bucket_extension.as_deref(),
|
||||
);
|
||||
|
||||
let media_links = MediaLinksBuilder::from_media_path_and_env(
|
||||
media_domain,
|
||||
server_environment,
|
||||
&bucket_path,
|
||||
);
|
||||
|
||||
let cover_image = MediaFileCoverImageDetails::from_optional_db_fields(
|
||||
&row.media_file_token,
|
||||
media_domain,
|
||||
server_environment,
|
||||
row.maybe_cover_public_bucket_directory_hash.as_deref(),
|
||||
row.maybe_cover_public_bucket_prefix.as_deref(),
|
||||
row.maybe_cover_public_bucket_extension.as_deref(),
|
||||
);
|
||||
|
||||
FolderMediaFileListItem {
|
||||
token: row.media_file_token,
|
||||
added_to_folder_at: row.added_to_folder_at,
|
||||
media_class: row.media_class,
|
||||
media_type: row.media_type,
|
||||
maybe_batch_token: row.maybe_batch_token,
|
||||
media_links,
|
||||
cover_image,
|
||||
creator_set_visibility: row.creator_set_visibility,
|
||||
is_user_upload: row.is_user_upload,
|
||||
is_intermediate_system_file: row.is_intermediate_system_file,
|
||||
maybe_title: row.maybe_title,
|
||||
maybe_prompt_token: row.maybe_prompt_token,
|
||||
maybe_origin_filename: row.maybe_origin_filename,
|
||||
// Schema stores `INT(10)`; widen to u64 for the wire shape.
|
||||
maybe_duration_millis: row.maybe_duration_millis.map(|n| n as u64),
|
||||
maybe_frame_width: row.maybe_frame_width,
|
||||
maybe_frame_height: row.maybe_frame_height,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user