mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
filter featured media by product category
This commit is contained in:
1 parent
6a30d0c0d2
commit
65f0718fb2
7 files changed
+269
-4
No files matched your search
@@ -97,6 +97,7 @@ pub struct ListFeaturedMediaFilesArgs<'a> {
|
||||
pub maybe_filter_media_types: Option<&'a HashSet<MediaFileType>>,
|
||||
pub maybe_filter_media_classes: Option<&'a HashSet<MediaFileClass>>,
|
||||
pub maybe_filter_engine_categories: Option<&'a HashSet<MediaFileEngineCategory>>,
|
||||
pub maybe_filter_product_categories: Option<&'a HashSet<MediaFileOriginProductCategory>>,
|
||||
pub maybe_offset: Option<usize>,
|
||||
pub cursor_is_reversed: bool,
|
||||
pub sort_ascending: bool,
|
||||
@@ -110,6 +111,7 @@ pub async fn list_featured_media_files(args: ListFeaturedMediaFilesArgs<'_>) ->
|
||||
args.maybe_filter_media_types,
|
||||
args.maybe_filter_media_classes,
|
||||
args.maybe_filter_engine_categories,
|
||||
args.maybe_filter_product_categories,
|
||||
args.limit,
|
||||
args.maybe_offset,
|
||||
args.cursor_is_reversed,
|
||||
@@ -185,6 +187,7 @@ fn query_builder<'a>(
|
||||
maybe_filter_media_types: Option<&HashSet<MediaFileType>>,
|
||||
maybe_filter_media_classes: Option<&HashSet<MediaFileClass>>,
|
||||
maybe_filter_engine_categories: Option<&HashSet<MediaFileEngineCategory>>,
|
||||
maybe_filter_product_categories: Option<&HashSet<MediaFileOriginProductCategory>>,
|
||||
limit: usize,
|
||||
maybe_offset: Option<usize>,
|
||||
cursor_is_reversed: bool,
|
||||
@@ -340,6 +343,28 @@ LEFT OUTER JOIN prompts
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(product_categories) = maybe_filter_product_categories {
|
||||
// NB: `WHERE IN` comma separated syntax will be wrong if list has zero length
|
||||
// We'll skip the predicate if the list isn't empty.
|
||||
if !product_categories.is_empty() {
|
||||
if !first_predicate_added {
|
||||
query_builder.push(" WHERE ");
|
||||
first_predicate_added = true;
|
||||
} else {
|
||||
query_builder.push(" AND ");
|
||||
}
|
||||
query_builder.push(" m.origin_product_category IN ( ");
|
||||
|
||||
let mut separated = query_builder.separated(", ");
|
||||
|
||||
for product_category in product_categories.iter() {
|
||||
separated.push_bind(product_category.to_str());
|
||||
}
|
||||
|
||||
separated.push_unseparated(") ");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Argument to control `is_intermediate_system_file`
|
||||
{
|
||||
if !first_predicate_added {
|
||||
|
||||
@@ -38,6 +38,7 @@ use tokens::tokens::user_bookmarks::*;
|
||||
use tokens::tokens::users::*;
|
||||
use tokens::tokens::zs_voice_datasets::*;
|
||||
|
||||
use crate::http_server::common_requests::auto_product_category::AutoProductCategory;
|
||||
use crate::http_server::common_requests::media_file_token_path_info::MediaFileTokenPathInfo;
|
||||
use crate::http_server::common_responses::media_file_cover_image_details::MediaFileCoverImageDetails;
|
||||
use crate::http_server::common_responses::media_file_cover_image_details::MediaFileDefaultCover;
|
||||
@@ -89,7 +90,6 @@ use crate::http_server::endpoints::media_files::upload::upload_audio_media_file_
|
||||
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_studio_shot::upload_studio_shot_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::*;
|
||||
@@ -97,6 +97,7 @@ use crate::http_server::endpoints::media_files::upload::upload_new_video_media_f
|
||||
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::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_studio_shot::upload_studio_shot_media_file_handler::*;
|
||||
use crate::http_server::endpoints::media_files::upload::upload_video::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;
|
||||
@@ -129,8 +130,6 @@ use crate::http_server::endpoints::weights::list::list_available_weights_handler
|
||||
use crate::http_server::endpoints::weights::list::list_featured_weights_handler::*;
|
||||
use crate::http_server::endpoints::weights::list::list_pinned_weights_handler::*;
|
||||
use crate::http_server::endpoints::weights::list::list_weights_by_user_handler::*;
|
||||
use crate::http_server::endpoints::weights::search::search_model_weights_http_get_handler::*;
|
||||
use crate::http_server::endpoints::weights::search::search_model_weights_http_post_handler::*;
|
||||
use crate::http_server::endpoints::weights::search::search_model_weights_impl::*;
|
||||
use crate::http_server::endpoints::weights::update::set_model_weight_cover_image_handler::*;
|
||||
use crate::http_server::endpoints::weights::update::update_weight_handler::*;
|
||||
@@ -267,6 +266,9 @@ use crate::http_server::web_utils::response_success_helpers::*;
|
||||
WeightsCategory,
|
||||
WeightsType,
|
||||
|
||||
// Other common enums
|
||||
AutoProductCategory,
|
||||
|
||||
// Common path info
|
||||
MediaFileTokenPathInfo,
|
||||
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
|
||||
/// AutoProductCategory can be used in APIs to auto-expand to multiple product categories,
|
||||
/// deprecated product categories, etc.
|
||||
///
|
||||
/// For example, "voice" expands to ["tts", "zs_voice", "voice_conversion"]
|
||||
///
|
||||
/// These can also alias bad product names, eg "live_portrait" is an alias for "face_mirror"
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash, PartialOrd, Ord, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AutoProductCategory {
|
||||
// ===== Synthetic product categories that alias or auto-expand to multiple ===== //
|
||||
|
||||
/// Synthetic category that expands to ["tts", "zs_voice", "voice_conversion"]
|
||||
Voice,
|
||||
|
||||
// ===== Synthetic product categories that alias others ===== //
|
||||
|
||||
/// Synthetic category that expands to ["face_animator"]
|
||||
Lipsync,
|
||||
|
||||
/// This is an alias for "face_mirror"
|
||||
LivePortrait,
|
||||
|
||||
// ===== Current Database Product Categories ===== //
|
||||
|
||||
FaceAnimator,
|
||||
|
||||
FaceMirror,
|
||||
|
||||
#[serde(rename = "vst")]
|
||||
VideoStyleTransfer,
|
||||
|
||||
#[serde(rename = "studio")]
|
||||
StorytellerStudio,
|
||||
|
||||
#[serde(rename = "tts")]
|
||||
TextToSpeech,
|
||||
|
||||
VoiceConversion,
|
||||
|
||||
#[serde(rename = "zs_voice")]
|
||||
ZeroShotVoice,
|
||||
|
||||
Mocap,
|
||||
|
||||
#[serde(rename = "image_gen")]
|
||||
ImageGeneration,
|
||||
|
||||
// ===== Deprecated Database Product Categories ===== //
|
||||
|
||||
#[deprecated]
|
||||
VideoFilter,
|
||||
|
||||
#[deprecated]
|
||||
Workflow,
|
||||
}
|
||||
|
||||
impl AutoProductCategory {
|
||||
pub fn expand_to_db_product_categories(&self) -> HashSet<MediaFileOriginProductCategory> {
|
||||
match self {
|
||||
// Synthetic product categories that auto-expand to multiple
|
||||
AutoProductCategory::Voice => HashSet::from([
|
||||
MediaFileOriginProductCategory::TextToSpeech,
|
||||
MediaFileOriginProductCategory::ZeroShotVoice,
|
||||
MediaFileOriginProductCategory::VoiceConversion,
|
||||
]),
|
||||
|
||||
// Synthetic product categories that alias other product categories
|
||||
AutoProductCategory::Lipsync => HashSet::from([MediaFileOriginProductCategory::FaceAnimator]),
|
||||
AutoProductCategory::LivePortrait => HashSet::from([MediaFileOriginProductCategory::FaceMirror]),
|
||||
|
||||
// Current database product categories
|
||||
AutoProductCategory::FaceAnimator => HashSet::from([MediaFileOriginProductCategory::FaceAnimator]),
|
||||
AutoProductCategory::FaceMirror => HashSet::from([MediaFileOriginProductCategory::FaceMirror]),
|
||||
AutoProductCategory::VideoStyleTransfer => HashSet::from([MediaFileOriginProductCategory::VideoStyleTransfer]),
|
||||
AutoProductCategory::StorytellerStudio => HashSet::from([MediaFileOriginProductCategory::StorytellerStudio]),
|
||||
AutoProductCategory::TextToSpeech => HashSet::from([MediaFileOriginProductCategory::TextToSpeech]),
|
||||
AutoProductCategory::VoiceConversion => HashSet::from([MediaFileOriginProductCategory::VoiceConversion]),
|
||||
AutoProductCategory::ZeroShotVoice => HashSet::from([MediaFileOriginProductCategory::ZeroShotVoice]),
|
||||
AutoProductCategory::Mocap => HashSet::from([MediaFileOriginProductCategory::Mocap]),
|
||||
AutoProductCategory::ImageGeneration => HashSet::from([MediaFileOriginProductCategory::ImageGeneration]),
|
||||
|
||||
// Deprecated database product categories
|
||||
AutoProductCategory::VideoFilter => HashSet::from([MediaFileOriginProductCategory::VideoFilter]),
|
||||
AutoProductCategory::Workflow => HashSet::from([MediaFileOriginProductCategory::Workflow]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
// Synthetic product categories that auto-expand to multiple
|
||||
"voice" => Ok(Self::Voice),
|
||||
|
||||
// Synthetic product categories that alias other product categories
|
||||
"lipsync" => Ok(Self::Lipsync),
|
||||
"live_portrait" => Ok(Self::LivePortrait),
|
||||
|
||||
// Current database product categories
|
||||
"face_animator" => Ok(Self::FaceAnimator),
|
||||
"face_mirror" => Ok(Self::FaceMirror),
|
||||
"vst" => Ok(Self::VideoStyleTransfer),
|
||||
"studio" => Ok(Self::StorytellerStudio),
|
||||
"tts" => Ok(Self::TextToSpeech),
|
||||
"voice_conversion" => Ok(Self::VoiceConversion),
|
||||
"zs_voice" => Ok(Self::ZeroShotVoice),
|
||||
"mocap" => Ok(Self::Mocap),
|
||||
"image_gen" => Ok(Self::ImageGeneration),
|
||||
|
||||
// Deprecated database product categories
|
||||
"video_filter" => Ok(Self::VideoFilter),
|
||||
"workflow" => Ok(Self::Workflow),
|
||||
|
||||
_ => Err(format!("Invalid AutoProductCategory: {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod auto_product_category;
|
||||
pub mod media_file_token_path_info;
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
|
||||
use crate::http_server::common_requests::auto_product_category::AutoProductCategory;
|
||||
|
||||
pub fn get_scoped_product_categories(
|
||||
maybe_query_param: Option<&str>
|
||||
) -> Option<HashSet<MediaFileOriginProductCategory>> {
|
||||
|
||||
let categories = match maybe_query_param {
|
||||
None => return None,
|
||||
Some(categories) => categories,
|
||||
};
|
||||
|
||||
// NB: This silently fails on invalid values. Probably not the best tactic.
|
||||
let categories= categories.split(",")
|
||||
.map(|c| AutoProductCategory::from_str(c))
|
||||
.flatten()
|
||||
.fold(HashSet::new(), |mut acc, cat| {
|
||||
acc.extend(cat.expand_to_db_product_categories());
|
||||
acc
|
||||
});
|
||||
|
||||
if categories.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(categories)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
|
||||
use crate::http_server::endpoints::media_files::helpers::get_scoped_product_categories::get_scoped_product_categories;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
assert_eq!(get_scoped_product_categories(None), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
assert_eq!(get_scoped_product_categories(Some("")), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn garbage() {
|
||||
assert_eq!(get_scoped_product_categories(Some("foo,bar,baz")), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn direct_map() {
|
||||
assert_eq!(
|
||||
get_scoped_product_categories(Some("mocap")),
|
||||
Some(HashSet::from([
|
||||
MediaFileOriginProductCategory::Mocap,
|
||||
]))
|
||||
);
|
||||
assert_eq!(
|
||||
get_scoped_product_categories(Some("face_animator,face_mirror")),
|
||||
Some(HashSet::from([
|
||||
MediaFileOriginProductCategory::FaceAnimator,
|
||||
MediaFileOriginProductCategory::FaceMirror
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_map() {
|
||||
assert_eq!(
|
||||
get_scoped_product_categories(Some("lipsync")),
|
||||
Some(HashSet::from([
|
||||
MediaFileOriginProductCategory::FaceAnimator,
|
||||
]))
|
||||
);
|
||||
assert_eq!(
|
||||
get_scoped_product_categories(Some("live_portrait")),
|
||||
Some(HashSet::from([
|
||||
MediaFileOriginProductCategory::FaceMirror,
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_map() {
|
||||
assert_eq!(
|
||||
get_scoped_product_categories(Some("voice")),
|
||||
Some(HashSet::from([
|
||||
MediaFileOriginProductCategory::TextToSpeech,
|
||||
MediaFileOriginProductCategory::VoiceConversion,
|
||||
MediaFileOriginProductCategory::ZeroShotVoice,
|
||||
]))
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
pub mod get_scoped_engine_categories;
|
||||
pub mod get_scoped_media_classes;
|
||||
pub mod get_scoped_media_types;
|
||||
pub mod get_scoped_media_types;
|
||||
pub mod get_scoped_product_categories;
|
||||
+16
@@ -31,6 +31,7 @@ use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
|
||||
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::endpoints::media_files::helpers::get_scoped_product_categories::get_scoped_product_categories;
|
||||
use crate::http_server::web_utils::bucket_urls::bucket_url_string_from_media_path::bucket_url_string_from_media_path;
|
||||
use crate::state::server_state::ServerState;
|
||||
use crate::util::allowed_explore_media_access::allowed_explore_media_access;
|
||||
@@ -73,6 +74,19 @@ pub struct ListFeaturedMediaFilesQueryParams {
|
||||
/// - `?filter_engine_categories=animation,character,object`
|
||||
/// - etc.
|
||||
pub filter_engine_categories: Option<String>,
|
||||
|
||||
/// NB: This can be one (or more comma-separated values) from `AutoProductCategory`.
|
||||
/// AutoProductCategory is not a db enum directly and can expand to multiple categories for some values.
|
||||
///
|
||||
/// Usage:
|
||||
/// - `?filter_products=voice`
|
||||
/// - `?filter_products=tts,voice_conversion,zs_voice`
|
||||
/// - `?filter_products=live_portrait`
|
||||
/// - `?filter_products=lipsync`
|
||||
/// - `?filter_products=vst`
|
||||
/// - `?filter_products=studio`
|
||||
/// - etc.
|
||||
pub filter_products: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
@@ -353,6 +367,7 @@ async fn database_lookup(
|
||||
let maybe_filter_media_types = get_scoped_media_types(query.filter_media_type.as_deref());
|
||||
let maybe_filter_media_classes = get_scoped_media_classes(query.filter_media_classes.as_deref());
|
||||
let maybe_filter_engine_categories = get_scoped_engine_categories(query.filter_engine_categories.as_deref());
|
||||
let maybe_filter_product_categories = get_scoped_product_categories(query.filter_engine_categories.as_deref());
|
||||
|
||||
// NB: No reason to show deleted or non-public featured items to mods or authors.
|
||||
// That's just confusing and wastes an extra query.
|
||||
@@ -367,6 +382,7 @@ async fn database_lookup(
|
||||
maybe_filter_media_types: maybe_filter_media_types.as_ref(),
|
||||
maybe_filter_media_classes: maybe_filter_media_classes.as_ref(),
|
||||
maybe_filter_engine_categories: maybe_filter_engine_categories.as_ref(),
|
||||
maybe_filter_product_categories: maybe_filter_product_categories.as_ref(),
|
||||
mysql_pool: &server_state.mysql_pool,
|
||||
}).await.map_err(|err| {
|
||||
error!("DB error: {:?}", err);
|
||||
|
||||
Reference in new issue
Block a user