From e536a78b1ebba41bcfdc1d480070a81acdfbaace Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 17 Apr 2026 01:38:26 -0400 Subject: [PATCH] rename variant, aspect ratio --- ...stimate_video_cost_artcraft_seedance2p0.rs | 12 +- ...te_video_cost_artcraft_seedance2p0_fast.rs | 12 +- ...ate_video_cost_seedance2pro_seedance2p0.rs | 6 +- ...ideo_cost_seedance2pro_seedance2p0_fast.rs | 6 +- .../execute_seedance2pro_seedance2p0.rs | 4 +- .../execute_seedance2pro_seedance2p0_fast.rs | 4 +- ...generate_video_seedance2pro_seedance2p0.rs | 48 ++-- ...ate_video_seedance2pro_seedance2p0_fast.rs | 48 ++-- .../requests/generate_video/generate_video.rs | 216 +++++++++--------- ...ce_2p0_multi_function_video_gen_handler.rs | 34 +-- .../generate/video/execute_generation.rs | 35 ++- .../tests/kinovi/test_kinovi_seedance_2p0.rs | 93 ++++++++ 12 files changed, 316 insertions(+), 202 deletions(-) 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 a0cb54c236..19519f350d 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 @@ -1,6 +1,6 @@ use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::Seedance2p0BatchCount; use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; -use seedance2pro_client::requests::generate_video::generate_video::{BatchCount, GenerateVideoArgs, ModelType, Resolution}; +use seedance2pro_client::requests::generate_video::generate_video::{KinoviBatchCount, GenerateVideoArgs, KinoviModelType, KinoviResolution}; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::PlanArtcraftSeedance2p0; use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; @@ -11,9 +11,9 @@ pub (crate) fn estimate_video_cost_artcraft_seedance2p0( let duration_seconds = plan.duration_seconds.unwrap_or(5).clamp(4, 15); let batch_count = match plan.batch_count { - Seedance2p0BatchCount::One => BatchCount::One, - Seedance2p0BatchCount::Two => BatchCount::Two, - Seedance2p0BatchCount::Four => BatchCount::Four, + Seedance2p0BatchCount::One => KinoviBatchCount::One, + Seedance2p0BatchCount::Two => KinoviBatchCount::Two, + Seedance2p0BatchCount::Four => KinoviBatchCount::Four, }; // TODO: Make a better client that doesn't require this. @@ -22,9 +22,9 @@ pub (crate) fn estimate_video_cost_artcraft_seedance2p0( let args = GenerateVideoArgs { session: &dummy_session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: String::new(), - resolution: Resolution::Square1x1, + resolution: KinoviResolution::Square1x1, duration_seconds, batch_count, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0_fast.rs index 9a2df79bcf..90918a63f5 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0_fast.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/artcraft/estimate_video_cost_artcraft_seedance2p0_fast.rs @@ -1,6 +1,6 @@ use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::Seedance2p0BatchCount; use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; -use seedance2pro_client::requests::generate_video::generate_video::{BatchCount, GenerateVideoArgs, ModelType, Resolution}; +use seedance2pro_client::requests::generate_video::generate_video::{KinoviBatchCount, GenerateVideoArgs, KinoviModelType, KinoviResolution}; use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::PlanArtcraftSeedance2p0; use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; @@ -11,18 +11,18 @@ pub(crate) fn estimate_video_cost_artcraft_seedance2p0_fast( let duration_seconds = plan.duration_seconds.unwrap_or(5).clamp(4, 15); let batch_count = match plan.batch_count { - Seedance2p0BatchCount::One => BatchCount::One, - Seedance2p0BatchCount::Two => BatchCount::Two, - Seedance2p0BatchCount::Four => BatchCount::Four, + Seedance2p0BatchCount::One => KinoviBatchCount::One, + Seedance2p0BatchCount::Two => KinoviBatchCount::Two, + Seedance2p0BatchCount::Four => KinoviBatchCount::Four, }; let dummy_session = Seedance2ProSession::from_cookies_string(String::new()); let args = GenerateVideoArgs { session: &dummy_session, - model_type: ModelType::Seedance2Fast, // <-- Fast, not Pro + model_type: KinoviModelType::Seedance2Fast, // <-- Fast, not Pro prompt: String::new(), - resolution: Resolution::Square1x1, + resolution: KinoviResolution::Square1x1, duration_seconds, batch_count, start_frame_url: None, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0.rs index 29e2fbc6ee..4bfcdfbc7e 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0.rs @@ -1,5 +1,5 @@ use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; -use seedance2pro_client::requests::generate_video::generate_video::{GenerateVideoArgs, ModelType, Resolution}; +use seedance2pro_client::requests::generate_video::generate_video::{GenerateVideoArgs, KinoviModelType, KinoviResolution}; use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::PlanSeedance2proSeedance2p0; use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; @@ -12,9 +12,9 @@ pub(crate) fn estimate_video_cost_seedance2pro_seedance2p0( let args = GenerateVideoArgs { session: &dummy_session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: String::new(), - resolution: Resolution::Square1x1, // Resolution does not affect cost + resolution: KinoviResolution::Square1x1, // Resolution does not affect cost duration_seconds: plan.duration_seconds, batch_count: plan.batch_count, start_frame_url: None, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs index 95f412f491..6723e7dade 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/cost/seedance2pro/estimate_video_cost_seedance2pro_seedance2p0_fast.rs @@ -1,5 +1,5 @@ use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; -use seedance2pro_client::requests::generate_video::generate_video::{GenerateVideoArgs, ModelType, Resolution}; +use seedance2pro_client::requests::generate_video::generate_video::{GenerateVideoArgs, KinoviModelType, KinoviResolution}; use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0_fast::PlanSeedance2proSeedance2p0Fast; use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate; @@ -12,9 +12,9 @@ pub(crate) fn estimate_video_cost_seedance2pro_seedance2p0_fast( let args = GenerateVideoArgs { session: &dummy_session, - model_type: ModelType::Seedance2Fast, // <-- Fast, not Pro + model_type: KinoviModelType::Seedance2Fast, // <-- Fast, not Pro prompt: String::new(), - resolution: Resolution::Square1x1, // Resolution does not affect cost + resolution: KinoviResolution::Square1x1, // Resolution does not affect cost duration_seconds: plan.duration_seconds, batch_count: plan.batch_count, start_frame_url: None, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs index 43702873ee..ff972afb15 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0.rs @@ -7,7 +7,7 @@ use crate::generate::generate_video::generate_video_response::{ use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::PlanSeedance2proSeedance2p0; use crate::utils::download_file::download_file; use seedance2pro_client::requests::generate_video::generate_video::{ - generate_video, GenerateVideoArgs, ModelType, + generate_video, GenerateVideoArgs, KinoviModelType, }; use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{ prepare_file_upload, PrepareFileUploadArgs, @@ -34,7 +34,7 @@ pub async fn execute_seedance2pro_seedance2p0( let args = GenerateVideoArgs { session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: plan.prompt.clone().unwrap_or_default(), resolution: plan.resolution, duration_seconds: plan.duration_seconds, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs index 46103fe143..589b3298b0 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/execute/seedance2pro/execute_seedance2pro_seedance2p0_fast.rs @@ -9,7 +9,7 @@ use crate::generate::generate_video::execute::seedance2pro::execute_seedance2pro upload_optional_url, upload_optional_url_list, }; use seedance2pro_client::requests::generate_video::generate_video::{ - generate_video, GenerateVideoArgs, ModelType, + generate_video, GenerateVideoArgs, KinoviModelType, }; /// Execute Seedance 2.0 Fast via the Seedance2Pro/Kinovi provider. @@ -31,7 +31,7 @@ pub async fn execute_seedance2pro_seedance2p0_fast( let args = GenerateVideoArgs { session, - model_type: ModelType::Seedance2Fast, // <-- Fast, not Pro + model_type: KinoviModelType::Seedance2Fast, // <-- Fast, not Pro prompt: plan.prompt.clone().unwrap_or_default(), resolution: plan.resolution, duration_seconds: plan.duration_seconds, diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0.rs index 36ef34af75..19b5c9824c 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0.rs @@ -8,7 +8,7 @@ use crate::errors::artcraft_router_error::ArtcraftRouterError; use crate::errors::client_error::ClientError; use crate::generate::generate_video::generate_video_request::GenerateVideoRequest; use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; -use seedance2pro_client::requests::generate_video::generate_video::{BatchCount, Resolution}; +use seedance2pro_client::requests::generate_video::generate_video::{KinoviBatchCount, KinoviResolution}; #[derive(Debug, Clone)] pub struct PlanSeedance2proSeedance2p0 { @@ -18,9 +18,9 @@ pub struct PlanSeedance2proSeedance2p0 { pub reference_image_urls: Option>, pub reference_video_urls: Option>, pub reference_audio_urls: Option>, - pub resolution: Resolution, + pub resolution: KinoviResolution, pub duration_seconds: u8, - pub batch_count: BatchCount, + pub batch_count: KinoviBatchCount, } pub fn plan_generate_video_seedance2pro_seedance2p0( @@ -107,26 +107,26 @@ fn resolve_audio_list_ref_urls( fn plan_resolution( aspect_ratio: Option, strategy: RequestMismatchMitigationStrategy, -) -> Result { +) -> Result { match aspect_ratio { // No preference or auto — default to landscape None | Some(CommonAspectRatio::Auto) | Some(CommonAspectRatio::Auto2k) - | Some(CommonAspectRatio::Auto4k) => Ok(Resolution::Landscape16x9), + | Some(CommonAspectRatio::Auto4k) => Ok(KinoviResolution::Landscape16x9), // Direct mappings Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => { - Ok(Resolution::Landscape16x9) + Ok(KinoviResolution::Landscape16x9) } Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => { - Ok(Resolution::Portrait9x16) + Ok(KinoviResolution::Portrait9x16) } Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => { - Ok(Resolution::Square1x1) + Ok(KinoviResolution::Square1x1) } - Some(CommonAspectRatio::WideFourByThree) => Ok(Resolution::Standard4x3), - Some(CommonAspectRatio::TallThreeByFour) => Ok(Resolution::Portrait3x4), + Some(CommonAspectRatio::WideFourByThree) => Ok(KinoviResolution::Standard4x3), + Some(CommonAspectRatio::TallThreeByFour) => Ok(KinoviResolution::Portrait3x4), // Mismatches — apply strategy Some(unsupported) => match strategy { @@ -145,15 +145,15 @@ fn plan_resolution( } /// Pick the nearest supported resolution by AR value (width / height). -fn nearest_resolution(aspect_ratio: CommonAspectRatio) -> Resolution { +fn nearest_resolution(aspect_ratio: CommonAspectRatio) -> KinoviResolution { match aspect_ratio { - CommonAspectRatio::WideFiveByFour => Resolution::Standard4x3, // 1.25, nearest 1.33 - CommonAspectRatio::WideThreeByTwo => Resolution::Standard4x3, // 1.50, nearest 1.33 - CommonAspectRatio::WideTwentyOneByNine => Resolution::Landscape16x9, // 2.33, nearest 1.78 - CommonAspectRatio::TallFourByFive => Resolution::Portrait3x4, // 0.80, nearest 0.75 - CommonAspectRatio::TallTwoByThree => Resolution::Portrait3x4, // 0.67, nearest 0.75 - CommonAspectRatio::TallNineByTwentyOne => Resolution::Portrait9x16, // 0.43, nearest 0.56 - _ => Resolution::Square1x1, + CommonAspectRatio::WideFiveByFour => KinoviResolution::Standard4x3, // 1.25, nearest 1.33 + CommonAspectRatio::WideThreeByTwo => KinoviResolution::Standard4x3, // 1.50, nearest 1.33 + CommonAspectRatio::WideTwentyOneByNine => KinoviResolution::Landscape16x9, // 2.33, nearest 1.78 + CommonAspectRatio::TallFourByFive => KinoviResolution::Portrait3x4, // 0.80, nearest 0.75 + CommonAspectRatio::TallTwoByThree => KinoviResolution::Portrait3x4, // 0.67, nearest 0.75 + CommonAspectRatio::TallNineByTwentyOne => KinoviResolution::Portrait9x16, // 0.43, nearest 0.56 + _ => KinoviResolution::Square1x1, } } @@ -161,13 +161,13 @@ fn nearest_resolution(aspect_ratio: CommonAspectRatio) -> Resolution { fn plan_batch_count( video_batch_count: Option, strategy: RequestMismatchMitigationStrategy, -) -> Result { +) -> Result { let count = video_batch_count.unwrap_or(1); match count { 0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)), - 1 => Ok(BatchCount::One), - 2 => Ok(BatchCount::Two), - 4 => Ok(BatchCount::Four), + 1 => Ok(KinoviBatchCount::One), + 2 => Ok(KinoviBatchCount::Two), + 4 => Ok(KinoviBatchCount::Four), _ => match strategy { RequestMismatchMitigationStrategy::ErrorOut => { Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { @@ -176,10 +176,10 @@ fn plan_batch_count( })) } RequestMismatchMitigationStrategy::PayMoreUpgrade => { - Ok(if count < 4 { BatchCount::Four } else { BatchCount::Four }) + Ok(if count < 4 { KinoviBatchCount::Four } else { KinoviBatchCount::Four }) } RequestMismatchMitigationStrategy::PayLessDowngrade => { - Ok(if count < 4 { BatchCount::Two } else { BatchCount::Four }) + Ok(if count < 4 { KinoviBatchCount::Two } else { KinoviBatchCount::Four }) } }, } diff --git a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs index e6cb1a9cab..392c24c057 100644 --- a/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs +++ b/crates/api_clients/artcraft/artcraft_router/src/generate/generate_video/plan/seedance2pro/plan_generate_video_seedance2pro_seedance2p0_fast.rs @@ -8,7 +8,7 @@ use crate::errors::artcraft_router_error::ArtcraftRouterError; use crate::errors::client_error::ClientError; use crate::generate::generate_video::generate_video_request::GenerateVideoRequest; use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan; -use seedance2pro_client::requests::generate_video::generate_video::{BatchCount, Resolution}; +use seedance2pro_client::requests::generate_video::generate_video::{KinoviBatchCount, KinoviResolution}; #[derive(Debug, Clone)] pub struct PlanSeedance2proSeedance2p0Fast { @@ -18,9 +18,9 @@ pub struct PlanSeedance2proSeedance2p0Fast { pub reference_image_urls: Option>, pub reference_video_urls: Option>, pub reference_audio_urls: Option>, - pub resolution: Resolution, + pub resolution: KinoviResolution, pub duration_seconds: u8, - pub batch_count: BatchCount, + pub batch_count: KinoviBatchCount, } pub fn plan_generate_video_seedance2pro_seedance2p0_fast( @@ -102,24 +102,24 @@ fn resolve_audio_list_ref_urls( fn plan_resolution( aspect_ratio: Option, strategy: RequestMismatchMitigationStrategy, -) -> Result { +) -> Result { match aspect_ratio { None | Some(CommonAspectRatio::Auto) | Some(CommonAspectRatio::Auto2k) - | Some(CommonAspectRatio::Auto4k) => Ok(Resolution::Landscape16x9), + | Some(CommonAspectRatio::Auto4k) => Ok(KinoviResolution::Landscape16x9), Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => { - Ok(Resolution::Landscape16x9) + Ok(KinoviResolution::Landscape16x9) } Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => { - Ok(Resolution::Portrait9x16) + Ok(KinoviResolution::Portrait9x16) } Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => { - Ok(Resolution::Square1x1) + Ok(KinoviResolution::Square1x1) } - Some(CommonAspectRatio::WideFourByThree) => Ok(Resolution::Standard4x3), - Some(CommonAspectRatio::TallThreeByFour) => Ok(Resolution::Portrait3x4), + Some(CommonAspectRatio::WideFourByThree) => Ok(KinoviResolution::Standard4x3), + Some(CommonAspectRatio::TallThreeByFour) => Ok(KinoviResolution::Portrait3x4), Some(unsupported) => match strategy { RequestMismatchMitigationStrategy::ErrorOut => { @@ -136,28 +136,28 @@ fn plan_resolution( } } -fn nearest_resolution(aspect_ratio: CommonAspectRatio) -> Resolution { +fn nearest_resolution(aspect_ratio: CommonAspectRatio) -> KinoviResolution { match aspect_ratio { - CommonAspectRatio::WideFiveByFour => Resolution::Standard4x3, - CommonAspectRatio::WideThreeByTwo => Resolution::Standard4x3, - CommonAspectRatio::WideTwentyOneByNine => Resolution::Landscape16x9, - CommonAspectRatio::TallFourByFive => Resolution::Portrait3x4, - CommonAspectRatio::TallTwoByThree => Resolution::Portrait3x4, - CommonAspectRatio::TallNineByTwentyOne => Resolution::Portrait9x16, - _ => Resolution::Square1x1, + CommonAspectRatio::WideFiveByFour => KinoviResolution::Standard4x3, + CommonAspectRatio::WideThreeByTwo => KinoviResolution::Standard4x3, + CommonAspectRatio::WideTwentyOneByNine => KinoviResolution::Landscape16x9, + CommonAspectRatio::TallFourByFive => KinoviResolution::Portrait3x4, + CommonAspectRatio::TallTwoByThree => KinoviResolution::Portrait3x4, + CommonAspectRatio::TallNineByTwentyOne => KinoviResolution::Portrait9x16, + _ => KinoviResolution::Square1x1, } } fn plan_batch_count( video_batch_count: Option, strategy: RequestMismatchMitigationStrategy, -) -> Result { +) -> Result { let count = video_batch_count.unwrap_or(1); match count { 0 => Err(ArtcraftRouterError::Client(ClientError::UserRequestedZeroGenerations)), - 1 => Ok(BatchCount::One), - 2 => Ok(BatchCount::Two), - 4 => Ok(BatchCount::Four), + 1 => Ok(KinoviBatchCount::One), + 2 => Ok(KinoviBatchCount::Two), + 4 => Ok(KinoviBatchCount::Four), _ => match strategy { RequestMismatchMitigationStrategy::ErrorOut => { Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { @@ -166,10 +166,10 @@ fn plan_batch_count( })) } RequestMismatchMitigationStrategy::PayMoreUpgrade => { - Ok(if count < 4 { BatchCount::Four } else { BatchCount::Four }) + Ok(if count < 4 { KinoviBatchCount::Four } else { KinoviBatchCount::Four }) } RequestMismatchMitigationStrategy::PayLessDowngrade => { - Ok(if count < 4 { BatchCount::Two } else { BatchCount::Four }) + Ok(if count < 4 { KinoviBatchCount::Two } else { KinoviBatchCount::Four }) } }, } diff --git a/crates/api_clients/seedance2pro_client/src/requests/generate_video/generate_video.rs b/crates/api_clients/seedance2pro_client/src/requests/generate_video/generate_video.rs index 1ea714e38c..c5427e3240 100644 --- a/crates/api_clients/seedance2pro_client/src/requests/generate_video/generate_video.rs +++ b/crates/api_clients/seedance2pro_client/src/requests/generate_video/generate_video.rs @@ -17,16 +17,16 @@ pub struct GenerateVideoArgs<'a> { pub session: &'a Seedance2ProSession, /// Seedance 2.0 Pro vs Fast - pub model_type: ModelType, + pub model_type: KinoviModelType, pub prompt: String, - pub resolution: Resolution, + pub resolution: KinoviResolution, /// Duration in seconds (4–15). pub duration_seconds: u8, - pub batch_count: BatchCount, + pub batch_count: KinoviBatchCount, /// Optional start frame image URL (keyframe mode). pub start_frame_url: Option, @@ -91,14 +91,14 @@ impl GenerateVideoArgs<'_> { /// - Batch 1 = 1×, Batch 2 = 2×, Batch 4 = 4× pub fn estimate_credits(&self) -> u32 { let credits_per_second = match self.model_type { - ModelType::Seedance2Pro => 40, // 40 credits per sec - ModelType::Seedance2Fast => 28, // 28 credits per sec + KinoviModelType::Seedance2Pro => 40, // 40 credits per sec + KinoviModelType::Seedance2Fast => 28, // 28 credits per sec }; let per_video = u32::from(self.duration_seconds) * credits_per_second; let batch_multiplier = match self.batch_count { - BatchCount::One => 1, - BatchCount::Two => 2, - BatchCount::Four => 4, + KinoviBatchCount::One => 1, + KinoviBatchCount::Two => 2, + KinoviBatchCount::Four => 4, }; per_video * batch_multiplier } @@ -107,9 +107,9 @@ impl GenerateVideoArgs<'_> { let credits = self.estimate_credits() as f64; let credits_per_dollar = match self.model_type { // Legacy pricing from seedance2-pro.com: 25,000 credits for $99.99 (~250 credits/$1) - ModelType::Seedance2Pro => 250.0, + KinoviModelType::Seedance2Pro => 250.0, // Seedance 2 Fast pricing: 22,000 credits for $99.99 (~220 credits/$1) - ModelType::Seedance2Fast => 220.0, + KinoviModelType::Seedance2Fast => 220.0, }; let cost = credits / credits_per_dollar * 100.0; cost.round() as u64 @@ -120,7 +120,7 @@ impl GenerateVideoArgs<'_> { /// Video resolution / aspect ratio. #[derive(Debug, Clone, Copy)] -pub enum Resolution { +pub enum KinoviResolution { /// 16:9 landscape (1280x720) Landscape16x9, /// 9:16 portrait (720x1280) @@ -133,7 +133,7 @@ pub enum Resolution { Portrait3x4, } -impl Resolution { +impl KinoviResolution { fn as_str(&self) -> &'static str { match self { Self::Landscape16x9 => "1280x720", @@ -147,13 +147,13 @@ impl Resolution { /// Number of videos to generate in a single request. #[derive(Debug, Clone, Copy)] -pub enum BatchCount { +pub enum KinoviBatchCount { One, Two, Four, } -impl BatchCount { +impl KinoviBatchCount { fn as_u8(&self) -> u8 { match self { Self::One => 1, @@ -165,14 +165,14 @@ impl BatchCount { /// The Seedance model variant to use. #[derive(Debug, Clone, Copy)] -pub enum ModelType { +pub enum KinoviModelType { /// Seedance 2.0 Pro (higher quality, slower). Seedance2Pro, /// Seedance 2.0 Fast (lower quality, faster). Seedance2Fast, } -impl ModelType { +impl KinoviModelType { fn as_api_str(&self) -> &'static str { match self { Self::Seedance2Pro => "seedance-20", @@ -364,14 +364,14 @@ mod tests { Seedance2ProSession::from_cookies_string(String::new()) } - fn args_with(model_type: ModelType, duration_seconds: u8, batch_count: BatchCount) -> GenerateVideoArgs<'static> { + fn args_with(model_type: KinoviModelType, duration_seconds: u8, batch_count: KinoviBatchCount) -> GenerateVideoArgs<'static> { // Safety: the dummy session is leaked so the reference is 'static for test purposes. let session = Box::leak(Box::new(dummy_session())); GenerateVideoArgs { session, model_type, prompt: String::new(), - resolution: Resolution::Square1x1, + resolution: KinoviResolution::Square1x1, duration_seconds, batch_count, start_frame_url: None, @@ -385,97 +385,97 @@ mod tests { } } - fn pro(duration_seconds: u8, batch_count: BatchCount) -> GenerateVideoArgs<'static> { - args_with(ModelType::Seedance2Pro, duration_seconds, batch_count) + fn pro(duration_seconds: u8, batch_count: KinoviBatchCount) -> GenerateVideoArgs<'static> { + args_with(KinoviModelType::Seedance2Pro, duration_seconds, batch_count) } - fn fast(duration_seconds: u8, batch_count: BatchCount) -> GenerateVideoArgs<'static> { - args_with(ModelType::Seedance2Fast, duration_seconds, batch_count) + fn fast(duration_seconds: u8, batch_count: KinoviBatchCount) -> GenerateVideoArgs<'static> { + args_with(KinoviModelType::Seedance2Fast, duration_seconds, batch_count) } #[test] fn test_estimate_credits_pro() { // 40 credits per second, batch 1 - assert_eq!(pro(4, BatchCount::One).estimate_credits(), 160); - assert_eq!(pro(5, BatchCount::One).estimate_credits(), 200); - assert_eq!(pro(6, BatchCount::One).estimate_credits(), 240); - assert_eq!(pro(7, BatchCount::One).estimate_credits(), 280); - assert_eq!(pro(15, BatchCount::One).estimate_credits(), 600); + assert_eq!(pro(4, KinoviBatchCount::One).estimate_credits(), 160); + assert_eq!(pro(5, KinoviBatchCount::One).estimate_credits(), 200); + assert_eq!(pro(6, KinoviBatchCount::One).estimate_credits(), 240); + assert_eq!(pro(7, KinoviBatchCount::One).estimate_credits(), 280); + assert_eq!(pro(15, KinoviBatchCount::One).estimate_credits(), 600); // Batch 2 = 2× - assert_eq!(pro(4, BatchCount::Two).estimate_credits(), 320); - assert_eq!(pro(5, BatchCount::Two).estimate_credits(), 400); - assert_eq!(pro(15, BatchCount::Two).estimate_credits(), 1200); + assert_eq!(pro(4, KinoviBatchCount::Two).estimate_credits(), 320); + assert_eq!(pro(5, KinoviBatchCount::Two).estimate_credits(), 400); + assert_eq!(pro(15, KinoviBatchCount::Two).estimate_credits(), 1200); // Batch 4 = 4× - assert_eq!(pro(4, BatchCount::Four).estimate_credits(), 640); - assert_eq!(pro(5, BatchCount::Four).estimate_credits(), 800); - assert_eq!(pro(15, BatchCount::Four).estimate_credits(), 2400); + assert_eq!(pro(4, KinoviBatchCount::Four).estimate_credits(), 640); + assert_eq!(pro(5, KinoviBatchCount::Four).estimate_credits(), 800); + assert_eq!(pro(15, KinoviBatchCount::Four).estimate_credits(), 2400); } #[test] fn test_estimate_credits_fast() { // 28 credits per second, batch 1 - assert_eq!(fast(4, BatchCount::One).estimate_credits(), 112); - assert_eq!(fast(5, BatchCount::One).estimate_credits(), 140); - assert_eq!(fast(6, BatchCount::One).estimate_credits(), 168); - assert_eq!(fast(7, BatchCount::One).estimate_credits(), 196); - assert_eq!(fast(15, BatchCount::One).estimate_credits(), 420); + assert_eq!(fast(4, KinoviBatchCount::One).estimate_credits(), 112); + assert_eq!(fast(5, KinoviBatchCount::One).estimate_credits(), 140); + assert_eq!(fast(6, KinoviBatchCount::One).estimate_credits(), 168); + assert_eq!(fast(7, KinoviBatchCount::One).estimate_credits(), 196); + assert_eq!(fast(15, KinoviBatchCount::One).estimate_credits(), 420); // Batch 2 = 2× - assert_eq!(fast(4, BatchCount::Two).estimate_credits(), 224); - assert_eq!(fast(5, BatchCount::Two).estimate_credits(), 280); - assert_eq!(fast(15, BatchCount::Two).estimate_credits(), 840); + assert_eq!(fast(4, KinoviBatchCount::Two).estimate_credits(), 224); + assert_eq!(fast(5, KinoviBatchCount::Two).estimate_credits(), 280); + assert_eq!(fast(15, KinoviBatchCount::Two).estimate_credits(), 840); // Batch 4 = 4× - assert_eq!(fast(4, BatchCount::Four).estimate_credits(), 448); - assert_eq!(fast(5, BatchCount::Four).estimate_credits(), 560); - assert_eq!(fast(15, BatchCount::Four).estimate_credits(), 1680); + assert_eq!(fast(4, KinoviBatchCount::Four).estimate_credits(), 448); + assert_eq!(fast(5, KinoviBatchCount::Four).estimate_credits(), 560); + assert_eq!(fast(15, KinoviBatchCount::Four).estimate_credits(), 1680); } #[test] fn test_estimate_cost_usd_cents_pro() { // 40 credits per second, batch 1 - assert_eq!(pro(4, BatchCount::One).estimate_cost_in_usd_cents(), 64); - assert_eq!(pro(5, BatchCount::One).estimate_cost_in_usd_cents(), 80); - assert_eq!(pro(6, BatchCount::One).estimate_cost_in_usd_cents(), 96); - assert_eq!(pro(7, BatchCount::One).estimate_cost_in_usd_cents(), 112); - assert_eq!(pro(15, BatchCount::One).estimate_cost_in_usd_cents(), 240); + assert_eq!(pro(4, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 64); + assert_eq!(pro(5, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 80); + assert_eq!(pro(6, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 96); + assert_eq!(pro(7, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 112); + assert_eq!(pro(15, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 240); // Batch 2 = 2× - assert_eq!(pro(4, BatchCount::Two).estimate_cost_in_usd_cents(), 128); - assert_eq!(pro(5, BatchCount::Two).estimate_cost_in_usd_cents(), 160); - assert_eq!(pro(15, BatchCount::Two).estimate_cost_in_usd_cents(), 480); + assert_eq!(pro(4, KinoviBatchCount::Two).estimate_cost_in_usd_cents(), 128); + assert_eq!(pro(5, KinoviBatchCount::Two).estimate_cost_in_usd_cents(), 160); + assert_eq!(pro(15, KinoviBatchCount::Two).estimate_cost_in_usd_cents(), 480); // Batch 4 = 4× - assert_eq!(pro(4, BatchCount::Four).estimate_cost_in_usd_cents(), 256); - assert_eq!(pro(5, BatchCount::Four).estimate_cost_in_usd_cents(), 320); - assert_eq!(pro(15, BatchCount::Four).estimate_cost_in_usd_cents(), 960); + assert_eq!(pro(4, KinoviBatchCount::Four).estimate_cost_in_usd_cents(), 256); + assert_eq!(pro(5, KinoviBatchCount::Four).estimate_cost_in_usd_cents(), 320); + assert_eq!(pro(15, KinoviBatchCount::Four).estimate_cost_in_usd_cents(), 960); } #[test] fn test_estimate_cost_usd_cents_fast() { // 28 credits per second, 220 credits/$1 (22,000 credits for $99.99) // 28 * 4 = 112 credits => 112 / 220 * 100 = 50.9 => 51 cents - assert_eq!(fast(4, BatchCount::One).estimate_cost_in_usd_cents(), 51); + assert_eq!(fast(4, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 51); // 28 * 5 = 140 credits => 140 / 220 * 100 = 63.6 => 64 cents - assert_eq!(fast(5, BatchCount::One).estimate_cost_in_usd_cents(), 64); + assert_eq!(fast(5, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 64); // 28 * 6 = 168 credits => 168 / 220 * 100 = 76.4 => 76 cents - assert_eq!(fast(6, BatchCount::One).estimate_cost_in_usd_cents(), 76); + assert_eq!(fast(6, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 76); // 28 * 7 = 196 credits => 196 / 220 * 100 = 89.1 => 89 cents - assert_eq!(fast(7, BatchCount::One).estimate_cost_in_usd_cents(), 89); + assert_eq!(fast(7, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 89); // 28 * 15 = 420 credits => 420 / 220 * 100 = 190.9 => 191 cents - assert_eq!(fast(15, BatchCount::One).estimate_cost_in_usd_cents(), 191); + assert_eq!(fast(15, KinoviBatchCount::One).estimate_cost_in_usd_cents(), 191); // Batch 2 = 2× - assert_eq!(fast(4, BatchCount::Two).estimate_cost_in_usd_cents(), 102); - assert_eq!(fast(5, BatchCount::Two).estimate_cost_in_usd_cents(), 127); - assert_eq!(fast(15, BatchCount::Two).estimate_cost_in_usd_cents(), 382); + assert_eq!(fast(4, KinoviBatchCount::Two).estimate_cost_in_usd_cents(), 102); + assert_eq!(fast(5, KinoviBatchCount::Two).estimate_cost_in_usd_cents(), 127); + assert_eq!(fast(15, KinoviBatchCount::Two).estimate_cost_in_usd_cents(), 382); // Batch 4 = 4× - assert_eq!(fast(4, BatchCount::Four).estimate_cost_in_usd_cents(), 204); - assert_eq!(fast(5, BatchCount::Four).estimate_cost_in_usd_cents(), 255); - assert_eq!(fast(15, BatchCount::Four).estimate_cost_in_usd_cents(), 764); + assert_eq!(fast(4, KinoviBatchCount::Four).estimate_cost_in_usd_cents(), 204); + assert_eq!(fast(5, KinoviBatchCount::Four).estimate_cost_in_usd_cents(), 255); + assert_eq!(fast(15, KinoviBatchCount::Four).estimate_cost_in_usd_cents(), 764); } } @@ -494,11 +494,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "A corgi eating a cake in a fancy kitchen.".to_string(), - resolution: Resolution::Square1x1, + resolution: KinoviResolution::Square1x1, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, @@ -524,11 +524,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "A dog shakes the glasses off its head. The camera pans out as the shiba shakes. The shiba barks.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: Some("https://static.seedance2-pro.com/materials/20260219/1771496300184-fb32e08c.jpg".to_string()), end_frame_url: None, reference_image_urls: None, @@ -553,11 +553,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "The dog in @2 is in the office at @1 without the man. The office is dark and moonlight streams in through the windows. Particles of dust gleam in the moon beams. Suddenly, the dog jumps walks in front of the desk and barks.".to_string(), - resolution: Resolution::Standard4x3, + resolution: KinoviResolution::Standard4x3, duration_seconds: 10, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: Some(vec![ @@ -585,11 +585,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "Change the Video @video1 to night time.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, @@ -616,11 +616,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "Put the robot in @video1 next to the house in @image1".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: Some(vec![ @@ -673,11 +673,11 @@ mod tests { let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "Change @video1 to night time".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, @@ -725,11 +725,11 @@ mod tests { let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "The corgi dog watches the lake.".to_string(), - resolution: Resolution::Portrait9x16, + resolution: KinoviResolution::Portrait9x16, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: Some(upload_result.public_url), end_frame_url: None, reference_image_urls: None, @@ -777,11 +777,11 @@ mod tests { let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Fast, + model_type: KinoviModelType::Seedance2Fast, prompt: "A corgi dog runs along the lake shore, splashing water. Camera follows.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: Some(upload_result.public_url), end_frame_url: None, reference_image_urls: None, @@ -836,11 +836,11 @@ mod tests { let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Fast, + model_type: KinoviModelType::Seedance2Fast, prompt: "The dog in @1 is running through the scenery in @3 towards the building in @2. Golden hour lighting.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: Some(uploaded_urls), @@ -888,11 +888,11 @@ mod tests { let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Fast, + model_type: KinoviModelType::Seedance2Fast, prompt: "A fantasy forest with mushrooms glowing in the dark. Fireflies dance between the trees. A small character walks along a winding path.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, @@ -924,11 +924,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "@Steampunk Clown is juggling flaming torches in a circus tent.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, @@ -954,11 +954,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Fast, + model_type: KinoviModelType::Seedance2Fast, prompt: "@Mochi the female shiba inu is eating a cheese pizza while standing on the table".to_string(), - resolution: Resolution::Portrait9x16, + resolution: KinoviResolution::Portrait9x16, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, @@ -984,11 +984,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: "@Steampunk Clown is walking up to pet a dog on the couch.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: Some(vec![ @@ -1016,11 +1016,11 @@ mod tests { let session = test_session()?; let args = GenerateVideoArgs { session: &session, - model_type: ModelType::Seedance2Fast, + model_type: KinoviModelType::Seedance2Fast, prompt: "@Steampunk Clown and @Mochi are playing fetch in a sunny park.".to_string(), - resolution: Resolution::Landscape16x9, + resolution: KinoviResolution::Landscape16x9, duration_seconds: 5, - batch_count: BatchCount::One, + batch_count: KinoviBatchCount::One, start_frame_url: None, end_frame_url: None, reference_image_urls: None, diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs index fba9a65f85..f98925f8ad 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs @@ -39,7 +39,7 @@ use pager::notification::notification_details_builder::NotificationDetailsBuilde use pager::notification::notification_urgency::NotificationUrgency; use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; use seedance2pro_client::requests::generate_video::generate_video::{ - generate_video, BatchCount, GenerateVideoArgs, GenerateVideoResponse, ModelType, Resolution, + generate_video, KinoviBatchCount, GenerateVideoArgs, GenerateVideoResponse, KinoviModelType, KinoviResolution, }; use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{ prepare_file_upload, PrepareFileUploadArgs, @@ -458,32 +458,32 @@ pub async fn seedance_2p0_multi_function_video_gen_handler( // ======================== Helpers ======================== -fn map_resolution(aspect_ratio: Option) -> Resolution { +fn map_resolution(aspect_ratio: Option) -> KinoviResolution { match aspect_ratio { - Some(Seedance2p0AspectRatio::Landscape16x9) => Resolution::Landscape16x9, - Some(Seedance2p0AspectRatio::Portrait9x16) => Resolution::Portrait9x16, - Some(Seedance2p0AspectRatio::Square1x1) => Resolution::Square1x1, - Some(Seedance2p0AspectRatio::Standard4x3) => Resolution::Standard4x3, - Some(Seedance2p0AspectRatio::Portrait3x4) => Resolution::Portrait3x4, - None => Resolution::Landscape16x9, + Some(Seedance2p0AspectRatio::Landscape16x9) => KinoviResolution::Landscape16x9, + Some(Seedance2p0AspectRatio::Portrait9x16) => KinoviResolution::Portrait9x16, + Some(Seedance2p0AspectRatio::Square1x1) => KinoviResolution::Square1x1, + Some(Seedance2p0AspectRatio::Standard4x3) => KinoviResolution::Standard4x3, + Some(Seedance2p0AspectRatio::Portrait3x4) => KinoviResolution::Portrait3x4, + None => KinoviResolution::Landscape16x9, } } -fn map_batch_count(batch_count: Option) -> BatchCount { +fn map_batch_count(batch_count: Option) -> KinoviBatchCount { match batch_count { - Some(Seedance2p0BatchCount::One) | None => BatchCount::One, - Some(Seedance2p0BatchCount::Two) => BatchCount::Two, - Some(Seedance2p0BatchCount::Four) => BatchCount::Four, + Some(Seedance2p0BatchCount::One) | None => KinoviBatchCount::One, + Some(Seedance2p0BatchCount::Two) => KinoviBatchCount::Two, + Some(Seedance2p0BatchCount::Four) => KinoviBatchCount::Four, } } /// Estimate the cost without needing uploaded URLs. We construct a temporary /// `GenerateVideoArgs` with dummy values for the session and URL fields. -fn estimate_cost_upfront(resolution: Resolution, batch_count: BatchCount, duration_seconds: u8) -> u64 { +fn estimate_cost_upfront(resolution: KinoviResolution, batch_count: KinoviBatchCount, duration_seconds: u8) -> u64 { let dummy_session = Seedance2ProSession::from_cookies_string(String::new()); let args = GenerateVideoArgs { session: &dummy_session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt: String::new(), resolution, duration_seconds, @@ -506,8 +506,8 @@ async fn upload_and_generate( session: &Seedance2ProSession, request: &Seedance2p0MultiFunctionVideoGenRequest, file_urls_by_token: &HashMap, - resolution: Resolution, - batch_count: BatchCount, + resolution: KinoviResolution, + batch_count: KinoviBatchCount, duration_seconds: u8, kinovi_character_ids: Option>, ) -> Result { @@ -574,7 +574,7 @@ async fn upload_and_generate( let video_gen_args = GenerateVideoArgs { session, - model_type: ModelType::Seedance2Pro, + model_type: KinoviModelType::Seedance2Pro, prompt, resolution, duration_seconds, diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/execute_generation.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/execute_generation.rs index abb39aed4a..94673cc2a6 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/execute_generation.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/execute_generation.rs @@ -11,11 +11,12 @@ use url::Url; use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest; use artcraft_router::api::provider::Provider; use artcraft_router::generate::generate_video::generate_video_response::GenerateVideoResponse; +use enums::common::generation::common_aspect_ratio::CommonAspectRatio; use enums::common::generation::common_generation_mode::CommonGenerationMode; use enums::common::generation::common_video_model::CommonVideoModel; use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession; use seedance2pro_client::requests::generate_video::generate_video::{ - generate_video, BatchCount, GenerateVideoArgs, ModelType, Resolution, + generate_video, KinoviBatchCount, GenerateVideoArgs, KinoviModelType, KinoviResolution, }; use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{ prepare_file_upload, PrepareFileUploadArgs, @@ -167,19 +168,21 @@ async fn execute_generation_kinovi( }; // Map aspect ratio / duration / batch from the request. - let resolution = Resolution::Landscape16x9; // TODO: Map from request.aspect_ratio + let resolution = map_common_aspect_ratio_to_kinovi_resolution(request.aspect_ratio); + let duration_seconds = request.duration_seconds.unwrap_or(5).clamp(4, 15) as u8; + let batch_count = match request.video_batch_count { - Some(2) => BatchCount::Two, - Some(4) => BatchCount::Four, - _ => BatchCount::One, + Some(2) => KinoviBatchCount::Two, + Some(4) => KinoviBatchCount::Four, + _ => KinoviBatchCount::One, }; let prompt = request.prompt.clone().unwrap_or_default(); let model_type = match request.model { - Some(CommonVideoModel::Seedance2p0Fast) => ModelType::Seedance2Fast, - _ => ModelType::Seedance2Pro, + Some(CommonVideoModel::Seedance2p0Fast) => KinoviModelType::Seedance2Fast, + _ => KinoviModelType::Seedance2Pro, }; let video_gen_args = GenerateVideoArgs { @@ -309,3 +312,21 @@ async fn upload_url_to_seedance2pro( Ok(upload_result.public_url) } + +/// Map a CommonAspectRatio to the Seedance2Pro Resolution enum. +fn map_common_aspect_ratio_to_kinovi_resolution(aspect_ratio: Option) -> KinoviResolution { + match aspect_ratio { + Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => KinoviResolution::Landscape16x9, + Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => KinoviResolution::Portrait9x16, + Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => KinoviResolution::Square1x1, + Some(CommonAspectRatio::WideFourByThree) => KinoviResolution::Standard4x3, + Some(CommonAspectRatio::TallThreeByFour) => KinoviResolution::Portrait3x4, + // For unsupported aspect ratios, pick the nearest match. + Some(CommonAspectRatio::WideFiveByFour) | Some(CommonAspectRatio::WideThreeByTwo) => KinoviResolution::Standard4x3, + Some(CommonAspectRatio::WideTwentyOneByNine) => KinoviResolution::Landscape16x9, + Some(CommonAspectRatio::TallFourByFive) | Some(CommonAspectRatio::TallTwoByThree) => KinoviResolution::Portrait3x4, + Some(CommonAspectRatio::TallNineByTwentyOne) => KinoviResolution::Portrait9x16, + // Auto or None — default to landscape. + _ => KinoviResolution::Landscape16x9, + } +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs index f6916eb501..1b56cde6f0 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/kinovi/test_kinovi_seedance_2p0.rs @@ -455,5 +455,98 @@ mod tests { panic!("wrong plan variant"); } } + + // -- Aspect ratio tests -- + + #[test] + fn aspect_ratio_wide_16x9() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), Some(CommonAspectRatio::WideSixteenByNine), Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Landscape16x9)); + } else { panic!("wrong plan variant"); } + } + + #[test] + fn aspect_ratio_tall_9x16() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), Some(CommonAspectRatio::TallNineBySixteen), Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Portrait9x16)); + } else { panic!("wrong plan variant"); } + } + + #[test] + fn aspect_ratio_square() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), Some(CommonAspectRatio::Square), Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Square1x1)); + } else { panic!("wrong plan variant"); } + } + + #[test] + fn aspect_ratio_4x3() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), Some(CommonAspectRatio::WideFourByThree), Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Standard4x3)); + } else { panic!("wrong plan variant"); } + } + + #[test] + fn aspect_ratio_3x4() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), Some(CommonAspectRatio::TallThreeByFour), Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Portrait3x4)); + } else { panic!("wrong plan variant"); } + } + + #[test] + fn aspect_ratio_none_defaults_to_landscape() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), None, Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Landscape16x9)); + } else { panic!("wrong plan variant"); } + } + + #[test] + fn aspect_ratio_auto_defaults_to_landscape() { + use seedance2pro_client::requests::generate_video::generate_video::KinoviResolution; + let req = make_request( + Some("test"), Some(CommonAspectRatio::Auto), Some(5), None, + None, None, None, None, None, + ); + let d = distill_text(&req); + if let VideoGenerationPlan::Seedance2proSeedance2p0(plan) = d.plan() { + assert!(matches!(plan.resolution, KinoviResolution::Landscape16x9)); + } else { panic!("wrong plan variant"); } + } } }