mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
seedance pricing calculation
This commit is contained in:
1 parent
4631971865
commit
02cb7558e4
2 files changed
+127
-6
No files matched your search
+120
-5
@@ -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<T>,
|
||||
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<Seedance1LiteAspectRatio>,
|
||||
pub camera_fixed: bool,
|
||||
pub seed: Option<u32>,
|
||||
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 <U: IntoUrl, T: IntoUrl, V: IntoUrl> 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<U: IntoUrl, T: IntoUrl, V: IntoUrl>(
|
||||
args: Seedance1LiteArgs<'_, U, T, V>
|
||||
) -> Result<WebhookResponse, FalErrorPlus> {
|
||||
@@ -41,8 +99,22 @@ pub async fn enqueue_seedance_1_lite_image_to_video_webhook<U: IntoUrl, T: IntoU
|
||||
let resolution = match args.resolution {
|
||||
Seedance1LiteResolution::FourEightyP => 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<U: IntoUrl, T: IntoU
|
||||
prompt: args.prompt.to_string(),
|
||||
duration,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
// TODO: Add these later
|
||||
camera_fixed: None,
|
||||
seed: -1,
|
||||
@@ -77,6 +150,47 @@ mod tests {
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
use test_data::web::image_urls::{JUNO_AT_LAKE_IMAGE_URL, TALL_MOCHI_WITH_GLASSES_IMAGE_URL};
|
||||
use crate::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
|
||||
#[test]
|
||||
fn test_cost() {
|
||||
let api_key = FalApiKey::from_str("");
|
||||
|
||||
let mut args = Seedance1LiteArgs {
|
||||
image_url: "",
|
||||
end_frame_image_url: Some(""),
|
||||
prompt: "",
|
||||
api_key: &api_key,
|
||||
camera_fixed: false,
|
||||
duration: Seedance1LiteDuration::FiveSeconds,
|
||||
resolution: Seedance1LiteResolution::SevenTwentyP,
|
||||
aspect_ratio: None,
|
||||
seed: None,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
// NB: Constant value specified by FAL
|
||||
args.duration = Seedance1LiteDuration::FiveSeconds;
|
||||
args.resolution = Seedance1LiteResolution::SevenTwentyP;
|
||||
let cost = args.calculate_cost_in_cents();
|
||||
assert_eq!(cost, 18);
|
||||
|
||||
// NB: Calculations follow...
|
||||
args.duration = Seedance1LiteDuration::FiveSeconds;
|
||||
args.resolution = Seedance1LiteResolution::TenEightyP;
|
||||
let cost = args.calculate_cost_in_cents();
|
||||
assert_eq!(cost, 55);
|
||||
|
||||
args.duration = Seedance1LiteDuration::FiveSeconds;
|
||||
args.resolution = Seedance1LiteResolution::FourEightyP;
|
||||
let cost = args.calculate_cost_in_cents();
|
||||
assert_eq!(cost, 9);
|
||||
|
||||
args.duration = Seedance1LiteDuration::TenSeconds;
|
||||
args.resolution = Seedance1LiteResolution::TenEightyP;
|
||||
let cost = args.calculate_cost_in_cents();
|
||||
assert_eq!(cost, 110);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
@@ -94,6 +208,7 @@ mod tests {
|
||||
camera_fixed: false,
|
||||
duration: Seedance1LiteDuration::FiveSeconds,
|
||||
resolution: Seedance1LiteResolution::SevenTwentyP,
|
||||
aspect_ratio: None,
|
||||
seed: None,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
+7
-1
@@ -12,10 +12,16 @@ pub struct ImageToVideoRequest {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub end_image_url: Option<String>,
|
||||
|
||||
/// 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<String>,
|
||||
|
||||
/// Aspect ratio.
|
||||
/// Defaults to "auto"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio: Option<String>,
|
||||
|
||||
/// The duration of the generated video in seconds
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<String>,
|
||||
|
||||
Reference in new issue
Block a user