rename variant, aspect ratio

This commit is contained in:
Brandon Thomas committed 2026-04-17 01:38:26 -04:00
1 parent b6f3f55579
commit e536a78b1e
12 files changed
+316 -202

No files matched your search

@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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<Vec<String>>,
pub reference_video_urls: Option<Vec<String>>,
pub reference_audio_urls: Option<Vec<String>>,
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<CommonAspectRatio>,
strategy: RequestMismatchMitigationStrategy,
) -> Result<Resolution, ArtcraftRouterError> {
) -> Result<KinoviResolution, ArtcraftRouterError> {
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<u16>,
strategy: RequestMismatchMitigationStrategy,
) -> Result<BatchCount, ArtcraftRouterError> {
) -> Result<KinoviBatchCount, ArtcraftRouterError> {
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 })
}
},
}
@@ -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<Vec<String>>,
pub reference_video_urls: Option<Vec<String>>,
pub reference_audio_urls: Option<Vec<String>>,
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<CommonAspectRatio>,
strategy: RequestMismatchMitigationStrategy,
) -> Result<Resolution, ArtcraftRouterError> {
) -> Result<KinoviResolution, ArtcraftRouterError> {
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<u16>,
strategy: RequestMismatchMitigationStrategy,
) -> Result<BatchCount, ArtcraftRouterError> {
) -> Result<KinoviBatchCount, ArtcraftRouterError> {
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 })
}
},
}
@@ -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<String>,
@@ -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,
@@ -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<Seedance2p0AspectRatio>) -> Resolution {
fn map_resolution(aspect_ratio: Option<Seedance2p0AspectRatio>) -> 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<Seedance2p0BatchCount>) -> BatchCount {
fn map_batch_count(batch_count: Option<Seedance2p0BatchCount>) -> 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<MediaFileToken, Url>,
resolution: Resolution,
batch_count: BatchCount,
resolution: KinoviResolution,
batch_count: KinoviBatchCount,
duration_seconds: u8,
kinovi_character_ids: Option<Vec<String>>,
) -> Result<SeedanceGenerationResult, AdvancedCommonWebError> {
@@ -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,
@@ -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<CommonAspectRatio>) -> 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,
}
}
@@ -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"); }
}
}
}