From 75539a2026df9991f287ee9e2ef3c3f3fba8bdbe Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 8 Mar 2026 14:47:02 -0400 Subject: [PATCH] improve types --- ...timate_video_cost_artcraft_kling3p0_pro.rs | 1 + ...e_video_cost_artcraft_kling3p0_standard.rs | 1 + .../http_kling_3p0_pro_image_to_video.rs | 5 + .../video/http_kling_3p0_pro_text_to_video.rs | 5 + .../http_kling_3p0_standard_image_to_video.rs | 5 + .../http_kling_3p0_standard_text_to_video.rs | 5 + ...ue_kling_3p0_pro_image_to_video_webhook.rs | 175 ++++++++++++++++-- ...ing_3p0_standard_image_to_video_webhook.rs | 168 +++++++++++++++-- ...eue_kling_3p0_pro_text_to_video_webhook.rs | 44 +++++ ...ling_3p0_standard_text_to_video_webhook.rs | 44 +++++ ...p0_pro_multi_function_video_gen_handler.rs | 35 ++-- ...andard_multi_function_video_gen_handler.rs | 35 ++-- 12 files changed, 462 insertions(+), 61 deletions(-) diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_pro.rs index 795dadc1ff..96f103b77a 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_pro.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_pro.rs @@ -36,6 +36,7 @@ pub(crate) fn estimate_video_cost_artcraft_kling3p0_pro( negative_prompt: None, duration, aspect_ratio: None, + shot_type: None, webhook_url: "https://example.com", api_key: &api_key, }; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_standard.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_standard.rs index 6a70e74ec0..ae63683699 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_standard.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_kling3p0_standard.rs @@ -36,6 +36,7 @@ pub(crate) fn estimate_video_cost_artcraft_kling3p0_standard( negative_prompt: None, duration, aspect_ratio: None, + shot_type: None, webhook_url: "https://example.com", api_key: &api_key, }; diff --git a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_image_to_video.rs b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_image_to_video.rs index 699e483707..5a8529faac 100644 --- a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_image_to_video.rs +++ b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_image_to_video.rs @@ -30,6 +30,11 @@ pub struct Kling3p0ProImageToVideoInput { #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, + /// Shot type for multi-shot video generation. + /// Possible enum values: "customize", "intelligent" + #[serde(skip_serializing_if = "Option::is_none")] + pub shot_type: Option, + /// The CFG (Classifier Free Guidance) scale. /// Default value: 0.5 #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_text_to_video.rs b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_text_to_video.rs index 94354d5ed1..c6f0ea1368 100644 --- a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_text_to_video.rs +++ b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_pro_text_to_video.rs @@ -23,6 +23,11 @@ pub struct Kling3p0ProTextToVideoInput { #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, + /// Shot type for multi-shot video generation. + /// Possible enum values: "customize", "intelligent" + #[serde(skip_serializing_if = "Option::is_none")] + pub shot_type: Option, + /// The CFG (Classifier Free Guidance) scale. /// Default value: 0.5 #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_image_to_video.rs b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_image_to_video.rs index 2e5f3798f3..0f93a20e3d 100644 --- a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_image_to_video.rs +++ b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_image_to_video.rs @@ -30,6 +30,11 @@ pub struct Kling3p0StandardImageToVideoInput { #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, + /// Shot type for multi-shot video generation. + /// Possible enum values: "customize", "intelligent" + #[serde(skip_serializing_if = "Option::is_none")] + pub shot_type: Option, + /// The CFG (Classifier Free Guidance) scale. /// Default value: 0.5 #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_text_to_video.rs b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_text_to_video.rs index 0d87ac13bb..e4fb811262 100644 --- a/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_text_to_video.rs +++ b/crates/api_clients/fal_client/src/requests/http/video/http_kling_3p0_standard_text_to_video.rs @@ -23,6 +23,11 @@ pub struct Kling3p0StandardTextToVideoInput { #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option, + /// Shot type for multi-shot video generation. + /// Possible enum values: "customize", "intelligent" + #[serde(skip_serializing_if = "Option::is_none")] + pub shot_type: Option, + /// The CFG (Classifier Free Guidance) scale. /// Default value: 0.5 #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_pro_image_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_pro_image_to_video_webhook.rs index b9bbc29bee..37afbdb9dc 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_pro_image_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_pro_image_to_video_webhook.rs @@ -2,12 +2,12 @@ use crate::creds::fal_api_key::FalApiKey; use crate::error::classify_fal_error::classify_fal_error; use crate::error::fal_error_plus::FalErrorPlus; use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents}; -use crate::requests::webhook::video::text::enqueue_kling_3p0_pro_text_to_video_webhook::EnqueueKling3p0ProTextToVideoDuration; use crate::requests::http::video::http_kling_3p0_pro_image_to_video::{kling_3p0_pro_image_to_video, Kling3p0ProImageToVideoInput}; use fal::webhook::WebhookResponse; use reqwest::IntoUrl; pub struct EnqueueKling3p0ProImageToVideoArgs<'a, R: IntoUrl> { + // Request required pub prompt: String, pub image_url: String, @@ -15,28 +15,91 @@ pub struct EnqueueKling3p0ProImageToVideoArgs<'a, R: IntoUrl> { pub end_image_url: Option, pub generate_audio: Option, pub negative_prompt: Option, - pub duration: Option, + pub duration: Option, + pub aspect_ratio: Option, + pub shot_type: Option, // Fulfillment pub webhook_url: R, pub api_key: &'a FalApiKey, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0ProImageToVideoDuration { + ThreeSeconds, + FourSeconds, + FiveSeconds, + SixSeconds, + SevenSeconds, + EightSeconds, + NineSeconds, + TenSeconds, + ElevenSeconds, + TwelveSeconds, + ThirteenSeconds, + FourteenSeconds, + FifteenSeconds, +} + +impl EnqueueKling3p0ProImageToVideoDuration { + pub fn to_seconds(&self) -> u64 { + match self { + Self::ThreeSeconds => 3, + Self::FourSeconds => 4, + Self::FiveSeconds => 5, + Self::SixSeconds => 6, + Self::SevenSeconds => 7, + Self::EightSeconds => 8, + Self::NineSeconds => 9, + Self::TenSeconds => 10, + Self::ElevenSeconds => 11, + Self::TwelveSeconds => 12, + Self::ThirteenSeconds => 13, + Self::FourteenSeconds => 14, + Self::FifteenSeconds => 15, + } + } + + pub fn to_str(&self) -> &'static str { + match self { + Self::ThreeSeconds => "3", + Self::FourSeconds => "4", + Self::FiveSeconds => "5", + Self::SixSeconds => "6", + Self::SevenSeconds => "7", + Self::EightSeconds => "8", + Self::NineSeconds => "9", + Self::TenSeconds => "10", + Self::ElevenSeconds => "11", + Self::TwelveSeconds => "12", + Self::ThirteenSeconds => "13", + Self::FourteenSeconds => "14", + Self::FifteenSeconds => "15", + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0ProImageToVideoAspectRatio { + Square, + SixteenByNine, + NineBySixteen, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0ProImageToVideoShotType { + Customize, + Intelligent, +} + impl FalRequestCostCalculator for EnqueueKling3p0ProImageToVideoArgs<'_, U> { fn calculate_cost_in_cents(&self) -> UsdCents { - // Fal Docs: - // For every second of video you generated, you will be charged $0.224 (audio off) - // or $0.336 (audio on), - // if voice control is used while generating audio you will be charged $0.392. - // For example, a 5s video with audio on and voice control will cost $1.96 - - // Our Docs: // Same pricing as text-to-video for Kling 3.0 Pro: // Audio off: $0.224/second // Audio on: $0.336/second let generate_audio = self.generate_audio.unwrap_or(true); let duration_secs = self.duration - .unwrap_or(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds) + .unwrap_or(EnqueueKling3p0ProImageToVideoDuration::FiveSeconds) .to_seconds(); let rate = if generate_audio { 336u64 } else { 224u64 }; @@ -53,14 +116,30 @@ pub async fn enqueue_kling_3p0_pro_image_to_video_webhook( let duration = args.duration .map(|d| d.to_str().to_string()); + let aspect_ratio = args.aspect_ratio + .map(|ar| match ar { + EnqueueKling3p0ProImageToVideoAspectRatio::Square => "1:1", + EnqueueKling3p0ProImageToVideoAspectRatio::SixteenByNine => "16:9", + EnqueueKling3p0ProImageToVideoAspectRatio::NineBySixteen => "9:16", + }) + .map(|s| s.to_string()); + + let shot_type = args.shot_type + .map(|st| match st { + EnqueueKling3p0ProImageToVideoShotType::Customize => "customize", + EnqueueKling3p0ProImageToVideoShotType::Intelligent => "intelligent", + }) + .map(|s| s.to_string()); + let request = Kling3p0ProImageToVideoInput { prompt: args.prompt, image_url: args.image_url, end_image_url: args.end_image_url, - aspect_ratio: None, + aspect_ratio, generate_audio: args.generate_audio, duration, negative_prompt: args.negative_prompt, + shot_type, cfg_scale: None, }; @@ -92,7 +171,9 @@ mod tests { end_image_url: None, generate_audio: Some(false), negative_prompt: None, - duration: Some(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds), + duration: Some(EnqueueKling3p0ProImageToVideoDuration::FiveSeconds), + aspect_ratio: None, + shot_type: None, webhook_url: "https://example.com/webhook", api_key: &api_key, }; @@ -102,22 +183,22 @@ mod tests { assert_eq!(args.calculate_cost_in_cents(), 112); // 10s: (224 * 10 + 9) / 10 = 2249 / 10 = 224 - args.duration = Some(EnqueueKling3p0ProTextToVideoDuration::TenSeconds); + args.duration = Some(EnqueueKling3p0ProImageToVideoDuration::TenSeconds); assert_eq!(args.calculate_cost_in_cents(), 224); // 15s: (224 * 15 + 9) / 10 = 3369 / 10 = 336 - args.duration = Some(EnqueueKling3p0ProTextToVideoDuration::FifteenSeconds); + args.duration = Some(EnqueueKling3p0ProImageToVideoDuration::FifteenSeconds); assert_eq!(args.calculate_cost_in_cents(), 336); // Audio on: $0.336/sec args.generate_audio = Some(true); // 5s: (336 * 5 + 9) / 10 = 1689 / 10 = 168 - args.duration = Some(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds); + args.duration = Some(EnqueueKling3p0ProImageToVideoDuration::FiveSeconds); assert_eq!(args.calculate_cost_in_cents(), 168); // 10s: (336 * 10 + 9) / 10 = 3369 / 10 = 336 - args.duration = Some(EnqueueKling3p0ProTextToVideoDuration::TenSeconds); + args.duration = Some(EnqueueKling3p0ProImageToVideoDuration::TenSeconds); assert_eq!(args.calculate_cost_in_cents(), 336); } @@ -130,10 +211,12 @@ mod tests { let args = EnqueueKling3p0ProImageToVideoArgs { image_url: TREX_SKELETON_IMAGE_URL.to_string(), prompt: "the t-rex skeleton gets off the podium and begins walking to the camera, then bites".to_string(), - duration: Some(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds), + duration: Some(EnqueueKling3p0ProImageToVideoDuration::FiveSeconds), + aspect_ratio: Some(EnqueueKling3p0ProImageToVideoAspectRatio::SixteenByNine), generate_audio: Some(true), negative_prompt: None, end_image_url: None, + shot_type: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -144,21 +227,77 @@ mod tests { Ok(()) } + #[tokio::test] + #[ignore] // manually run — fires a real API request per variant (expensive) + async fn test_all_aspect_ratios() -> AnyhowResult<()> { + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + let api_key = FalApiKey::from_str(&secret); + + for ar in EnqueueKling3p0ProImageToVideoAspectRatio::iter() { + println!("--- aspect ratio: {:?} ---", ar); + let args = EnqueueKling3p0ProImageToVideoArgs { + image_url: TREX_SKELETON_IMAGE_URL.to_string(), + prompt: "the skeleton comes alive and roars at the camera".to_string(), + duration: Some(EnqueueKling3p0ProImageToVideoDuration::ThreeSeconds), + aspect_ratio: Some(ar), + generate_audio: Some(false), + negative_prompt: None, + end_image_url: None, + shot_type: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + let result = enqueue_kling_3p0_pro_image_to_video_webhook(args).await?; + println!("result: {:?}", result); + } + + Ok(()) + } + #[tokio::test] #[ignore] // manually run — fires a real API request per variant (expensive) async fn test_all_durations() -> AnyhowResult<()> { let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; let api_key = FalApiKey::from_str(&secret); - for dur in EnqueueKling3p0ProTextToVideoDuration::iter() { + for dur in EnqueueKling3p0ProImageToVideoDuration::iter() { println!("--- duration: {:?} ---", dur); let args = EnqueueKling3p0ProImageToVideoArgs { image_url: TREX_SKELETON_IMAGE_URL.to_string(), prompt: "the skeleton slowly turns its head and roars".to_string(), duration: Some(dur), + aspect_ratio: Some(EnqueueKling3p0ProImageToVideoAspectRatio::SixteenByNine), generate_audio: Some(false), negative_prompt: None, end_image_url: None, + shot_type: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + let result = enqueue_kling_3p0_pro_image_to_video_webhook(args).await?; + println!("result: {:?}", result); + } + + Ok(()) + } + + #[tokio::test] + #[ignore] // manually run — fires a real API request per variant (expensive) + async fn test_all_shot_types() -> AnyhowResult<()> { + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + let api_key = FalApiKey::from_str(&secret); + + for st in EnqueueKling3p0ProImageToVideoShotType::iter() { + println!("--- shot type: {:?} ---", st); + let args = EnqueueKling3p0ProImageToVideoArgs { + image_url: TREX_SKELETON_IMAGE_URL.to_string(), + prompt: "the skeleton lurches forward and snaps its jaws".to_string(), + duration: Some(EnqueueKling3p0ProImageToVideoDuration::FiveSeconds), + aspect_ratio: Some(EnqueueKling3p0ProImageToVideoAspectRatio::SixteenByNine), + generate_audio: Some(true), + negative_prompt: None, + end_image_url: None, + shot_type: Some(st), api_key: &api_key, webhook_url: "https://example.com/webhook", }; diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_standard_image_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_standard_image_to_video_webhook.rs index 8c4b1dc932..3252341af7 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_standard_image_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_kling_3p0_standard_image_to_video_webhook.rs @@ -2,12 +2,12 @@ use crate::creds::fal_api_key::FalApiKey; use crate::error::classify_fal_error::classify_fal_error; use crate::error::fal_error_plus::FalErrorPlus; use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents}; -use crate::requests::webhook::video::text::enqueue_kling_3p0_standard_text_to_video_webhook::EnqueueKling3p0StandardTextToVideoDuration; use crate::requests::http::video::http_kling_3p0_standard_image_to_video::{kling_3p0_standard_image_to_video, Kling3p0StandardImageToVideoInput}; use fal::webhook::WebhookResponse; use reqwest::IntoUrl; pub struct EnqueueKling3p0StandardImageToVideoArgs<'a, R: IntoUrl> { + // Request required pub prompt: String, pub image_url: String, @@ -15,13 +15,83 @@ pub struct EnqueueKling3p0StandardImageToVideoArgs<'a, R: IntoUrl> { pub end_image_url: Option, pub generate_audio: Option, pub negative_prompt: Option, - pub duration: Option, + pub duration: Option, + pub aspect_ratio: Option, + pub shot_type: Option, // Fulfillment pub webhook_url: R, pub api_key: &'a FalApiKey, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0StandardImageToVideoDuration { + ThreeSeconds, + FourSeconds, + FiveSeconds, + SixSeconds, + SevenSeconds, + EightSeconds, + NineSeconds, + TenSeconds, + ElevenSeconds, + TwelveSeconds, + ThirteenSeconds, + FourteenSeconds, + FifteenSeconds, +} + +impl EnqueueKling3p0StandardImageToVideoDuration { + pub fn to_seconds(&self) -> u64 { + match self { + Self::ThreeSeconds => 3, + Self::FourSeconds => 4, + Self::FiveSeconds => 5, + Self::SixSeconds => 6, + Self::SevenSeconds => 7, + Self::EightSeconds => 8, + Self::NineSeconds => 9, + Self::TenSeconds => 10, + Self::ElevenSeconds => 11, + Self::TwelveSeconds => 12, + Self::ThirteenSeconds => 13, + Self::FourteenSeconds => 14, + Self::FifteenSeconds => 15, + } + } + + pub fn to_str(&self) -> &'static str { + match self { + Self::ThreeSeconds => "3", + Self::FourSeconds => "4", + Self::FiveSeconds => "5", + Self::SixSeconds => "6", + Self::SevenSeconds => "7", + Self::EightSeconds => "8", + Self::NineSeconds => "9", + Self::TenSeconds => "10", + Self::ElevenSeconds => "11", + Self::TwelveSeconds => "12", + Self::ThirteenSeconds => "13", + Self::FourteenSeconds => "14", + Self::FifteenSeconds => "15", + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0StandardImageToVideoAspectRatio { + Square, + SixteenByNine, + NineBySixteen, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0StandardImageToVideoShotType { + Customize, + Intelligent, +} + impl FalRequestCostCalculator for EnqueueKling3p0StandardImageToVideoArgs<'_, U> { fn calculate_cost_in_cents(&self) -> UsdCents { // Same pricing as text-to-video for Kling 3.0 Standard: @@ -29,7 +99,7 @@ impl FalRequestCostCalculator for EnqueueKling3p0StandardImageToVid // Audio on: $0.252/second let generate_audio = self.generate_audio.unwrap_or(true); let duration_secs = self.duration - .unwrap_or(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds) + .unwrap_or(EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds) .to_seconds(); let rate = if generate_audio { 252u64 } else { 168u64 }; @@ -46,14 +116,30 @@ pub async fn enqueue_kling_3p0_standard_image_to_video_webhook( let duration = args.duration .map(|d| d.to_str().to_string()); + let aspect_ratio = args.aspect_ratio + .map(|ar| match ar { + EnqueueKling3p0StandardImageToVideoAspectRatio::Square => "1:1", + EnqueueKling3p0StandardImageToVideoAspectRatio::SixteenByNine => "16:9", + EnqueueKling3p0StandardImageToVideoAspectRatio::NineBySixteen => "9:16", + }) + .map(|s| s.to_string()); + + let shot_type = args.shot_type + .map(|st| match st { + EnqueueKling3p0StandardImageToVideoShotType::Customize => "customize", + EnqueueKling3p0StandardImageToVideoShotType::Intelligent => "intelligent", + }) + .map(|s| s.to_string()); + let request = Kling3p0StandardImageToVideoInput { prompt: args.prompt, image_url: args.image_url, end_image_url: args.end_image_url, - aspect_ratio: None, // Not applicable for image-to-video + aspect_ratio, generate_audio: args.generate_audio, duration, negative_prompt: args.negative_prompt, + shot_type, cfg_scale: None, }; @@ -85,7 +171,9 @@ mod tests { end_image_url: None, generate_audio: Some(false), negative_prompt: None, - duration: Some(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds), + duration: Some(EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds), + aspect_ratio: None, + shot_type: None, webhook_url: "https://example.com/webhook", api_key: &api_key, }; @@ -95,22 +183,22 @@ mod tests { assert_eq!(args.calculate_cost_in_cents(), 84); // 10s: (168 * 10 + 9) / 10 = 1689 / 10 = 168 - args.duration = Some(EnqueueKling3p0StandardTextToVideoDuration::TenSeconds); + args.duration = Some(EnqueueKling3p0StandardImageToVideoDuration::TenSeconds); assert_eq!(args.calculate_cost_in_cents(), 168); // 15s: (168 * 15 + 9) / 10 = 2529 / 10 = 252 - args.duration = Some(EnqueueKling3p0StandardTextToVideoDuration::FifteenSeconds); + args.duration = Some(EnqueueKling3p0StandardImageToVideoDuration::FifteenSeconds); assert_eq!(args.calculate_cost_in_cents(), 252); // Audio on: $0.252/sec args.generate_audio = Some(true); // 5s: (252 * 5 + 9) / 10 = 1269 / 10 = 126 - args.duration = Some(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds); + args.duration = Some(EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds); assert_eq!(args.calculate_cost_in_cents(), 126); // 10s: (252 * 10 + 9) / 10 = 2529 / 10 = 252 - args.duration = Some(EnqueueKling3p0StandardTextToVideoDuration::TenSeconds); + args.duration = Some(EnqueueKling3p0StandardImageToVideoDuration::TenSeconds); assert_eq!(args.calculate_cost_in_cents(), 252); } @@ -123,10 +211,12 @@ mod tests { let args = EnqueueKling3p0StandardImageToVideoArgs { image_url: TREX_SKELETON_IMAGE_URL.to_string(), prompt: "the t-rex skeleton gets off the podium and begins walking toward the camera".to_string(), - duration: Some(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds), + duration: Some(EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds), + aspect_ratio: Some(EnqueueKling3p0StandardImageToVideoAspectRatio::SixteenByNine), generate_audio: Some(true), negative_prompt: None, end_image_url: None, + shot_type: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -137,21 +227,77 @@ mod tests { Ok(()) } + #[tokio::test] + #[ignore] // manually run — fires a real API request per variant (expensive) + async fn test_all_aspect_ratios() -> AnyhowResult<()> { + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + let api_key = FalApiKey::from_str(&secret); + + for ar in EnqueueKling3p0StandardImageToVideoAspectRatio::iter() { + println!("--- aspect ratio: {:?} ---", ar); + let args = EnqueueKling3p0StandardImageToVideoArgs { + image_url: TREX_SKELETON_IMAGE_URL.to_string(), + prompt: "the skeleton comes alive and roars at the camera".to_string(), + duration: Some(EnqueueKling3p0StandardImageToVideoDuration::ThreeSeconds), + aspect_ratio: Some(ar), + generate_audio: Some(false), + negative_prompt: None, + end_image_url: None, + shot_type: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + let result = enqueue_kling_3p0_standard_image_to_video_webhook(args).await?; + println!("result: {:?}", result); + } + + Ok(()) + } + #[tokio::test] #[ignore] // manually run — fires a real API request per variant (expensive) async fn test_all_durations() -> AnyhowResult<()> { let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; let api_key = FalApiKey::from_str(&secret); - for dur in EnqueueKling3p0StandardTextToVideoDuration::iter() { + for dur in EnqueueKling3p0StandardImageToVideoDuration::iter() { println!("--- duration: {:?} ---", dur); let args = EnqueueKling3p0StandardImageToVideoArgs { image_url: TREX_SKELETON_IMAGE_URL.to_string(), prompt: "the skeleton slowly turns its head and opens its jaw".to_string(), duration: Some(dur), + aspect_ratio: Some(EnqueueKling3p0StandardImageToVideoAspectRatio::SixteenByNine), generate_audio: Some(false), negative_prompt: None, end_image_url: None, + shot_type: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + let result = enqueue_kling_3p0_standard_image_to_video_webhook(args).await?; + println!("result: {:?}", result); + } + + Ok(()) + } + + #[tokio::test] + #[ignore] // manually run — fires a real API request per variant (expensive) + async fn test_all_shot_types() -> AnyhowResult<()> { + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + let api_key = FalApiKey::from_str(&secret); + + for st in EnqueueKling3p0StandardImageToVideoShotType::iter() { + println!("--- shot type: {:?} ---", st); + let args = EnqueueKling3p0StandardImageToVideoArgs { + image_url: TREX_SKELETON_IMAGE_URL.to_string(), + prompt: "the skeleton lurches forward dramatically".to_string(), + duration: Some(EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds), + aspect_ratio: Some(EnqueueKling3p0StandardImageToVideoAspectRatio::SixteenByNine), + generate_audio: Some(true), + negative_prompt: None, + end_image_url: None, + shot_type: Some(st), api_key: &api_key, webhook_url: "https://example.com/webhook", }; diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_pro_text_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_pro_text_to_video_webhook.rs index 8539b75e5f..455570c044 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_pro_text_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_pro_text_to_video_webhook.rs @@ -14,6 +14,7 @@ pub struct EnqueueKling3p0ProTextToVideoArgs<'a, R: IntoUrl> { pub negative_prompt: Option, pub duration: Option, pub aspect_ratio: Option, + pub shot_type: Option, // Fulfillment pub webhook_url: R, @@ -82,6 +83,12 @@ pub enum EnqueueKling3p0ProTextToVideoAspectRatio { NineBySixteen, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0ProTextToVideoShotType { + Customize, + Intelligent, +} + impl FalRequestCostCalculator for EnqueueKling3p0ProTextToVideoArgs<'_, U> { fn calculate_cost_in_cents(&self) -> UsdCents { // Kling 3.0 Pro pricing: @@ -114,12 +121,20 @@ pub async fn enqueue_kling_3p0_pro_text_to_video_webhook( }) .map(|s| s.to_string()); + let shot_type = args.shot_type + .map(|st| match st { + EnqueueKling3p0ProTextToVideoShotType::Customize => "customize", + EnqueueKling3p0ProTextToVideoShotType::Intelligent => "intelligent", + }) + .map(|s| s.to_string()); + let request = Kling3p0ProTextToVideoInput { prompt: args.prompt, generate_audio: args.generate_audio, duration, aspect_ratio, negative_prompt: args.negative_prompt, + shot_type, cfg_scale: None, }; @@ -150,6 +165,7 @@ mod tests { negative_prompt: None, duration: Some(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds), aspect_ratio: None, + shot_type: None, webhook_url: "https://example.com/webhook", api_key: &api_key, }; @@ -198,6 +214,7 @@ mod tests { negative_prompt: None, duration: Some(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds), aspect_ratio: Some(EnqueueKling3p0ProTextToVideoAspectRatio::SixteenByNine), + shot_type: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -222,6 +239,7 @@ mod tests { negative_prompt: None, duration: Some(EnqueueKling3p0ProTextToVideoDuration::ThreeSeconds), aspect_ratio: Some(ar), + shot_type: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -246,6 +264,32 @@ mod tests { negative_prompt: None, duration: Some(dur), aspect_ratio: Some(EnqueueKling3p0ProTextToVideoAspectRatio::SixteenByNine), + shot_type: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + let result = enqueue_kling_3p0_pro_text_to_video_webhook(args).await?; + println!("result: {:?}", result); + } + + Ok(()) + } + + #[tokio::test] + #[ignore] // manually run — fires a real API request per variant (expensive) + async fn test_all_shot_types() -> AnyhowResult<()> { + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + let api_key = FalApiKey::from_str(&secret); + + for st in EnqueueKling3p0ProTextToVideoShotType::iter() { + println!("--- shot type: {:?} ---", st); + let args = EnqueueKling3p0ProTextToVideoArgs { + prompt: "an eagle soars over a snow-capped mountain range".to_string(), + generate_audio: Some(true), + negative_prompt: None, + duration: Some(EnqueueKling3p0ProTextToVideoDuration::FiveSeconds), + aspect_ratio: Some(EnqueueKling3p0ProTextToVideoAspectRatio::SixteenByNine), + shot_type: Some(st), api_key: &api_key, webhook_url: "https://example.com/webhook", }; diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_standard_text_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_standard_text_to_video_webhook.rs index 196e6a85a4..5dac6a41d8 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_standard_text_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_kling_3p0_standard_text_to_video_webhook.rs @@ -14,6 +14,7 @@ pub struct EnqueueKling3p0StandardTextToVideoArgs<'a, R: IntoUrl> { pub negative_prompt: Option, pub duration: Option, pub aspect_ratio: Option, + pub shot_type: Option, // Fulfillment pub webhook_url: R, @@ -82,6 +83,12 @@ pub enum EnqueueKling3p0StandardTextToVideoAspectRatio { NineBySixteen, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::EnumIter)] +pub enum EnqueueKling3p0StandardTextToVideoShotType { + Customize, + Intelligent, +} + impl FalRequestCostCalculator for EnqueueKling3p0StandardTextToVideoArgs<'_, U> { fn calculate_cost_in_cents(&self) -> UsdCents { // Kling 3.0 Standard pricing: @@ -115,12 +122,20 @@ pub async fn enqueue_kling_3p0_standard_text_to_video_webhook( }) .map(|s| s.to_string()); + let shot_type = args.shot_type + .map(|st| match st { + EnqueueKling3p0StandardTextToVideoShotType::Customize => "customize", + EnqueueKling3p0StandardTextToVideoShotType::Intelligent => "intelligent", + }) + .map(|s| s.to_string()); + let request = Kling3p0StandardTextToVideoInput { prompt: args.prompt, generate_audio: args.generate_audio, duration, aspect_ratio, negative_prompt: args.negative_prompt, + shot_type, cfg_scale: None, }; @@ -151,6 +166,7 @@ mod tests { negative_prompt: None, duration: Some(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds), aspect_ratio: None, + shot_type: None, webhook_url: "https://example.com/webhook", api_key: &api_key, }; @@ -199,6 +215,7 @@ mod tests { negative_prompt: None, duration: Some(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds), aspect_ratio: Some(EnqueueKling3p0StandardTextToVideoAspectRatio::SixteenByNine), + shot_type: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -223,6 +240,7 @@ mod tests { negative_prompt: None, duration: Some(EnqueueKling3p0StandardTextToVideoDuration::ThreeSeconds), aspect_ratio: Some(ar), + shot_type: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -247,6 +265,32 @@ mod tests { negative_prompt: None, duration: Some(dur), aspect_ratio: Some(EnqueueKling3p0StandardTextToVideoAspectRatio::SixteenByNine), + shot_type: None, + api_key: &api_key, + webhook_url: "https://example.com/webhook", + }; + let result = enqueue_kling_3p0_standard_text_to_video_webhook(args).await?; + println!("result: {:?}", result); + } + + Ok(()) + } + + #[tokio::test] + #[ignore] // manually run — fires a real API request per variant (expensive) + async fn test_all_shot_types() -> AnyhowResult<()> { + let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?; + let api_key = FalApiKey::from_str(&secret); + + for st in EnqueueKling3p0StandardTextToVideoShotType::iter() { + println!("--- shot type: {:?} ---", st); + let args = EnqueueKling3p0StandardTextToVideoArgs { + prompt: "a bird takes flight from a tree branch".to_string(), + generate_audio: Some(true), + negative_prompt: None, + duration: Some(EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds), + aspect_ratio: Some(EnqueueKling3p0StandardTextToVideoAspectRatio::SixteenByNine), + shot_type: Some(st), api_key: &api_key, webhook_url: "https://example.com/webhook", }; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_pro_multi_function_video_gen_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_pro_multi_function_video_gen_handler.rs index 066960e179..d803825fd2 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_pro_multi_function_video_gen_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_pro_multi_function_video_gen_handler.rs @@ -21,7 +21,7 @@ use enums::common::model_type::ModelType; use enums::common::visibility::Visibility; use fal_client::creds::open_ai_api_key::OpenAiApiKey; use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator; -use fal_client::requests::webhook::video::image::enqueue_kling_3p0_pro_image_to_video_webhook::{enqueue_kling_3p0_pro_image_to_video_webhook, EnqueueKling3p0ProImageToVideoArgs}; +use fal_client::requests::webhook::video::image::enqueue_kling_3p0_pro_image_to_video_webhook::{enqueue_kling_3p0_pro_image_to_video_webhook, EnqueueKling3p0ProImageToVideoArgs, EnqueueKling3p0ProImageToVideoDuration}; use fal_client::requests::webhook::video::text::enqueue_kling_3p0_pro_text_to_video_webhook::{enqueue_kling_3p0_pro_text_to_video_webhook, EnqueueKling3p0ProTextToVideoArgs, EnqueueKling3p0ProTextToVideoAspectRatio, EnqueueKling3p0ProTextToVideoDuration}; use http_server_common::request::get_request_ip::get_request_ip; use log::{error, info, warn}; @@ -40,22 +40,22 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken; use tokens::tokens::media_files::MediaFileToken; use utoipa::ToSchema; -fn map_duration_i2v(duration: Option) -> EnqueueKling3p0ProTextToVideoDuration { +fn map_duration_i2v(duration: Option) -> EnqueueKling3p0ProImageToVideoDuration { match duration { - Some(Kling3p0ProMultiFunctionVideoGenDuration::ThreeSeconds) => EnqueueKling3p0ProTextToVideoDuration::ThreeSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::FourSeconds) => EnqueueKling3p0ProTextToVideoDuration::FourSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::FiveSeconds) => EnqueueKling3p0ProTextToVideoDuration::FiveSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::SixSeconds) => EnqueueKling3p0ProTextToVideoDuration::SixSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::SevenSeconds) => EnqueueKling3p0ProTextToVideoDuration::SevenSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::EightSeconds) => EnqueueKling3p0ProTextToVideoDuration::EightSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::NineSeconds) => EnqueueKling3p0ProTextToVideoDuration::NineSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::TenSeconds) => EnqueueKling3p0ProTextToVideoDuration::TenSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::ElevenSeconds) => EnqueueKling3p0ProTextToVideoDuration::ElevenSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::TwelveSeconds) => EnqueueKling3p0ProTextToVideoDuration::TwelveSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::ThirteenSeconds) => EnqueueKling3p0ProTextToVideoDuration::ThirteenSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::FourteenSeconds) => EnqueueKling3p0ProTextToVideoDuration::FourteenSeconds, - Some(Kling3p0ProMultiFunctionVideoGenDuration::FifteenSeconds) => EnqueueKling3p0ProTextToVideoDuration::FifteenSeconds, - None => EnqueueKling3p0ProTextToVideoDuration::FiveSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::ThreeSeconds) => EnqueueKling3p0ProImageToVideoDuration::ThreeSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::FourSeconds) => EnqueueKling3p0ProImageToVideoDuration::FourSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::FiveSeconds) => EnqueueKling3p0ProImageToVideoDuration::FiveSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::SixSeconds) => EnqueueKling3p0ProImageToVideoDuration::SixSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::SevenSeconds) => EnqueueKling3p0ProImageToVideoDuration::SevenSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::EightSeconds) => EnqueueKling3p0ProImageToVideoDuration::EightSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::NineSeconds) => EnqueueKling3p0ProImageToVideoDuration::NineSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::TenSeconds) => EnqueueKling3p0ProImageToVideoDuration::TenSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::ElevenSeconds) => EnqueueKling3p0ProImageToVideoDuration::ElevenSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::TwelveSeconds) => EnqueueKling3p0ProImageToVideoDuration::TwelveSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::ThirteenSeconds) => EnqueueKling3p0ProImageToVideoDuration::ThirteenSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::FourteenSeconds) => EnqueueKling3p0ProImageToVideoDuration::FourteenSeconds, + Some(Kling3p0ProMultiFunctionVideoGenDuration::FifteenSeconds) => EnqueueKling3p0ProImageToVideoDuration::FifteenSeconds, + None => EnqueueKling3p0ProImageToVideoDuration::FiveSeconds, } } @@ -206,6 +206,8 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler( generate_audio: request.generate_audio, negative_prompt: request.negative_prompt.clone(), duration: Some(duration), + aspect_ratio: None, + shot_type: None, webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; @@ -241,6 +243,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler( generate_audio: request.generate_audio, duration: Some(duration), aspect_ratio: Some(aspect_ratio), + shot_type: None, webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_standard_multi_function_video_gen_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_standard_multi_function_video_gen_handler.rs index 4f44496a21..3a1ba41425 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_standard_multi_function_video_gen_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/kling_3p0_standard_multi_function_video_gen_handler.rs @@ -21,7 +21,7 @@ use enums::common::model_type::ModelType; use enums::common::visibility::Visibility; use fal_client::creds::open_ai_api_key::OpenAiApiKey; use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator; -use fal_client::requests::webhook::video::image::enqueue_kling_3p0_standard_image_to_video_webhook::{enqueue_kling_3p0_standard_image_to_video_webhook, EnqueueKling3p0StandardImageToVideoArgs}; +use fal_client::requests::webhook::video::image::enqueue_kling_3p0_standard_image_to_video_webhook::{enqueue_kling_3p0_standard_image_to_video_webhook, EnqueueKling3p0StandardImageToVideoArgs, EnqueueKling3p0StandardImageToVideoDuration}; use fal_client::requests::webhook::video::text::enqueue_kling_3p0_standard_text_to_video_webhook::{enqueue_kling_3p0_standard_text_to_video_webhook, EnqueueKling3p0StandardTextToVideoArgs, EnqueueKling3p0StandardTextToVideoAspectRatio, EnqueueKling3p0StandardTextToVideoDuration}; use http_server_common::request::get_request_ip::get_request_ip; use log::{error, info, warn}; @@ -40,22 +40,22 @@ use tokens::tokens::generic_inference_jobs::InferenceJobToken; use tokens::tokens::media_files::MediaFileToken; use utoipa::ToSchema; -fn map_duration_i2v(duration: Option) -> EnqueueKling3p0StandardTextToVideoDuration { +fn map_duration_i2v(duration: Option) -> EnqueueKling3p0StandardImageToVideoDuration { match duration { - Some(Kling3p0StandardMultiFunctionVideoGenDuration::ThreeSeconds) => EnqueueKling3p0StandardTextToVideoDuration::ThreeSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::FourSeconds) => EnqueueKling3p0StandardTextToVideoDuration::FourSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::FiveSeconds) => EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::SixSeconds) => EnqueueKling3p0StandardTextToVideoDuration::SixSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::SevenSeconds) => EnqueueKling3p0StandardTextToVideoDuration::SevenSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::EightSeconds) => EnqueueKling3p0StandardTextToVideoDuration::EightSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::NineSeconds) => EnqueueKling3p0StandardTextToVideoDuration::NineSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::TenSeconds) => EnqueueKling3p0StandardTextToVideoDuration::TenSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::ElevenSeconds) => EnqueueKling3p0StandardTextToVideoDuration::ElevenSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::TwelveSeconds) => EnqueueKling3p0StandardTextToVideoDuration::TwelveSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::ThirteenSeconds) => EnqueueKling3p0StandardTextToVideoDuration::ThirteenSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::FourteenSeconds) => EnqueueKling3p0StandardTextToVideoDuration::FourteenSeconds, - Some(Kling3p0StandardMultiFunctionVideoGenDuration::FifteenSeconds) => EnqueueKling3p0StandardTextToVideoDuration::FifteenSeconds, - None => EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::ThreeSeconds) => EnqueueKling3p0StandardImageToVideoDuration::ThreeSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::FourSeconds) => EnqueueKling3p0StandardImageToVideoDuration::FourSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::FiveSeconds) => EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::SixSeconds) => EnqueueKling3p0StandardImageToVideoDuration::SixSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::SevenSeconds) => EnqueueKling3p0StandardImageToVideoDuration::SevenSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::EightSeconds) => EnqueueKling3p0StandardImageToVideoDuration::EightSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::NineSeconds) => EnqueueKling3p0StandardImageToVideoDuration::NineSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::TenSeconds) => EnqueueKling3p0StandardImageToVideoDuration::TenSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::ElevenSeconds) => EnqueueKling3p0StandardImageToVideoDuration::ElevenSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::TwelveSeconds) => EnqueueKling3p0StandardImageToVideoDuration::TwelveSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::ThirteenSeconds) => EnqueueKling3p0StandardImageToVideoDuration::ThirteenSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::FourteenSeconds) => EnqueueKling3p0StandardImageToVideoDuration::FourteenSeconds, + Some(Kling3p0StandardMultiFunctionVideoGenDuration::FifteenSeconds) => EnqueueKling3p0StandardImageToVideoDuration::FifteenSeconds, + None => EnqueueKling3p0StandardImageToVideoDuration::FiveSeconds, } } @@ -206,6 +206,8 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler( generate_audio: request.generate_audio, negative_prompt: request.negative_prompt.clone(), duration: Some(duration), + aspect_ratio: None, + shot_type: None, webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; @@ -241,6 +243,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler( generate_audio: request.generate_audio, duration: Some(duration), aspect_ratio: Some(aspect_ratio), + shot_type: None, webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, };