diff --git a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1_lite_image_to_video_webhook.rs b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1_lite_image_to_video_webhook.rs index 89789834c6..6e95bcb3f9 100644 --- a/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1_lite_image_to_video_webhook.rs +++ b/crates/api_clients/fal_client/src/requests/webhook/video/image/enqueue_seedance_1_lite_image_to_video_webhook.rs @@ -1,6 +1,7 @@ use crate::creds::fal_api_key::FalApiKey; use crate::error::classify_fal_error::classify_fal_error; use crate::error::fal_error_plus::FalErrorPlus; +use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents}; use fal::endpoints::fal_ai::bytedance::seedance::v1::lite::image_to_video::image_to_video; use fal::endpoints::fal_ai::bytedance::seedance::v1::lite::image_to_video::ImageToVideoRequest; use fal::webhook::WebhookResponse; @@ -9,27 +10,84 @@ use reqwest::IntoUrl; pub struct Seedance1LiteArgs<'a, U: IntoUrl, T: IntoUrl, V: IntoUrl> { pub image_url: U, pub end_frame_image_url: Option, - pub webhook_url: V, pub prompt: &'a str, - pub api_key: &'a FalApiKey, - pub camera_fixed: bool, pub duration: Seedance1LiteDuration, pub resolution: Seedance1LiteResolution, + pub aspect_ratio: Option, + pub camera_fixed: bool, pub seed: Option, + pub api_key: &'a FalApiKey, + pub webhook_url: V, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Seedance1LiteDuration { FiveSeconds, TenSeconds, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Seedance1LiteResolution { FourEightyP, // 480p SevenTwentyP, // 720p + TenEightyP, // 1080p } +/// Possible enum values: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16, auto +#[derive(Copy, Clone, Debug)] +pub enum Seedance1LiteAspectRatio { + Auto, + TwentyOneByNine, + SixteenByNine, + FourByThree, + Square, + ThreeByFour, + NineBySixteen, +} + + +impl FalRequestCostCalculator for Seedance1LiteArgs<'_, U, T, V> { + fn calculate_cost_in_cents(&self) -> UsdCents { + // "Each 720p 5 second video costs $0.18. + // For other resolutions, 1 million video tokens costs $1.8. + // tokens(video) = (height x width x FPS x duration) / 1024." + + if self.resolution == Seedance1LiteResolution::SevenTwentyP + && self.duration == Seedance1LiteDuration::FiveSeconds + { + return 18; + } + + // TODO: Only correct for some aspect ratios for now. + let (width, height) = match self.resolution { + Seedance1LiteResolution::FourEightyP => (640u32, 480u32), // NB: Only for 4:3 ! + Seedance1LiteResolution::SevenTwentyP => (1280, 720), // NB: Only for 16:9 ! + Seedance1LiteResolution::TenEightyP => (1920, 1080), + }; + + let duration = match self.duration { + Seedance1LiteDuration::FiveSeconds => 5.0, + Seedance1LiteDuration::TenSeconds => 10.0, + }; + + // TODO: Not sure if FPS is right. + // Inferred from https://help.scenario.com/en/articles/seedance-models-the-essentials/ + const FPS : f64 = 30.0; + + let tokens = (height as f64) * (width as f64) * FPS * duration; + let tokens = tokens / 1024.0; + + let cost = tokens * 1.8 / 1_000_000.0; + let cost = cost * 100.0; // Dollars to cents. + let cost = cost.ceil(); // NB: This is probably what Fal does. + + cost as UsdCents + } +} + + +/// Seedance 1 Lite Image-to-Video +/// https://fal.ai/models/fal-ai/bytedance/seedance/v1/lite/image-to-video pub async fn enqueue_seedance_1_lite_image_to_video_webhook( args: Seedance1LiteArgs<'_, U, T, V> ) -> Result { @@ -41,8 +99,22 @@ pub async fn enqueue_seedance_1_lite_image_to_video_webhook Some("480p".to_string()), Seedance1LiteResolution::SevenTwentyP => Some("720p".to_string()), + Seedance1LiteResolution::TenEightyP => Some("1080p".to_string()), }; + /// Possible enum values: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16, auto + let aspect_ratio = args.aspect_ratio + .map(|r| match r { + Seedance1LiteAspectRatio::Auto => "auto", + Seedance1LiteAspectRatio::TwentyOneByNine => "21:9", + Seedance1LiteAspectRatio::SixteenByNine => "16:9", + Seedance1LiteAspectRatio::FourByThree => "4:3", + Seedance1LiteAspectRatio::Square => "1:1", + Seedance1LiteAspectRatio::ThreeByFour => "3:4", + Seedance1LiteAspectRatio::NineBySixteen => "9:16", + }) + .map(|s| s.to_string()); + let image_url = args.image_url.as_str().to_string(); let end_image_url = args.end_frame_image_url @@ -54,6 +126,7 @@ pub async fn enqueue_seedance_1_lite_image_to_video_webhook, - /// The resolution of the generated video frame + /// The resolution of the generated video frame. + /// Defaults to "720p" #[serde(skip_serializing_if = "Option::is_none")] pub resolution: Option, + /// Aspect ratio. + /// Defaults to "auto" + #[serde(skip_serializing_if = "Option::is_none")] + pub aspect_ratio: Option, + /// The duration of the generated video in seconds #[serde(skip_serializing_if = "Option::is_none")] pub duration: Option,