mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
refactor out API types so we can use it in a client
This commit is contained in:
1 parent
9782bd33bc
commit
9a4be8a142
21 files changed
+581
-219
No files matched your search
Generated
+2
@@ -698,11 +698,13 @@ dependencies = [
|
||||
name = "artcraft_api_defs"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"enums",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokens",
|
||||
"url",
|
||||
"utoipa",
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
@@ -8,25 +8,29 @@ authors = [
|
||||
]
|
||||
publish = false
|
||||
|
||||
[package.metadata.cargo-udeps.ignore]
|
||||
normal = ["workspace-build-acceleration"]
|
||||
|
||||
[lib]
|
||||
name = "artcraft_api_defs"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[package.metadata.cargo-udeps.ignore]
|
||||
normal = ["workspace-build-acceleration"]
|
||||
|
||||
[dependencies]
|
||||
# Hackari workspace (faster compile times)
|
||||
workspace-build-acceleration = { workspace = true }
|
||||
workspace-build-acceleration.workspace = true
|
||||
|
||||
# Internal
|
||||
enums = { workspace = true }
|
||||
tokens = { workspace = true }
|
||||
enums.workspace = true
|
||||
tokens.workspace = true
|
||||
|
||||
# External
|
||||
chrono.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
# Serialization
|
||||
serde = { workspace = true }
|
||||
serde_derive = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
utoipa = { version = "4", features = ["chrono", "actix_extras"] }
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod responses;
|
||||
+14
-2
@@ -1,6 +1,6 @@
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use serde::Serialize;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Details about submitted live portrait jobs (request arguments only)
|
||||
#[derive(Serialize, ToSchema)]
|
||||
@@ -18,3 +18,15 @@ pub struct JobDetailsLivePortraitRequest {
|
||||
/// This video must contain a face.
|
||||
pub face_driver_media_file_token: MediaFileToken,
|
||||
}
|
||||
|
||||
/// Details about submitted lipsync jobs (request arguments only)
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct JobDetailsLipsyncRequest {
|
||||
/// Media file token for the source audio.
|
||||
/// This is probably an audio file, but in the future we might pull audio from video.
|
||||
pub audio_source_token: MediaFileToken,
|
||||
|
||||
/// Media file token for the source visuals.
|
||||
/// This is either an image or video.
|
||||
pub image_or_video_source_token: MediaFileToken,
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
use url::Url;
|
||||
|
||||
/// Links to media file locations (bucket, CDN, etc.)
|
||||
#[derive(Serialize, ToSchema, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct MediaLinks {
|
||||
/// Primary link to the asset via the CDN.
|
||||
pub cdn_url: Url,
|
||||
|
||||
/// Template to construct thumbnail URLs.
|
||||
/// Replace the string `{WIDTH}` with the desired width.
|
||||
/// Only relevant for image media files. (Video media files instead have
|
||||
/// video previews, which, in turn, have their own thumbnail templates.)
|
||||
pub maybe_thumbnail_template: Option<String>,
|
||||
|
||||
/// Video preview images (still and animated gif) for mp4 video files.
|
||||
/// These are only set for video media files.
|
||||
/// These are not set for "cover images" that are gif or webms, but rather video files.
|
||||
pub maybe_video_previews: Option<VideoPreviews>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct VideoPreviews {
|
||||
/// A static single frame preview image of the video.
|
||||
pub still: Url,
|
||||
|
||||
/// An animated gif preview of the video.
|
||||
pub animated: Url,
|
||||
|
||||
/// A template used to construct the still thumbnail URL.
|
||||
/// Replace the string `{WIDTH}` with the desired width.
|
||||
pub still_thumbnail_template: String,
|
||||
|
||||
/// A template used to construct the animated thumbnail URL.
|
||||
/// Replace the string `{WIDTH}` with the desired width.
|
||||
pub animated_thumbnail_template: String,
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod job_details;
|
||||
pub mod media_links;
|
||||
@@ -0,0 +1,124 @@
|
||||
use crate::common::responses::job_details::{JobDetailsLipsyncRequest, JobDetailsLivePortraitRequest};
|
||||
use crate::common::responses::media_links::MediaLinks;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
pub const LIST_SESSION_JOBS_URL_PATH: &str = "/v1/jobs/session";
|
||||
|
||||
#[derive(Deserialize, ToSchema, IntoParams)]
|
||||
pub struct ListSessionJobsQueryParams {
|
||||
//pub sort_ascending: Option<bool>,
|
||||
//pub page_size: Option<usize>, // TODO(bt,2024-04-23): One page for now.
|
||||
//pub page_index: Option<usize>, // TODO(bt,2024-04-23): One page for now.
|
||||
|
||||
/// NB: This can be one (or more comma-separated values) from `JobStatusPlus`.
|
||||
/// ?include_states=pending or ?include_states=pending,started,complete_success (etc.)
|
||||
pub include_states: Option<String>,
|
||||
|
||||
/// The opposite of include_states, this filters out states from view.
|
||||
pub exclude_states: Option<String>,
|
||||
|
||||
// TODO(bt,2024-04-23): Add the ability for users to dismiss completed/dead jobs from view.
|
||||
}
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionJobsSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// This is not paginated and is limited to showing 100 jobs.
|
||||
pub jobs: Vec<ListSessionJobsItem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionJobsItem {
|
||||
pub job_token: InferenceJobToken,
|
||||
|
||||
pub request: ListSessionRequestDetailsResponse,
|
||||
pub status: ListSessionStatusDetailsResponse,
|
||||
pub maybe_result: Option<ListSessionResultDetailsResponse>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Details about what the user requested for generation
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionRequestDetailsResponse {
|
||||
pub inference_category: InferenceCategory,
|
||||
pub maybe_model_type: Option<String>,
|
||||
pub maybe_model_token: Option<String>,
|
||||
|
||||
/// OPTIONAL. Title of the model, if it has one
|
||||
pub maybe_model_title: Option<String>,
|
||||
|
||||
/// OPTIONAL. If the result was TTS, this is the raw inference text.
|
||||
pub maybe_raw_inference_text: Option<String>,
|
||||
|
||||
/// OPTIONAL. For Comfy / Video Style Transfer jobs, this might include
|
||||
/// the name of the selected style.
|
||||
pub maybe_style_name: Option<StyleTransferName>,
|
||||
|
||||
/// OPTIONAL. For Live Portrait jobs, this is additional information on the request.
|
||||
pub maybe_live_portrait_details: Option<JobDetailsLivePortraitRequest>,
|
||||
|
||||
/// OPTIONAL. For lipsync jobs (face fusion and sad talker), this is additional
|
||||
/// information on the request.
|
||||
pub maybe_lipsync_details: Option<JobDetailsLipsyncRequest>,
|
||||
}
|
||||
|
||||
/// Details about the ongoing job status
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionStatusDetailsResponse {
|
||||
/// Primary status from the database (a state machine).
|
||||
pub status: JobStatusPlus,
|
||||
|
||||
/// Extra, temporary status from Redis.
|
||||
/// This can denote inference progress, and the Python code can write to it.
|
||||
pub maybe_extra_status_description: Option<String>,
|
||||
|
||||
pub maybe_assigned_worker: Option<String>,
|
||||
pub maybe_assigned_cluster: Option<String>,
|
||||
|
||||
pub maybe_first_started_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// If the job is currently running, this is how long it has been running in seconds.
|
||||
/// This is heuristic estimate since we don't record the precise start time across runs.
|
||||
pub maybe_current_execution_duration_seconds: Option<u64>,
|
||||
|
||||
pub attempt_count: u8,
|
||||
|
||||
/// Whether the frontend needs to maintain a keepalive check.
|
||||
/// This is typically only for non-premium users.
|
||||
pub requires_keepalive: bool,
|
||||
|
||||
/// An enum the frontend can use to display localized/I18N error
|
||||
/// messages. These pertain to both transient and permanent failures.
|
||||
pub maybe_failure_category: Option<FrontendFailureCategory>,
|
||||
|
||||
/// This is an integer number between 0 and 100 (both inclusive) that
|
||||
/// reports the completeness.
|
||||
pub progress_percentage: u8,
|
||||
}
|
||||
|
||||
/// Details about the completed result (if any)
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionResultDetailsResponse {
|
||||
pub entity_type: String,
|
||||
pub entity_token: String,
|
||||
|
||||
/// (DEPRECATED) URL path to the media file
|
||||
#[deprecated(note="This field doesn't point to the full URL. Use media_links instead to leverage the CDN.")]
|
||||
pub maybe_public_bucket_media_path: Option<String>,
|
||||
|
||||
/// Rich CDN links to the media, including thumbnails, previews, and more.
|
||||
pub media_links: MediaLinks,
|
||||
|
||||
pub maybe_successfully_completed_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod list_session_jobs;
|
||||
@@ -1 +1,3 @@
|
||||
pub mod generate;
|
||||
pub mod common;
|
||||
pub mod generate;
|
||||
pub mod jobs;
|
||||
@@ -1,5 +1,7 @@
|
||||
use utoipa::OpenApi;
|
||||
|
||||
use artcraft_api_defs::common::responses::job_details::JobDetailsLipsyncRequest;
|
||||
use artcraft_api_defs::common::responses::job_details::JobDetailsLivePortraitRequest;
|
||||
use artcraft_api_defs::generate::image::edit::gpt_image_1_edit_image::*;
|
||||
use artcraft_api_defs::generate::image::generate_flux_1_dev_text_to_image::GenerateFlux1DevTextToImageAspectRatio;
|
||||
use artcraft_api_defs::generate::image::generate_flux_1_dev_text_to_image::GenerateFlux1DevTextToImageNumImages;
|
||||
@@ -43,44 +45,10 @@ use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::GenerateV
|
||||
use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::GenerateVeo2Duration;
|
||||
use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::GenerateVeo2ImageToVideoRequest;
|
||||
use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::GenerateVeo2ImageToVideoResponse;
|
||||
use artcraft_api_defs::jobs::list_session_jobs::*;
|
||||
use billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_error::CreateCheckoutSessionError;
|
||||
use billing_component::stripe::http_endpoints::checkout::create::stripe_create_checkout_session_json_handler::*;
|
||||
use billing_component::users::http_endpoints::list_active_user_subscriptions_handler::*;
|
||||
use enums::by_table::beta_keys::beta_key_product::BetaKeyProduct;
|
||||
use enums::by_table::comments::comment_entity_type::CommentEntityType;
|
||||
use enums::by_table::featured_items::featured_item_entity_type::FeaturedItemEntityType;
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType;
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_subtype::MediaFileSubtype;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::by_table::model_weights::{weights_category::WeightsCategory, weights_types::WeightsType};
|
||||
use enums::by_table::prompts::prompt_type::PromptType;
|
||||
use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType;
|
||||
use enums::by_table::user_ratings::entity_type::UserRatingEntityType;
|
||||
use enums::by_table::user_ratings::rating_value::UserRatingValue;
|
||||
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::common::visibility::Visibility;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use enums_public::by_table::media_files::public_media_file_model_type::PublicMediaFileModelType;
|
||||
use enums_public::by_table::model_weights::public_weights_types::PublicWeightsType;
|
||||
use tokens::tokens::batch_generations::*;
|
||||
use tokens::tokens::beta_keys::*;
|
||||
use tokens::tokens::browser_session_logs::*;
|
||||
use tokens::tokens::comments::*;
|
||||
use tokens::tokens::generic_inference_jobs::*;
|
||||
use tokens::tokens::media_files::*;
|
||||
use tokens::tokens::model_weights::*;
|
||||
use tokens::tokens::prompts::*;
|
||||
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::cover_image_links::CoverImageLinks;
|
||||
@@ -128,7 +96,40 @@ use crate::http_server::endpoints::featured_items::delete_featured_item_handler:
|
||||
use crate::http_server::endpoints::featured_items::get_is_featured_item_handler::*;
|
||||
use crate::http_server::endpoints::image_studio::prompt::enqueue_studio_image_generation_handler::*;
|
||||
use crate::http_server::endpoints::image_studio::upload::upload_snapshot_media_file_handler::*;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::live_portrait::JobDetailsLivePortraitRequest;
|
||||
use enums::by_table::beta_keys::beta_key_product::BetaKeyProduct;
|
||||
use enums::by_table::comments::comment_entity_type::CommentEntityType;
|
||||
use enums::by_table::featured_items::featured_item_entity_type::FeaturedItemEntityType;
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::by_table::media_files::media_file_animation_type::MediaFileAnimationType;
|
||||
use enums::by_table::media_files::media_file_class::MediaFileClass;
|
||||
use enums::by_table::media_files::media_file_engine_category::MediaFileEngineCategory;
|
||||
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
|
||||
use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory;
|
||||
use enums::by_table::media_files::media_file_subtype::MediaFileSubtype;
|
||||
use enums::by_table::media_files::media_file_type::MediaFileType;
|
||||
use enums::by_table::model_weights::{weights_category::WeightsCategory, weights_types::WeightsType};
|
||||
use enums::by_table::prompts::prompt_type::PromptType;
|
||||
use enums::by_table::users::user_feature_flag::UserFeatureFlag;
|
||||
use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType;
|
||||
use enums::by_table::user_ratings::entity_type::UserRatingEntityType;
|
||||
use enums::by_table::user_ratings::rating_value::UserRatingValue;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::common::visibility::Visibility;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use enums_public::by_table::media_files::public_media_file_model_type::PublicMediaFileModelType;
|
||||
use enums_public::by_table::model_weights::public_weights_types::PublicWeightsType;
|
||||
use tokens::tokens::batch_generations::*;
|
||||
use tokens::tokens::beta_keys::*;
|
||||
use tokens::tokens::browser_session_logs::*;
|
||||
use tokens::tokens::comments::*;
|
||||
use tokens::tokens::generic_inference_jobs::*;
|
||||
use tokens::tokens::media_files::*;
|
||||
use tokens::tokens::model_weights::*;
|
||||
use tokens::tokens::prompts::*;
|
||||
use tokens::tokens::users::*;
|
||||
use tokens::tokens::user_bookmarks::*;
|
||||
use tokens::tokens::zs_voice_datasets::*;
|
||||
use crate::http_server::endpoints::inference_job::delete::dismiss_finished_session_jobs_handler::*;
|
||||
use crate::http_server::endpoints::inference_job::delete::terminate_inference_job_handler::*;
|
||||
use crate::http_server::endpoints::inference_job::get::batch_get_inference_job_status_handler::*;
|
||||
@@ -363,6 +364,7 @@ use crate::http_server::web_utils::response_success_helpers::*;
|
||||
|
||||
// Common response structs
|
||||
JobDetailsLivePortraitRequest,
|
||||
JobDetailsLipsyncRequest,
|
||||
MediaFileLivePortraitDetails,
|
||||
MediaFileModelDetails,
|
||||
MediaFileOriginDetails,
|
||||
|
||||
+2
@@ -9,6 +9,8 @@ use server_environment::ServerEnvironment;
|
||||
// TODO(bt,2024-09-05): Worth reducing the quality at all?
|
||||
const QUALITY : u8 = 95;
|
||||
|
||||
/// TODO(bt, 2025-07-14): Migrate to `artcraft_api_deps` crate's `MediaLinks`.
|
||||
/// Use `MediaLinksBuilder` (in this crate) to assist migration.
|
||||
|
||||
/// Links to media file locations (bucket, CDN, etc.)
|
||||
#[derive(Serialize, ToSchema, Debug, Clone, Eq, PartialEq)]
|
||||
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
use crate::http_server::common_responses::media::cdn_link::{get_cdn_host, new_cdn_url};
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use artcraft_api_defs::common::responses::media_links::{MediaLinks, VideoPreviews};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use server_environment::ServerEnvironment;
|
||||
use url::Url;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
// TODO(bt,2024-09-05): Worth reducing the quality at all?
|
||||
const QUALITY : u8 = 95;
|
||||
|
||||
|
||||
/// Links to media file locations (bucket, CDN, etc.)
|
||||
///
|
||||
/// This migrates the constructors to the `artcraft_api_defs` crate
|
||||
pub struct MediaLinksBuilder {}
|
||||
|
||||
impl MediaLinksBuilder {
|
||||
#[deprecated(note = "we need to start passing the domain and the server environment to generate the CDN")]
|
||||
pub fn from_media_path(
|
||||
domain: MediaDomain,
|
||||
bucket_path: &MediaFileBucketPath,
|
||||
) -> MediaLinks {
|
||||
Self::from_media_path_and_env(domain, ServerEnvironment::Production, bucket_path)
|
||||
}
|
||||
|
||||
#[deprecated(note = "we need to start passing the domain and the server environment to generate the CDN")]
|
||||
pub fn from_rooted_path(
|
||||
domain: MediaDomain,
|
||||
rooted_path: &str,
|
||||
) -> MediaLinks {
|
||||
Self::from_rooted_path_and_env(domain, ServerEnvironment::Production, rooted_path)
|
||||
}
|
||||
|
||||
pub fn from_media_path_and_env(
|
||||
domain: MediaDomain,
|
||||
server_environment: ServerEnvironment,
|
||||
bucket_path: &MediaFileBucketPath,
|
||||
) -> MediaLinks {
|
||||
let rooted_path = bucket_path.get_full_object_path_str();
|
||||
Self::from_rooted_path_and_env(domain, server_environment, rooted_path)
|
||||
}
|
||||
|
||||
pub fn from_rooted_path_and_env(
|
||||
domain: MediaDomain,
|
||||
server_environment: ServerEnvironment,
|
||||
rooted_path: &str,
|
||||
) -> MediaLinks {
|
||||
let mut cdn_url = new_cdn_url(domain, server_environment);
|
||||
cdn_url.set_path(rooted_path);
|
||||
MediaLinks {
|
||||
cdn_url,
|
||||
maybe_thumbnail_template: thumbnail_template(domain, server_environment, rooted_path),
|
||||
maybe_video_previews: VideoPreviewsBuilder::from_rooted_path(domain, rooted_path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VideoPreviewsBuilder {}
|
||||
|
||||
impl VideoPreviewsBuilder {
|
||||
fn from_rooted_path(
|
||||
domain: MediaDomain,
|
||||
rooted_path: &str,
|
||||
) -> Option<VideoPreviews> {
|
||||
if !rooted_path.ends_with(".mp4") {
|
||||
return None;
|
||||
}
|
||||
Some(VideoPreviews {
|
||||
still: video_preview(domain, rooted_path, PreviewType::Jpg),
|
||||
animated: video_preview(domain, rooted_path, PreviewType::Gif),
|
||||
still_thumbnail_template: video_preview_thumbnail_template(domain, rooted_path, PreviewType::Jpg),
|
||||
animated_thumbnail_template: video_preview_thumbnail_template(domain, rooted_path, PreviewType::Gif),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
enum PreviewType {
|
||||
Gif,
|
||||
Jpg
|
||||
}
|
||||
|
||||
/// Returns a jpeg or gif preview of the video.
|
||||
fn video_preview(media_domain: MediaDomain, rooted_path: &str, thumbnail_type: PreviewType) -> Url {
|
||||
let host = media_domain.cdn_url_str();
|
||||
let rooted_path = match thumbnail_type {
|
||||
PreviewType::Gif => format!("{rooted_path}-thumb.gif"),
|
||||
PreviewType::Jpg => format!("{rooted_path}-thumb.jpg"),
|
||||
};
|
||||
let mut url = media_domain.new_cdn_url();
|
||||
url.set_path(&rooted_path);
|
||||
url
|
||||
}
|
||||
|
||||
/// Returns a thumbnail template for image
|
||||
fn thumbnail_template(media_domain: MediaDomain, server_environment: ServerEnvironment, rooted_path: &str) -> Option<String> {
|
||||
if !rooted_path.ends_with(".jpg")
|
||||
&& !rooted_path.ends_with(".png")
|
||||
&& !rooted_path.ends_with(".gif") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let host = get_cdn_host(media_domain, server_environment);
|
||||
|
||||
// NB(bt,2025-02-01): Development doesn't currently support thumbnails, so serve the full image.
|
||||
match server_environment {
|
||||
ServerEnvironment::Development => Some(format!("{host}{rooted_path}")), // NB(bt,2025-02-01): No thumbnails in development.
|
||||
ServerEnvironment::Production => Some(format!("{host}/cdn-cgi/image/width={{WIDTH}},quality={QUALITY}{rooted_path}"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a thumbnail template for video
|
||||
fn video_preview_thumbnail_template(media_domain: MediaDomain, rooted_path: &str, thumbnail_type: PreviewType) -> String {
|
||||
let host = media_domain.cdn_url_str();
|
||||
let rooted_path = match thumbnail_type {
|
||||
PreviewType::Gif => format!("{rooted_path}-thumb.gif"),
|
||||
PreviewType::Jpg => format!("{rooted_path}-thumb.jpg"),
|
||||
};
|
||||
format!("{host}/cdn-cgi/image/width={{WIDTH}},quality={QUALITY}{rooted_path}")
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
|
||||
mod fakeyou {
|
||||
use super::*;
|
||||
use server_environment::ServerEnvironment;
|
||||
|
||||
const DOMAIN : MediaDomain = MediaDomain::FakeYou;
|
||||
|
||||
// TODO(bt,2025-01-31): Write robust tests for this.
|
||||
#[test]
|
||||
fn spot_check_new_case() {
|
||||
let links = MediaLinksBuilder::from_rooted_path_and_env(MediaDomain::FakeYou, ServerEnvironment::Production, "/foo/bar.wav");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/foo/bar.wav");
|
||||
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;let links = MediaLinksBuilder::from_rooted_path_and_env(MediaDomain::Storyteller, ServerEnvironment::Development, "/foo/bar.wav");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://pub-c8a4a5bdbdb048f286b77bdf9f786ff2.r2.dev/foo/bar.wav");
|
||||
}
|
||||
|
||||
mod rooted_path {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn wav_file() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.wav");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/foo/bar.wav");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glb_file() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.glb");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/foo/bar.glb");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jpg_image() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.jpg");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/foo/bar.jpg");
|
||||
assert_eq!(links.maybe_thumbnail_template, Some("https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/foo/bar.jpg".to_string()));
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp4_video() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.mp4");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/foo/bar.mp4");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
let video_previews = links.maybe_video_previews.expect("should have previews");
|
||||
assert_eq!(video_previews.animated.as_str(), "https://cdn-2.fakeyou.com/foo/bar.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still.as_str(), "https://cdn-2.fakeyou.com/foo/bar.mp4-thumb.jpg");
|
||||
assert_eq!(video_previews.animated_thumbnail_template, "https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/foo/bar.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still_thumbnail_template, "https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/foo/bar.mp4-thumb.jpg");
|
||||
}
|
||||
}
|
||||
|
||||
mod media_path {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn wav_file() {
|
||||
// https://storage.googleapis.com/vocodes-public/media/9/4/a/2/7/94a27nmbd0bqmd10tg0pp3hz45zytf67/fakeyou_94a27nmbd0bqmd10tg0pp3hz45zytf67.wav
|
||||
let media_path = MediaFileBucketPath::from_object_hash("94a27nmbd0bqmd10tg0pp3hz45zytf67", Some("fakeyou_"), Some(".wav"));
|
||||
let links = MediaLinksBuilder::from_media_path(DOMAIN, &media_path);
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/media/9/4/a/2/7/94a27nmbd0bqmd10tg0pp3hz45zytf67/fakeyou_94a27nmbd0bqmd10tg0pp3hz45zytf67.wav");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn png_image() {
|
||||
/// https://storage.googleapis.com/vocodes-public/media/3/7/m/b/3/37mb3gh8fmj85y21thvbv08bzv24atjt/upload_37mb3gh8fmj85y21thvbv08bzv24atjt.png
|
||||
let media_path = MediaFileBucketPath::from_object_hash("37mb3gh8fmj85y21thvbv08bzv24atjt", Some("upload_"), Some(".png"));
|
||||
let links = MediaLinksBuilder::from_media_path(DOMAIN, &media_path);
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/media/3/7/m/b/3/37mb3gh8fmj85y21thvbv08bzv24atjt/upload_37mb3gh8fmj85y21thvbv08bzv24atjt.png");
|
||||
assert_eq!(links.maybe_thumbnail_template, Some("https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/media/3/7/m/b/3/37mb3gh8fmj85y21thvbv08bzv24atjt/upload_37mb3gh8fmj85y21thvbv08bzv24atjt.png".to_string()));
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp4_video() {
|
||||
// https://storage.googleapis.com/vocodes-public/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4
|
||||
let media_path = MediaFileBucketPath::from_object_hash("t6cnyw4g3e8k7carkk2bvrt6nd3fycjv", Some("storyteller_"), Some(".mp4"));
|
||||
let links = MediaLinksBuilder::from_media_path(DOMAIN, &media_path);
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn-2.fakeyou.com/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
let video_previews = links.maybe_video_previews.expect("should have previews");
|
||||
assert_eq!(video_previews.animated.as_str(), "https://cdn-2.fakeyou.com/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still.as_str(), "https://cdn-2.fakeyou.com/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.jpg");
|
||||
assert_eq!(video_previews.animated_thumbnail_template, "https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still_thumbnail_template, "https://cdn-2.fakeyou.com/cdn-cgi/image/width={WIDTH},quality=95/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.jpg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod storyteller {
|
||||
use super::*;
|
||||
|
||||
const DOMAIN : MediaDomain = MediaDomain::Storyteller;
|
||||
|
||||
mod rooted_path {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn wav_file() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.wav");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/foo/bar.wav");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glb_file() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.glb");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/foo/bar.glb");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jpg_image() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.jpg");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/foo/bar.jpg");
|
||||
assert_eq!(links.maybe_thumbnail_template, Some("https://cdn.storyteller.ai/cdn-cgi/image/width={WIDTH},quality=95/foo/bar.jpg".to_string()));
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp4_video() {
|
||||
let links = MediaLinksBuilder::from_rooted_path(DOMAIN, "/foo/bar.mp4");
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/foo/bar.mp4");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
let video_previews = links.maybe_video_previews.expect("should have previews");
|
||||
assert_eq!(video_previews.animated.as_str(), "https://cdn.storyteller.ai/foo/bar.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still.as_str(), "https://cdn.storyteller.ai/foo/bar.mp4-thumb.jpg");
|
||||
assert_eq!(video_previews.animated_thumbnail_template, "https://cdn.storyteller.ai/cdn-cgi/image/width={WIDTH},quality=95/foo/bar.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still_thumbnail_template, "https://cdn.storyteller.ai/cdn-cgi/image/width={WIDTH},quality=95/foo/bar.mp4-thumb.jpg");
|
||||
}
|
||||
}
|
||||
|
||||
mod media_path {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn wav_file() {
|
||||
// https://storage.googleapis.com/vocodes-public/media/9/4/a/2/7/94a27nmbd0bqmd10tg0pp3hz45zytf67/fakeyou_94a27nmbd0bqmd10tg0pp3hz45zytf67.wav
|
||||
let media_path = MediaFileBucketPath::from_object_hash("94a27nmbd0bqmd10tg0pp3hz45zytf67", Some("fakeyou_"), Some(".wav"));
|
||||
let links = MediaLinksBuilder::from_media_path(DOMAIN, &media_path);
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/media/9/4/a/2/7/94a27nmbd0bqmd10tg0pp3hz45zytf67/fakeyou_94a27nmbd0bqmd10tg0pp3hz45zytf67.wav");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn png_image() {
|
||||
/// https://storage.googleapis.com/vocodes-public/media/3/7/m/b/3/37mb3gh8fmj85y21thvbv08bzv24atjt/upload_37mb3gh8fmj85y21thvbv08bzv24atjt.png
|
||||
let media_path = MediaFileBucketPath::from_object_hash("37mb3gh8fmj85y21thvbv08bzv24atjt", Some("upload_"), Some(".png"));
|
||||
let links = MediaLinksBuilder::from_media_path(DOMAIN, &media_path);
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/media/3/7/m/b/3/37mb3gh8fmj85y21thvbv08bzv24atjt/upload_37mb3gh8fmj85y21thvbv08bzv24atjt.png");
|
||||
assert_eq!(links.maybe_thumbnail_template, Some("https://cdn.storyteller.ai/cdn-cgi/image/width={WIDTH},quality=95/media/3/7/m/b/3/37mb3gh8fmj85y21thvbv08bzv24atjt/upload_37mb3gh8fmj85y21thvbv08bzv24atjt.png".to_string()));
|
||||
assert_eq!(links.maybe_video_previews, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mp4_video() {
|
||||
// https://storage.googleapis.com/vocodes-public/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4
|
||||
let media_path = MediaFileBucketPath::from_object_hash("t6cnyw4g3e8k7carkk2bvrt6nd3fycjv", Some("storyteller_"), Some(".mp4"));
|
||||
let links = MediaLinksBuilder::from_media_path(DOMAIN, &media_path);
|
||||
assert_eq!(links.cdn_url.as_str(), "https://cdn.storyteller.ai/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4");
|
||||
assert_eq!(links.maybe_thumbnail_template, None);
|
||||
let video_previews = links.maybe_video_previews.expect("should have previews");
|
||||
assert_eq!(video_previews.animated.as_str(), "https://cdn.storyteller.ai/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still.as_str(), "https://cdn.storyteller.ai/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.jpg");
|
||||
assert_eq!(video_previews.animated_thumbnail_template, "https://cdn.storyteller.ai/cdn-cgi/image/width={WIDTH},quality=95/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.gif");
|
||||
assert_eq!(video_previews.still_thumbnail_template, "https://cdn.storyteller.ai/cdn-cgi/image/width={WIDTH},quality=95/media/t/6/c/n/y/t6cnyw4g3e8k7carkk2bvrt6nd3fycjv/storyteller_t6cnyw4g3e8k7carkk2bvrt6nd3fycjv.mp4-thumb.jpg");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub mod cover_image_links;
|
||||
pub mod media_domain;
|
||||
pub mod media_file_cover_image_details;
|
||||
pub mod media_links;
|
||||
pub mod media_links_builder;
|
||||
pub mod weights_cover_image_details;
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Details about submitted lipsync jobs (request arguments only)
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct JobDetailsLipsyncRequest {
|
||||
/// Media file token for the source audio.
|
||||
/// This is probably an audio file, but in the future we might pull audio from video.
|
||||
pub audio_source_token: MediaFileToken,
|
||||
|
||||
/// Media file token for the source visuals.
|
||||
/// This is either an image or video.
|
||||
pub image_or_video_source_token: MediaFileToken,
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
pub mod lipsync;
|
||||
pub mod live_portrait;
|
||||
+10
-13
@@ -2,20 +2,8 @@ use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web_lab::extract::Query;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::error;
|
||||
use r2d2_redis::redis::Commands;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links::MediaLinks;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::lipsync::JobDetailsLipsyncRequest;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::live_portrait::JobDetailsLivePortraitRequest;
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_lipsync_details::extract_lipsync_details;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_live_portrait_details::extract_live_portrait_details;
|
||||
@@ -24,16 +12,26 @@ use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_m
|
||||
use crate::http_server::web_utils::filter_model_name::maybe_filter_model_name;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web_lab::extract::Query;
|
||||
use artcraft_api_defs::common::responses::job_details::{JobDetailsLipsyncRequest, JobDetailsLivePortraitRequest};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use bucket_paths::legacy::typified_paths::public::voice_conversion_results::bucket_file_path::VoiceConversionResultOriginalFilePath;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use log::error;
|
||||
use mysql_queries::queries::generic_inference::web::batch_get_inference_job_status::batch_get_inference_job_status;
|
||||
use mysql_queries::queries::generic_inference::web::job_status::GenericInferenceJobStatus;
|
||||
use r2d2_redis::redis::Commands;
|
||||
use redis_common::redis_keys::RedisKeys;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
/// For certain jobs or job classes (eg. non-premium), we kill the jobs if the user hasn't
|
||||
/// maintained a keepalive. This prevents wasted work when users who are unlikely to return
|
||||
@@ -41,7 +39,6 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
/// typically do not require keepalive.
|
||||
const JOB_KEEPALIVE_TTL_SECONDS : usize = 60 * 3;
|
||||
|
||||
|
||||
#[derive(Deserialize, ToSchema, IntoParams)]
|
||||
pub struct BatchGetInferenceJobStatusQueryParams {
|
||||
/// A grab bag of tokens of various types.
|
||||
|
||||
+9
-11
@@ -1,19 +1,8 @@
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::error;
|
||||
use r2d2_redis::redis::{Commands, RedisResult};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links::MediaLinks;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::lipsync::JobDetailsLipsyncRequest;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::live_portrait::JobDetailsLivePortraitRequest;
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_lipsync_details::extract_lipsync_details;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_live_portrait_details::extract_live_portrait_details;
|
||||
@@ -22,16 +11,25 @@ use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_m
|
||||
use crate::http_server::web_utils::filter_model_name::maybe_filter_model_name;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Json, Path};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use artcraft_api_defs::common::responses::job_details::{JobDetailsLipsyncRequest, JobDetailsLivePortraitRequest};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use bucket_paths::legacy::typified_paths::public::voice_conversion_results::bucket_file_path::VoiceConversionResultOriginalFilePath;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use log::error;
|
||||
use mysql_queries::queries::generic_inference::web::get_inference_job_status::get_inference_job_status;
|
||||
use mysql_queries::queries::generic_inference::web::job_status::GenericInferenceJobStatus;
|
||||
use r2d2_redis::redis::{Commands, RedisResult};
|
||||
use redis_common::redis_keys::RedisKeys;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// For certain jobs or job classes (eg. non-premium), we kill the jobs if the user hasn't
|
||||
/// maintained a keepalive. This prevents wasted work when users who are unlikely to return
|
||||
|
||||
+12
-126
@@ -2,20 +2,9 @@ use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web_lab::extract::Query;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{error, warn};
|
||||
use r2d2_redis::redis::Commands;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
use crate::http_server::common_responses::media::media_domain::MediaDomain;
|
||||
use crate::http_server::common_responses::media::media_links::MediaLinks;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::lipsync::JobDetailsLipsyncRequest;
|
||||
use crate::http_server::endpoints::inference_job::common_responses::live_portrait::JobDetailsLivePortraitRequest;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::inference_job::utils::estimates::estimate_job_progress::estimate_job_progress;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_lipsync_details::extract_lipsync_details;
|
||||
use crate::http_server::endpoints::inference_job::utils::extractors::extract_live_portrait_details::extract_live_portrait_details;
|
||||
@@ -24,18 +13,28 @@ use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_m
|
||||
use crate::http_server::web_utils::filter_model_name::maybe_filter_model_name;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::state::server_state::ServerState;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use actix_web_lab::extract::Query;
|
||||
use artcraft_api_defs::jobs::list_session_jobs::{ListSessionJobsItem, ListSessionJobsQueryParams, ListSessionJobsSuccessResponse, ListSessionRequestDetailsResponse, ListSessionResultDetailsResponse, ListSessionStatusDetailsResponse};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use bucket_paths::legacy::typified_paths::public::voice_conversion_results::bucket_file_path::VoiceConversionResultOriginalFilePath;
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::by_table::generic_inference_jobs::frontend_failure_category::FrontendFailureCategory;
|
||||
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
|
||||
use enums::common::job_status_plus::JobStatusPlus;
|
||||
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
|
||||
use log::{error, warn};
|
||||
use mysql_queries::queries::generic_inference::web::job_status::GenericInferenceJobStatus;
|
||||
use mysql_queries::queries::generic_inference::web::list_session_jobs::{list_session_jobs_from_connection, ListSessionJobsForUserArgs, SessionUser};
|
||||
use primitives::numerics::i64_to_u64_zero_clamped::i64_to_u64_zero_clamped;
|
||||
use r2d2_redis::redis::Commands;
|
||||
use redis_common::redis_keys::RedisKeys;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
|
||||
/// For certain jobs or job classes (eg. non-premium), we kill the jobs if the user hasn't
|
||||
/// maintained a keepalive. This prevents wasted work when users who are unlikely to return
|
||||
@@ -43,118 +42,6 @@ use tokens::tokens::media_files::MediaFileToken;
|
||||
/// typically do not require keepalive.
|
||||
const JOB_KEEPALIVE_TTL_SECONDS : usize = 60 * 3;
|
||||
|
||||
#[derive(Deserialize, ToSchema, IntoParams)]
|
||||
pub struct ListSessionJobsQueryParams {
|
||||
//pub sort_ascending: Option<bool>,
|
||||
//pub page_size: Option<usize>, // TODO(bt,2024-04-23): One page for now.
|
||||
//pub page_index: Option<usize>, // TODO(bt,2024-04-23): One page for now.
|
||||
|
||||
/// NB: This can be one (or more comma-separated values) from `JobStatusPlus`.
|
||||
/// ?include_states=pending or ?include_states=pending,started,complete_success (etc.)
|
||||
pub include_states: Option<String>,
|
||||
|
||||
/// The opposite of include_states, this filters out states from view.
|
||||
pub exclude_states: Option<String>,
|
||||
|
||||
// TODO(bt,2024-04-23): Add the ability for users to dismiss completed/dead jobs from view.
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionJobsSuccessResponse {
|
||||
pub success: bool,
|
||||
|
||||
/// This is not paginated and is limited to showing 100 jobs.
|
||||
pub jobs: Vec<ListSessionJobsItem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionJobsItem {
|
||||
pub job_token: InferenceJobToken,
|
||||
|
||||
pub request: ListSessionRequestDetailsResponse,
|
||||
pub status: ListSessionStatusDetailsResponse,
|
||||
pub maybe_result: Option<ListSessionResultDetailsResponse>,
|
||||
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Details about what the user requested for generation
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionRequestDetailsResponse {
|
||||
pub inference_category: InferenceCategory,
|
||||
pub maybe_model_type: Option<String>,
|
||||
pub maybe_model_token: Option<String>,
|
||||
|
||||
/// OPTIONAL. Title of the model, if it has one
|
||||
pub maybe_model_title: Option<String>,
|
||||
|
||||
/// OPTIONAL. If the result was TTS, this is the raw inference text.
|
||||
pub maybe_raw_inference_text: Option<String>,
|
||||
|
||||
/// OPTIONAL. For Comfy / Video Style Transfer jobs, this might include
|
||||
/// the name of the selected style.
|
||||
pub maybe_style_name: Option<StyleTransferName>,
|
||||
|
||||
/// OPTIONAL. For Live Portrait jobs, this is additional information on the request.
|
||||
pub maybe_live_portrait_details: Option<JobDetailsLivePortraitRequest>,
|
||||
|
||||
/// OPTIONAL. For lipsync jobs (face fusion and sad talker), this is additional
|
||||
/// information on the request.
|
||||
pub maybe_lipsync_details: Option<JobDetailsLipsyncRequest>,
|
||||
}
|
||||
|
||||
/// Details about the ongoing job status
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionStatusDetailsResponse {
|
||||
/// Primary status from the database (a state machine).
|
||||
pub status: JobStatusPlus,
|
||||
|
||||
/// Extra, temporary status from Redis.
|
||||
/// This can denote inference progress, and the Python code can write to it.
|
||||
pub maybe_extra_status_description: Option<String>,
|
||||
|
||||
pub maybe_assigned_worker: Option<String>,
|
||||
pub maybe_assigned_cluster: Option<String>,
|
||||
|
||||
pub maybe_first_started_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// If the job is currently running, this is how long it has been running in seconds.
|
||||
/// This is heuristic estimate since we don't record the precise start time across runs.
|
||||
pub maybe_current_execution_duration_seconds: Option<u64>,
|
||||
|
||||
pub attempt_count: u8,
|
||||
|
||||
/// Whether the frontend needs to maintain a keepalive check.
|
||||
/// This is typically only for non-premium users.
|
||||
pub requires_keepalive: bool,
|
||||
|
||||
/// An enum the frontend can use to display localized/I18N error
|
||||
/// messages. These pertain to both transient and permanent failures.
|
||||
pub maybe_failure_category: Option<FrontendFailureCategory>,
|
||||
|
||||
/// This is an integer number between 0 and 100 (both inclusive) that
|
||||
/// reports the completeness.
|
||||
pub progress_percentage: u8,
|
||||
}
|
||||
|
||||
/// Details about the completed result (if any)
|
||||
#[derive(Serialize, ToSchema)]
|
||||
pub struct ListSessionResultDetailsResponse {
|
||||
pub entity_type: String,
|
||||
pub entity_token: String,
|
||||
|
||||
/// (DEPRECATED) URL path to the media file
|
||||
#[deprecated(note="This field doesn't point to the full URL. Use media_links instead to leverage the CDN.")]
|
||||
pub maybe_public_bucket_media_path: Option<String>,
|
||||
|
||||
/// Rich CDN links to the media, including thumbnails, previews, and more.
|
||||
pub media_links: MediaLinks,
|
||||
|
||||
pub maybe_successfully_completed_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, ToSchema)]
|
||||
pub enum ListSessionJobsError {
|
||||
ServerError,
|
||||
@@ -472,7 +359,7 @@ fn db_record_to_response_payload(
|
||||
ListSessionResultDetailsResponse {
|
||||
entity_type: result_details.entity_type,
|
||||
entity_token: result_details.entity_token,
|
||||
media_links: MediaLinks::from_rooted_path(media_domain, &public_bucket_media_path),
|
||||
media_links: MediaLinksBuilder::from_rooted_path(media_domain, &public_bucket_media_path),
|
||||
maybe_public_bucket_media_path: Some(public_bucket_media_path),
|
||||
maybe_successfully_completed_at: result_details.maybe_successfully_completed_at,
|
||||
}
|
||||
@@ -481,4 +368,3 @@ fn db_record_to_response_payload(
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod admin;
|
||||
pub mod common_responses;
|
||||
pub mod delete;
|
||||
pub mod get;
|
||||
pub mod list;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::http_server::endpoints::inference_job::common_responses::lipsync::JobDetailsLipsyncRequest;
|
||||
use artcraft_api_defs::common::responses::job_details::JobDetailsLipsyncRequest;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::PolymorphicInferenceArgs;
|
||||
use mysql_queries::payloads::generic_inference_args::inner_payloads::face_fusion_payload::FaceFusionPayload;
|
||||
use mysql_queries::payloads::generic_inference_args::inner_payloads::lipsync_payload::{LipsyncAnimationAudioSource, LipsyncAnimationImageSource, LipsyncArgs};
|
||||
|
||||
+2
-3
@@ -1,7 +1,6 @@
|
||||
use artcraft_api_defs::common::responses::job_details::JobDetailsLivePortraitRequest;
|
||||
use mysql_queries::payloads::generic_inference_args::generic_inference_args::PolymorphicInferenceArgs;
|
||||
|
||||
use crate::http_server::endpoints::inference_job::common_responses::live_portrait::JobDetailsLivePortraitRequest;
|
||||
|
||||
pub fn extract_live_portrait_details(args: &PolymorphicInferenceArgs) -> Option<JobDetailsLivePortraitRequest> {
|
||||
let lp =
|
||||
if let PolymorphicInferenceArgs::Lp(live_portrait_args) = args {
|
||||
@@ -47,4 +46,4 @@ mod tests {
|
||||
assert_eq!(details.source_media_file_token.as_str(), "portrait");
|
||||
assert_eq!(details.face_driver_media_file_token.as_str(), "driver");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user