search endpoint + docs

This commit is contained in:
Brandon Thomas committed 2024-06-30 22:23:07 -04:00
1 parent 47a3c7935a
commit 6f4e780c7d
14 files changed
+303 -14

No files matched your search

@@ -80,6 +80,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::*;
use crate::http_server::endpoints::media_files::list::list_media_files_handler::*;
use crate::http_server::endpoints::media_files::list::list_pinned_media_files_handler::*;
use crate::http_server::endpoints::media_files::search::search_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;
@@ -157,6 +158,7 @@ use crate::http_server::web_utils::response_success_helpers::*;
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::search::search_media_files_handler::search_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,
@@ -293,6 +295,10 @@ use crate::http_server::web_utils::response_success_helpers::*;
CreateFeaturedItemSuccessResponse,
CreateSceneError,
CreateSceneSuccessResponse,
SearchMediaFilesError,
SearchMediaFilesSuccessResponse,
SearchMediaFilesQueryParams,
SearchMediaFileListItem,
CreateUserBookmarkError,
CreateUserBookmarkRequest,
CreateUserBookmarkSuccessResponse,
@@ -35,9 +35,6 @@ use crate::http_server::common_responses::pagination_page::PaginationPage;
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
use crate::http_server::endpoints::beta_keys::create_beta_keys_handler::CreateBetaKeysError;
use crate::http_server::endpoints::events::list_events::ListEventsError;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::web_utils::require_moderator::{require_moderator, RequireModeratorError};
use crate::http_server::web_utils::require_user_session::{require_user_session, RequireUserSessionError};
use crate::server_state::ServerState;
@@ -28,9 +28,9 @@ use crate::http_server::common_responses::media_file_cover_image_details::{Media
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::endpoints::media_files::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::server_state::ServerState;
use crate::util::allowed_explore_media_access::allowed_explore_media_access;
@@ -27,9 +27,9 @@ use crate::http_server::common_responses::media_file_cover_image_details::{Media
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
use crate::http_server::common_responses::pagination_page::PaginationPage;
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::endpoints::media_files::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
use crate::util::allowed_studio_access::allowed_studio_access;
@@ -27,9 +27,9 @@ use crate::http_server::common_responses::media_file_cover_image_details::{Media
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::list::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::endpoints::media_files::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
use crate::util::allowed_explore_media_access::allowed_explore_media_access;
@@ -1,4 +1,3 @@
pub mod helpers;
pub mod list_featured_media_files_handler;
pub mod list_media_files_by_batch_token_handler;
pub mod list_media_files_for_user_handler;
@@ -1,6 +1,8 @@
pub mod delete;
pub mod edit;
pub mod get;
pub mod helpers;
pub mod list;
pub mod search;
pub mod upload;
pub mod upsert_upload;
pub mod upsert_upload;
@@ -0,0 +1 @@
pub mod search_media_files_handler;
@@ -0,0 +1,279 @@
use std::sync::Arc;
use actix_web::{HttpMessage, HttpRequest, HttpResponse, web};
use actix_web::error::ResponseError;
use actix_web::http::StatusCode;
use actix_web::web::Query;
use chrono::{DateTime, Utc};
use log::warn;
use utoipa::{IntoParams, ToSchema};
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
use elasticsearch_schema::searches::search_media_files::{search_media_files, SearchArgs};
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_model_type::MediaFileOriginModelType;
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::view_as::ViewAs;
use enums::common::visibility::Visibility;
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
use tokens::tokens::media_files::MediaFileToken;
use users_component::common_responses::user_details_lite::UserDetailsLight;
use crate::http_server::common_responses::media_file_cover_image_details::{MediaFileCoverImageDetails, MediaFileDefaultCover};
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
use crate::http_server::endpoints::media_files::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
use crate::util::allowed_explore_media_access::allowed_explore_media_access;
use crate::util::allowed_studio_access::allowed_studio_access;
#[derive(Deserialize, ToSchema, IntoParams)]
pub struct SearchMediaFilesQueryParams {
/// REQUIRED: The search term.
///
/// This supports typeahead, but the request should still be debounced.
pub search_term: String,
/// NB: This can be one (or more comma-separated values) from `MediaFileClass`,
/// which are the broad category of media files: image, video, etc.
///
/// Usage:
/// - `?filter_media_classes=audio`
/// - `?filter_media_classes=image,video`
/// - etc.
pub filter_media_classes: Option<String>,
/// NB: This can be one (or more comma-separated values) from `MediaFileType`,
/// which are mimetype-like / format-like categories of media files: glb, gltf,
/// scene_json, etc.
///
/// Usage:
/// - `?filter_media_type=scene_json`
/// - `?filter_media_type=glb,gltf`
/// - etc.
pub filter_media_type: Option<String>,
/// NB: This can be one (or more comma-separated values) from `MediaFileEngineCategory`.
///
/// Usage:
/// - `?filter_engine_categories=scene`
/// - `?filter_engine_categories=animation,character,object`
/// - etc.
pub filter_engine_categories: Option<String>,
}
#[derive(Serialize, ToSchema)]
pub struct SearchMediaFilesSuccessResponse {
pub success: bool,
pub results: Vec<SearchMediaFileListItem>,
}
#[derive(Serialize, ToSchema)]
pub struct SearchMediaFileListItem {
pub token: MediaFileToken,
/// The coarse-grained class of media file: image, video, etc.
pub media_class: MediaFileClass,
/// 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,
/// If this is an engine/3D asset, this is the broad category (scene,
/// animation, etc.) of that object.
/// This can also be used for filtering in list/batch endpoints.
pub maybe_engine_category: Option<MediaFileEngineCategory>,
/// If this is an engine/3D asset for an animation or a rig that can
/// be animated with either (or both) skeletal or blend shape animations,
/// this describes the animation regime used or supported.
pub maybe_animation_type: Option<MediaFileAnimationType>,
// #[deprecated(note="Use MediaFileOriginDetails instead")]
// pub origin_category: MediaFileOriginCategory,
// #[deprecated(note="Use MediaFileOriginDetails instead")]
// pub origin_product_category: MediaFileOriginProductCategory,
// #[deprecated(note="Use MediaFileOriginDetails instead")]
// pub maybe_origin_model_type: Option<MediaFileOriginModelType>,
// #[deprecated(note="Use MediaFileOriginDetails instead")]
// pub maybe_origin_model_token: Option<String>,
// /// Details where the media file came from.
// pub origin: MediaFileOriginDetails,
/// URL to the media file.
pub public_bucket_path: String,
/// 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,
pub maybe_creator: Option<UserDetailsLight>,
// /// Statistics about the media file
// pub stats: SimpleEntityStats,
pub creator_set_visibility: Visibility,
/// The name or title of the media file (optional)
pub maybe_title: Option<String>,
// /// Text transcripts for TTS, etc.
// pub maybe_text_transcript: Option<String>,
// /// For Comfy / Video Style Transfer jobs, this might include
// /// the name of the selected style.
// pub maybe_style_name: Option<StyleTransferName>,
// /// Duration for audio and video files, if available.
// /// Measured in milliseconds.
// pub maybe_duration_millis: Option<u64>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, ToSchema)]
pub enum SearchMediaFilesError {
ServerError,
NotAuthorized,
}
impl ResponseError for SearchMediaFilesError {
fn status_code(&self) -> StatusCode {
match *self {
SearchMediaFilesError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
SearchMediaFilesError::NotAuthorized => StatusCode::UNAUTHORIZED,
}
}
fn error_response(&self) -> HttpResponse {
let error_reason = match self {
SearchMediaFilesError::ServerError => "server error".to_string(),
SearchMediaFilesError::NotAuthorized => "not authorized".to_string(),
};
to_simple_json_error(&error_reason, self.status_code())
}
}
impl std::fmt::Display for SearchMediaFilesError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
/// Search for media files based on various criteria.
#[utoipa::path(
get,
tag = "Media Files",
path = "/v1/media_files/search",
params(SearchMediaFilesQueryParams),
responses(
(status = 200, description = "List Featured Media Files", body = SearchMediaFilesSuccessResponse),
(status = 500, description = "Server error", body = SearchMediaFilesError),
),
)]
pub async fn search_media_files_handler(
http_request: HttpRequest,
query: Query<SearchMediaFilesQueryParams>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, SearchMediaFilesError>
{
let mut maybe_filter_media_types = get_scoped_media_types(query.filter_media_type.as_deref());
let mut maybe_filter_media_classes = get_scoped_media_classes(query.filter_media_classes.as_deref());
let mut maybe_filter_engine_categories = get_scoped_engine_categories(query.filter_engine_categories.as_deref());
let results = search_media_files(SearchArgs {
search_term: &query.search_term,
maybe_media_classes: maybe_filter_media_classes,
maybe_media_types: maybe_filter_media_types,
maybe_engine_categories: maybe_filter_engine_categories,
client: &server_state.elasticsearch,
}).await;
let results = match results {
Ok(results) => results,
Err(err) => {
warn!("Searching error: {:?}", err);
return Err(SearchMediaFilesError::ServerError);
}
};
let results = results.into_iter()
.map(|result| SearchMediaFileListItem {
token: result.token.clone(),
media_class: result.media_class,
media_type: result.media_type,
maybe_engine_category: result.maybe_engine_category,
maybe_animation_type: result.maybe_animation_type,
// origin: MediaFileOriginDetails::from_db_fields_str(
// result.origin_category,
// result.origin_product_category,
// result.maybe_origin_model_type,
// result.maybe_origin_model_token.as_deref(),
// result.maybe_origin_model_title.as_deref()),
// origin_category: result.origin_category,
// origin_product_category: result.origin_product_category,
// maybe_origin_model_type: result.maybe_origin_model_type,
// maybe_origin_model_token: result.maybe_origin_model_token,
public_bucket_path: MediaFileBucketPath::from_object_hash(
&result.public_bucket_directory_hash,
result.maybe_public_bucket_prefix.as_deref(),
result.maybe_public_bucket_extension.as_deref())
.get_full_object_path_str()
.to_string(),
cover_image: MediaFileCoverImageDetails::from_optional_db_fields(
&result.token,
result.maybe_cover_image_public_bucket_hash.as_deref(),
result.maybe_cover_image_public_bucket_prefix.as_deref(),
result.maybe_cover_image_public_bucket_extension.as_deref(),
),
maybe_creator: UserDetailsLight::from_optional_db_fields_owned(
result.maybe_creator_user_token,
result.maybe_creator_username,
result.maybe_creator_display_name,
result.maybe_creator_gravatar_hash,
),
// stats: SimpleEntityStats {
// positive_rating_count: result.maybe_ratings_positive_count.unwrap_or(0),
// bookmark_count: result.maybe_bookmark_count.unwrap_or(0),
// },
creator_set_visibility: result.creator_set_visibility,
maybe_title: result.maybe_title,
// maybe_text_transcript: result.maybe_text_transcript,
// maybe_style_name: result.maybe_prompt_args
// .as_ref()
// .and_then(|args| args.style_name.as_ref())
// .and_then(|style| style.to_style_name()),
// maybe_duration_millis: result.maybe_duration_millis,
created_at: result.created_at,
updated_at: result.updated_at,
})
.collect::<Vec<_>>();
let response = SearchMediaFilesSuccessResponse {
success: true,
results,
};
let body = serde_json::to_string(&response)
.map_err(|e| SearchMediaFilesError::ServerError)?;
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}
@@ -15,6 +15,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::search::search_media_files_handler::search_media_files_handler;
use crate::http_server::endpoints::media_files::upload::upload_audio_media_file_handler::upload_audio_media_file_handler;
use crate::http_server::endpoints::media_files::upload::upload_engine_asset::upload_engine_asset_media_file_handler::upload_engine_asset_media_file_handler;
use crate::http_server::endpoints::media_files::upload::upload_generic::upload_media_file_handler::upload_media_file_handler;
@@ -85,6 +86,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("/search")
.route(web::get().to(search_media_files_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(web::resource("/upload")
.route(web::post().to(upload_media_file_handler))
.route(web::head().to(|| HttpResponse::Ok()))