diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_video_cost.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_video_cost.rs index 75efd93f8b..32cf88424e 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_video_cost.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/cost_estimate/estimate_video_cost.rs @@ -9,7 +9,10 @@ use enums::common::generation_provider::GenerationProvider; pub const ESTIMATE_VIDEO_COST_PATH: &str = "/v1/generate/cost_estimate/video"; /// Request body for the video cost estimate endpoint. -#[derive(Serialize, Deserialize, ToSchema)] +/// +/// Every model cares about its own set of parameters and not all +/// parameters may be relevant for every model. +#[derive(Serialize, Deserialize, ToSchema, Debug)] pub struct EstimateVideoCostRequest { /// The video model to estimate costs for. pub model: CommonVideoModel, @@ -34,10 +37,13 @@ pub struct EstimateVideoCostRequest { /// Number of videos to generate in parallel. pub video_batch_count: Option, + + /// Whether to generate audio for the video. + pub generate_audio: Option, } /// Describes the type of video generation being requested. -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Serialize, Deserialize, ToSchema, Debug)] #[serde(tag = "type", rename_all = "snake_case")] pub enum GenerationMode { TextToVideo, @@ -99,6 +105,7 @@ mod tests { resolution: None, duration_seconds: None, video_batch_count: None, + generate_audio: None, }; let serialized = serde_json::to_string(&request).unwrap(); assert!(serialized.contains("\"generation_mode\":{\"type\":\"text_to_video\"}")); @@ -115,6 +122,7 @@ mod tests { resolution: None, duration_seconds: None, video_batch_count: None, + generate_audio: None, }; let serialized = serde_json::to_string(&request).unwrap(); assert!(serialized.contains("\"generation_mode\":{\"type\":\"reference_image_to_video\",\"count\":1}")); diff --git a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen.rs b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen.rs index a29a2ceaf6..75272c89a6 100644 --- a/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen.rs +++ b/crates/api_clients/artcraft/artcraft_api_defs/src/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen.rs @@ -6,7 +6,7 @@ use utoipa::ToSchema; pub const SEEDANCE_1P5_PRO_MULTI_FUNCTION_VIDEO_GEN_PATH: &str = "/v1/generate/video/multi_function/seedance_1p5_pro"; /// Both text-to-video and image-to-video in one request. -#[derive(Serialize, Deserialize, ToSchema)] +#[derive(Serialize, Deserialize, ToSchema, Debug)] pub struct Seedance1p5ProMultiFunctionVideoGenRequest { /// Idempotency token to prevent duplicate requests. pub uuid_idempotency_token: String, @@ -38,6 +38,9 @@ pub struct Seedance1p5ProMultiFunctionVideoGenRequest { /// Optional. pub aspect_ratio: Option, + + /// Whether to generate audio. + pub generate_audio: Option, } #[derive(Serialize, Deserialize, ToSchema, Copy, Clone, Debug)] 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 96f103b77a..02adc39999 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 @@ -32,7 +32,7 @@ pub(crate) fn estimate_video_cost_artcraft_kling3p0_pro( let api_key = fal_client::creds::fal_api_key::FalApiKey::from_str(""); let args = EnqueueKling3p0ProTextToVideoArgs { prompt: String::new(), - generate_audio: None, + generate_audio: plan.generate_audio, negative_prompt: None, duration, aspect_ratio: None, @@ -65,6 +65,7 @@ mod tests { model: CommonVideoModel::Kling3p0Pro, provider: Provider::Artcraft, prompt: None, + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -72,6 +73,7 @@ mod tests { aspect_ratio: None, duration_seconds: Some(duration_seconds), video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, }; 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 ae63683699..74dc69a9e2 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 @@ -32,7 +32,7 @@ pub(crate) fn estimate_video_cost_artcraft_kling3p0_standard( let api_key = fal_client::creds::fal_api_key::FalApiKey::from_str(""); let args = EnqueueKling3p0StandardTextToVideoArgs { prompt: String::new(), - generate_audio: None, + generate_audio: plan.generate_audio, negative_prompt: None, duration, aspect_ratio: None, @@ -65,6 +65,7 @@ mod tests { model: CommonVideoModel::Kling3p0Standard, provider: Provider::Artcraft, prompt: None, + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -72,6 +73,7 @@ mod tests { aspect_ratio: None, duration_seconds: Some(duration_seconds), video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, }; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance1p5_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance1p5_pro.rs index 9f29004924..4fcafae8be 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance1p5_pro.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance1p5_pro.rs @@ -39,6 +39,7 @@ pub(crate) fn estimate_video_cost_artcraft_seedance1p5_pro( duration, resolution, aspect_ratio: None, + generate_audio: plan.generate_audio, webhook_url: "https://example.com", api_key: &api_key, }; @@ -67,6 +68,7 @@ mod tests { model: CommonVideoModel::Seedance1p5Pro, provider: Provider::Artcraft, prompt: None, + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -74,6 +76,7 @@ mod tests { aspect_ratio: None, duration_seconds: Some(duration_seconds), video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, }; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0.rs index 5157a0b489..9c0597eeb4 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0.rs @@ -57,6 +57,7 @@ mod tests { model: CommonVideoModel::Seedance2p0, provider: Provider::Artcraft, prompt: None, + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -64,6 +65,7 @@ mod tests { aspect_ratio: None, duration_seconds: Some(duration_seconds), video_batch_count: Some(video_batch_count), + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, }; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_pro.rs index ea84236730..60bbf906c5 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_pro.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_pro.rs @@ -15,11 +15,11 @@ pub async fn execute_artcraft_kling3p0_pro( let request = Kling3p0ProMultiFunctionVideoGenRequest { uuid_idempotency_token: plan.idempotency_token.clone(), prompt: plan.prompt.map(|p| p.to_string()), - negative_prompt: None, + negative_prompt: plan.negative_prompt.map(|s| s.to_string()), image_media_token: plan.start_frame.map(|t| t.to_owned()), end_image_media_token: plan.end_frame.map(|t| t.to_owned()), duration: plan.duration, - generate_audio: None, + generate_audio: plan.generate_audio, aspect_ratio: plan.aspect_ratio, }; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_standard.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_standard.rs index 0520fc4ba6..fcd5d894ad 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_standard.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_kling3p0_standard.rs @@ -15,11 +15,11 @@ pub async fn execute_artcraft_kling3p0_standard( let request = Kling3p0StandardMultiFunctionVideoGenRequest { uuid_idempotency_token: plan.idempotency_token.clone(), prompt: plan.prompt.map(|p| p.to_string()), - negative_prompt: None, + negative_prompt: plan.negative_prompt.map(|s| s.to_string()), image_media_token: plan.start_frame.map(|t| t.to_owned()), end_image_media_token: plan.end_frame.map(|t| t.to_owned()), duration: plan.duration, - generate_audio: None, + generate_audio: plan.generate_audio, aspect_ratio: plan.aspect_ratio, }; diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_seedance1p5_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_seedance1p5_pro.rs index 727b7dd755..be67049d7c 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_seedance1p5_pro.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/artcraft/generate_video_artcraft_seedance1p5_pro.rs @@ -20,6 +20,7 @@ pub async fn execute_artcraft_seedance1p5_pro( aspect_ratio: plan.aspect_ratio, duration: plan.duration, resolution: plan.resolution, + generate_audio: plan.generate_audio, }; let response = seedance_1p5_pro_multi_function_video_gen( diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs index c7890eee5f..c3f208a792 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/generate_video_request.rs @@ -22,8 +22,8 @@ pub struct GenerateVideoRequest<'a> { /// The prompt for the video generation pub prompt: Option<&'a str>, - // /// Some models support negative prompts - // pub negative_prompt: Option, + /// Some models support negative prompts + pub negative_prompt: Option<&'a str>, /// Starting keyframe (optional). pub start_frame: Option>, @@ -46,9 +46,11 @@ pub struct GenerateVideoRequest<'a> { /// How many videos to generate. pub video_batch_count: Option, - // /// Whether to turn on/off audio. - // /// Not all models support audio, not all models have a choice. - // pub generate_audio: Option, + /// Whether to turn on/off audio. + /// Not all models support audio, not all models have a choice. + /// Some models will default this to true, others will default it to false, + /// so it's best to be explicit. + pub generate_audio: Option, /// If the request is a mismatch with the (model/provider), how to mitigate it. pub request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_pro.rs index dc74c8c143..0775c79878 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_pro.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_pro.rs @@ -13,10 +13,12 @@ use tokens::tokens::media_files::MediaFileToken; #[derive(Debug, Clone)] pub struct PlanArtcraftKling3p0Pro<'a> { pub prompt: Option<&'a str>, + pub negative_prompt: Option<&'a str>, pub start_frame: Option<&'a MediaFileToken>, pub end_frame: Option<&'a MediaFileToken>, pub aspect_ratio: Option, pub duration: Option, + pub generate_audio: Option, pub idempotency_token: String, } @@ -33,10 +35,12 @@ pub fn plan_generate_video_artcraft_kling3p0_pro<'a>( Ok(PlanArtcraftKling3p0Pro { prompt: request.prompt, + negative_prompt: request.negative_prompt, start_frame, end_frame, aspect_ratio, duration, + generate_audio: request.generate_audio, idempotency_token: request.get_or_generate_idempotency_token(), }) } @@ -169,6 +173,7 @@ mod tests { model: CommonVideoModel::Kling3p0Pro, provider: Provider::Artcraft, prompt: Some("a cat in space"), + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -176,6 +181,7 @@ mod tests { aspect_ratio: None, duration_seconds: None, video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, } diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_standard.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_standard.rs index 329434bd43..8f8adb3f1b 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_standard.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_kling3p0_standard.rs @@ -13,10 +13,12 @@ use tokens::tokens::media_files::MediaFileToken; #[derive(Debug, Clone)] pub struct PlanArtcraftKling3p0Standard<'a> { pub prompt: Option<&'a str>, + pub negative_prompt: Option<&'a str>, pub start_frame: Option<&'a MediaFileToken>, pub end_frame: Option<&'a MediaFileToken>, pub aspect_ratio: Option, pub duration: Option, + pub generate_audio: Option, pub idempotency_token: String, } @@ -33,10 +35,12 @@ pub fn plan_generate_video_artcraft_kling3p0_standard<'a>( Ok(PlanArtcraftKling3p0Standard { prompt: request.prompt, + negative_prompt: request.negative_prompt, start_frame, end_frame, aspect_ratio, duration, + generate_audio: request.generate_audio, idempotency_token: request.get_or_generate_idempotency_token(), }) } @@ -169,6 +173,7 @@ mod tests { model: CommonVideoModel::Kling3p0Standard, provider: Provider::Artcraft, prompt: Some("a cat in space"), + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -176,6 +181,7 @@ mod tests { aspect_ratio: None, duration_seconds: None, video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, } diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_seedance1p5_pro.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_seedance1p5_pro.rs index bfa3f103d0..de7f375ae0 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_seedance1p5_pro.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/artcraft/plan_generate_video_artcraft_seedance1p5_pro.rs @@ -19,6 +19,7 @@ pub struct PlanArtcraftSeedance1p5Pro<'a> { pub aspect_ratio: Option, pub duration: Option, pub resolution: Option, + pub generate_audio: Option, pub idempotency_token: String, } @@ -41,6 +42,7 @@ pub fn plan_generate_video_artcraft_seedance1p5_pro<'a>( aspect_ratio, duration, resolution, + generate_audio: request.generate_audio, idempotency_token: request.get_or_generate_idempotency_token(), }) } @@ -184,6 +186,7 @@ mod tests { model: CommonVideoModel::Seedance1p5Pro, provider: Provider::Artcraft, prompt: Some("a cat in space"), + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -191,6 +194,7 @@ mod tests { aspect_ratio: None, duration_seconds: None, video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, } diff --git a/crates/api_clients/artcraft/artcraft_router/src/test_helpers.rs b/crates/api_clients/artcraft/artcraft_router/src/test_helpers.rs index c105ae9e5c..7768143a4b 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/test_helpers.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/test_helpers.rs @@ -214,6 +214,7 @@ pub fn base_video_request() -> GenerateVideoRequest<'static> { model: CommonVideoModel::Seedance2p0, provider: Provider::Artcraft, prompt: Some("a cat in space"), + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -221,6 +222,7 @@ pub fn base_video_request() -> GenerateVideoRequest<'static> { aspect_ratio: None, duration_seconds: None, video_batch_count: None, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut, idempotency_token: None, } diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1p5_pro_image_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1p5_pro_image_to_video_webhook.rs index 9d648df4c7..5e65a706fe 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1p5_pro_image_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1p5_pro_image_to_video_webhook.rs @@ -16,6 +16,7 @@ pub struct EnqueueSeedance1p5ProImageToVideoArgs<'a, R: IntoUrl> { pub resolution: Option, pub duration: Option, pub aspect_ratio: Option, + pub generate_audio: Option, // Fulfillment pub webhook_url: R, @@ -62,10 +63,13 @@ impl FalRequestCostCalculator for EnqueueSeedance1p5ProImageToVideo let resolution = self.resolution.unwrap_or(EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP); let duration = self.duration.unwrap_or(EnqueueSeedance1p5ProImageToVideoDuration::FiveSeconds); + let audio = self.generate_audio.unwrap_or(true); + let dollars_per_million_tokens = if audio { 2.4 } else { 1.2 }; + if resolution == EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP && duration == EnqueueSeedance1p5ProImageToVideoDuration::FiveSeconds { - return 26; + return if audio { 26 } else { 13 }; } // TODO: Only correct for some aspect ratios for now. @@ -92,7 +96,7 @@ impl FalRequestCostCalculator for EnqueueSeedance1p5ProImageToVideo let tokens = (height as f64) * (width as f64) * FPS * duration_secs; let tokens = tokens / 1024.0; - let cost = tokens * 2.4 / 1_000_000.0; + let cost = tokens * dollars_per_million_tokens / 1_000_000.0; let cost = cost * 100.0; // Dollars to cents. let cost = cost.ceil(); @@ -150,7 +154,7 @@ pub async fn enqueue_seedance_1p5_pro_image_to_video_webhook( camera_fixed: None, seed: None, enable_safety_checker: Some(false), - generate_audio: Some(true), + generate_audio: Some(args.generate_audio.unwrap_or(true)), }; let result = seedance_1p5_pro_image_to_video(request) @@ -183,6 +187,7 @@ mod tests { duration: Some(EnqueueSeedance1p5ProImageToVideoDuration::FiveSeconds), resolution: Some(EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP), aspect_ratio: None, + generate_audio: None, webhook_url: "https://example.com/webhook", }; @@ -207,6 +212,43 @@ mod tests { assert_eq!(cost, 146); } + #[test] + fn test_cost_audio_off() { + let api_key = FalApiKey::from_str(""); + + let mut args = EnqueueSeedance1p5ProImageToVideoArgs { + prompt: String::new(), + image_url: String::new(), + end_image_url: None, + api_key: &api_key, + duration: Some(EnqueueSeedance1p5ProImageToVideoDuration::FiveSeconds), + resolution: Some(EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP), + aspect_ratio: None, + generate_audio: Some(false), + webhook_url: "https://example.com/webhook", + }; + + // 720p 5s without audio = half of 26 + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 13); + + // Calculated values — half of audio-on costs (ceil) + args.duration = Some(EnqueueSeedance1p5ProImageToVideoDuration::TenSeconds); + args.resolution = Some(EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP); + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 33); + + args.duration = Some(EnqueueSeedance1p5ProImageToVideoDuration::FiveSeconds); + args.resolution = Some(EnqueueSeedance1p5ProImageToVideoResolution::TenEightyP); + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 37); + + args.duration = Some(EnqueueSeedance1p5ProImageToVideoDuration::TenSeconds); + args.resolution = Some(EnqueueSeedance1p5ProImageToVideoResolution::TenEightyP); + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 73); + } + #[tokio::test] #[ignore] // manually run — fires a real API request and incurs cost async fn test() -> AnyhowResult<()> { @@ -220,6 +262,7 @@ mod tests { aspect_ratio: Some(EnqueueSeedance1p5ProImageToVideoAspectRatio::SixteenByNine), resolution: Some(EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP), end_image_url: None, + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -245,6 +288,7 @@ mod tests { aspect_ratio: Some(ar), resolution: None, end_image_url: None, + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -270,6 +314,7 @@ mod tests { aspect_ratio: Some(EnqueueSeedance1p5ProImageToVideoAspectRatio::SixteenByNine), resolution: None, end_image_url: None, + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -295,6 +340,7 @@ mod tests { aspect_ratio: Some(EnqueueSeedance1p5ProImageToVideoAspectRatio::SixteenByNine), resolution: Some(res), end_image_url: None, + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_seedance_1p5_pro_text_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_seedance_1p5_pro_text_to_video_webhook.rs index 6043b7134a..276f4c9c0f 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_seedance_1p5_pro_text_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/text/enqueue_seedance_1p5_pro_text_to_video_webhook.rs @@ -14,6 +14,7 @@ pub struct EnqueueSeedance1p5ProTextToVideoArgs<'a, R: IntoUrl> { pub resolution: Option, pub duration: Option, pub aspect_ratio: Option, + pub generate_audio: Option, // Fulfillment pub webhook_url: R, @@ -53,17 +54,22 @@ pub enum EnqueueSeedance1p5ProTextToVideoAspectRatio { impl FalRequestCostCalculator for EnqueueSeedance1p5ProTextToVideoArgs<'_, U> { fn calculate_cost_in_cents(&self) -> UsdCents { + // Fal: // "Each 720p 5 second video with audio costs roughly $0.26. // For other resolutions, 1 million video tokens with audio costs $2.4. + // Without audio, the price is 1.2 per million tokens. // tokens(video) = (height x width x FPS x duration) / 1024." let resolution = self.resolution.unwrap_or(EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP); let duration = self.duration.unwrap_or(EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds); + let audio = self.generate_audio.unwrap_or(true); + let dollars_per_million_tokens = if audio { 2.4 } else { 1.2 }; + if resolution == EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP && duration == EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds { - return 26; + return if audio { 26 } else { 13 }; } // TODO: Only correct for some aspect ratios for now. @@ -90,7 +96,7 @@ impl FalRequestCostCalculator for EnqueueSeedance1p5ProTextToVideoA let tokens = (height as f64) * (width as f64) * FPS * duration_secs; let tokens = tokens / 1024.0; - let cost = tokens * 2.4 / 1_000_000.0; + let cost = tokens * dollars_per_million_tokens / 1_000_000.0; let cost = cost * 100.0; // Dollars to cents. let cost = cost.ceil(); @@ -146,7 +152,7 @@ pub async fn enqueue_seedance_1p5_pro_text_to_video_webhook( camera_fixed: None, seed: None, enable_safety_checker: Some(false), - generate_audio: Some(true), + generate_audio: Some(args.generate_audio.unwrap_or(true)), }; let result = seedance_1p5_pro_text_to_video(request) @@ -176,6 +182,7 @@ mod tests { duration: Some(EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds), resolution: Some(EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP), aspect_ratio: None, + generate_audio: None, webhook_url: "https://example.com/webhook", }; @@ -200,6 +207,41 @@ mod tests { assert_eq!(cost, 146); } + #[test] + fn test_cost_audio_off() { + let api_key = FalApiKey::from_str(""); + + let mut args = EnqueueSeedance1p5ProTextToVideoArgs { + prompt: String::new(), + api_key: &api_key, + duration: Some(EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds), + resolution: Some(EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP), + aspect_ratio: None, + generate_audio: Some(false), + webhook_url: "https://example.com/webhook", + }; + + // 720p 5s without audio = half of 26 + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 13); + + // Calculated values — half of audio-on costs (ceil) + args.duration = Some(EnqueueSeedance1p5ProTextToVideoDuration::TenSeconds); + args.resolution = Some(EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP); + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 33); + + args.duration = Some(EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds); + args.resolution = Some(EnqueueSeedance1p5ProTextToVideoResolution::TenEightyP); + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 37); + + args.duration = Some(EnqueueSeedance1p5ProTextToVideoDuration::TenSeconds); + args.resolution = Some(EnqueueSeedance1p5ProTextToVideoResolution::TenEightyP); + let cost = args.calculate_cost_in_cents(); + assert_eq!(cost, 73); + } + #[tokio::test] #[ignore] // manually run — fires a real API request and incurs cost async fn test() -> AnyhowResult<()> { @@ -211,6 +253,7 @@ mod tests { duration: Some(EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds), aspect_ratio: Some(EnqueueSeedance1p5ProTextToVideoAspectRatio::SixteenByNine), resolution: Some(EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP), + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -234,6 +277,7 @@ mod tests { duration: Some(EnqueueSeedance1p5ProTextToVideoDuration::FourSeconds), aspect_ratio: Some(ar), resolution: None, + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -257,6 +301,7 @@ mod tests { duration: Some(dur), aspect_ratio: Some(EnqueueSeedance1p5ProTextToVideoAspectRatio::SixteenByNine), resolution: None, + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; @@ -280,6 +325,7 @@ mod tests { duration: Some(EnqueueSeedance1p5ProTextToVideoDuration::FourSeconds), aspect_ratio: Some(EnqueueSeedance1p5ProTextToVideoAspectRatio::SixteenByNine), resolution: Some(res), + generate_audio: None, api_key: &api_key, webhook_url: "https://example.com/webhook", }; diff --git a/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_video_cost_command.rs b/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_video_cost_command.rs index 5155a406a5..95eac732c6 100644 --- a/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_video_cost_command.rs +++ b/crates/desktop/artcraft/src/core/commands/cost_estimate/estimate_video_cost_command.rs @@ -7,8 +7,8 @@ use artcraft_api_defs::generate::cost_estimate::estimate_video_cost::{ EstimateVideoCostError, EstimateVideoCostErrorType, EstimateVideoCostRequest, EstimateVideoCostResponse, }; -use log::info; use artcraft_client::endpoints::generate::cost_estimate::video::estimate_video_cost::estimate_video_cost; +use log::debug; use tauri::State; impl SerializeMarker for EstimateVideoCostResponse {} @@ -18,7 +18,6 @@ pub async fn estimate_video_cost_command( request: EstimateVideoCostRequest, app_env_configs: State<'_, AppEnvConfigs>, ) -> ResponseOrError { - info!("estimate_video_cost_command called"); let result = estimate_video_cost( &app_env_configs.storyteller_host, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_1p5_pro.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_1p5_pro.rs deleted file mode 100644 index e675e16a62..0000000000 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_1p5_pro.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::core::commands::enqueue::generate_error::GenerateError; -use crate::core::commands::enqueue::image_to_video::enqueue_image_to_video_command::EnqueueImageToVideoRequest; -use crate::core::commands::enqueue::task_enqueue_success::TaskEnqueueSuccess; -use crate::core::events::generation_events::common::GenerationModel; -use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; -use artcraft_router::api::common_video_model::CommonVideoModel; -use artcraft_router::api::image_ref::ImageRef; -use artcraft_router::api::provider::Provider; -use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy; -use artcraft_router::client::router_artcraft_client::RouterArtcraftClient; -use artcraft_router::client::router_client::RouterClient; -use artcraft_router::generate::generate_video::generate_video_request::GenerateVideoRequest; -use enums::common::generation_provider::GenerationProvider; -use enums::tauri::tasks::task_type::TaskType; -use log::{error, info}; -use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet; - -pub(super) async fn handle_artcraft_seedance_1p5_pro( - request: &EnqueueImageToVideoRequest, - app_env_configs: &AppEnvConfigs, - creds: &StorytellerCredentialSet, -) -> Result { - let client = RouterClient::Artcraft(RouterArtcraftClient::new( - app_env_configs.storyteller_host.clone(), - creds.clone(), - )); - - let start_frame = request.image_media_token.as_ref().map(ImageRef::MediaFileToken); - let end_frame = request.end_frame_image_media_token.as_ref().map(ImageRef::MediaFileToken); - - let router_request = GenerateVideoRequest { - model: CommonVideoModel::Seedance1p5Pro, - provider: Provider::Artcraft, - prompt: request.prompt.as_deref(), - start_frame, - end_frame, - reference_images: None, - resolution: None, - aspect_ratio: request.aspect_ratio, - duration_seconds: request.duration_seconds, - video_batch_count: request.video_batch_count, - request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade, - idempotency_token: None, - }; - - let plan = router_request.build()?; - - info!("Video Generation Plan: {:?}", plan); - - let response = match plan.generate_video(&client).await { - Ok(resp) => { - info!("Successfully enqueued."); - resp - } - Err(err) => { - error!("Failed to enqueue: {:?}", err); - return Err(GenerateError::from(err)); - } - }; - - let job_id = response.get_artcraft_payload() - .map(|p| p.inference_job_token.to_string()) - .ok_or(GenerateError::ResponseHadNoJobTokens)?; - - Ok(TaskEnqueueSuccess { - task_type: TaskType::VideoGeneration, - model: Some(GenerationModel::Seedance1p5Pro), - provider: GenerationProvider::Artcraft, - provider_job_id: Some(job_id), - }) -} diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_2p0.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_2p0.rs index 9d78079c71..d0c390ce3d 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_2p0.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_seedance_2p0.rs @@ -34,6 +34,7 @@ pub(super) async fn handle_artcraft_seedance_2p0( model: CommonVideoModel::Seedance2p0, provider: Provider::Artcraft, prompt: request.prompt.as_deref(), + negative_prompt: None, start_frame, end_frame, reference_images, @@ -41,6 +42,7 @@ pub(super) async fn handle_artcraft_seedance_2p0( aspect_ratio: request.aspect_ratio, duration_seconds: request.duration_seconds, video_batch_count: request.video_batch_count, + generate_audio: None, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade, idempotency_token: None, }; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video.rs index 9200e9f961..8e9496b89a 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video.rs @@ -9,7 +9,6 @@ use crate::core::commands::enqueue::image_to_video::artcraft::handle_artcraft_vi use crate::core::commands::enqueue::image_to_video::artcraft::handle_artcraft_seedance_1_lite::handle_artcraft_seedance_1_lite; use crate::core::events::generation_events::common::GenerationModel; use artcraft_router::api::common_video_model::CommonVideoModel; -use crate::core::commands::enqueue::image_to_video::artcraft::handle_artcraft_seedance_1p5_pro::handle_artcraft_seedance_1p5_pro; use crate::core::commands::enqueue::image_to_video::artcraft::handle_artcraft_seedance_2p0::handle_artcraft_seedance_2p0; use crate::core::commands::enqueue::image_to_video::artcraft::handle_artcraft_sora2::handle_artcraft_sora2; use crate::core::commands::enqueue::image_to_video::artcraft::handle_artcraft_sora2_pro::handle_artcraft_sora2_pro; @@ -46,7 +45,7 @@ pub async fn handle_video_artcraft( Some(VideoModel::Kling3p0Standard) => handle_artcraft_video_via_router(request, app_env_configs, &creds, CommonVideoModel::Kling3p0Standard, GenerationModel::Kling3p0Standard).await, Some(VideoModel::Kling3p0Pro) => handle_artcraft_video_via_router(request, app_env_configs, &creds, CommonVideoModel::Kling3p0Pro, GenerationModel::Kling3p0Pro).await, Some(VideoModel::Seedance10Lite) => handle_artcraft_seedance_1_lite(request, app_env_configs, &creds).await, - Some(VideoModel::Seedance1p5Pro) => handle_artcraft_seedance_1p5_pro(request, app_env_configs, &creds).await, + Some(VideoModel::Seedance1p5Pro) => handle_artcraft_video_via_router(request, app_env_configs, &creds, CommonVideoModel::Seedance1p5Pro, GenerationModel::Seedance1p5Pro).await, Some(VideoModel::Seedance2p0) => handle_artcraft_seedance_2p0(request, app_env_configs, &creds).await, Some(VideoModel::Sora2) => handle_artcraft_sora2(request, app_env_configs, &creds).await, Some(VideoModel::Sora2Pro) => handle_artcraft_sora2_pro(request, app_env_configs, &creds).await, diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video_via_router.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video_via_router.rs index f47bed464d..8ebf109c74 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video_via_router.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/handle_artcraft_video_via_router.rs @@ -34,6 +34,7 @@ pub(super) async fn handle_artcraft_video_via_router( model, provider: Provider::Artcraft, prompt: request.prompt.as_deref(), + negative_prompt: None, start_frame, end_frame, reference_images: None, @@ -41,6 +42,7 @@ pub(super) async fn handle_artcraft_video_via_router( aspect_ratio: request.aspect_ratio, duration_seconds: request.duration_seconds, video_batch_count: request.video_batch_count, + generate_audio: request.generate_audio, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade, idempotency_token: None, }; diff --git a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/mod.rs b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/mod.rs index b35410abf7..8e87cf4f2e 100644 --- a/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/mod.rs +++ b/crates/desktop/artcraft/src/core/commands/enqueue/image_to_video/artcraft/mod.rs @@ -6,7 +6,6 @@ mod handle_artcraft_kling_2p5_turbo_pro; mod handle_artcraft_kling_2p6_pro; mod handle_artcraft_video_via_router; mod handle_artcraft_seedance_1_lite; -mod handle_artcraft_seedance_1p5_pro; mod handle_artcraft_seedance_2p0; mod handle_artcraft_sora2; mod handle_artcraft_sora2_pro; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/cost_estimate/video/estimate_video_cost_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/cost_estimate/video/estimate_video_cost_handler.rs index 0e227ad4ac..806699c1d4 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/cost_estimate/video/estimate_video_cost_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/cost_estimate/video/estimate_video_cost_handler.rs @@ -42,6 +42,7 @@ pub async fn estimate_video_cost_handler( model: router_model, provider: router_provider, prompt: None, + negative_prompt: None, start_frame: None, end_frame: None, reference_images: None, @@ -49,6 +50,7 @@ pub async fn estimate_video_cost_handler( aspect_ratio: router_aspect_ratio, duration_seconds: request.duration_seconds, video_batch_count: request.video_batch_count, + generate_audio: request.generate_audio, request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayLessDowngrade, idempotency_token: None, }; 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 40927ac93d..daf4b4ad27 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 @@ -200,6 +200,9 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler( let apriori_job_token = InferenceJobToken::generate(); + // Most people will want audio + let generate_audio = request.generate_audio.unwrap_or(true); + let fal_result; if let Some(start_frame_url) = maybe_start_frame_image_url { @@ -212,7 +215,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler( prompt: request.prompt.as_deref().unwrap_or("").to_string(), image_url: start_frame_url, end_image_url: maybe_end_frame_image_url, - generate_audio: request.generate_audio, + generate_audio: Some(generate_audio), negative_prompt: request.negative_prompt.clone(), duration: Some(duration), aspect_ratio, @@ -249,7 +252,7 @@ pub async fn kling_3p0_pro_multi_function_video_gen_handler( let args = EnqueueKling3p0ProTextToVideoArgs { prompt: request.prompt.as_deref().unwrap_or("").to_string(), negative_prompt: request.negative_prompt.clone(), - generate_audio: request.generate_audio, + generate_audio: Some(generate_audio), duration: Some(duration), aspect_ratio: Some(aspect_ratio), shot_type: None, 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 3298e561a1..f4d65b754a 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 @@ -200,6 +200,9 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler( let apriori_job_token = InferenceJobToken::generate(); + // Most people will want audio + let generate_audio = request.generate_audio.unwrap_or(true); + let fal_result; if let Some(start_frame_url) = maybe_start_frame_image_url { @@ -212,7 +215,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler( prompt: request.prompt.as_deref().unwrap_or("").to_string(), image_url: start_frame_url, end_image_url: maybe_end_frame_image_url, - generate_audio: request.generate_audio, + generate_audio: Some(generate_audio), negative_prompt: request.negative_prompt.clone(), duration: Some(duration), aspect_ratio, @@ -249,7 +252,7 @@ pub async fn kling_3p0_standard_multi_function_video_gen_handler( let args = EnqueueKling3p0StandardTextToVideoArgs { prompt: request.prompt.as_deref().unwrap_or("").to_string(), negative_prompt: request.negative_prompt.clone(), - generate_audio: request.generate_audio, + generate_audio: Some(generate_audio), duration: Some(duration), aspect_ratio: Some(aspect_ratio), shot_type: None, diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen_handler.rs index 974f54c456..2b34dad811 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_1p5_pro_multi_function_video_gen_handler.rs @@ -120,6 +120,8 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler( server_state: web::Data> ) -> Result, CommonWebError> { + info!("Request: {:?}", request); + payments_error_test(&request.prompt.as_deref().unwrap_or(""))?; let mut mysql_connection = server_state.mysql_pool @@ -204,6 +206,9 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler( let apriori_job_token = InferenceJobToken::generate(); + // Most people will want audio + let generate_audio = request.generate_audio.unwrap_or(true); + let fal_result; if let Some(image_url) = maybe_image_url { @@ -220,6 +225,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler( duration: Some(duration), aspect_ratio: Some(aspect_ratio), resolution: Some(resolution), + generate_audio: Some(generate_audio), webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; @@ -254,6 +260,7 @@ pub async fn seedance_1p5_pro_multi_function_video_gen_handler( duration: Some(duration), aspect_ratio: Some(aspect_ratio), resolution: Some(resolution), + generate_audio: Some(generate_audio), webhook_url: &server_state.fal.webhook_url, api_key: &server_state.fal.api_key, }; diff --git a/frontend/libs/components/pricing-modal/src/lib/useVideoCostEstimate.ts b/frontend/libs/components/pricing-modal/src/lib/useVideoCostEstimate.ts index 66731c8d0f..074c914ee8 100644 --- a/frontend/libs/components/pricing-modal/src/lib/useVideoCostEstimate.ts +++ b/frontend/libs/components/pricing-modal/src/lib/useVideoCostEstimate.ts @@ -29,6 +29,7 @@ export function useVideoCostEstimate( const inputMode = usePromptVideoStore((s) => s.inputMode); const referenceImages = usePromptVideoStore((s) => s.referenceImages); const endFrameImage = usePromptVideoStore((s) => s.endFrameImage); + const generateWithSound = usePromptVideoStore((s) => s.generateWithSound); useEffect(() => { if (activePage !== ModelPage.ImageToVideo || !selectedModel) { @@ -65,6 +66,7 @@ export function useVideoCostEstimate( generation_mode: generationMode, aspect_ratio: commonAspectRatio ?? undefined, duration_seconds: duration ?? undefined, + generate_audio: generateWithSound, }) .then((result) => { if (isEstimateVideoCostSuccess(result)) { @@ -89,6 +91,7 @@ export function useVideoCostEstimate( inputMode, referenceImages.length, !!endFrameImage, + generateWithSound, ]); return { isLoading }; diff --git a/frontend/libs/model-list/src/lib/lists/VideoModels.ts b/frontend/libs/model-list/src/lib/lists/VideoModels.ts index c90d000528..a905d4f57f 100644 --- a/frontend/libs/model-list/src/lib/lists/VideoModels.ts +++ b/frontend/libs/model-list/src/lib/lists/VideoModels.ts @@ -144,7 +144,7 @@ export const VIDEO_MODELS: VideoModel[] = [ durationOptions: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], defaultDuration: 5, progressBarTime: 100000, - generateWithSound: false, + generateWithSound: true, }), new VideoModel({ id: "kling_3p0_pro", @@ -179,7 +179,7 @@ export const VIDEO_MODELS: VideoModel[] = [ durationOptions: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], defaultDuration: 5, progressBarTime: 100000, - generateWithSound: false, + generateWithSound: true, }), new VideoModel({ id: "seedance_1_0_lite", @@ -244,6 +244,7 @@ export const VIDEO_MODELS: VideoModel[] = [ resolutionOptions: ["480p", "720p", "1080p"], defaultResolution: "720p", supportsSystemPrompt: false, + generateWithSound: true, progressBarTime: 100000, }), new VideoModel({ diff --git a/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateVideoCost.ts b/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateVideoCost.ts index 0afb920709..ee3af5a022 100644 --- a/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateVideoCost.ts +++ b/frontend/libs/tauri-api/src/lib/cost_estimate/EstimateVideoCost.ts @@ -16,6 +16,7 @@ export interface EstimateVideoCostRequest { resolution?: CommonVideoResolution; duration_seconds?: number; video_batch_count?: number; + generate_audio?: boolean; } export interface EstimateVideoCostPayload {