mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
add veo3 handler
This commit is contained in:
1 parent
c386a02bc9
commit
424a49c6a2
8 files changed
+375
-21
No files matched your search
+25
-9
@@ -6,12 +6,13 @@ use fal::webhook::WebhookResponse;
|
||||
use reqwest::IntoUrl;
|
||||
|
||||
pub struct Veo3Args<'a, U: IntoUrl, V: IntoUrl> {
|
||||
pub prompt: &'a str,
|
||||
pub image_url: U,
|
||||
pub prompt: &'a str,
|
||||
pub duration: Veo3Duration,
|
||||
pub api_key: &'a FalApiKey,
|
||||
pub aspect_ratio: Veo3AspectRatio,
|
||||
pub resolution: Veo3Resolution,
|
||||
pub generate_audio: bool,
|
||||
pub api_key: &'a FalApiKey,
|
||||
pub webhook_url: V,
|
||||
}
|
||||
|
||||
@@ -21,6 +22,14 @@ pub enum Veo3Duration {
|
||||
EightSeconds,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3AspectRatio {
|
||||
Default,
|
||||
WideSixteenNine,
|
||||
TallNineSixteen,
|
||||
Square,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3Resolution {
|
||||
Default,
|
||||
@@ -35,6 +44,13 @@ pub async fn enqueue_veo_3_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
|
||||
Veo3Duration::Default => None,
|
||||
Veo3Duration::EightSeconds => Some("8s".to_string()),
|
||||
};
|
||||
|
||||
let aspect_ratio = match args.aspect_ratio {
|
||||
Veo3AspectRatio::Default => None,
|
||||
Veo3AspectRatio::WideSixteenNine => Some("16:9".to_string()),
|
||||
Veo3AspectRatio::TallNineSixteen => Some("9:16".to_string()),
|
||||
Veo3AspectRatio::Square => Some("1:1".to_string()),
|
||||
};
|
||||
|
||||
let resolution= match args.resolution {
|
||||
Veo3Resolution::Default => None,
|
||||
@@ -47,6 +63,7 @@ pub async fn enqueue_veo_3_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
|
||||
let request = ImageToVideoInput {
|
||||
image_url,
|
||||
prompt: args.prompt.to_string(),
|
||||
aspect_ratio,
|
||||
resolution,
|
||||
duration,
|
||||
generate_audio: Some(args.generate_audio),
|
||||
@@ -64,28 +81,27 @@ pub async fn enqueue_veo_3_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::creds::fal_api_key::FalApiKey;
|
||||
use crate::requests::webhook::video::enqueue_veo_3_image_to_video_webhook::{enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3Duration, Veo3Resolution};
|
||||
use crate::requests::webhook::video::enqueue_veo_3_image_to_video_webhook::{enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3AspectRatio, Veo3Duration, Veo3Resolution};
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
use test_data::web::image_urls::JUNO_AT_LAKE_IMAGE_URL;
|
||||
use test_data::web::image_urls::{JUNO_AT_LAKE_IMAGE_URL, TREX_SKELETON_IMAGE_URL};
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test() -> AnyhowResult<()> {
|
||||
let image_url = JUNO_AT_LAKE_IMAGE_URL;
|
||||
|
||||
// XXX: Don't commit secrets!
|
||||
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
|
||||
|
||||
let api_key = FalApiKey::from_str(&secret);
|
||||
|
||||
let args = Veo3Args {
|
||||
image_url: image_url,
|
||||
prompt: "corgi at the lake. the corgi barks. camera pulls back to show a wider shot of the lake as the corgi jumps into the water. there are sounds of barking and splashing. cinematic, 8k, ultra wide shot",
|
||||
image_url: TREX_SKELETON_IMAGE_URL,
|
||||
prompt: "the t-rex skeleton starts walking towards the camera and roars",
|
||||
api_key: &api_key,
|
||||
duration: Veo3Duration::EightSeconds,
|
||||
generate_audio: true,
|
||||
aspect_ratio: Veo3AspectRatio::WideSixteenNine,
|
||||
resolution: Veo3Resolution::TenEightyP,
|
||||
generate_audio: true,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
pub const GENERATE_VEO_3_IMAGE_TO_VIDEO_URL_PATH: &str = "/v1/generate/video/veo_3_image_to_video";
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct GenerateVeo3ImageToVideoRequest {
|
||||
/// Idempotency token to prevent duplicate requests.
|
||||
pub uuid_idempotency_token: String,
|
||||
|
||||
/// Source of the image file to convert to video.
|
||||
/// It must be an image.
|
||||
pub media_file_token: Option<MediaFileToken>,
|
||||
|
||||
/// Optional text prompt.
|
||||
pub prompt: Option<String>,
|
||||
|
||||
/// Optional: aspect ratio of the generated video.
|
||||
pub aspect_ratio: Option<GenerateVeo3AspectRatio>,
|
||||
|
||||
/// Optional: resolution of the generated video.
|
||||
pub resolution: Option<GenerateVeo3Resolution>,
|
||||
|
||||
/// Optional: aspect ratio of the generated video.
|
||||
pub duration: Option<GenerateVeo3Duration>,
|
||||
|
||||
/// Optional: Generate an audio track along with the video
|
||||
pub generate_audio: Option<bool>,
|
||||
|
||||
// /// Optional.
|
||||
// pub enhance_prompt: Option<bool>,
|
||||
// /// Optional. Whether to automatically attempt to fix prompts that fail
|
||||
// /// content policy or other validation checks by rewriting them
|
||||
// /// Default value: true
|
||||
// pub auto_fix: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenerateVeo3AspectRatio {
|
||||
WideSixteenNine, // 16:9
|
||||
TallNineSixteen, // 9:16
|
||||
Square, // 1:1
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenerateVeo3Resolution {
|
||||
SevenTwentyP, // 720p
|
||||
TenEightyP, // 1080p
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenerateVeo3Duration {
|
||||
EightSeconds,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct GenerateVeo3ImageToVideoResponse {
|
||||
pub success: bool,
|
||||
pub inference_job_token: InferenceJobToken,
|
||||
}
|
||||
@@ -2,4 +2,5 @@ pub mod generate_kling_1_6_pro_image_to_video;
|
||||
pub mod generate_kling_2_1_master_image_to_video;
|
||||
pub mod generate_kling_2_1_pro_image_to_video;
|
||||
pub mod generate_seedance_1_0_lite_image_to_video;
|
||||
pub mod generate_veo_2_image_to_video;
|
||||
pub mod generate_veo_2_image_to_video;
|
||||
pub mod generate_veo_3_image_to_video;
|
||||
@@ -67,6 +67,8 @@ pub enum ModelType {
|
||||
Seedance10Lite,
|
||||
#[serde(rename = "veo_2")]
|
||||
Veo2,
|
||||
#[serde(rename = "veo_3")]
|
||||
Veo3,
|
||||
|
||||
// 3D Object generation models
|
||||
#[serde(rename = "hunyuan_3d_2p0")]
|
||||
@@ -110,6 +112,7 @@ impl ModelType {
|
||||
Self::Kling21Master => "kling_2p1_master",
|
||||
Self::Seedance10Lite => "seedance_1p0_lite",
|
||||
Self::Veo2 => "veo_2",
|
||||
Self::Veo3 => "veo_3",
|
||||
|
||||
// 3D Object generation models
|
||||
Self::Hunyuan3d2_0 => "hunyuan_3d_2p0",
|
||||
@@ -145,6 +148,7 @@ impl ModelType {
|
||||
"kling_2p1_master" => Ok(Self::Kling21Master),
|
||||
"seedance_1p0_lite" => Ok(Self::Seedance10Lite),
|
||||
"veo_2" => Ok(Self::Veo2),
|
||||
"veo_3" => Ok(Self::Veo3),
|
||||
|
||||
// 3D Object generation models
|
||||
"hunyuan_3d_2p0" => Ok(Self::Hunyuan3d2_0),
|
||||
@@ -184,6 +188,7 @@ impl ModelType {
|
||||
Self::Kling21Master,
|
||||
Self::Seedance10Lite,
|
||||
Self::Veo2,
|
||||
Self::Veo3,
|
||||
|
||||
// 3D Object generation models
|
||||
Self::Hunyuan3d2_0,
|
||||
@@ -227,6 +232,7 @@ mod tests {
|
||||
assert_serialization(ModelType::Kling21Master, "kling_2p1_master");
|
||||
assert_serialization(ModelType::Seedance10Lite, "seedance_1p0_lite");
|
||||
assert_serialization(ModelType::Veo2, "veo_2");
|
||||
assert_serialization(ModelType::Veo3, "veo_3");
|
||||
// 3D Object generation models
|
||||
assert_serialization(ModelType::Hunyuan3d2_0, "hunyuan_3d_2p0");
|
||||
assert_serialization(ModelType::Hunyuan3d2_1, "hunyuan_3d_2p1");
|
||||
@@ -260,6 +266,7 @@ mod tests {
|
||||
assert_eq!(ModelType::Kling21Master.to_str(), "kling_2p1_master");
|
||||
assert_eq!(ModelType::Seedance10Lite.to_str(), "seedance_1p0_lite");
|
||||
assert_eq!(ModelType::Veo2.to_str(), "veo_2");
|
||||
assert_eq!(ModelType::Veo3.to_str(), "veo_3");
|
||||
|
||||
// 3D Object generation models
|
||||
assert_eq!(ModelType::Hunyuan3d2_0.to_str(), "hunyuan_3d_2p0");
|
||||
@@ -293,6 +300,7 @@ mod tests {
|
||||
assert_eq!(ModelType::from_str("kling_2p1_master").unwrap(), ModelType::Kling21Master);
|
||||
assert_eq!(ModelType::from_str("seedance_1p0_lite").unwrap(), ModelType::Seedance10Lite);
|
||||
assert_eq!(ModelType::from_str("veo_2").unwrap(), ModelType::Veo2);
|
||||
assert_eq!(ModelType::from_str("veo_3").unwrap(), ModelType::Veo3);
|
||||
// 3D Object generation models
|
||||
assert_eq!(ModelType::from_str("hunyuan_3d_2p0").unwrap(), ModelType::Hunyuan3d2_0);
|
||||
assert_eq!(ModelType::from_str("hunyuan_3d_2p1").unwrap(), ModelType::Hunyuan3d2_1);
|
||||
@@ -301,7 +309,7 @@ mod tests {
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = ModelType::all_variants();
|
||||
assert_eq!(variants.len(), 25);
|
||||
assert_eq!(variants.len(), 26);
|
||||
// Image models
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Flux1Dev));
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Flux1Schnell));
|
||||
@@ -327,6 +335,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Kling21Master));
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Seedance10Lite));
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Veo2));
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Veo3));
|
||||
// 3D Object generation models
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Hunyuan3d2_0));
|
||||
assert_eq!(variants.pop_first(), Some(ModelType::Hunyuan3d2_1));
|
||||
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::http_server::common_responses::common_web_error::CommonWebError;
|
||||
use crate::http_server::common_responses::media::media_links_builder::MediaLinksBuilder;
|
||||
use crate::http_server::endpoints::analytics::log_browser_session_handler::LogBrowserSessionError;
|
||||
use crate::http_server::endpoints::media_files::helpers::get_media_domain::get_media_domain;
|
||||
use crate::http_server::validations::validate_idempotency_token_format::validate_idempotency_token_format;
|
||||
use crate::state::server_state::ServerState;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::{web, HttpRequest};
|
||||
use anyhow::anyhow;
|
||||
use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::{GenerateVeo3AspectRatio, GenerateVeo3Duration, GenerateVeo3ImageToVideoRequest, GenerateVeo3ImageToVideoResponse, GenerateVeo3Resolution};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use enums::by_table::prompts::prompt_type::PromptType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::model_type::ModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use fal_client::requests::webhook::video::enqueue_veo_3_image_to_video_webhook::{enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3AspectRatio, Veo3Duration, Veo3Resolution};
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use log::{error, info, warn};
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::insert_generic_inference_job_for_fal_queue;
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::FalCategory;
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::InsertGenericInferenceForFalArgs;
|
||||
use mysql_queries::queries::idepotency_tokens::insert_idempotency_token::insert_idempotency_token;
|
||||
use mysql_queries::queries::media_files::get::get_media_file::get_media_file_with_connection;
|
||||
use mysql_queries::queries::prompts::insert_prompt::{insert_prompt, InsertPromptArgs};
|
||||
use sqlx::Acquire;
|
||||
use tokens::tokens::prompts::PromptToken;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// Veo 3 Image to Video
|
||||
#[utoipa::path(
|
||||
post,
|
||||
tag = "Generate Videos",
|
||||
path = "/v1/generate/video/veo_3_image_to_video",
|
||||
responses(
|
||||
(status = 200, description = "Success", body = GenerateVeo3ImageToVideoResponse),
|
||||
),
|
||||
params(
|
||||
("request" = GenerateVeo3ImageToVideoRequest, description = "Payload for Request"),
|
||||
)
|
||||
)]
|
||||
pub async fn generate_veo_3_image_to_video_handler(
|
||||
http_request: HttpRequest,
|
||||
request: Json<GenerateVeo3ImageToVideoRequest>,
|
||||
server_state: web::Data<Arc<ServerState>>
|
||||
) -> Result<Json<GenerateVeo3ImageToVideoResponse>, CommonWebError> {
|
||||
let mut mysql_connection = server_state.mysql_pool
|
||||
.acquire()
|
||||
.await?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Session checker error: {:?}", e);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_avt_token = server_state
|
||||
.avt_cookie_manager
|
||||
.get_avt_token_from_request(&http_request);
|
||||
|
||||
// TODO: Limit usage for new accounts. Billing, free credits metering, etc.
|
||||
|
||||
//let user_session = match maybe_user_session {
|
||||
// Some(session) => session,
|
||||
// None => {
|
||||
// warn!("not logged in");
|
||||
// return Err(RemoveImageBackgroundError::NotAuthorized);
|
||||
// }
|
||||
//};
|
||||
|
||||
let media_file_token = match &request.media_file_token {
|
||||
Some(token) => token,
|
||||
None => {
|
||||
warn!("No media file token provided");
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("No media file token provided".to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(reason) = validate_idempotency_token_format(&request.uuid_idempotency_token) {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage(reason));
|
||||
}
|
||||
|
||||
insert_idempotency_token(&request.uuid_idempotency_token, &mut *mysql_connection)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error inserting idempotency token: {:?}", err);
|
||||
CommonWebError::BadInputWithSimpleMessage("invalid idempotency token".to_string())
|
||||
})?;
|
||||
const IS_MOD : bool = false;
|
||||
|
||||
let media_file_lookup_result = get_media_file_with_connection(
|
||||
media_file_token,
|
||||
IS_MOD,
|
||||
&mut mysql_connection,
|
||||
).await;
|
||||
|
||||
let media_file = match media_file_lookup_result {
|
||||
Ok(Some(media_file)) => media_file,
|
||||
Ok(None) => {
|
||||
warn!("MediaFile not found: {:?}", media_file_token);
|
||||
return Err(CommonWebError::NotFound);
|
||||
},
|
||||
Err(err) => {
|
||||
warn!("Error looking up media_file: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
if !media_file.media_type.is_jpg_or_png_or_legacy_image() {
|
||||
return Err(CommonWebError::BadInputWithSimpleMessage("Media file must be a JPG or PNG image".to_string()));
|
||||
}
|
||||
|
||||
let media_domain = get_media_domain(&http_request);
|
||||
|
||||
let bucket_path = MediaFileBucketPath::from_object_hash(
|
||||
&media_file.public_bucket_directory_hash,
|
||||
media_file.maybe_public_bucket_prefix.as_deref(),
|
||||
media_file.maybe_public_bucket_extension.as_deref());
|
||||
|
||||
let media_links = MediaLinksBuilder::from_media_path_and_env(
|
||||
media_domain,
|
||||
server_state.server_environment,
|
||||
&bucket_path);
|
||||
|
||||
info!("Fal webhook URL: {}", server_state.fal.webhook_url);
|
||||
|
||||
let prompt = request.prompt
|
||||
.as_deref()
|
||||
.map(|prompt| prompt.trim())
|
||||
.unwrap_or_else(|| "");
|
||||
|
||||
let aspect_ratio = match &request.aspect_ratio {
|
||||
Some(GenerateVeo3AspectRatio::WideSixteenNine) => Veo3AspectRatio::WideSixteenNine,
|
||||
Some(GenerateVeo3AspectRatio::TallNineSixteen) => Veo3AspectRatio::TallNineSixteen,
|
||||
Some(GenerateVeo3AspectRatio::Square) => Veo3AspectRatio::Square,
|
||||
None => Veo3AspectRatio::WideSixteenNine,
|
||||
};
|
||||
|
||||
let resolution = match &request.resolution {
|
||||
Some(GenerateVeo3Resolution::SevenTwentyP) => Veo3Resolution::SevenTwentyP,
|
||||
Some(GenerateVeo3Resolution::TenEightyP) => Veo3Resolution::TenEightyP,
|
||||
None => Veo3Resolution::Default,
|
||||
};
|
||||
|
||||
let duration = match &request.duration {
|
||||
Some(GenerateVeo3Duration::EightSeconds) => Veo3Duration::EightSeconds,
|
||||
None => Veo3Duration::EightSeconds,
|
||||
};
|
||||
|
||||
let generate_audio = request.generate_audio
|
||||
.unwrap_or(false);
|
||||
|
||||
let args = Veo3Args {
|
||||
image_url: media_links.cdn_url,
|
||||
prompt,
|
||||
duration,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
generate_audio,
|
||||
webhook_url: &server_state.fal.webhook_url,
|
||||
api_key: &server_state.fal.api_key,
|
||||
};
|
||||
|
||||
let fal_result = enqueue_veo_3_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!("Error calling enqueue_veo_3_image_to_video_webhook: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
let external_job_id = fal_result.request_id
|
||||
.ok_or_else(|| {
|
||||
warn!("Fal request_id is None");
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
info!("Fal request_id: {}", external_job_id);
|
||||
|
||||
let ip_address = get_request_ip(&http_request);
|
||||
|
||||
let mut transaction = mysql_connection
|
||||
.begin()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error starting MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
// NB: Don't fail the job if the query fails.
|
||||
let prompt_result = insert_prompt(InsertPromptArgs {
|
||||
maybe_apriori_prompt_token: None,
|
||||
prompt_type: PromptType::ArtcraftApp,
|
||||
maybe_creator_user_token: maybe_user_session
|
||||
.as_ref()
|
||||
.map(|s| &s.user_token),
|
||||
maybe_model_type: Some(ModelType::Veo3),
|
||||
maybe_generation_provider: Some(GenerationProvider::Artcraft),
|
||||
maybe_positive_prompt: Some(prompt),
|
||||
maybe_negative_prompt: None,
|
||||
maybe_other_args: None,
|
||||
creator_ip_address: &ip_address,
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}).await;
|
||||
|
||||
let prompt_token = match prompt_result {
|
||||
Ok(token) => Some(token),
|
||||
Err(err) => {
|
||||
warn!("Error inserting prompt: {:?}", err);
|
||||
None // Don't fail the job if the prompt insertion fails.
|
||||
}
|
||||
};
|
||||
|
||||
let db_result = insert_generic_inference_job_for_fal_queue(InsertGenericInferenceForFalArgs {
|
||||
uuid_idempotency_token: &request.uuid_idempotency_token,
|
||||
maybe_external_third_party_id: &external_job_id,
|
||||
fal_category: FalCategory::VideoGeneration,
|
||||
maybe_inference_args: None,
|
||||
maybe_prompt_token: prompt_token.as_ref(),
|
||||
maybe_creator_user_token: maybe_user_session
|
||||
.as_ref()
|
||||
.map(|s| &s.user_token),
|
||||
maybe_avt_token: maybe_avt_token.as_ref(),
|
||||
creator_ip_address: &ip_address,
|
||||
creator_set_visibility: Visibility::Public,
|
||||
mysql_executor: &mut *transaction,
|
||||
phantom: Default::default(),
|
||||
}).await;
|
||||
|
||||
let job_token = match db_result {
|
||||
Ok(token) => token,
|
||||
Err(err) => {
|
||||
warn!("Error inserting generic inference job for FAL queue: {:?}", err);
|
||||
return Err(CommonWebError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
let _r = transaction
|
||||
.commit()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error committing MySQL transaction: {:?}", err);
|
||||
CommonWebError::ServerError
|
||||
})?;
|
||||
|
||||
Ok(Json(GenerateVeo3ImageToVideoResponse {
|
||||
success: true,
|
||||
inference_job_token: job_token,
|
||||
}))
|
||||
}
|
||||
@@ -2,4 +2,5 @@ pub mod generate_kling_1_6_pro_video_handler;
|
||||
pub mod generate_kling_2_1_master_video_handler;
|
||||
pub mod generate_kling_2_1_pro_video_handler;
|
||||
pub mod generate_seedance_1_0_lite_image_to_video_handler;
|
||||
pub mod generate_veo_2_image_to_video_handler;
|
||||
pub mod generate_veo_2_image_to_video_handler;
|
||||
pub mod generate_veo_3_image_to_video_handler;
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::http_server::endpoints::generate::image::edit::flux_pro_kontext_max_edit_image_handler::flux_pro_kontext_max_edit_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::edit::gemini_25_flash_edit_image_handler::gemini_25_flash_edit_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::edit::gpt_image_1_edit_image_handler::gpt_image_1_edit_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_1_dev_text_to_image_handler::generate_flux_1_dev_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_1_schnell_text_to_image_handler::generate_flux_1_schnell_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_pro_11_text_to_image_handler::generate_flux_pro_11_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_flux_pro_11_ultra_text_to_image_handler::generate_flux_pro_11_ultra_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::generate_gpt_image_1_text_to_image_handler::generate_gpt_image_1_text_to_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::inpaint::flux_dev_juggernaut_inpaint_handler::flux_dev_juggernaut_inpaint_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::inpaint::flux_pro_1_inpaint_handler::flux_pro_1_inpaint_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::remove_image_background_handler::remove_image_background_handler;
|
||||
use crate::http_server::endpoints::generate::object::generate_hunyuan_2_0_image_to_3d_handler::generate_hunyuan_2_0_image_to_3d_handler;
|
||||
@@ -14,12 +16,11 @@ use crate::http_server::endpoints::generate::video::generate_kling_2_1_master_vi
|
||||
use crate::http_server::endpoints::generate::video::generate_kling_2_1_pro_video_handler::generate_kling_2_1_pro_video_handler;
|
||||
use crate::http_server::endpoints::generate::video::generate_seedance_1_0_lite_image_to_video_handler::generate_seedance_1_0_lite_image_to_video_handler;
|
||||
use crate::http_server::endpoints::generate::video::generate_veo_2_image_to_video_handler::generate_veo_2_image_to_video_handler;
|
||||
use crate::http_server::endpoints::generate::video::generate_veo_3_image_to_video_handler::generate_veo_3_image_to_video_handler;
|
||||
use actix_http::body::MessageBody;
|
||||
use actix_service::ServiceFactory;
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
use actix_web::{web, App, Error, HttpResponse};
|
||||
use crate::http_server::endpoints::generate::image::edit::gemini_25_flash_edit_image_handler::gemini_25_flash_edit_image_handler;
|
||||
use crate::http_server::endpoints::generate::image::inpaint::flux_dev_juggernaut_inpaint_handler::flux_dev_juggernaut_inpaint_image_handler;
|
||||
|
||||
pub fn add_generate_routes<T, B> (app: App<T>) -> App<T>
|
||||
where
|
||||
@@ -47,7 +48,7 @@ where
|
||||
.route(web::post().to(gpt_image_1_edit_image_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
|
||||
|
||||
)
|
||||
.service(web::scope("/inpaint")
|
||||
.service(web::resource("/flux_dev_juggernaut")
|
||||
@@ -105,6 +106,10 @@ where
|
||||
.route(web::post().to(generate_veo_2_image_to_video_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(web::resource("/veo_3_image_to_video")
|
||||
.route(web::post().to(generate_veo_3_image_to_video_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
.service(web::scope("/object")
|
||||
.service(web::resource("/hunyuan_2.0_image_to_3d")
|
||||
|
||||
@@ -28,26 +28,29 @@ pub struct File {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct ImageToVideoInput {
|
||||
/// The text prompt describing how the image should be animated
|
||||
/// "A lego chef cooking eggs"
|
||||
pub prompt: String,
|
||||
|
||||
/// URL of the input image to animate. Should be 720p or higher resolution.
|
||||
/// "https://fal.media/files/elephant/6fq8JDSjb1osE_c3J_F2H.png"
|
||||
pub image_url: String,
|
||||
|
||||
/// The text prompt describing how the image should be animated
|
||||
/// "A lego chef cooking eggs"
|
||||
pub prompt: String,
|
||||
|
||||
/// The duration of the generated video in seconds
|
||||
/// eg "8s".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<String>,
|
||||
|
||||
/// Generate audio
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub generate_audio: Option<bool>,
|
||||
pub aspect_ratio: Option<String>,
|
||||
|
||||
/// Resolution, eg. "720p"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resolution: Option<String>,
|
||||
|
||||
/// Generate audio
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub generate_audio: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
Reference in new issue
Block a user