mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-10 12:01:36 +00:00
artcraft router: all video is on "v2"; artcraft desktop: all models on "omni"; storyteller-web: omni on video v2 (#1577)
This is a lot of legacy path cleanup. These are now the only valid paths: - artcraft website: storyteller-web omni video gen -> artcraft_router v2 video pipeline - artcraft desktop: artcraft_router v2 video pipeline (for artcraft provider) -> storyteller-web omni video gen
This commit is contained in:
1 parent
c36c0d283c
commit
370d2fc494
188 files changed
+433
-15220
No files matched your search
Generated
-1
@@ -805,7 +805,6 @@ dependencies = [
|
||||
"gmicloud_client",
|
||||
"grok_api_client",
|
||||
"log",
|
||||
"muapi_client",
|
||||
"reqwest 0.12.23",
|
||||
"seedance2pro_client",
|
||||
"serde",
|
||||
|
||||
@@ -22,7 +22,6 @@ artcraft_client.workspace = true
|
||||
fal_client.workspace = true
|
||||
gmicloud_client.workspace = true
|
||||
grok_api_client.workspace = true
|
||||
muapi_client.workspace = true
|
||||
seedance2pro_client.workspace = true
|
||||
tokens.workspace = true
|
||||
url_utils.workspace = true
|
||||
|
||||
@@ -8,6 +8,5 @@ pub enum Provider {
|
||||
Fal,
|
||||
GmiCloud,
|
||||
GrokApi,
|
||||
Muapi,
|
||||
Seedance2Pro,
|
||||
}
|
||||
@@ -8,5 +8,4 @@ pub mod router_fal_client;
|
||||
pub mod router_fal_webhook_optional_client;
|
||||
pub mod router_gmicloud_client;
|
||||
pub mod router_grok_api_client;
|
||||
pub mod router_muapi_client;
|
||||
pub mod router_seedance2pro_client;
|
||||
@@ -3,7 +3,6 @@ use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::client::router_fal_webhook_optional_client::RouterFalWebhookOptionalClient;
|
||||
use crate::client::router_gmicloud_client::RouterGmiCloudClient;
|
||||
use crate::client::router_grok_api_client::RouterGrokApiClient;
|
||||
use crate::client::router_muapi_client::RouterMuapiClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::errors::client_error::{ClientError, ClientType};
|
||||
|
||||
@@ -13,7 +12,6 @@ pub struct MultiRouterClient {
|
||||
pub(crate) fal_webhook_optional_client: Option<RouterFalWebhookOptionalClient>,
|
||||
pub(crate) gmicloud_client: Option<RouterGmiCloudClient>,
|
||||
pub(crate) grok_api_client: Option<RouterGrokApiClient>,
|
||||
pub(crate) muapi_client: Option<RouterMuapiClient>,
|
||||
pub(crate) seedance2pro_client: Option<RouterSeedance2ProClient>,
|
||||
}
|
||||
|
||||
@@ -43,11 +41,6 @@ impl MultiRouterClient {
|
||||
.ok_or(ClientError::ClientNotConfigured(ClientType::GrokApi))
|
||||
}
|
||||
|
||||
pub fn get_muapi_client_ref(&self) -> Result<&RouterMuapiClient, ClientError> {
|
||||
self.muapi_client.as_ref()
|
||||
.ok_or(ClientError::ClientNotConfigured(ClientType::Muapi))
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ClientError> {
|
||||
self.seedance2pro_client.as_ref()
|
||||
.ok_or(ClientError::ClientNotConfigured(ClientType::Seedance2Pro))
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::client::router_fal_webhook_optional_client::RouterFalWebhookOptionalClient;
|
||||
use crate::client::router_gmicloud_client::RouterGmiCloudClient;
|
||||
use crate::client::router_grok_api_client::RouterGrokApiClient;
|
||||
use crate::client::router_muapi_client::RouterMuapiClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
|
||||
pub struct MultiRouterClientBuilder {
|
||||
@@ -13,7 +12,6 @@ pub struct MultiRouterClientBuilder {
|
||||
fal_webhook_optional_client: Option<RouterFalWebhookOptionalClient>,
|
||||
gmicloud_client: Option<RouterGmiCloudClient>,
|
||||
grok_api_client: Option<RouterGrokApiClient>,
|
||||
muapi_client: Option<RouterMuapiClient>,
|
||||
seedance2pro_client: Option<RouterSeedance2ProClient>,
|
||||
}
|
||||
|
||||
@@ -25,7 +23,6 @@ impl MultiRouterClientBuilder {
|
||||
fal_webhook_optional_client: None,
|
||||
gmicloud_client: None,
|
||||
grok_api_client: None,
|
||||
muapi_client: None,
|
||||
seedance2pro_client: None,
|
||||
}
|
||||
}
|
||||
@@ -55,11 +52,6 @@ impl MultiRouterClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_muapi_client(mut self, client: RouterMuapiClient) -> Self {
|
||||
self.muapi_client = Some(client);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_seedance2pro_client(mut self, client: RouterSeedance2ProClient) -> Self {
|
||||
self.seedance2pro_client = Some(client);
|
||||
self
|
||||
@@ -72,7 +64,6 @@ impl MultiRouterClientBuilder {
|
||||
fal_webhook_optional_client: self.fal_webhook_optional_client,
|
||||
gmicloud_client: self.gmicloud_client,
|
||||
grok_api_client: self.grok_api_client,
|
||||
muapi_client: self.muapi_client,
|
||||
seedance2pro_client: self.seedance2pro_client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::client::router_fal_webhook_optional_client::RouterFalWebhookOptionalClient;
|
||||
use crate::client::router_gmicloud_client::RouterGmiCloudClient;
|
||||
use crate::client::router_grok_api_client::RouterGrokApiClient;
|
||||
use crate::client::router_muapi_client::RouterMuapiClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::errors::client_error::{ClientError, ClientType};
|
||||
|
||||
@@ -15,7 +14,6 @@ pub enum RouterClient {
|
||||
FalWebhookOptional(RouterFalWebhookOptionalClient),
|
||||
GmiCloud(RouterGmiCloudClient),
|
||||
GrokApi(RouterGrokApiClient),
|
||||
Muapi(RouterMuapiClient),
|
||||
Seedance2Pro(RouterSeedance2ProClient),
|
||||
}
|
||||
|
||||
@@ -60,14 +58,6 @@ impl RouterClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_muapi_client_ref(&self) -> Result<&RouterMuapiClient, ClientError> {
|
||||
match self {
|
||||
RouterClient::Muapi(client) => Ok(client),
|
||||
RouterClient::Multi(multi) => multi.get_muapi_client_ref(),
|
||||
_ => Err(ClientError::ClientNotConfigured(ClientType::Muapi)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ClientError> {
|
||||
match self {
|
||||
RouterClient::Seedance2Pro(client) => Ok(client),
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
use muapi_client::creds::muapi_session::MuapiSession;
|
||||
|
||||
pub struct RouterMuapiClient {
|
||||
pub(crate) session: MuapiSession,
|
||||
}
|
||||
|
||||
impl RouterMuapiClient {
|
||||
pub fn new(session: MuapiSession) -> Self {
|
||||
RouterMuapiClient { session }
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ pub enum ClientType {
|
||||
FalWebhookOptional,
|
||||
GmiCloud,
|
||||
GrokApi,
|
||||
Muapi,
|
||||
Seedance2Pro,
|
||||
}
|
||||
|
||||
@@ -22,7 +21,6 @@ impl Display for ClientType {
|
||||
Self::FalWebhookOptional => write!(f, "FalWebhookOptional"),
|
||||
Self::GmiCloud => write!(f, "GmiCloud"),
|
||||
Self::GrokApi => write!(f, "GrokApi"),
|
||||
Self::Muapi => write!(f, "Muapi"),
|
||||
Self::Seedance2Pro => write!(f, "Seedance2Pro"),
|
||||
}
|
||||
}
|
||||
@@ -49,9 +47,6 @@ pub enum ClientError {
|
||||
/// Fal only accepts image URLs for image inputs, not media tokens.
|
||||
FalOnlySupportsUrls,
|
||||
|
||||
/// Muapi only accepts URLs for image inputs, not media tokens.
|
||||
MuapiOnlySupportsUrls,
|
||||
|
||||
/// Seedance2Pro only accepts URLs for media inputs, not media tokens.
|
||||
Seedance2ProOnlySupportsUrls,
|
||||
|
||||
@@ -91,9 +86,6 @@ impl Display for ClientError {
|
||||
Self::FalOnlySupportsUrls => {
|
||||
write!(f, "Fal only supports image URLs for image inputs, not media tokens")
|
||||
}
|
||||
Self::MuapiOnlySupportsUrls => {
|
||||
write!(f, "Muapi only supports URLs for image inputs; resolve media tokens to URLs before calling this provider")
|
||||
}
|
||||
Self::Seedance2ProOnlySupportsUrls => {
|
||||
write!(f, "Seedance2Pro only supports URLs for media inputs; resolve media tokens to URLs before calling this provider")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use artcraft_client::error::storyteller_error::StorytellerError;
|
||||
use fal_client::error::fal_error_plus::FalErrorPlus;
|
||||
use gmicloud_client::error::gmicloud_error::GmiCloudError;
|
||||
use grok_api_client::error::grok_error::GrokError;
|
||||
use muapi_client::error::muapi_error::MuapiError;
|
||||
use seedance2pro_client::error::seedance2pro_error::Seedance2ProError;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -13,7 +12,6 @@ pub enum ProviderError {
|
||||
Fal(FalErrorPlus),
|
||||
GmiCloud(GmiCloudError),
|
||||
Grok(GrokError),
|
||||
Muapi(MuapiError),
|
||||
Seedance2Pro(Seedance2ProError),
|
||||
}
|
||||
|
||||
@@ -26,7 +24,6 @@ impl Display for ProviderError {
|
||||
Self::Fal(e) => write!(f, "Fal provider error: {}", e),
|
||||
Self::GmiCloud(e) => write!(f, "GmiCloud provider error: {}", e),
|
||||
Self::Grok(e) => write!(f, "Grok provider error: {}", e),
|
||||
Self::Muapi(e) => write!(f, "Muapi provider error: {}", e),
|
||||
Self::Seedance2Pro(e) => write!(f, "Seedance2Pro provider error: {}", e),
|
||||
}
|
||||
}
|
||||
@@ -56,12 +53,6 @@ impl From<GrokError> for ProviderError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MuapiError> for ProviderError {
|
||||
fn from(error: MuapiError) -> Self {
|
||||
Self::Muapi(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Seedance2ProError> for ProviderError {
|
||||
fn from(error: Seedance2ProError) -> Self {
|
||||
Self::Seedance2Pro(error)
|
||||
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_3p0_pro_multi_function_video_gen::Kling3p0ProMultiFunctionVideoGenDuration;
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::text::enqueue_kling_3p0_pro_text_to_video_webhook::{
|
||||
EnqueueKling3p0ProTextToVideoRequest,
|
||||
EnqueueKling3p0ProTextToVideoDuration,
|
||||
};
|
||||
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_pro::PlanArtcraftKling3p0Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling3p0_pro(
|
||||
plan: &PlanArtcraftKling3p0Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let duration = plan.duration
|
||||
.map(|d| match d {
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::ThreeSeconds => EnqueueKling3p0ProTextToVideoDuration::ThreeSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::FourSeconds => EnqueueKling3p0ProTextToVideoDuration::FourSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::FiveSeconds => EnqueueKling3p0ProTextToVideoDuration::FiveSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::SixSeconds => EnqueueKling3p0ProTextToVideoDuration::SixSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::SevenSeconds => EnqueueKling3p0ProTextToVideoDuration::SevenSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::EightSeconds => EnqueueKling3p0ProTextToVideoDuration::EightSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::NineSeconds => EnqueueKling3p0ProTextToVideoDuration::NineSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::TenSeconds => EnqueueKling3p0ProTextToVideoDuration::TenSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::ElevenSeconds => EnqueueKling3p0ProTextToVideoDuration::ElevenSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::TwelveSeconds => EnqueueKling3p0ProTextToVideoDuration::TwelveSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::ThirteenSeconds => EnqueueKling3p0ProTextToVideoDuration::ThirteenSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::FourteenSeconds => EnqueueKling3p0ProTextToVideoDuration::FourteenSeconds,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration::FifteenSeconds => EnqueueKling3p0ProTextToVideoDuration::FifteenSeconds,
|
||||
});
|
||||
|
||||
// Use text-to-video cost calculator (same pricing for both modes)
|
||||
let req = EnqueueKling3p0ProTextToVideoRequest {
|
||||
prompt: String::new(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: None,
|
||||
duration,
|
||||
aspect_ratio: None,
|
||||
shot_type: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = req.calculate_cost_in_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(duration_seconds: u16) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Kling3p0Pro,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_estimate_cost_usd_cents() {
|
||||
// Kling 3.0 Pro: $0.336/sec (audio on, the default when None)
|
||||
// Formula: (336 * duration_secs + 9) / 10 → ceiling division
|
||||
// 5s: (336 * 5 + 9) / 10 = 1689 / 10 = 168
|
||||
assert_eq!(estimate_usd_cents(5), 168);
|
||||
// 10s: (336 * 10 + 9) / 10 = 3369 / 10 = 336
|
||||
assert_eq!(estimate_usd_cents(10), 336);
|
||||
// 15s: (336 * 15 + 9) / 10 = 5049 / 10 = 504
|
||||
assert_eq!(estimate_usd_cents(15), 504);
|
||||
}
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_3p0_standard_multi_function_video_gen::Kling3p0StandardMultiFunctionVideoGenDuration;
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::text::enqueue_kling_3p0_standard_text_to_video_webhook::{
|
||||
EnqueueKling3p0StandardTextToVideoRequest,
|
||||
EnqueueKling3p0StandardTextToVideoDuration,
|
||||
};
|
||||
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_standard::PlanArtcraftKling3p0Standard;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling3p0_standard(
|
||||
plan: &PlanArtcraftKling3p0Standard,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let duration = plan.duration
|
||||
.map(|d| match d {
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::ThreeSeconds => EnqueueKling3p0StandardTextToVideoDuration::ThreeSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::FourSeconds => EnqueueKling3p0StandardTextToVideoDuration::FourSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::FiveSeconds => EnqueueKling3p0StandardTextToVideoDuration::FiveSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::SixSeconds => EnqueueKling3p0StandardTextToVideoDuration::SixSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::SevenSeconds => EnqueueKling3p0StandardTextToVideoDuration::SevenSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::EightSeconds => EnqueueKling3p0StandardTextToVideoDuration::EightSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::NineSeconds => EnqueueKling3p0StandardTextToVideoDuration::NineSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::TenSeconds => EnqueueKling3p0StandardTextToVideoDuration::TenSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::ElevenSeconds => EnqueueKling3p0StandardTextToVideoDuration::ElevenSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::TwelveSeconds => EnqueueKling3p0StandardTextToVideoDuration::TwelveSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::ThirteenSeconds => EnqueueKling3p0StandardTextToVideoDuration::ThirteenSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::FourteenSeconds => EnqueueKling3p0StandardTextToVideoDuration::FourteenSeconds,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration::FifteenSeconds => EnqueueKling3p0StandardTextToVideoDuration::FifteenSeconds,
|
||||
});
|
||||
|
||||
// Use text-to-video cost calculator (same pricing for both modes)
|
||||
let req = EnqueueKling3p0StandardTextToVideoRequest {
|
||||
prompt: String::new(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: None,
|
||||
duration,
|
||||
aspect_ratio: None,
|
||||
shot_type: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = req.calculate_cost_in_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(duration_seconds: u16) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Kling3p0Standard,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_estimate_cost_usd_cents() {
|
||||
// Kling 3.0 Standard: $0.252/sec (audio on, the default when None)
|
||||
// Formula: (252 * duration_secs + 9) / 10 → ceiling division
|
||||
// 5s: (252 * 5 + 9) / 10 = 1269 / 10 = 126
|
||||
assert_eq!(estimate_usd_cents(5), 126);
|
||||
// 10s: (252 * 10 + 9) / 10 = 2529 / 10 = 252
|
||||
assert_eq!(estimate_usd_cents(10), 252);
|
||||
// 15s: (252 * 15 + 9) / 10 = 3789 / 10 = 378
|
||||
assert_eq!(estimate_usd_cents(15), 378);
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::PlanArtcraftKling16Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling_1_6_pro(
|
||||
plan: &PlanArtcraftKling16Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v1p6_pro: 5s = $0.475 (rounded up to 48¢), 10s = $0.95 (95¢).
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 95 } else { 48 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::PlanArtcraftKling21Master;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling_2_1_master(
|
||||
plan: &PlanArtcraftKling21Master,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p1_master: 5s = $1.40, 10s = $2.80.
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 280 } else { 140 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::PlanArtcraftKling21Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling_2_1_pro(
|
||||
plan: &PlanArtcraftKling21Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p1_pro: 5s = $0.45, 10s = $0.90.
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 90 } else { 45 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::PlanArtcraftKling2p5TurboPro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling_2_5_turbo_pro(
|
||||
plan: &PlanArtcraftKling2p5TurboPro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p5_turbo_pro: 5s = $0.35, 10s = $0.70.
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 70 } else { 35 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::PlanArtcraftKling2p6Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_kling_2_6_pro(
|
||||
plan: &PlanArtcraftKling2p6Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p6_pro:
|
||||
// audio off: $0.07/sec → 5s=35¢, 10s=70¢
|
||||
// audio on: $0.14/sec → 5s=70¢, 10s=140¢
|
||||
// Default duration is 5s; default generate_audio is true.
|
||||
let cost_in_usd_cents: u64 = match (plan.generate_audio_for_cost(), plan.is_ten_seconds()) {
|
||||
(false, false) => 35,
|
||||
(false, true) => 70,
|
||||
(true, false) => 70,
|
||||
(true, true) => 140,
|
||||
};
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_1p5_pro_multi_function_video_gen::Seedance1p5ProMultiFunctionVideoGenDuration;
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::text::enqueue_seedance_1p5_pro_text_to_video_webhook::{
|
||||
EnqueueSeedance1p5ProTextToVideoRequest,
|
||||
EnqueueSeedance1p5ProTextToVideoDuration,
|
||||
EnqueueSeedance1p5ProTextToVideoResolution,
|
||||
};
|
||||
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance1p5_pro::PlanArtcraftSeedance1p5Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_seedance1p5_pro(
|
||||
plan: &PlanArtcraftSeedance1p5Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let duration = plan.duration
|
||||
.map(|d| match d {
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::FourSeconds => EnqueueSeedance1p5ProTextToVideoDuration::FourSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::FiveSeconds => EnqueueSeedance1p5ProTextToVideoDuration::FiveSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::SixSeconds => EnqueueSeedance1p5ProTextToVideoDuration::SixSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::SevenSeconds => EnqueueSeedance1p5ProTextToVideoDuration::SevenSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::EightSeconds => EnqueueSeedance1p5ProTextToVideoDuration::EightSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::NineSeconds => EnqueueSeedance1p5ProTextToVideoDuration::NineSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::TenSeconds => EnqueueSeedance1p5ProTextToVideoDuration::TenSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::ElevenSeconds => EnqueueSeedance1p5ProTextToVideoDuration::ElevenSeconds,
|
||||
Seedance1p5ProMultiFunctionVideoGenDuration::TwelveSeconds => EnqueueSeedance1p5ProTextToVideoDuration::TwelveSeconds,
|
||||
});
|
||||
|
||||
let resolution = plan.resolution
|
||||
.map(|r| match r {
|
||||
artcraft_api_defs::generate::video::multi_function::seedance_1p5_pro_multi_function_video_gen::Seedance1p5ProMultiFunctionVideoGenResolution::FourEightyP => EnqueueSeedance1p5ProTextToVideoResolution::FourEightyP,
|
||||
artcraft_api_defs::generate::video::multi_function::seedance_1p5_pro_multi_function_video_gen::Seedance1p5ProMultiFunctionVideoGenResolution::SevenTwentyP => EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP,
|
||||
artcraft_api_defs::generate::video::multi_function::seedance_1p5_pro_multi_function_video_gen::Seedance1p5ProMultiFunctionVideoGenResolution::TenEightyP => EnqueueSeedance1p5ProTextToVideoResolution::TenEightyP,
|
||||
});
|
||||
|
||||
// Use text-to-video cost calculator (same pricing for both modes)
|
||||
let req = EnqueueSeedance1p5ProTextToVideoRequest {
|
||||
prompt: String::new(),
|
||||
duration,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = req.calculate_cost_in_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(duration_seconds: u16) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance1p5Pro,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_estimate_cost_usd_cents() {
|
||||
// Default resolution is 720p (when None)
|
||||
// 720p 5s = 26 cents (constant from Fal)
|
||||
assert_eq!(estimate_usd_cents(5), 26);
|
||||
|
||||
// Other durations use token-based calculation
|
||||
assert_eq!(estimate_usd_cents(4), 26);
|
||||
assert_eq!(estimate_usd_cents(10), 65);
|
||||
assert_eq!(estimate_usd_cents(12), 78);
|
||||
}
|
||||
}
|
||||
-204
@@ -1,204 +0,0 @@
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::Seedance2p0BatchCount;
|
||||
use seedance2pro_client::requests::generate_video::generate_video::{KinoviAspectRatio, KinoviBatchCount, KinoviGenerateVideoRequest, KinoviModelType, KinoviOutputResolution};
|
||||
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::PlanArtcraftSeedance2p0;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub (crate) fn estimate_video_cost_artcraft_seedance2p0(
|
||||
plan: &PlanArtcraftSeedance2p0,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let duration_seconds = plan.duration_seconds.unwrap_or(5).clamp(4, 15);
|
||||
|
||||
let batch_count = match plan.batch_count {
|
||||
Seedance2p0BatchCount::One => KinoviBatchCount::One,
|
||||
Seedance2p0BatchCount::Two => KinoviBatchCount::Two,
|
||||
Seedance2p0BatchCount::Four => KinoviBatchCount::Four,
|
||||
};
|
||||
|
||||
let output_resolution = plan.resolution.map(map_common_resolution_to_kinovi);
|
||||
|
||||
let request = KinoviGenerateVideoRequest {
|
||||
model_type: KinoviModelType::Seedance2Pro,
|
||||
prompt: String::new(),
|
||||
aspect_ratio: KinoviAspectRatio::Square1x1,
|
||||
duration_seconds,
|
||||
batch_count,
|
||||
output_resolution,
|
||||
|
||||
// TODO: This is a cost factor
|
||||
reference_video_urls: None,
|
||||
|
||||
// NB: These do not contribute to costs in the Seedance2 integration
|
||||
start_frame_url: None,
|
||||
end_frame_url: None,
|
||||
reference_image_urls: None,
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = request.estimate_cost_in_usd_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_common_resolution_to_kinovi(resolution: CommonResolution) -> KinoviOutputResolution {
|
||||
match resolution {
|
||||
CommonResolution::FourEightyP => KinoviOutputResolution::FourEightyP,
|
||||
CommonResolution::SevenTwentyP => KinoviOutputResolution::SevenTwentyP,
|
||||
CommonResolution::TenEightyP => KinoviOutputResolution::TenEightyP,
|
||||
// For resolutions that don't map directly, default to 720p
|
||||
_ => KinoviOutputResolution::SevenTwentyP,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
// -- 720p (default / legacy pricing: 250 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn pro_720p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP)), 64);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)), 80);
|
||||
assert_eq!(estimate_usd_cents(6, 1, Some(CommonResolution::SevenTwentyP)), 96);
|
||||
assert_eq!(estimate_usd_cents(7, 1, Some(CommonResolution::SevenTwentyP)), 112);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP)), 240);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_720p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(4, 2, Some(CommonResolution::SevenTwentyP)), 128);
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::SevenTwentyP)), 160);
|
||||
assert_eq!(estimate_usd_cents(15, 2, Some(CommonResolution::SevenTwentyP)), 480);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_720p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(4, 4, Some(CommonResolution::SevenTwentyP)), 256);
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::SevenTwentyP)), 320);
|
||||
assert_eq!(estimate_usd_cents(15, 4, Some(CommonResolution::SevenTwentyP)), 960);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_none_resolution_same_as_720p() {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(5, 1, None),
|
||||
estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
|
||||
// -- 480p (new pricing: 193 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn pro_480p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::FourEightyP)), 31);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP)), 39);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::FourEightyP)), 78);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::FourEightyP)), 117);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_480p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::FourEightyP)), 78);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_480p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::FourEightyP)), 155);
|
||||
}
|
||||
|
||||
// -- 1080p (new pricing: 193 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn pro_1080p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::TenEightyP)), 187);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP)), 233);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::TenEightyP)), 466);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::TenEightyP)), 699);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_1080p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::TenEightyP)), 466);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_1080p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::TenEightyP)), 933);
|
||||
}
|
||||
|
||||
// -- Relative pricing --
|
||||
|
||||
#[test]
|
||||
fn pro_480p_cheaper_than_720p_cheaper_than_1080p() {
|
||||
let c480 = estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP));
|
||||
let c720 = estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c1080 = estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP));
|
||||
assert!(c480 < c720, "480p ({}) should be cheaper than 720p ({})", c480, c720);
|
||||
assert!(c720 < c1080, "720p ({}) should be cheaper than 1080p ({})", c720, c1080);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_duration() {
|
||||
let c4 = estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c10 = estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c15 = estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(c4 < c10);
|
||||
assert!(c10 < c15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_batch() {
|
||||
let b1 = estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP));
|
||||
let b2 = estimate_usd_cents(5, 2, Some(CommonResolution::TenEightyP));
|
||||
let b4 = estimate_usd_cents(5, 4, Some(CommonResolution::TenEightyP));
|
||||
assert!(b1 < b2);
|
||||
assert!(b2 < b4);
|
||||
}
|
||||
}
|
||||
-295
@@ -1,295 +0,0 @@
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::Seedance2p0BatchCount;
|
||||
use seedance2pro_client::requests::generate_video::generate_video::{KinoviGenerateVideoRequest, KinoviBatchCount, KinoviModelType, KinoviOutputResolution, KinoviAspectRatio};
|
||||
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::PlanArtcraftSeedance2p0;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_seedance2p0_fast(
|
||||
plan: &PlanArtcraftSeedance2p0,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let duration_seconds = plan.duration_seconds.unwrap_or(5).clamp(4, 15);
|
||||
|
||||
let batch_count = match plan.batch_count {
|
||||
Seedance2p0BatchCount::One => KinoviBatchCount::One,
|
||||
Seedance2p0BatchCount::Two => KinoviBatchCount::Two,
|
||||
Seedance2p0BatchCount::Four => KinoviBatchCount::Four,
|
||||
};
|
||||
|
||||
let output_resolution = plan.resolution.map(map_common_resolution_to_kinovi);
|
||||
|
||||
let request = KinoviGenerateVideoRequest {
|
||||
model_type: KinoviModelType::Seedance2Fast,
|
||||
prompt: String::new(),
|
||||
aspect_ratio: KinoviAspectRatio::Square1x1,
|
||||
duration_seconds,
|
||||
batch_count,
|
||||
output_resolution,
|
||||
|
||||
// TODO: This is a cost factor
|
||||
reference_video_urls: None,
|
||||
|
||||
// NB: These do not contribute to costs in the Seedance2 integration
|
||||
start_frame_url: None,
|
||||
end_frame_url: None,
|
||||
reference_image_urls: None,
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = request.estimate_cost_in_usd_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_common_resolution_to_kinovi(resolution: CommonResolution) -> KinoviOutputResolution {
|
||||
match resolution {
|
||||
CommonResolution::FourEightyP => KinoviOutputResolution::FourEightyP,
|
||||
CommonResolution::SevenTwentyP => KinoviOutputResolution::SevenTwentyP,
|
||||
CommonResolution::TenEightyP => KinoviOutputResolution::TenEightyP,
|
||||
// For resolutions that don't map directly, default to 720p
|
||||
_ => KinoviOutputResolution::SevenTwentyP,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0Fast,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
fn estimate_usd_cents_downgrade(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0Fast,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayLessDowngrade,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
fn estimate_pro_usd_cents(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
// -- 720p (default / legacy pricing: 220 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn fast_720p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP)), 51);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)), 64);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP)), 127);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP)), 191);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_720p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::SevenTwentyP)), 127);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_720p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::SevenTwentyP)), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_none_resolution_same_as_720p() {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(5, 1, None),
|
||||
estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
|
||||
// -- 480p (new pricing: 193 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn fast_480p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::FourEightyP)), 21);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP)), 26);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::FourEightyP)), 52);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::FourEightyP)), 78);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_480p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::FourEightyP)), 52);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_480p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::FourEightyP)), 104);
|
||||
}
|
||||
|
||||
// -- 1080p is not supported by Fast; ErrorOut rejects it --
|
||||
|
||||
#[test]
|
||||
fn fast_1080p_error_out_rejects() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0Fast,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: Some(CommonResolution::TenEightyP),
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
};
|
||||
assert!(request.build().is_err());
|
||||
}
|
||||
|
||||
// -- 1080p rounds down to 720p with PayLessDowngrade --
|
||||
|
||||
#[test]
|
||||
fn fast_1080p_downgraded_same_as_720p() {
|
||||
for duration in [4, 5, 10, 15] {
|
||||
assert_eq!(
|
||||
estimate_usd_cents_downgrade(duration, 1, Some(CommonResolution::TenEightyP)),
|
||||
estimate_usd_cents(duration, 1, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Relative pricing --
|
||||
|
||||
#[test]
|
||||
fn fast_480p_cheaper_than_720p() {
|
||||
let c480 = estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP));
|
||||
let c720 = estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(c480 < c720, "480p ({}) should be cheaper than 720p ({})", c480, c720);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_is_cheaper_than_pro() {
|
||||
for res in [
|
||||
Some(CommonResolution::FourEightyP),
|
||||
Some(CommonResolution::SevenTwentyP),
|
||||
] {
|
||||
for duration in [4, 5, 10, 15] {
|
||||
let fast = estimate_usd_cents(duration, 1, res);
|
||||
let pro = estimate_pro_usd_cents(duration, 1, res);
|
||||
assert!(
|
||||
fast < pro,
|
||||
"Fast ({}) should be cheaper than Pro ({}) at {}s {:?}",
|
||||
fast, pro, duration, res,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_duration() {
|
||||
let c4 = estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c10 = estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c15 = estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(c4 < c10);
|
||||
assert!(c10 < c15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_batch() {
|
||||
let b1 = estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let b2 = estimate_usd_cents(5, 2, Some(CommonResolution::SevenTwentyP));
|
||||
let b4 = estimate_usd_cents(5, 4, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(b1 < b2);
|
||||
assert!(b2 < b4);
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
use artcraft_api_defs::generate::video::generate_seedance_1_0_lite_image_to_video::GenerateSeedance10LiteResolution;
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::image::enqueue_seedance_1_lite_image_to_video_webhook::{
|
||||
Seedance1LiteDuration, Seedance1LiteRequest, Seedance1LiteResolution,
|
||||
};
|
||||
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance_1_0_lite::PlanArtcraftSeedance10Lite;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_seedance_1_0_lite(
|
||||
plan: &PlanArtcraftSeedance10Lite,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Legacy handler defaults: 5 seconds, 720p. Delegate to the Fal client's
|
||||
// cost calculator to guarantee parity with what we actually charge.
|
||||
let duration = if plan.duration_seconds_for_cost() == 10 {
|
||||
Seedance1LiteDuration::TenSeconds
|
||||
} else {
|
||||
Seedance1LiteDuration::FiveSeconds
|
||||
};
|
||||
let resolution = match plan.resolution_for_cost() {
|
||||
GenerateSeedance10LiteResolution::FourEightyP => Seedance1LiteResolution::FourEightyP,
|
||||
GenerateSeedance10LiteResolution::SevenTwentyP => Seedance1LiteResolution::SevenTwentyP,
|
||||
};
|
||||
|
||||
let req = Seedance1LiteRequest {
|
||||
image_url: String::new(),
|
||||
end_frame_image_url: None,
|
||||
prompt: String::new(),
|
||||
duration,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
camera_fixed: false,
|
||||
seed: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = req.calculate_cost_in_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_sora_2::PlanArtcraftSora2;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_sora_2(
|
||||
plan: &PlanArtcraftSora2,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Sora 2: $0.10/second. Default duration (Fal client): 4 seconds.
|
||||
let cost_in_usd_cents = plan.duration_seconds_for_cost() * 10;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_sora_2_pro::PlanArtcraftSora2Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_sora_2_pro(
|
||||
plan: &PlanArtcraftSora2Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Sora 2 Pro: $0.30/second @ 720p, $0.50/second @ 1080p.
|
||||
// Default duration 4s. Default resolution: 1080p for text-to-video, 720p (Auto) for image-to-video.
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.is_ten_eighty_p_for_cost() { 50 } else { 30 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_2::PlanArtcraftVeo2;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_veo_2(
|
||||
plan: &PlanArtcraftVeo2,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client veo_2 cost calculator: 5s = $2.50, +$0.50/s above 5s.
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let extra = seconds.saturating_sub(5);
|
||||
let cost_in_usd_cents = 250 + extra * 50;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3::PlanArtcraftVeo3;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_veo_3(
|
||||
plan: &PlanArtcraftVeo3,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client veo_3 cost calculator (720p/1080p):
|
||||
// $0.20/sec audio off, $0.40/sec audio on. Default 8s, audio off (legacy handler).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio_for_cost() { 40 } else { 20 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3_fast::PlanArtcraftVeo3Fast;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_veo_3_fast(
|
||||
plan: &PlanArtcraftVeo3Fast,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client veo_3_fast cost calculator (720p/1080p):
|
||||
// $0.10/sec audio off, $0.15/sec audio on. Default 8s, audio off (legacy handler).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio_for_cost() { 15 } else { 10 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3p1::PlanArtcraftVeo3p1;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_veo_3p1(
|
||||
plan: &PlanArtcraftVeo3p1,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client veo_3p1 cost calculator (720p/1080p):
|
||||
// $0.20/sec audio off, $0.40/sec audio on. Default 6s, audio on (legacy handler).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio_for_cost() { 40 } else { 20 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3p1_fast::PlanArtcraftVeo3p1Fast;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_artcraft_veo_3p1_fast(
|
||||
plan: &PlanArtcraftVeo3p1Fast,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client veo_3p1_fast cost calculator (720p/1080p):
|
||||
// $0.10/sec audio off, $0.15/sec audio on. Default 6s, audio on (legacy handler).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio_for_cost() { 15 } else { 10 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
pub mod estimate_video_cost_artcraft_kling_1_6_pro;
|
||||
pub mod estimate_video_cost_artcraft_kling_2_1_master;
|
||||
pub mod estimate_video_cost_artcraft_kling_2_1_pro;
|
||||
pub mod estimate_video_cost_artcraft_kling_2_5_turbo_pro;
|
||||
pub mod estimate_video_cost_artcraft_kling_2_6_pro;
|
||||
pub mod estimate_video_cost_artcraft_kling3p0_pro;
|
||||
pub mod estimate_video_cost_artcraft_kling3p0_standard;
|
||||
pub mod estimate_video_cost_artcraft_seedance_1_0_lite;
|
||||
pub mod estimate_video_cost_artcraft_seedance1p5_pro;
|
||||
pub mod estimate_video_cost_artcraft_seedance2p0;
|
||||
pub mod estimate_video_cost_artcraft_seedance2p0_fast;
|
||||
pub mod estimate_video_cost_artcraft_sora_2;
|
||||
pub mod estimate_video_cost_artcraft_sora_2_pro;
|
||||
pub mod estimate_video_cost_artcraft_veo_2;
|
||||
pub mod estimate_video_cost_artcraft_veo_3;
|
||||
pub mod estimate_video_cost_artcraft_veo_3_fast;
|
||||
pub mod estimate_video_cost_artcraft_veo_3p1;
|
||||
pub mod estimate_video_cost_artcraft_veo_3p1_fast;
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_1_6_pro::PlanFalKling16Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_1_6_pro(
|
||||
plan: &PlanFalKling16Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v1p6_pro: 5s = 48¢, 10s = 95¢ (Default → 5s).
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 95 } else { 48 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_1_master::PlanFalKling21Master;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_2_1_master(
|
||||
plan: &PlanFalKling21Master,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p1_master: 5s = $1.40, 10s = $2.80.
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 280 } else { 140 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_1_pro::PlanFalKling21Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_2_1_pro(
|
||||
plan: &PlanFalKling21Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p1_pro: 5s = 45¢, 10s = 90¢.
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 90 } else { 45 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_5_turbo_pro::PlanFalKling2p5TurboPro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_2_5_turbo_pro(
|
||||
plan: &PlanFalKling2p5TurboPro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p5_turbo_pro: 5s = 35¢, 10s = 70¢. None → 5s.
|
||||
let cost_in_usd_cents: u64 = if plan.is_ten_seconds() { 70 } else { 35 };
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_6_pro::PlanFalKling2p6Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_2_6_pro(
|
||||
plan: &PlanFalKling2p6Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_v2p6_pro:
|
||||
// audio off: $0.07/sec → 5s=35¢, 10s=70¢
|
||||
// audio on: $0.14/sec → 5s=70¢, 10s=140¢
|
||||
// Default duration is 5s; default generate_audio is true.
|
||||
let cost_in_usd_cents: u64 = match (plan.generate_audio_for_cost(), plan.is_ten_seconds()) {
|
||||
(false, false) => 35,
|
||||
(false, true) => 70,
|
||||
(true, false) => 70,
|
||||
(true, true) => 140,
|
||||
};
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_pro::PlanFalKling3p0Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_3p0_pro(
|
||||
plan: &PlanFalKling3p0Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_3p0_pro:
|
||||
// audio off: $0.224/sec (rate=224 in tenths-of-cents)
|
||||
// audio on: $0.336/sec (rate=336)
|
||||
// ceiling-divided to whole cents.
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let rate: u64 = if plan.generate_audio_for_cost() { 336 } else { 224 };
|
||||
let cost_in_usd_cents = (rate * seconds + 9) / 10;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_standard::PlanFalKling3p0Standard;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_kling_3p0_standard(
|
||||
plan: &PlanFalKling3p0Standard,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Mirrors fal_client kling_3p0_standard:
|
||||
// audio off: $0.168/sec (rate=168)
|
||||
// audio on: $0.252/sec (rate=252)
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let rate: u64 = if plan.generate_audio_for_cost() { 252 } else { 168 };
|
||||
let cost_in_usd_cents = (rate * seconds + 9) / 10;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::image::enqueue_seedance_1_lite_image_to_video_webhook::Seedance1LiteRequest;
|
||||
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_seedance_1_0_lite::PlanFalSeedance10Lite;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_seedance_1_0_lite(
|
||||
plan: &PlanFalSeedance10Lite,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Delegate to the Fal client's cost calculator to guarantee parity with billing.
|
||||
let req = Seedance1LiteRequest {
|
||||
image_url: plan.image_url.clone(),
|
||||
end_frame_image_url: plan.end_image_url.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration,
|
||||
resolution: plan.resolution,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
camera_fixed: false,
|
||||
seed: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = req.calculate_cost_in_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::text::enqueue_seedance_1p5_pro_text_to_video_webhook::{
|
||||
EnqueueSeedance1p5ProTextToVideoRequest, EnqueueSeedance1p5ProTextToVideoDuration,
|
||||
EnqueueSeedance1p5ProTextToVideoResolution,
|
||||
};
|
||||
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_seedance_1p5_pro::{
|
||||
FalSeedance1p5ProDuration, FalSeedance1p5ProResolution, PlanFalSeedance1p5Pro,
|
||||
};
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_seedance_1p5_pro(
|
||||
plan: &PlanFalSeedance1p5Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Seedance 1.5 Pro t2v and i2v have identical pricing; delegate to the t2v
|
||||
// calculator to guarantee parity with billing.
|
||||
let req = EnqueueSeedance1p5ProTextToVideoRequest {
|
||||
prompt: String::new(),
|
||||
resolution: plan.resolution.map(to_t2v_resolution),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: None,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = req.calculate_cost_in_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalSeedance1p5ProResolution) -> EnqueueSeedance1p5ProTextToVideoResolution {
|
||||
match r {
|
||||
FalSeedance1p5ProResolution::FourEightyP => EnqueueSeedance1p5ProTextToVideoResolution::FourEightyP,
|
||||
FalSeedance1p5ProResolution::SevenTwentyP => EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP,
|
||||
FalSeedance1p5ProResolution::TenEightyP => EnqueueSeedance1p5ProTextToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalSeedance1p5ProDuration) -> EnqueueSeedance1p5ProTextToVideoDuration {
|
||||
use EnqueueSeedance1p5ProTextToVideoDuration as T;
|
||||
match d {
|
||||
FalSeedance1p5ProDuration::Four => T::FourSeconds,
|
||||
FalSeedance1p5ProDuration::Five => T::FiveSeconds,
|
||||
FalSeedance1p5ProDuration::Six => T::SixSeconds,
|
||||
FalSeedance1p5ProDuration::Seven => T::SevenSeconds,
|
||||
FalSeedance1p5ProDuration::Eight => T::EightSeconds,
|
||||
FalSeedance1p5ProDuration::Nine => T::NineSeconds,
|
||||
FalSeedance1p5ProDuration::Ten => T::TenSeconds,
|
||||
FalSeedance1p5ProDuration::Eleven => T::ElevenSeconds,
|
||||
FalSeedance1p5ProDuration::Twelve => T::TwelveSeconds,
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_sora_2::PlanFalSora2;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_sora_2(
|
||||
plan: &PlanFalSora2,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Sora 2: $0.10/second. Default duration (Fal client): 4s.
|
||||
let cost_in_usd_cents = plan.duration_seconds_for_cost() * 10;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_sora_2_pro::PlanFalSora2Pro;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_sora_2_pro(
|
||||
plan: &PlanFalSora2Pro,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// Sora 2 Pro: $0.30/second @ 720p, $0.50/second @ 1080p.
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.is_ten_eighty_p_for_cost() { 50 } else { 30 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_2::PlanFalVeo2;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_veo_2(
|
||||
plan: &PlanFalVeo2,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// 5s = $2.50, +$0.50 per additional second.
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let extra = seconds.saturating_sub(5);
|
||||
let cost_in_usd_cents = 250 + extra * 50;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3::PlanFalVeo3;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_veo_3(
|
||||
plan: &PlanFalVeo3,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// $0.20/sec audio off, $0.40/sec audio on (720p/1080p).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio { 40 } else { 20 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3_fast::PlanFalVeo3Fast;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_veo_3_fast(
|
||||
plan: &PlanFalVeo3Fast,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// $0.10/sec audio off, $0.15/sec audio on (720p/1080p).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio { 15 } else { 10 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1::PlanFalVeo3p1;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_veo_3p1(
|
||||
plan: &PlanFalVeo3p1,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// $0.20/sec audio off, $0.40/sec audio on (720p/1080p).
|
||||
let seconds = plan.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.generate_audio_for_cost() { 40 } else { 20 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1_fast::PlanFalVeo3p1Fast;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_fal_veo_3p1_fast(
|
||||
plan: &PlanFalVeo3p1Fast,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// $0.10/sec audio off, $0.15/sec audio on (720p/1080p).
|
||||
let seconds = plan.inner.duration_seconds_for_cost();
|
||||
let per_second_cents: u64 = if plan.inner.generate_audio_for_cost() { 15 } else { 10 };
|
||||
let cost_in_usd_cents = per_second_cents * seconds;
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_usd_cents),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
pub(crate) mod estimate_video_cost_fal_kling_1_6_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_kling_2_1_master;
|
||||
pub(crate) mod estimate_video_cost_fal_kling_2_1_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_kling_2_5_turbo_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_kling_2_6_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_kling_3p0_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_kling_3p0_standard;
|
||||
pub(crate) mod estimate_video_cost_fal_seedance_1_0_lite;
|
||||
pub(crate) mod estimate_video_cost_fal_seedance_1p5_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_sora_2;
|
||||
pub(crate) mod estimate_video_cost_fal_sora_2_pro;
|
||||
pub(crate) mod estimate_video_cost_fal_veo_2;
|
||||
pub(crate) mod estimate_video_cost_fal_veo_3;
|
||||
pub(crate) mod estimate_video_cost_fal_veo_3_fast;
|
||||
pub(crate) mod estimate_video_cost_fal_veo_3p1;
|
||||
pub(crate) mod estimate_video_cost_fal_veo_3p1_fast;
|
||||
@@ -1,4 +0,0 @@
|
||||
pub (crate) mod artcraft;
|
||||
pub (crate) mod fal;
|
||||
pub (crate) mod muapi;
|
||||
pub (crate) mod seedance2pro;
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
use crate::generate::generate_video::plan::muapi::plan_generate_video_muapi_seedance2p0::{
|
||||
MuapiSeedance2p0Mode, PlanMuapiSeedance2p0,
|
||||
};
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
use muapi_client::creds::muapi_api_key::MuapiApiKey;
|
||||
use muapi_client::creds::muapi_session::MuapiSession;
|
||||
use muapi_client::requests::seedance_2p0_image_to_video::seedance_2p0_image_to_video::Seedance2p0ImageToVideoArgs;
|
||||
use muapi_client::requests::seedance_2p0_text_to_video::seedance_2p0_text_to_video::Seedance2p0TextToVideoArgs;
|
||||
|
||||
pub(crate) fn estimate_video_cost_muapi_seedance2p0(
|
||||
plan: &PlanMuapiSeedance2p0,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
// A dummy session is sufficient — cost estimation does not make any network calls.
|
||||
let dummy_session = MuapiSession::new(MuapiApiKey::new(String::new()));
|
||||
|
||||
let cost_in_usd_cents;
|
||||
let cost_in_credits;
|
||||
|
||||
match &plan.mode {
|
||||
MuapiSeedance2p0Mode::TextToVideo { aspect_ratio, duration, quality } => {
|
||||
let args = Seedance2p0TextToVideoArgs {
|
||||
session: &dummy_session,
|
||||
prompt: String::new(),
|
||||
aspect_ratio: *aspect_ratio,
|
||||
duration: *duration,
|
||||
quality: *quality,
|
||||
};
|
||||
|
||||
cost_in_usd_cents = args.estimate_cost_in_usd_cents();
|
||||
|
||||
// NB: This is off by a factor of 100 because they use a fractional system.
|
||||
cost_in_credits = (args.estimate_credits() * 100.0f64).round() as u64;
|
||||
}
|
||||
MuapiSeedance2p0Mode::ImageToVideo { aspect_ratio, duration, quality, .. } => {
|
||||
let args = Seedance2p0ImageToVideoArgs {
|
||||
session: &dummy_session,
|
||||
prompt: String::new(),
|
||||
image_urls: vec![],
|
||||
aspect_ratio: *aspect_ratio,
|
||||
duration: *duration,
|
||||
quality: *quality,
|
||||
};
|
||||
|
||||
cost_in_usd_cents = args.estimate_cost_in_usd_cents();
|
||||
|
||||
// NB: This is off by a factor of 100 because they use a fractional system.
|
||||
cost_in_credits = (args.estimate_credits() * 100.0f64).round() as u64;
|
||||
}
|
||||
}
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_credits),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
pub mod estimate_video_cost_muapi_seedance2p0;
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::PlanSeedance2proSeedance2p0;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_seedance2pro_seedance2p0(
|
||||
plan: &PlanSeedance2proSeedance2p0,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let cost_in_credits = plan.request.estimate_credits();
|
||||
let cost_in_usd_cents = plan.request.estimate_cost_in_usd_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_credits as u64),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0,
|
||||
provider: Provider::Seedance2Pro,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
// -- 720p (default / legacy pricing: 250 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn pro_720p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP)), 64);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)), 80);
|
||||
assert_eq!(estimate_usd_cents(6, 1, Some(CommonResolution::SevenTwentyP)), 96);
|
||||
assert_eq!(estimate_usd_cents(7, 1, Some(CommonResolution::SevenTwentyP)), 112);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP)), 160);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP)), 240);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_720p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(4, 2, Some(CommonResolution::SevenTwentyP)), 128);
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::SevenTwentyP)), 160);
|
||||
assert_eq!(estimate_usd_cents(15, 2, Some(CommonResolution::SevenTwentyP)), 480);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_720p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(4, 4, Some(CommonResolution::SevenTwentyP)), 256);
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::SevenTwentyP)), 320);
|
||||
assert_eq!(estimate_usd_cents(15, 4, Some(CommonResolution::SevenTwentyP)), 960);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_none_resolution_same_as_720p() {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(5, 1, None),
|
||||
estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
|
||||
// -- 480p (new pricing: 193 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn pro_480p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::FourEightyP)), 31);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP)), 39);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::FourEightyP)), 78);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::FourEightyP)), 117);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_480p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::FourEightyP)), 78);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_480p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::FourEightyP)), 155);
|
||||
}
|
||||
|
||||
// -- 1080p (new pricing: 193 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn pro_1080p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::TenEightyP)), 187);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP)), 233);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::TenEightyP)), 466);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::TenEightyP)), 699);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_1080p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::TenEightyP)), 466);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pro_1080p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::TenEightyP)), 933);
|
||||
}
|
||||
|
||||
// -- Relative pricing --
|
||||
|
||||
#[test]
|
||||
fn pro_480p_cheaper_than_720p_cheaper_than_1080p() {
|
||||
let c480 = estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP));
|
||||
let c720 = estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c1080 = estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP));
|
||||
assert!(c480 < c720, "480p ({}) should be cheaper than 720p ({})", c480, c720);
|
||||
assert!(c720 < c1080, "720p ({}) should be cheaper than 1080p ({})", c720, c1080);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_duration() {
|
||||
let c4 = estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c10 = estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c15 = estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(c4 < c10);
|
||||
assert!(c10 < c15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_batch() {
|
||||
let b1 = estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP));
|
||||
let b2 = estimate_usd_cents(5, 2, Some(CommonResolution::TenEightyP));
|
||||
let b4 = estimate_usd_cents(5, 4, Some(CommonResolution::TenEightyP));
|
||||
assert!(b1 < b2);
|
||||
assert!(b2 < b4);
|
||||
}
|
||||
}
|
||||
-212
@@ -1,212 +0,0 @@
|
||||
use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0_fast::PlanSeedance2proSeedance2p0Fast;
|
||||
use crate::generate::generate_video::video_generation_cost_estimate::VideoGenerationCostEstimate;
|
||||
|
||||
pub(crate) fn estimate_video_cost_seedance2pro_seedance2p0_fast(
|
||||
plan: &PlanSeedance2proSeedance2p0Fast,
|
||||
) -> VideoGenerationCostEstimate {
|
||||
let cost_in_credits = plan.request.estimate_credits();
|
||||
let cost_in_usd_cents = plan.request.estimate_cost_in_usd_cents();
|
||||
|
||||
VideoGenerationCostEstimate {
|
||||
cost_in_credits: Some(cost_in_credits as u64),
|
||||
cost_in_usd_cents: Some(cost_in_usd_cents),
|
||||
is_free: false,
|
||||
is_unlimited: false,
|
||||
is_rate_limited: false,
|
||||
has_watermark: false,
|
||||
failures_are_refunded: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
|
||||
fn estimate_usd_cents(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0Fast,
|
||||
provider: Provider::Seedance2Pro,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
fn estimate_pro_usd_cents(
|
||||
duration_seconds: u16,
|
||||
video_batch_count: u16,
|
||||
resolution: Option<CommonResolution>,
|
||||
) -> u64 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0,
|
||||
provider: Provider::Seedance2Pro,
|
||||
prompt: None,
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
idempotency_token: None,
|
||||
};
|
||||
request.build()
|
||||
.expect("build should succeed")
|
||||
.estimate_costs()
|
||||
.cost_in_usd_cents
|
||||
.expect("cost_in_usd_cents should be present")
|
||||
}
|
||||
|
||||
// -- 720p (default / legacy pricing: 220 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn fast_720p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP)), 51);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)), 64);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP)), 127);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP)), 191);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_720p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::SevenTwentyP)), 127);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_720p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::SevenTwentyP)), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_none_resolution_same_as_720p() {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(5, 1, None),
|
||||
estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
|
||||
// -- 480p (new pricing: 193 credits/$1) --
|
||||
|
||||
#[test]
|
||||
fn fast_480p_batch_1() {
|
||||
assert_eq!(estimate_usd_cents(4, 1, Some(CommonResolution::FourEightyP)), 21);
|
||||
assert_eq!(estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP)), 26);
|
||||
assert_eq!(estimate_usd_cents(10, 1, Some(CommonResolution::FourEightyP)), 52);
|
||||
assert_eq!(estimate_usd_cents(15, 1, Some(CommonResolution::FourEightyP)), 78);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_480p_batch_2() {
|
||||
assert_eq!(estimate_usd_cents(5, 2, Some(CommonResolution::FourEightyP)), 52);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_480p_batch_4() {
|
||||
assert_eq!(estimate_usd_cents(5, 4, Some(CommonResolution::FourEightyP)), 104);
|
||||
}
|
||||
|
||||
// -- 1080p maps to 720p for Fast (Kinovi doesn't support 1080p fast) --
|
||||
|
||||
#[test]
|
||||
fn fast_1080p_same_as_720p() {
|
||||
for duration in [4, 5, 10, 15] {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(duration, 1, Some(CommonResolution::TenEightyP)),
|
||||
estimate_usd_cents(duration, 1, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_1080p_batch_2() {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(5, 2, Some(CommonResolution::TenEightyP)),
|
||||
estimate_usd_cents(5, 2, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_1080p_batch_4() {
|
||||
assert_eq!(
|
||||
estimate_usd_cents(5, 4, Some(CommonResolution::TenEightyP)),
|
||||
estimate_usd_cents(5, 4, Some(CommonResolution::SevenTwentyP)),
|
||||
);
|
||||
}
|
||||
|
||||
// -- Relative pricing --
|
||||
|
||||
#[test]
|
||||
fn fast_480p_cheaper_than_720p() {
|
||||
let c480 = estimate_usd_cents(5, 1, Some(CommonResolution::FourEightyP));
|
||||
let c720 = estimate_usd_cents(5, 1, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(c480 < c720, "480p ({}) should be cheaper than 720p ({})", c480, c720);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fast_is_cheaper_than_pro() {
|
||||
for res in [
|
||||
Some(CommonResolution::FourEightyP),
|
||||
Some(CommonResolution::SevenTwentyP),
|
||||
Some(CommonResolution::TenEightyP),
|
||||
] {
|
||||
for duration in [4, 5, 10, 15] {
|
||||
let fast = estimate_usd_cents(duration, 1, res);
|
||||
let pro = estimate_pro_usd_cents(duration, 1, res);
|
||||
assert!(
|
||||
fast < pro,
|
||||
"Fast ({}) should be cheaper than Pro ({}) at {}s {:?}",
|
||||
fast, pro, duration, res,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_duration() {
|
||||
let c4 = estimate_usd_cents(4, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c10 = estimate_usd_cents(10, 1, Some(CommonResolution::SevenTwentyP));
|
||||
let c15 = estimate_usd_cents(15, 1, Some(CommonResolution::SevenTwentyP));
|
||||
assert!(c4 < c10);
|
||||
assert!(c10 < c15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cost_scales_with_batch() {
|
||||
let b1 = estimate_usd_cents(5, 1, Some(CommonResolution::TenEightyP));
|
||||
let b2 = estimate_usd_cents(5, 2, Some(CommonResolution::TenEightyP));
|
||||
let b4 = estimate_usd_cents(5, 4, Some(CommonResolution::TenEightyP));
|
||||
assert!(b1 < b2);
|
||||
assert!(b2 < b4);
|
||||
}
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
pub mod estimate_video_cost_seedance2pro_seedance2p0;
|
||||
pub mod estimate_video_cost_seedance2pro_seedance2p0_fast;
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_pro::PlanArtcraftKling3p0Pro;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_3p0_pro_multi_function_video_gen::Kling3p0ProMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::kling_3p0_pro_multi_function_video_gen::kling_3p0_pro_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_kling3p0_pro(
|
||||
plan: &PlanArtcraftKling3p0Pro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling3p0ProMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
image_media_token: plan.start_frame.clone(),
|
||||
end_image_media_token: plan.end_frame.clone(),
|
||||
duration: plan.duration,
|
||||
generate_audio: plan.generate_audio,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = kling_3p0_pro_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_standard::PlanArtcraftKling3p0Standard;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_3p0_standard_multi_function_video_gen::Kling3p0StandardMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::kling_3p0_standard_multi_function_video_gen::kling_3p0_standard_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_kling3p0_standard(
|
||||
plan: &PlanArtcraftKling3p0Standard,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling3p0StandardMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
image_media_token: plan.start_frame.clone(),
|
||||
end_image_media_token: plan.end_frame.clone(),
|
||||
duration: plan.duration,
|
||||
generate_audio: plan.generate_audio,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = kling_3p0_standard_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::PlanArtcraftKling16Pro;
|
||||
use artcraft_api_defs::generate::video::generate_kling_1_6_pro_image_to_video::GenerateKling16ProImageToVideoRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_kling_16_pro_image_to_video::generate_kling_16_pro_image_to_video;
|
||||
|
||||
pub async fn execute_artcraft_kling_1_6_pro(
|
||||
plan: &PlanArtcraftKling16Pro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = GenerateKling16ProImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: Some(plan.start_frame.clone()),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
duration: plan.duration,
|
||||
};
|
||||
|
||||
let response = generate_kling_16_pro_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::PlanArtcraftKling21Master;
|
||||
use artcraft_api_defs::generate::video::generate_kling_2_1_master_image_to_video::GenerateKling21MasterImageToVideoRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_kling_21_master_image_to_video::generate_kling_21_master_image_to_video;
|
||||
|
||||
pub async fn execute_artcraft_kling_2_1_master(
|
||||
plan: &PlanArtcraftKling21Master,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = GenerateKling21MasterImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: Some(plan.start_frame.clone()),
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
duration: plan.duration,
|
||||
};
|
||||
|
||||
let response = generate_kling_21_master_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::PlanArtcraftKling21Pro;
|
||||
use artcraft_api_defs::generate::video::generate_kling_2_1_pro_image_to_video::GenerateKling21ProImageToVideoRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_kling_21_pro_image_to_video::generate_kling_21_pro_image_to_video;
|
||||
|
||||
pub async fn execute_artcraft_kling_2_1_pro(
|
||||
plan: &PlanArtcraftKling21Pro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = GenerateKling21ProImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: Some(plan.start_frame.clone()),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
duration: plan.duration,
|
||||
};
|
||||
|
||||
let response = generate_kling_21_pro_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::PlanArtcraftKling2p5TurboPro;
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_2_5_turbo_multi_function_video_gen::Kling2p5TurboProMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::kling_2p5_turbo_pro_multi_function_video_gen::kling_2p5_turbo_pro_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_kling_2_5_turbo_pro(
|
||||
plan: &PlanArtcraftKling2p5TurboPro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling2p5TurboProMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
start_frame_image_media_token: plan.start_frame.clone(),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = kling_2p5_turbo_pro_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::PlanArtcraftKling2p6Pro;
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_2_6_multi_function_video_gen::Kling2p6ProMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::kling_2p6_pro_multi_function_video_gen::kling_2p6_pro_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_kling_2_6_pro(
|
||||
plan: &PlanArtcraftKling2p6Pro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling2p6ProMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
start_frame_image_media_token: plan.start_frame.clone(),
|
||||
duration: plan.duration,
|
||||
generate_audio: plan.generate_audio,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = kling_2p6_pro_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance1p5_pro::PlanArtcraftSeedance1p5Pro;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_1p5_pro_multi_function_video_gen::Seedance1p5ProMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::seedance_1p5_pro_multi_function_video_gen::seedance_1p5_pro_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_seedance1p5_pro(
|
||||
plan: &PlanArtcraftSeedance1p5Pro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Seedance1p5ProMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
start_frame_image_media_token: plan.start_frame.clone(),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
duration: plan.duration,
|
||||
resolution: plan.resolution,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let response = seedance_1p5_pro_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::PlanArtcraftSeedance2p0;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::api::common_resolution::CommonResolution as CommonResolutionRouter;
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::{
|
||||
Seedance2p0MultiFunctionVideoGenRequest, Seedance2p0OutputResolution,
|
||||
};
|
||||
use artcraft_client::endpoints::generate::video::multi_function::seedance_2p0_multi_function_video_gen::seedance_2p0_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_seedance2p0(
|
||||
plan: &PlanArtcraftSeedance2p0,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let output_resolution = plan.resolution.map(|r| match r {
|
||||
CommonResolutionRouter::FourEightyP => Seedance2p0OutputResolution::FourEightyP,
|
||||
CommonResolutionRouter::SevenTwentyP => Seedance2p0OutputResolution::SevenTwentyP,
|
||||
CommonResolutionRouter::TenEightyP => Seedance2p0OutputResolution::TenEightyP,
|
||||
_ => Seedance2p0OutputResolution::SevenTwentyP,
|
||||
});
|
||||
|
||||
let request = Seedance2p0MultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
start_frame_media_token: plan.start_frame.clone(),
|
||||
end_frame_media_token: plan.end_frame.clone(),
|
||||
reference_image_media_tokens: plan.reference_images.clone(),
|
||||
reference_video_media_tokens: plan.reference_videos.clone(),
|
||||
reference_audio_media_tokens: plan.reference_audio.clone(),
|
||||
reference_character_tokens: plan.reference_characters.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
output_resolution,
|
||||
duration_seconds: plan.duration_seconds,
|
||||
batch_count: Some(plan.batch_count),
|
||||
};
|
||||
|
||||
let response = seedance_2p0_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: response.all_inference_job_tokens,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use crate::test_helpers::{base_video_request, get_artcraft_client};
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_text_to_video_seedance_2p0() {
|
||||
let client = get_artcraft_client();
|
||||
let request = crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
video_batch_count: Some(1),
|
||||
prompt: Some("a cat walking through a cyberpunk city at night".to_string()),
|
||||
..base_video_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job token: {:?}", payload.inference_job_token);
|
||||
println!("All job tokens: {:?}", payload.all_inference_job_tokens);
|
||||
|
||||
assert_eq!(1, 2); // NB: Intentional failure to inspect the response above.
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_text_to_video_seedance_2p0_batch_two() {
|
||||
let client = get_artcraft_client();
|
||||
let request = crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::Square),
|
||||
video_batch_count: Some(2),
|
||||
prompt: Some("a dog surfing a wave, cinematic".to_string()),
|
||||
..base_video_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job tokens ({} total):", payload.all_inference_job_tokens.len());
|
||||
for token in &payload.all_inference_job_tokens {
|
||||
println!(" {:?}", token);
|
||||
}
|
||||
|
||||
assert_eq!(1, 2); // NB: Intentional failure to inspect the response above.
|
||||
}
|
||||
}
|
||||
-236
@@ -1,236 +0,0 @@
|
||||
use crate::api::common_resolution::CommonResolution as CommonResolutionRouter;
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::PlanArtcraftSeedance2p0;
|
||||
use artcraft_api_defs::generate::video::multi_function::seedance_2p0_multi_function_video_gen::{Seedance2p0AspectRatio, Seedance2p0BatchCount};
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use artcraft_client::endpoints::omni_gen::generate::video::omni_gen_video::omni_gen_video_generate;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
|
||||
/// Execute Seedance 2.0 Fast via the Artcraft omni-gen video endpoint.
|
||||
///
|
||||
/// Unlike Seedance 2.0 Pro (which uses the legacy dedicated endpoint),
|
||||
/// Seedance 2.0 Fast routes through the omni-gen unified video endpoint.
|
||||
pub async fn execute_artcraft_seedance2p0_fast(
|
||||
plan: &PlanArtcraftSeedance2p0,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let aspect_ratio = plan.aspect_ratio.map(|ar| match ar {
|
||||
Seedance2p0AspectRatio::Landscape16x9 => CommonAspectRatio::WideSixteenByNine,
|
||||
Seedance2p0AspectRatio::Portrait9x16 => CommonAspectRatio::TallNineBySixteen,
|
||||
Seedance2p0AspectRatio::Square1x1 => CommonAspectRatio::Square,
|
||||
Seedance2p0AspectRatio::Standard4x3 => CommonAspectRatio::WideFourByThree,
|
||||
Seedance2p0AspectRatio::Portrait3x4 => CommonAspectRatio::TallThreeByFour,
|
||||
});
|
||||
|
||||
let resolution = plan.resolution.map(|r| match r {
|
||||
CommonResolutionRouter::FourEightyP => CommonResolution::FourEightyP,
|
||||
CommonResolutionRouter::SevenTwentyP => CommonResolution::SevenTwentyP,
|
||||
CommonResolutionRouter::TenEightyP => CommonResolution::TenEightyP,
|
||||
CommonResolutionRouter::HalfK => CommonResolution::HalfK,
|
||||
CommonResolutionRouter::OneK => CommonResolution::OneK,
|
||||
CommonResolutionRouter::TwoK => CommonResolution::TwoK,
|
||||
CommonResolutionRouter::ThreeK => CommonResolution::ThreeK,
|
||||
CommonResolutionRouter::FourK => CommonResolution::FourK,
|
||||
});
|
||||
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
idempotency_token: Some(plan.idempotency_token.clone()),
|
||||
model: Some(CommonVideoModel::Seedance2p0Fast),
|
||||
prompt: plan.prompt.clone(),
|
||||
start_frame_image_media_token: plan.start_frame.clone(),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
reference_image_media_tokens: plan.reference_images.clone(),
|
||||
reference_video_media_tokens: plan.reference_videos.clone(),
|
||||
reference_audio_media_tokens: plan.reference_audio.clone(),
|
||||
reference_character_tokens: plan.reference_characters.clone(),
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
duration_seconds: plan.duration_seconds.map(|d| d as u16),
|
||||
video_batch_count: Some(match plan.batch_count {
|
||||
Seedance2p0BatchCount::One => 1,
|
||||
Seedance2p0BatchCount::Two => 2,
|
||||
Seedance2p0BatchCount::Four => 4,
|
||||
}),
|
||||
quality: None,
|
||||
generate_audio: None,
|
||||
negative_prompt: None,
|
||||
};
|
||||
|
||||
let response = omni_gen_video_generate(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::common_resolution::CommonResolution;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use crate::test_helpers::{base_video_request, get_artcraft_client};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
fn fast_request() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Seedance2p0Fast,
|
||||
prompt: Some("a corgi walking through a cyberpunk city at night".to_string()),
|
||||
..base_video_request()
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_text_to_video_fast_720p_wide() {
|
||||
let client = get_artcraft_client();
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
resolution: Some(CommonResolution::SevenTwentyP),
|
||||
video_batch_count: Some(1),
|
||||
..fast_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job token: {:?}", payload.inference_job_token);
|
||||
assert_eq!(1, 2); // NB: Intentional failure to inspect the response above.
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_text_to_video_fast_480p_square() {
|
||||
let client = get_artcraft_client();
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::Square),
|
||||
resolution: Some(CommonResolution::FourEightyP),
|
||||
video_batch_count: Some(1),
|
||||
..fast_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job token: {:?}", payload.inference_job_token);
|
||||
assert_eq!(1, 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_image_to_video_fast_with_start_keyframe() {
|
||||
let client = get_artcraft_client();
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::MediaFileToken(
|
||||
MediaFileToken::new("mf_test_keyframe_placeholder".to_string()),
|
||||
)),
|
||||
aspect_ratio: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
resolution: Some(CommonResolution::SevenTwentyP),
|
||||
video_batch_count: Some(1),
|
||||
..fast_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job token: {:?}", payload.inference_job_token);
|
||||
assert_eq!(1, 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_text_to_video_fast_batch_2() {
|
||||
let client = get_artcraft_client();
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::TallNineBySixteen),
|
||||
resolution: Some(CommonResolution::SevenTwentyP),
|
||||
video_batch_count: Some(2),
|
||||
..fast_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job tokens ({} total):", payload.all_inference_job_tokens.len());
|
||||
for token in &payload.all_inference_job_tokens {
|
||||
println!(" {:?}", token);
|
||||
}
|
||||
assert_eq!(1, 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // manually run — fires a real API request and incurs cost
|
||||
async fn test_text_to_video_fast_with_reference_images() {
|
||||
let client = get_artcraft_client();
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
reference_images: Some(crate::api::image_list_ref::ImageListRef::MediaFileTokens(vec![
|
||||
MediaFileToken::new("mf_test_ref_image_placeholder".to_string()),
|
||||
])),
|
||||
aspect_ratio: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
resolution: Some(CommonResolution::SevenTwentyP),
|
||||
video_batch_count: Some(1),
|
||||
..fast_request()
|
||||
};
|
||||
|
||||
let plan = request.build().unwrap();
|
||||
let result = plan.generate_video(&client).await;
|
||||
println!("Result: {:?}", result);
|
||||
let response = result.expect("generate_video request failed");
|
||||
let payload = response.get_artcraft_payload().expect("expected Artcraft payload");
|
||||
println!("Job token: {:?}", payload.inference_job_token);
|
||||
assert_eq!(1, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_includes_resolution() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
resolution: Some(CommonResolution::FourEightyP),
|
||||
..fast_request()
|
||||
};
|
||||
let plan = request.build().unwrap();
|
||||
let VideoGenerationPlan::ArtcraftSeedance2p0Fast(p) = plan else { panic!("wrong variant") };
|
||||
assert!(matches!(p.resolution, Some(CommonResolution::FourEightyP)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_includes_characters() {
|
||||
use crate::api::character_list_ref::CharacterListRef;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
reference_character_tokens: Some(CharacterListRef::CharacterTokens(vec![
|
||||
CharacterToken::new("char_test123".to_string()),
|
||||
])),
|
||||
..fast_request()
|
||||
};
|
||||
let plan = request.build().unwrap();
|
||||
let VideoGenerationPlan::ArtcraftSeedance2p0Fast(p) = plan else { panic!("wrong variant") };
|
||||
assert!(p.reference_characters.is_some());
|
||||
assert_eq!(p.reference_characters.unwrap().len(), 1);
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance_1_0_lite::PlanArtcraftSeedance10Lite;
|
||||
use artcraft_api_defs::generate::video::generate_seedance_1_0_lite_image_to_video::GenerateSeedance10LiteImageToVideoRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_seedance_1_0_lite_image_to_video::generate_seedance_1_0_lite_image_to_video;
|
||||
|
||||
pub async fn execute_artcraft_seedance_1_0_lite(
|
||||
plan: &PlanArtcraftSeedance10Lite,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = GenerateSeedance10LiteImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: Some(plan.start_frame.clone()),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
resolution: plan.resolution,
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = generate_seedance_1_0_lite_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_sora_2::PlanArtcraftSora2;
|
||||
use artcraft_api_defs::generate::video::multi_function::sora_2_multi_function_video_gen::Sora2MultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::sora_2_multi_function_video_gen::sora_2_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_sora_2(
|
||||
plan: &PlanArtcraftSora2,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Sora2MultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
image_media_token: plan.start_frame.clone(),
|
||||
resolution: plan.resolution,
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = sora_2_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_sora_2_pro::PlanArtcraftSora2Pro;
|
||||
use artcraft_api_defs::generate::video::multi_function::sora_2_pro_multi_function_video_gen::Sora2ProMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::sora_2_pro_multi_function_video_gen::sora_2_pro_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_sora_2_pro(
|
||||
plan: &PlanArtcraftSora2Pro,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Sora2ProMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
image_media_token: plan.start_frame.clone(),
|
||||
resolution: plan.resolution,
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let response = sora_2_pro_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_2::PlanArtcraftVeo2;
|
||||
use artcraft_api_defs::generate::video::generate_veo_2_image_to_video::GenerateVeo2ImageToVideoRequest;
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_veo_2_image_to_video::generate_veo_2_image_to_video;
|
||||
use artcraft_client::endpoints::omni_gen::generate::video::omni_gen_video::omni_gen_video_generate;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
|
||||
pub async fn execute_artcraft_veo_2(
|
||||
plan: &PlanArtcraftVeo2,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let inference_job_token = match plan.start_frame.clone() {
|
||||
Some(media_file_token) => {
|
||||
// Image-to-video: use the legacy dedicated endpoint.
|
||||
let request = GenerateVeo2ImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: Some(media_file_token.to_owned()),
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: plan.aspect_ratio.clone(),
|
||||
duration: plan.duration.clone(),
|
||||
};
|
||||
|
||||
let response = generate_veo_2_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
response.inference_job_token
|
||||
}
|
||||
None => {
|
||||
// Text-to-video: use the omni-gen unified endpoint.
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
idempotency_token: Some(plan.idempotency_token.clone()),
|
||||
model: Some(CommonVideoModel::Veo2),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: None,
|
||||
start_frame_image_media_token: None,
|
||||
end_frame_image_media_token: None,
|
||||
reference_image_media_tokens: None,
|
||||
reference_video_media_tokens: None,
|
||||
reference_audio_media_tokens: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
quality: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
};
|
||||
|
||||
let response = omni_gen_video_generate(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
response.inference_job_token
|
||||
}
|
||||
};
|
||||
|
||||
let all_tokens = vec![inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3::PlanArtcraftVeo3;
|
||||
use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::GenerateVeo3ImageToVideoRequest;
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_veo_3_image_to_video::generate_veo_3_image_to_video;
|
||||
use artcraft_client::endpoints::omni_gen::generate::video::omni_gen_video::omni_gen_video_generate;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
|
||||
pub async fn execute_artcraft_veo_3(
|
||||
plan: &PlanArtcraftVeo3,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let inference_job_token = match plan.start_frame.clone() {
|
||||
Some(media_file_token) => {
|
||||
// Image-to-video: use the legacy dedicated endpoint.
|
||||
let request = GenerateVeo3ImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: Some(media_file_token.to_owned()),
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
resolution: plan.resolution,
|
||||
duration: plan.duration,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let response = generate_veo_3_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
response.inference_job_token
|
||||
}
|
||||
None => {
|
||||
// Text-to-video: use the omni-gen unified endpoint.
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
idempotency_token: Some(plan.idempotency_token.clone()),
|
||||
model: Some(CommonVideoModel::Veo3),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: None,
|
||||
start_frame_image_media_token: None,
|
||||
end_frame_image_media_token: None,
|
||||
reference_image_media_tokens: None,
|
||||
reference_video_media_tokens: None,
|
||||
reference_audio_media_tokens: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
quality: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let response = omni_gen_video_generate(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
response.inference_job_token
|
||||
}
|
||||
};
|
||||
|
||||
let all_tokens = vec![inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3_fast::PlanArtcraftVeo3Fast;
|
||||
use artcraft_api_defs::generate::video::generate_veo_3_fast_image_to_video::GenerateVeo3FastImageToVideoRequest;
|
||||
use artcraft_client::endpoints::generate::video::generate_veo_3_fast_image_to_video::generate_veo_3_fast_image_to_video;
|
||||
|
||||
pub async fn execute_artcraft_veo_3_fast(
|
||||
plan: &PlanArtcraftVeo3Fast,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = GenerateVeo3FastImageToVideoRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
media_file_token: plan.start_frame.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
resolution: plan.resolution,
|
||||
duration: plan.duration,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let response = generate_veo_3_fast_image_to_video(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3p1::PlanArtcraftVeo3p1;
|
||||
use artcraft_api_defs::generate::video::multi_function::veo_3p1_multi_function_video_gen::Veo3p1MultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::veo_3p1_multi_function_video_gen::veo_3p1_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_veo_3p1(
|
||||
plan: &PlanArtcraftVeo3p1,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Veo3p1MultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
start_frame_image_media_token: plan.start_frame.clone(),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
resolution: plan.resolution,
|
||||
generate_audio: plan.generate_audio,
|
||||
enhance_prompt: None,
|
||||
seed: None,
|
||||
auto_fix: None,
|
||||
};
|
||||
|
||||
let response = veo_3p1_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
ArtcraftVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3p1_fast::PlanArtcraftVeo3p1Fast;
|
||||
use artcraft_api_defs::generate::video::multi_function::veo_3p1_fast_multi_function_video_gen::Veo3p1FastMultiFunctionVideoGenRequest;
|
||||
use artcraft_client::endpoints::generate::video::multi_function::veo_3p1_fast_multi_function_video_gen::veo_3p1_fast_multi_function_video_gen;
|
||||
|
||||
pub async fn execute_artcraft_veo_3p1_fast(
|
||||
plan: &PlanArtcraftVeo3p1Fast,
|
||||
artcraft_client: &RouterArtcraftClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Veo3p1FastMultiFunctionVideoGenRequest {
|
||||
uuid_idempotency_token: plan.idempotency_token.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
start_frame_image_media_token: plan.start_frame.clone(),
|
||||
end_frame_image_media_token: plan.end_frame.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
resolution: plan.resolution,
|
||||
generate_audio: plan.generate_audio,
|
||||
enhance_prompt: None,
|
||||
seed: None,
|
||||
auto_fix: None,
|
||||
};
|
||||
|
||||
let response = veo_3p1_fast_multi_function_video_gen(
|
||||
&artcraft_client.api_host,
|
||||
Some(&artcraft_client.credentials),
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Storyteller(err)))?;
|
||||
|
||||
let all_tokens = vec![response.inference_job_token.clone()];
|
||||
Ok(GenerateVideoResponse::Artcraft(ArtcraftVideoResponsePayload {
|
||||
inference_job_token: response.inference_job_token,
|
||||
all_inference_job_tokens: all_tokens,
|
||||
}))
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
pub mod generate_video_artcraft_kling_1_6_pro;
|
||||
pub mod generate_video_artcraft_kling_2_1_master;
|
||||
pub mod generate_video_artcraft_kling_2_1_pro;
|
||||
pub mod generate_video_artcraft_kling_2_5_turbo_pro;
|
||||
pub mod generate_video_artcraft_kling_2_6_pro;
|
||||
pub mod generate_video_artcraft_kling3p0_pro;
|
||||
pub mod generate_video_artcraft_kling3p0_standard;
|
||||
pub mod generate_video_artcraft_seedance_1_0_lite;
|
||||
pub mod generate_video_artcraft_seedance1p5_pro;
|
||||
pub mod generate_video_artcraft_seedance2p0;
|
||||
pub mod generate_video_artcraft_seedance2p0_fast;
|
||||
pub mod generate_video_artcraft_sora_2;
|
||||
pub mod generate_video_artcraft_sora_2_pro;
|
||||
pub mod generate_video_artcraft_veo_2;
|
||||
pub mod generate_video_artcraft_veo_3;
|
||||
pub mod generate_video_artcraft_veo_3_fast;
|
||||
pub mod generate_video_artcraft_veo_3p1;
|
||||
pub mod generate_video_artcraft_veo_3p1_fast;
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_1_6_pro::PlanFalKling16Pro;
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_v1p6_pro_image_to_video_webhook::{
|
||||
enqueue_kling_v1p6_pro_image_to_video_webhook, Kling1p6ProArgs, Kling1p6ProRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_1_6_pro(
|
||||
plan: &PlanFalKling16Pro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling1p6ProRequest {
|
||||
image_url: plan.image_url.clone(),
|
||||
end_frame_image_url: plan.end_image_url.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let outbound_request: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
|
||||
let args = Kling1p6ProArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
|
||||
let webhook_response = enqueue_kling_v1p6_pro_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_1_master::PlanFalKling21Master;
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_v2p1_master_image_to_video_webhook::{
|
||||
enqueue_kling_v2p1_master_image_to_video_webhook, Kling2p1MasterArgs, Kling2p1MasterRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_2_1_master(
|
||||
plan: &PlanFalKling21Master,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling2p1MasterRequest {
|
||||
image_url: plan.image_url.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let outbound_request: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
|
||||
let args = Kling2p1MasterArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
|
||||
let webhook_response = enqueue_kling_v2p1_master_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_1_pro::PlanFalKling21Pro;
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_v2p1_pro_image_to_video_webhook::{
|
||||
enqueue_kling_v2p1_pro_image_to_video_webhook, Kling2p1ProArgs, Kling2p1ProRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_2_1_pro(
|
||||
plan: &PlanFalKling21Pro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Kling2p1ProRequest {
|
||||
image_url: plan.image_url.clone(),
|
||||
end_frame_image_url: plan.end_image_url.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
};
|
||||
|
||||
let outbound_request: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
|
||||
let args = Kling2p1ProArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
|
||||
let webhook_response = enqueue_kling_v2p1_pro_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_5_turbo_pro::{
|
||||
FalKling2p5TurboProAspectRatio, FalKling2p5TurboProDuration, FalKling2p5TurboProMode,
|
||||
PlanFalKling2p5TurboPro,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_v2p5_turbo_pro_image_to_video_webhook::{
|
||||
enqueue_kling_v2p5_turbo_pro_image_to_video_webhook, EnqueueKlingV2p5TurboProImageToVideoArgs,
|
||||
EnqueueKlingV2p5TurboProImageToVideoRequest, EnqueueKlingV2p5TurboProImageToVideoDurationSeconds,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_kling_v2p5_turbo_pro_text_to_video_webhook::{
|
||||
enqueue_kling_v2p5_turbo_pro_text_to_video_webhook, EnqueueKlingV2p5TurboProTextToVideoArgs,
|
||||
EnqueueKlingV2p5TurboProTextToVideoRequest, EnqueueKlingV2p5TurboProTextToVideoAspectRatio,
|
||||
EnqueueKlingV2p5TurboProTextToVideoDurationSeconds,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_2_5_turbo_pro(
|
||||
plan: &PlanFalKling2p5TurboPro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalKling2p5TurboProMode::TextToVideo => {
|
||||
let request = EnqueueKlingV2p5TurboProTextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKlingV2p5TurboProTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_v2p5_turbo_pro_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalKling2p5TurboProMode::ImageToVideo { image_url, end_image_url } => {
|
||||
let request = EnqueueKlingV2p5TurboProImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
tail_image_url: end_image_url.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKlingV2p5TurboProImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_v2p5_turbo_pro_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalKling2p5TurboProDuration) -> EnqueueKlingV2p5TurboProTextToVideoDurationSeconds {
|
||||
match d {
|
||||
FalKling2p5TurboProDuration::Five => EnqueueKlingV2p5TurboProTextToVideoDurationSeconds::Five,
|
||||
FalKling2p5TurboProDuration::Ten => EnqueueKlingV2p5TurboProTextToVideoDurationSeconds::Ten,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalKling2p5TurboProAspectRatio) -> EnqueueKlingV2p5TurboProTextToVideoAspectRatio {
|
||||
match a {
|
||||
FalKling2p5TurboProAspectRatio::Square => EnqueueKlingV2p5TurboProTextToVideoAspectRatio::Square,
|
||||
FalKling2p5TurboProAspectRatio::SixteenByNine => EnqueueKlingV2p5TurboProTextToVideoAspectRatio::SixteenByNine,
|
||||
FalKling2p5TurboProAspectRatio::NineBySixteen => EnqueueKlingV2p5TurboProTextToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalKling2p5TurboProDuration) -> EnqueueKlingV2p5TurboProImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalKling2p5TurboProDuration::Five => EnqueueKlingV2p5TurboProImageToVideoDurationSeconds::Five,
|
||||
FalKling2p5TurboProDuration::Ten => EnqueueKlingV2p5TurboProImageToVideoDurationSeconds::Ten,
|
||||
}
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_6_pro::{
|
||||
FalKling2p6ProAspectRatio, FalKling2p6ProDuration, FalKling2p6ProMode, PlanFalKling2p6Pro,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_v2p6_pro_image_to_video_webhook::{
|
||||
enqueue_kling_v2p6_pro_image_to_video_webhook, EnqueueKlingV2p6ProImageToVideoArgs,
|
||||
EnqueueKlingV2p6ProImageToVideoRequest, EnqueueKlingV2p6ProImageToVideoDurationSeconds,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_kling_v2p6_pro_text_to_video_webhook::{
|
||||
enqueue_kling_v2p6_pro_text_to_video_webhook, EnqueueKlingV2p6ProTextToVideoArgs,
|
||||
EnqueueKlingV2p6ProTextToVideoRequest, EnqueueKlingV2p6ProTextToVideoAspectRatio,
|
||||
EnqueueKlingV2p6ProTextToVideoDurationSeconds,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_2_6_pro(
|
||||
plan: &PlanFalKling2p6Pro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalKling2p6ProMode::TextToVideo => {
|
||||
let request = EnqueueKlingV2p6ProTextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKlingV2p6ProTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_v2p6_pro_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalKling2p6ProMode::ImageToVideo { image_url } => {
|
||||
let request = EnqueueKlingV2p6ProImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKlingV2p6ProImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_v2p6_pro_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalKling2p6ProDuration) -> EnqueueKlingV2p6ProTextToVideoDurationSeconds {
|
||||
match d {
|
||||
FalKling2p6ProDuration::Five => EnqueueKlingV2p6ProTextToVideoDurationSeconds::Five,
|
||||
FalKling2p6ProDuration::Ten => EnqueueKlingV2p6ProTextToVideoDurationSeconds::Ten,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalKling2p6ProAspectRatio) -> EnqueueKlingV2p6ProTextToVideoAspectRatio {
|
||||
match a {
|
||||
FalKling2p6ProAspectRatio::Square => EnqueueKlingV2p6ProTextToVideoAspectRatio::Square,
|
||||
FalKling2p6ProAspectRatio::SixteenByNine => EnqueueKlingV2p6ProTextToVideoAspectRatio::SixteenByNine,
|
||||
FalKling2p6ProAspectRatio::NineBySixteen => EnqueueKlingV2p6ProTextToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalKling2p6ProDuration) -> EnqueueKlingV2p6ProImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalKling2p6ProDuration::Five => EnqueueKlingV2p6ProImageToVideoDurationSeconds::Five,
|
||||
FalKling2p6ProDuration::Ten => EnqueueKlingV2p6ProImageToVideoDurationSeconds::Ten,
|
||||
}
|
||||
}
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_pro::{
|
||||
FalKling3p0AspectRatio, FalKling3p0Duration, FalKling3p0Mode, PlanFalKling3p0Pro,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_3p0_pro_image_to_video_webhook::{
|
||||
enqueue_kling_3p0_pro_image_to_video_webhook, EnqueueKling3p0ProImageToVideoArgs,
|
||||
EnqueueKling3p0ProImageToVideoRequest, EnqueueKling3p0ProImageToVideoAspectRatio,
|
||||
EnqueueKling3p0ProImageToVideoDuration,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_kling_3p0_pro_text_to_video_webhook::{
|
||||
enqueue_kling_3p0_pro_text_to_video_webhook, EnqueueKling3p0ProTextToVideoArgs,
|
||||
EnqueueKling3p0ProTextToVideoRequest, EnqueueKling3p0ProTextToVideoAspectRatio,
|
||||
EnqueueKling3p0ProTextToVideoDuration,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_3p0_pro(
|
||||
plan: &PlanFalKling3p0Pro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalKling3p0Mode::TextToVideo => {
|
||||
let request = EnqueueKling3p0ProTextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
shot_type: None,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKling3p0ProTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_3p0_pro_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalKling3p0Mode::ImageToVideo { image_url, end_image_url } => {
|
||||
let request = EnqueueKling3p0ProImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
end_image_url: end_image_url.clone(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
shot_type: None,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKling3p0ProImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_3p0_pro_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalKling3p0Duration) -> EnqueueKling3p0ProTextToVideoDuration {
|
||||
use EnqueueKling3p0ProTextToVideoDuration as D;
|
||||
match d.0 {
|
||||
3 => D::ThreeSeconds,
|
||||
4 => D::FourSeconds,
|
||||
5 => D::FiveSeconds,
|
||||
6 => D::SixSeconds,
|
||||
7 => D::SevenSeconds,
|
||||
8 => D::EightSeconds,
|
||||
9 => D::NineSeconds,
|
||||
10 => D::TenSeconds,
|
||||
11 => D::ElevenSeconds,
|
||||
12 => D::TwelveSeconds,
|
||||
13 => D::ThirteenSeconds,
|
||||
14 => D::FourteenSeconds,
|
||||
_ => D::FifteenSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalKling3p0Duration) -> EnqueueKling3p0ProImageToVideoDuration {
|
||||
use EnqueueKling3p0ProImageToVideoDuration as D;
|
||||
match d.0 {
|
||||
3 => D::ThreeSeconds,
|
||||
4 => D::FourSeconds,
|
||||
5 => D::FiveSeconds,
|
||||
6 => D::SixSeconds,
|
||||
7 => D::SevenSeconds,
|
||||
8 => D::EightSeconds,
|
||||
9 => D::NineSeconds,
|
||||
10 => D::TenSeconds,
|
||||
11 => D::ElevenSeconds,
|
||||
12 => D::TwelveSeconds,
|
||||
13 => D::ThirteenSeconds,
|
||||
14 => D::FourteenSeconds,
|
||||
_ => D::FifteenSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalKling3p0AspectRatio) -> EnqueueKling3p0ProTextToVideoAspectRatio {
|
||||
match a {
|
||||
FalKling3p0AspectRatio::Square => EnqueueKling3p0ProTextToVideoAspectRatio::Square,
|
||||
FalKling3p0AspectRatio::SixteenByNine => EnqueueKling3p0ProTextToVideoAspectRatio::SixteenByNine,
|
||||
FalKling3p0AspectRatio::NineBySixteen => EnqueueKling3p0ProTextToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalKling3p0AspectRatio) -> EnqueueKling3p0ProImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalKling3p0AspectRatio::Square => EnqueueKling3p0ProImageToVideoAspectRatio::Square,
|
||||
FalKling3p0AspectRatio::SixteenByNine => EnqueueKling3p0ProImageToVideoAspectRatio::SixteenByNine,
|
||||
FalKling3p0AspectRatio::NineBySixteen => EnqueueKling3p0ProImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_pro::{
|
||||
FalKling3p0AspectRatio, FalKling3p0Duration, FalKling3p0Mode,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_standard::PlanFalKling3p0Standard;
|
||||
use fal_client::requests::webhook::video::image::enqueue_kling_3p0_standard_image_to_video_webhook::{
|
||||
enqueue_kling_3p0_standard_image_to_video_webhook, EnqueueKling3p0StandardImageToVideoArgs,
|
||||
EnqueueKling3p0StandardImageToVideoRequest, EnqueueKling3p0StandardImageToVideoAspectRatio,
|
||||
EnqueueKling3p0StandardImageToVideoDuration,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_kling_3p0_standard_text_to_video_webhook::{
|
||||
enqueue_kling_3p0_standard_text_to_video_webhook, EnqueueKling3p0StandardTextToVideoArgs,
|
||||
EnqueueKling3p0StandardTextToVideoRequest, EnqueueKling3p0StandardTextToVideoAspectRatio,
|
||||
EnqueueKling3p0StandardTextToVideoDuration,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_kling_3p0_standard(
|
||||
plan: &PlanFalKling3p0Standard,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalKling3p0Mode::TextToVideo => {
|
||||
let request = EnqueueKling3p0StandardTextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
shot_type: None,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKling3p0StandardTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_3p0_standard_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalKling3p0Mode::ImageToVideo { image_url, end_image_url } => {
|
||||
let request = EnqueueKling3p0StandardImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
end_image_url: end_image_url.clone(),
|
||||
generate_audio: plan.generate_audio,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
shot_type: None,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueKling3p0StandardImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_kling_3p0_standard_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalKling3p0Duration) -> EnqueueKling3p0StandardTextToVideoDuration {
|
||||
use EnqueueKling3p0StandardTextToVideoDuration as D;
|
||||
match d.0 {
|
||||
3 => D::ThreeSeconds,
|
||||
4 => D::FourSeconds,
|
||||
5 => D::FiveSeconds,
|
||||
6 => D::SixSeconds,
|
||||
7 => D::SevenSeconds,
|
||||
8 => D::EightSeconds,
|
||||
9 => D::NineSeconds,
|
||||
10 => D::TenSeconds,
|
||||
11 => D::ElevenSeconds,
|
||||
12 => D::TwelveSeconds,
|
||||
13 => D::ThirteenSeconds,
|
||||
14 => D::FourteenSeconds,
|
||||
_ => D::FifteenSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalKling3p0Duration) -> EnqueueKling3p0StandardImageToVideoDuration {
|
||||
use EnqueueKling3p0StandardImageToVideoDuration as D;
|
||||
match d.0 {
|
||||
3 => D::ThreeSeconds,
|
||||
4 => D::FourSeconds,
|
||||
5 => D::FiveSeconds,
|
||||
6 => D::SixSeconds,
|
||||
7 => D::SevenSeconds,
|
||||
8 => D::EightSeconds,
|
||||
9 => D::NineSeconds,
|
||||
10 => D::TenSeconds,
|
||||
11 => D::ElevenSeconds,
|
||||
12 => D::TwelveSeconds,
|
||||
13 => D::ThirteenSeconds,
|
||||
14 => D::FourteenSeconds,
|
||||
_ => D::FifteenSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalKling3p0AspectRatio) -> EnqueueKling3p0StandardTextToVideoAspectRatio {
|
||||
match a {
|
||||
FalKling3p0AspectRatio::Square => EnqueueKling3p0StandardTextToVideoAspectRatio::Square,
|
||||
FalKling3p0AspectRatio::SixteenByNine => EnqueueKling3p0StandardTextToVideoAspectRatio::SixteenByNine,
|
||||
FalKling3p0AspectRatio::NineBySixteen => EnqueueKling3p0StandardTextToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalKling3p0AspectRatio) -> EnqueueKling3p0StandardImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalKling3p0AspectRatio::Square => EnqueueKling3p0StandardImageToVideoAspectRatio::Square,
|
||||
FalKling3p0AspectRatio::SixteenByNine => EnqueueKling3p0StandardImageToVideoAspectRatio::SixteenByNine,
|
||||
FalKling3p0AspectRatio::NineBySixteen => EnqueueKling3p0StandardImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_seedance_1_0_lite::PlanFalSeedance10Lite;
|
||||
use fal_client::requests::webhook::video::image::enqueue_seedance_1_lite_image_to_video_webhook::{
|
||||
enqueue_seedance_1_lite_image_to_video_webhook, Seedance1LiteArgs, Seedance1LiteRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_seedance_1_0_lite(
|
||||
plan: &PlanFalSeedance10Lite,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Seedance1LiteRequest {
|
||||
image_url: plan.image_url.clone(),
|
||||
end_frame_image_url: plan.end_image_url.clone(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration,
|
||||
resolution: plan.resolution,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
camera_fixed: false,
|
||||
seed: None,
|
||||
};
|
||||
|
||||
let outbound_request: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
|
||||
let args = Seedance1LiteArgs {
|
||||
request,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
|
||||
let webhook_response = enqueue_seedance_1_lite_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_seedance_1p5_pro::{
|
||||
FalSeedance1p5ProAspectRatio, FalSeedance1p5ProDuration, FalSeedance1p5ProMode,
|
||||
FalSeedance1p5ProResolution, PlanFalSeedance1p5Pro,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_seedance_1p5_pro_image_to_video_webhook::{
|
||||
enqueue_seedance_1p5_pro_image_to_video_webhook, EnqueueSeedance1p5ProImageToVideoArgs,
|
||||
EnqueueSeedance1p5ProImageToVideoAspectRatio, EnqueueSeedance1p5ProImageToVideoDuration,
|
||||
EnqueueSeedance1p5ProImageToVideoRequest, EnqueueSeedance1p5ProImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_seedance_1p5_pro_text_to_video_webhook::{
|
||||
enqueue_seedance_1p5_pro_text_to_video_webhook, EnqueueSeedance1p5ProTextToVideoArgs,
|
||||
EnqueueSeedance1p5ProTextToVideoRequest, EnqueueSeedance1p5ProTextToVideoAspectRatio,
|
||||
EnqueueSeedance1p5ProTextToVideoDuration, EnqueueSeedance1p5ProTextToVideoResolution,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_seedance_1p5_pro(
|
||||
plan: &PlanFalSeedance1p5Pro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalSeedance1p5ProMode::TextToVideo => {
|
||||
let request = EnqueueSeedance1p5ProTextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
resolution: plan.resolution.map(to_t2v_resolution),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueSeedance1p5ProTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_seedance_1p5_pro_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalSeedance1p5ProMode::ImageToVideo { image_url, end_image_url } => {
|
||||
let request = EnqueueSeedance1p5ProImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
end_image_url: end_image_url.clone(),
|
||||
resolution: plan.resolution.map(to_i2v_resolution),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueSeedance1p5ProImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_seedance_1p5_pro_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalSeedance1p5ProResolution) -> EnqueueSeedance1p5ProTextToVideoResolution {
|
||||
match r {
|
||||
FalSeedance1p5ProResolution::FourEightyP => EnqueueSeedance1p5ProTextToVideoResolution::FourEightyP,
|
||||
FalSeedance1p5ProResolution::SevenTwentyP => EnqueueSeedance1p5ProTextToVideoResolution::SevenTwentyP,
|
||||
FalSeedance1p5ProResolution::TenEightyP => EnqueueSeedance1p5ProTextToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalSeedance1p5ProDuration) -> EnqueueSeedance1p5ProTextToVideoDuration {
|
||||
use EnqueueSeedance1p5ProTextToVideoDuration as T;
|
||||
match d {
|
||||
FalSeedance1p5ProDuration::Four => T::FourSeconds,
|
||||
FalSeedance1p5ProDuration::Five => T::FiveSeconds,
|
||||
FalSeedance1p5ProDuration::Six => T::SixSeconds,
|
||||
FalSeedance1p5ProDuration::Seven => T::SevenSeconds,
|
||||
FalSeedance1p5ProDuration::Eight => T::EightSeconds,
|
||||
FalSeedance1p5ProDuration::Nine => T::NineSeconds,
|
||||
FalSeedance1p5ProDuration::Ten => T::TenSeconds,
|
||||
FalSeedance1p5ProDuration::Eleven => T::ElevenSeconds,
|
||||
FalSeedance1p5ProDuration::Twelve => T::TwelveSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalSeedance1p5ProAspectRatio) -> EnqueueSeedance1p5ProTextToVideoAspectRatio {
|
||||
use EnqueueSeedance1p5ProTextToVideoAspectRatio as T;
|
||||
match a {
|
||||
FalSeedance1p5ProAspectRatio::Auto => T::Auto,
|
||||
FalSeedance1p5ProAspectRatio::TwentyOneByNine => T::TwentyOneByNine,
|
||||
FalSeedance1p5ProAspectRatio::SixteenByNine => T::SixteenByNine,
|
||||
FalSeedance1p5ProAspectRatio::FourByThree => T::FourByThree,
|
||||
FalSeedance1p5ProAspectRatio::Square => T::Square,
|
||||
FalSeedance1p5ProAspectRatio::ThreeByFour => T::ThreeByFour,
|
||||
FalSeedance1p5ProAspectRatio::NineBySixteen => T::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_resolution(r: FalSeedance1p5ProResolution) -> EnqueueSeedance1p5ProImageToVideoResolution {
|
||||
match r {
|
||||
FalSeedance1p5ProResolution::FourEightyP => EnqueueSeedance1p5ProImageToVideoResolution::FourEightyP,
|
||||
FalSeedance1p5ProResolution::SevenTwentyP => EnqueueSeedance1p5ProImageToVideoResolution::SevenTwentyP,
|
||||
FalSeedance1p5ProResolution::TenEightyP => EnqueueSeedance1p5ProImageToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalSeedance1p5ProDuration) -> EnqueueSeedance1p5ProImageToVideoDuration {
|
||||
use EnqueueSeedance1p5ProImageToVideoDuration as T;
|
||||
match d {
|
||||
FalSeedance1p5ProDuration::Four => T::FourSeconds,
|
||||
FalSeedance1p5ProDuration::Five => T::FiveSeconds,
|
||||
FalSeedance1p5ProDuration::Six => T::SixSeconds,
|
||||
FalSeedance1p5ProDuration::Seven => T::SevenSeconds,
|
||||
FalSeedance1p5ProDuration::Eight => T::EightSeconds,
|
||||
FalSeedance1p5ProDuration::Nine => T::NineSeconds,
|
||||
FalSeedance1p5ProDuration::Ten => T::TenSeconds,
|
||||
FalSeedance1p5ProDuration::Eleven => T::ElevenSeconds,
|
||||
FalSeedance1p5ProDuration::Twelve => T::TwelveSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalSeedance1p5ProAspectRatio) -> EnqueueSeedance1p5ProImageToVideoAspectRatio {
|
||||
use EnqueueSeedance1p5ProImageToVideoAspectRatio as T;
|
||||
match a {
|
||||
FalSeedance1p5ProAspectRatio::Auto => T::Auto,
|
||||
FalSeedance1p5ProAspectRatio::TwentyOneByNine => T::TwentyOneByNine,
|
||||
FalSeedance1p5ProAspectRatio::SixteenByNine => T::SixteenByNine,
|
||||
FalSeedance1p5ProAspectRatio::FourByThree => T::FourByThree,
|
||||
FalSeedance1p5ProAspectRatio::Square => T::Square,
|
||||
FalSeedance1p5ProAspectRatio::ThreeByFour => T::ThreeByFour,
|
||||
FalSeedance1p5ProAspectRatio::NineBySixteen => T::NineBySixteen,
|
||||
}
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_sora_2::{
|
||||
FalSora2AspectRatio, FalSora2Duration, FalSora2Mode, FalSora2Resolution, PlanFalSora2,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_sora_2_image_to_video_webhook::{
|
||||
enqueue_sora_2_image_to_video_webhook, EnqueueSora2ImageToVideoArgs,
|
||||
EnqueueSora2ImageToVideoAspectRatio, EnqueueSora2ImageToVideoDurationSeconds,
|
||||
EnqueueSora2ImageToVideoRequest, EnqueueSora2ImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_sora_2_text_to_video_webhook::{
|
||||
enqueue_sora_2_text_to_video_webhook, EnqueueSora2TextToVideoArgs,
|
||||
EnqueueSora2TextToVideoRequest, EnqueueSora2TextToVideoAspectRatio,
|
||||
EnqueueSora2TextToVideoDurationSeconds, EnqueueSora2TextToVideoResolution,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_sora_2(
|
||||
plan: &PlanFalSora2,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalSora2Mode::TextToVideo => {
|
||||
// Text-to-video does not support Auto aspect ratio or Auto resolution.
|
||||
let request = EnqueueSora2TextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
resolution: plan.resolution.and_then(to_t2v_resolution),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.and_then(to_t2v_aspect_ratio),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueSora2TextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_sora_2_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalSora2Mode::ImageToVideo { image_url } => {
|
||||
let request = EnqueueSora2ImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
resolution: plan.resolution.map(to_i2v_resolution),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueSora2ImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_sora_2_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalSora2Duration) -> EnqueueSora2TextToVideoDurationSeconds {
|
||||
match d {
|
||||
FalSora2Duration::Four => EnqueueSora2TextToVideoDurationSeconds::Four,
|
||||
FalSora2Duration::Eight => EnqueueSora2TextToVideoDurationSeconds::Eight,
|
||||
FalSora2Duration::Twelve => EnqueueSora2TextToVideoDurationSeconds::Twelve,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalSora2Resolution) -> Option<EnqueueSora2TextToVideoResolution> {
|
||||
match r {
|
||||
FalSora2Resolution::SevenTwentyP => Some(EnqueueSora2TextToVideoResolution::SevenTwentyP),
|
||||
FalSora2Resolution::Auto => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalSora2AspectRatio) -> Option<EnqueueSora2TextToVideoAspectRatio> {
|
||||
match a {
|
||||
FalSora2AspectRatio::SixteenByNine => Some(EnqueueSora2TextToVideoAspectRatio::SixteenByNine),
|
||||
FalSora2AspectRatio::NineBySixteen => Some(EnqueueSora2TextToVideoAspectRatio::NineBySixteen),
|
||||
FalSora2AspectRatio::Auto => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalSora2Duration) -> EnqueueSora2ImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalSora2Duration::Four => EnqueueSora2ImageToVideoDurationSeconds::Four,
|
||||
FalSora2Duration::Eight => EnqueueSora2ImageToVideoDurationSeconds::Eight,
|
||||
FalSora2Duration::Twelve => EnqueueSora2ImageToVideoDurationSeconds::Twelve,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_resolution(r: FalSora2Resolution) -> EnqueueSora2ImageToVideoResolution {
|
||||
match r {
|
||||
FalSora2Resolution::SevenTwentyP => EnqueueSora2ImageToVideoResolution::SevenTwentyP,
|
||||
FalSora2Resolution::Auto => EnqueueSora2ImageToVideoResolution::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalSora2AspectRatio) -> EnqueueSora2ImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalSora2AspectRatio::SixteenByNine => EnqueueSora2ImageToVideoAspectRatio::SixteenByNine,
|
||||
FalSora2AspectRatio::NineBySixteen => EnqueueSora2ImageToVideoAspectRatio::NineBySixteen,
|
||||
FalSora2AspectRatio::Auto => EnqueueSora2ImageToVideoAspectRatio::Auto,
|
||||
}
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_sora_2_pro::{
|
||||
FalSora2ProAspectRatio, FalSora2ProDuration, FalSora2ProMode, FalSora2ProResolution,
|
||||
PlanFalSora2Pro,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_sora_2_pro_image_to_video_webhook::{
|
||||
enqueue_sora_2_pro_image_to_video_webhook, EnqueueSora2ProImageToVideoArgs,
|
||||
EnqueueSora2ProImageToVideoAspectRatio, EnqueueSora2ProImageToVideoDurationSeconds,
|
||||
EnqueueSora2ProImageToVideoRequest, EnqueueSora2ProImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_sora_2_pro_text_to_video_webhook::{
|
||||
enqueue_sora_2_pro_text_to_video_webhook, EnqueueSora2ProTextToVideoArgs,
|
||||
EnqueueSora2ProTextToVideoRequest, EnqueueSora2ProTextToVideoAspectRatio,
|
||||
EnqueueSora2ProTextToVideoDurationSeconds, EnqueueSora2ProTextToVideoResolution,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_sora_2_pro(
|
||||
plan: &PlanFalSora2Pro,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalSora2ProMode::TextToVideo => {
|
||||
// Text-to-video does not support Auto aspect ratio or Auto resolution.
|
||||
let request = EnqueueSora2ProTextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
resolution: plan.resolution.and_then(to_t2v_resolution),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.and_then(to_t2v_aspect_ratio),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueSora2ProTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_sora_2_pro_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalSora2ProMode::ImageToVideo { image_url } => {
|
||||
let request = EnqueueSora2ProImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: image_url.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
resolution: plan.resolution.map(to_i2v_resolution),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueSora2ProImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_sora_2_pro_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalSora2ProDuration) -> EnqueueSora2ProTextToVideoDurationSeconds {
|
||||
match d {
|
||||
FalSora2ProDuration::Four => EnqueueSora2ProTextToVideoDurationSeconds::Four,
|
||||
FalSora2ProDuration::Eight => EnqueueSora2ProTextToVideoDurationSeconds::Eight,
|
||||
FalSora2ProDuration::Twelve => EnqueueSora2ProTextToVideoDurationSeconds::Twelve,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalSora2ProResolution) -> Option<EnqueueSora2ProTextToVideoResolution> {
|
||||
match r {
|
||||
FalSora2ProResolution::SevenTwentyP => Some(EnqueueSora2ProTextToVideoResolution::SevenTwentyP),
|
||||
FalSora2ProResolution::TenEightyP => Some(EnqueueSora2ProTextToVideoResolution::TenEightyP),
|
||||
FalSora2ProResolution::Auto => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalSora2ProAspectRatio) -> Option<EnqueueSora2ProTextToVideoAspectRatio> {
|
||||
match a {
|
||||
FalSora2ProAspectRatio::SixteenByNine => Some(EnqueueSora2ProTextToVideoAspectRatio::SixteenByNine),
|
||||
FalSora2ProAspectRatio::NineBySixteen => Some(EnqueueSora2ProTextToVideoAspectRatio::NineBySixteen),
|
||||
FalSora2ProAspectRatio::Auto => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalSora2ProDuration) -> EnqueueSora2ProImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalSora2ProDuration::Four => EnqueueSora2ProImageToVideoDurationSeconds::Four,
|
||||
FalSora2ProDuration::Eight => EnqueueSora2ProImageToVideoDurationSeconds::Eight,
|
||||
FalSora2ProDuration::Twelve => EnqueueSora2ProImageToVideoDurationSeconds::Twelve,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_resolution(r: FalSora2ProResolution) -> EnqueueSora2ProImageToVideoResolution {
|
||||
match r {
|
||||
FalSora2ProResolution::Auto => EnqueueSora2ProImageToVideoResolution::Auto,
|
||||
FalSora2ProResolution::SevenTwentyP => EnqueueSora2ProImageToVideoResolution::SevenTwentyP,
|
||||
FalSora2ProResolution::TenEightyP => EnqueueSora2ProImageToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalSora2ProAspectRatio) -> EnqueueSora2ProImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalSora2ProAspectRatio::Auto => EnqueueSora2ProImageToVideoAspectRatio::Auto,
|
||||
FalSora2ProAspectRatio::SixteenByNine => EnqueueSora2ProImageToVideoAspectRatio::SixteenByNine,
|
||||
FalSora2ProAspectRatio::NineBySixteen => EnqueueSora2ProImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_2::{
|
||||
FalVeo2Mode, PlanFalVeo2,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_2_image_to_video_webhook::{
|
||||
enqueue_veo_2_image_to_video_webhook, Veo2Args, Veo2AspectRatio, Veo2Request,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_2_text_to_video_webhook::{
|
||||
enqueue_veo_2_text_to_video_webhook, Veo2TextToVideoArgs, Veo2TextToVideoRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_veo_2(
|
||||
plan: &PlanFalVeo2,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalVeo2Mode::TextToVideo => {
|
||||
let request = Veo2TextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio.unwrap_or(Veo2AspectRatio::Auto),
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = Veo2TextToVideoArgs {
|
||||
request,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
let resp = enqueue_veo_2_text_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
(resp, outbound)
|
||||
}
|
||||
FalVeo2Mode::ImageToVideo { image_url } => {
|
||||
// Image-to-video does not support aspect_ratio — the output
|
||||
// inherits the source image's aspect ratio.
|
||||
let request = Veo2Request {
|
||||
image_url: image_url.to_string(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = Veo2Args {
|
||||
request,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
let resp = enqueue_veo_2_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
(resp, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3::{
|
||||
FalVeo3Duration, FalVeo3I2vAspectRatio, FalVeo3Mode, FalVeo3Resolution,
|
||||
FalVeo3T2vAspectRatio, PlanFalVeo3,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{
|
||||
enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3I2vAspectRatio, Veo3I2vDuration,
|
||||
Veo3I2vResolution, Veo3Request,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_3_text_to_video_webhook::{
|
||||
enqueue_veo_3_text_to_video_webhook, Veo3T2vAspectRatio, Veo3T2vDuration, Veo3T2vResolution,
|
||||
Veo3TextToVideoArgs, Veo3TextToVideoRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_veo_3(
|
||||
plan: &PlanFalVeo3,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalVeo3Mode::TextToVideo => {
|
||||
let request = Veo3TextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
duration: to_t2v_duration(plan.duration),
|
||||
aspect_ratio: plan.t2v_aspect_ratio.map(to_t2v_aspect_ratio).unwrap_or(Veo3T2vAspectRatio::Default),
|
||||
resolution: to_t2v_resolution(plan.resolution),
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = Veo3TextToVideoArgs {
|
||||
request,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
let resp = enqueue_veo_3_text_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
(resp, outbound)
|
||||
}
|
||||
FalVeo3Mode::ImageToVideo { image_url } => {
|
||||
let request = Veo3Request {
|
||||
image_url: image_url.to_string(),
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: to_i2v_duration(plan.duration),
|
||||
aspect_ratio: plan.i2v_aspect_ratio.map(to_i2v_aspect_ratio).unwrap_or(Veo3I2vAspectRatio::Auto),
|
||||
resolution: to_i2v_resolution(plan.resolution),
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = Veo3Args {
|
||||
request,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
let resp = enqueue_veo_3_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
(resp, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalVeo3Duration) -> Veo3I2vDuration {
|
||||
match d {
|
||||
FalVeo3Duration::Default => Veo3I2vDuration::Default,
|
||||
FalVeo3Duration::FourSeconds => Veo3I2vDuration::FourSeconds,
|
||||
FalVeo3Duration::SixSeconds => Veo3I2vDuration::SixSeconds,
|
||||
FalVeo3Duration::EightSeconds => Veo3I2vDuration::EightSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalVeo3Duration) -> Veo3T2vDuration {
|
||||
match d {
|
||||
FalVeo3Duration::Default => Veo3T2vDuration::Default,
|
||||
FalVeo3Duration::FourSeconds => Veo3T2vDuration::FourSeconds,
|
||||
FalVeo3Duration::SixSeconds => Veo3T2vDuration::SixSeconds,
|
||||
FalVeo3Duration::EightSeconds => Veo3T2vDuration::EightSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_resolution(r: FalVeo3Resolution) -> Veo3I2vResolution {
|
||||
match r {
|
||||
FalVeo3Resolution::Default => Veo3I2vResolution::Default,
|
||||
FalVeo3Resolution::SevenTwentyP => Veo3I2vResolution::SevenTwentyP,
|
||||
FalVeo3Resolution::TenEightyP => Veo3I2vResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalVeo3Resolution) -> Veo3T2vResolution {
|
||||
match r {
|
||||
FalVeo3Resolution::Default => Veo3T2vResolution::Default,
|
||||
FalVeo3Resolution::SevenTwentyP => Veo3T2vResolution::SevenTwentyP,
|
||||
FalVeo3Resolution::TenEightyP => Veo3T2vResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(ar: FalVeo3T2vAspectRatio) -> Veo3T2vAspectRatio {
|
||||
match ar {
|
||||
FalVeo3T2vAspectRatio::Default => Veo3T2vAspectRatio::Default,
|
||||
FalVeo3T2vAspectRatio::WideSixteenNine => Veo3T2vAspectRatio::WideSixteenNine,
|
||||
FalVeo3T2vAspectRatio::TallNineSixteen => Veo3T2vAspectRatio::TallNineSixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(ar: FalVeo3I2vAspectRatio) -> Veo3I2vAspectRatio {
|
||||
match ar {
|
||||
FalVeo3I2vAspectRatio::Auto => Veo3I2vAspectRatio::Auto,
|
||||
FalVeo3I2vAspectRatio::WideSixteenNine => Veo3I2vAspectRatio::WideSixteenNine,
|
||||
FalVeo3I2vAspectRatio::TallNineSixteen => Veo3I2vAspectRatio::TallNineSixteen,
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3_fast::PlanFalVeo3Fast;
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3_fast_image_to_video_webhook::{
|
||||
enqueue_veo_3_fast_image_to_video_webhook, Veo3FastArgs, Veo3FastRequest,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_veo_3_fast(
|
||||
plan: &PlanFalVeo3Fast,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let request = Veo3FastRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: plan.start_frame_url.clone(),
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
duration: plan.duration,
|
||||
resolution: plan.resolution,
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
|
||||
let outbound_request: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
|
||||
let args = Veo3FastArgs {
|
||||
request,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
|
||||
let webhook_response = enqueue_veo_3_fast_image_to_video_webhook(args)
|
||||
.await
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1::{
|
||||
FalVeo3p1AspectRatio, FalVeo3p1Duration, FalVeo3p1Mode, FalVeo3p1Resolution, PlanFalVeo3p1,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3p1_first_last_frame_image_to_video_webhook::{
|
||||
enqueue_veo_3p1_first_last_frame_image_to_video_webhook,
|
||||
EnqueueVeo3p1FirstLastFrameImageToVideoArgs,
|
||||
EnqueueVeo3p1FirstLastFrameImageToVideoAspectRatio,
|
||||
EnqueueVeo3p1FirstLastFrameImageToVideoDurationSeconds,
|
||||
EnqueueVeo3p1FirstLastFrameImageToVideoRequest,
|
||||
EnqueueVeo3p1FirstLastFrameImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3p1_image_to_video_webhook::{
|
||||
enqueue_veo_3p1_image_to_video_webhook, EnqueueVeo3p1ImageToVideoArgs,
|
||||
EnqueueVeo3p1ImageToVideoAspectRatio, EnqueueVeo3p1ImageToVideoDurationSeconds,
|
||||
EnqueueVeo3p1ImageToVideoRequest, EnqueueVeo3p1ImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_3p1_text_to_video_webhook::{
|
||||
enqueue_veo_3p1_text_to_video_webhook, EnqueueVeo3p1TextToVideoArgs,
|
||||
EnqueueVeo3p1TextToVideoRequest, EnqueueVeo3p1TextToVideoAspectRatio,
|
||||
EnqueueVeo3p1TextToVideoDurationSeconds, EnqueueVeo3p1TextToVideoResolution,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_veo_3p1(
|
||||
plan: &PlanFalVeo3p1,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let (webhook_response, outbound_request) = match &plan.mode {
|
||||
FalVeo3p1Mode::TextToVideo => {
|
||||
let request = EnqueueVeo3p1TextToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
duration: plan.duration.map(to_t2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
resolution: plan.resolution.map(to_t2v_resolution),
|
||||
generate_audio: plan.generate_audio,
|
||||
enhance_prompt: None,
|
||||
negative_prompt: plan.negative_prompt.clone(),
|
||||
seed: None,
|
||||
auto_fix: None,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueVeo3p1TextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_veo_3p1_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalVeo3p1Mode::ImageToVideo { start_frame_url } => {
|
||||
let request = EnqueueVeo3p1ImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
image_url: start_frame_url.clone(),
|
||||
duration: plan.duration.map(to_i2v_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
resolution: plan.resolution.map(to_i2v_resolution),
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueVeo3p1ImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_veo_3p1_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalVeo3p1Mode::FirstLastFrame { first_frame_url, last_frame_url } => {
|
||||
let request = EnqueueVeo3p1FirstLastFrameImageToVideoRequest {
|
||||
prompt: plan.prompt.clone(),
|
||||
first_frame_url: first_frame_url.clone(),
|
||||
last_frame_url: last_frame_url.clone(),
|
||||
duration: plan.duration.map(to_flf_duration),
|
||||
aspect_ratio: plan.aspect_ratio.map(to_flf_aspect_ratio),
|
||||
resolution: plan.resolution.map(to_flf_resolution),
|
||||
generate_audio: plan.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueVeo3p1FirstLastFrameImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_veo_3p1_first_last_frame_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalVeo3p1Duration) -> EnqueueVeo3p1TextToVideoDurationSeconds {
|
||||
match d {
|
||||
FalVeo3p1Duration::Four => EnqueueVeo3p1TextToVideoDurationSeconds::Four,
|
||||
FalVeo3p1Duration::Six => EnqueueVeo3p1TextToVideoDurationSeconds::Six,
|
||||
FalVeo3p1Duration::Eight => EnqueueVeo3p1TextToVideoDurationSeconds::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalVeo3p1AspectRatio) -> EnqueueVeo3p1TextToVideoAspectRatio {
|
||||
match a {
|
||||
FalVeo3p1AspectRatio::Auto => EnqueueVeo3p1TextToVideoAspectRatio::Auto,
|
||||
FalVeo3p1AspectRatio::SixteenByNine => EnqueueVeo3p1TextToVideoAspectRatio::SixteenByNine,
|
||||
FalVeo3p1AspectRatio::NineBySixteen => EnqueueVeo3p1TextToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalVeo3p1Resolution) -> EnqueueVeo3p1TextToVideoResolution {
|
||||
match r {
|
||||
FalVeo3p1Resolution::SevenTwentyP => EnqueueVeo3p1TextToVideoResolution::SevenTwentyP,
|
||||
FalVeo3p1Resolution::TenEightyP => EnqueueVeo3p1TextToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalVeo3p1Duration) -> EnqueueVeo3p1ImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalVeo3p1Duration::Four => EnqueueVeo3p1ImageToVideoDurationSeconds::Four,
|
||||
FalVeo3p1Duration::Six => EnqueueVeo3p1ImageToVideoDurationSeconds::Six,
|
||||
FalVeo3p1Duration::Eight => EnqueueVeo3p1ImageToVideoDurationSeconds::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalVeo3p1AspectRatio) -> EnqueueVeo3p1ImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalVeo3p1AspectRatio::Auto => EnqueueVeo3p1ImageToVideoAspectRatio::Auto,
|
||||
FalVeo3p1AspectRatio::SixteenByNine => EnqueueVeo3p1ImageToVideoAspectRatio::SixteenByNine,
|
||||
FalVeo3p1AspectRatio::NineBySixteen => EnqueueVeo3p1ImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_resolution(r: FalVeo3p1Resolution) -> EnqueueVeo3p1ImageToVideoResolution {
|
||||
match r {
|
||||
FalVeo3p1Resolution::SevenTwentyP => EnqueueVeo3p1ImageToVideoResolution::SevenTwentyP,
|
||||
FalVeo3p1Resolution::TenEightyP => EnqueueVeo3p1ImageToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flf_duration(d: FalVeo3p1Duration) -> EnqueueVeo3p1FirstLastFrameImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalVeo3p1Duration::Four => EnqueueVeo3p1FirstLastFrameImageToVideoDurationSeconds::Four,
|
||||
FalVeo3p1Duration::Six => EnqueueVeo3p1FirstLastFrameImageToVideoDurationSeconds::Six,
|
||||
FalVeo3p1Duration::Eight => EnqueueVeo3p1FirstLastFrameImageToVideoDurationSeconds::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flf_aspect_ratio(a: FalVeo3p1AspectRatio) -> EnqueueVeo3p1FirstLastFrameImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalVeo3p1AspectRatio::Auto => EnqueueVeo3p1FirstLastFrameImageToVideoAspectRatio::Auto,
|
||||
FalVeo3p1AspectRatio::SixteenByNine => EnqueueVeo3p1FirstLastFrameImageToVideoAspectRatio::SixteenByNine,
|
||||
FalVeo3p1AspectRatio::NineBySixteen => EnqueueVeo3p1FirstLastFrameImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flf_resolution(r: FalVeo3p1Resolution) -> EnqueueVeo3p1FirstLastFrameImageToVideoResolution {
|
||||
match r {
|
||||
FalVeo3p1Resolution::SevenTwentyP => EnqueueVeo3p1FirstLastFrameImageToVideoResolution::SevenTwentyP,
|
||||
FalVeo3p1Resolution::TenEightyP => EnqueueVeo3p1FirstLastFrameImageToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1::{
|
||||
FalVeo3p1AspectRatio, FalVeo3p1Duration, FalVeo3p1Mode, FalVeo3p1Resolution,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1_fast::PlanFalVeo3p1Fast;
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3p1_fast_first_last_frame_image_to_video_webhook::{
|
||||
enqueue_veo_3p1_fast_first_last_frame_image_to_video_webhook,
|
||||
EnqueueVeo3p1FastFirstLastFrameImageToVideoArgs,
|
||||
EnqueueVeo3p1FastFirstLastFrameImageToVideoAspectRatio,
|
||||
EnqueueVeo3p1FastFirstLastFrameImageToVideoDurationSeconds,
|
||||
EnqueueVeo3p1FastFirstLastFrameImageToVideoRequest,
|
||||
EnqueueVeo3p1FastFirstLastFrameImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3p1_fast_image_to_video_webhook::{
|
||||
enqueue_veo_3p1_fast_image_to_video_webhook, EnqueueVeo3p1FastImageToVideoArgs,
|
||||
EnqueueVeo3p1FastImageToVideoAspectRatio, EnqueueVeo3p1FastImageToVideoDurationSeconds,
|
||||
EnqueueVeo3p1FastImageToVideoRequest, EnqueueVeo3p1FastImageToVideoResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_3p1_fast_text_to_video_webhook::{
|
||||
enqueue_veo_3p1_fast_text_to_video_webhook, EnqueueVeo3p1FastTextToVideoArgs,
|
||||
EnqueueVeo3p1FastTextToVideoRequest, EnqueueVeo3p1FastTextToVideoAspectRatio,
|
||||
EnqueueVeo3p1FastTextToVideoDurationSeconds, EnqueueVeo3p1FastTextToVideoResolution,
|
||||
};
|
||||
|
||||
pub async fn execute_fal_veo_3p1_fast(
|
||||
plan: &PlanFalVeo3p1Fast,
|
||||
fal_client: &RouterFalClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let inner = &plan.inner;
|
||||
|
||||
let (webhook_response, outbound_request) = match &inner.mode {
|
||||
FalVeo3p1Mode::TextToVideo => {
|
||||
let request = EnqueueVeo3p1FastTextToVideoRequest {
|
||||
prompt: inner.prompt.clone(),
|
||||
duration: inner.duration.map(to_t2v_duration),
|
||||
aspect_ratio: inner.aspect_ratio.map(to_t2v_aspect_ratio),
|
||||
resolution: inner.resolution.map(to_t2v_resolution),
|
||||
generate_audio: inner.generate_audio,
|
||||
enhance_prompt: None,
|
||||
negative_prompt: inner.negative_prompt.clone(),
|
||||
seed: None,
|
||||
auto_fix: None,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueVeo3p1FastTextToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_veo_3p1_fast_text_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalVeo3p1Mode::ImageToVideo { start_frame_url } => {
|
||||
let request = EnqueueVeo3p1FastImageToVideoRequest {
|
||||
prompt: inner.prompt.clone(),
|
||||
image_url: start_frame_url.clone(),
|
||||
duration: inner.duration.map(to_i2v_duration),
|
||||
aspect_ratio: inner.aspect_ratio.map(to_i2v_aspect_ratio),
|
||||
resolution: inner.resolution.map(to_i2v_resolution),
|
||||
generate_audio: inner.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueVeo3p1FastImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_veo_3p1_fast_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
FalVeo3p1Mode::FirstLastFrame { first_frame_url, last_frame_url } => {
|
||||
let request = EnqueueVeo3p1FastFirstLastFrameImageToVideoRequest {
|
||||
prompt: inner.prompt.clone(),
|
||||
first_frame_url: first_frame_url.clone(),
|
||||
last_frame_url: last_frame_url.clone(),
|
||||
duration: inner.duration.map(to_flf_duration),
|
||||
aspect_ratio: inner.aspect_ratio.map(to_flf_aspect_ratio),
|
||||
resolution: inner.resolution.map(to_flf_resolution),
|
||||
generate_audio: inner.generate_audio,
|
||||
};
|
||||
let outbound: Arc<dyn Debug + Send + Sync> = Arc::new(request.clone());
|
||||
let args = EnqueueVeo3p1FastFirstLastFrameImageToVideoArgs {
|
||||
request,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
};
|
||||
(enqueue_veo_3p1_fast_first_last_frame_image_to_video_webhook(args).await, outbound)
|
||||
}
|
||||
};
|
||||
|
||||
let webhook_response = webhook_response
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Fal(FalVideoResponsePayload {
|
||||
request_id: webhook_response.request_id,
|
||||
gateway_request_id: webhook_response.gateway_request_id,
|
||||
maybe_outbound_request: Some(outbound_request),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_t2v_duration(d: FalVeo3p1Duration) -> EnqueueVeo3p1FastTextToVideoDurationSeconds {
|
||||
match d {
|
||||
FalVeo3p1Duration::Four => EnqueueVeo3p1FastTextToVideoDurationSeconds::Four,
|
||||
FalVeo3p1Duration::Six => EnqueueVeo3p1FastTextToVideoDurationSeconds::Six,
|
||||
FalVeo3p1Duration::Eight => EnqueueVeo3p1FastTextToVideoDurationSeconds::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_aspect_ratio(a: FalVeo3p1AspectRatio) -> EnqueueVeo3p1FastTextToVideoAspectRatio {
|
||||
match a {
|
||||
FalVeo3p1AspectRatio::Auto => EnqueueVeo3p1FastTextToVideoAspectRatio::Auto,
|
||||
FalVeo3p1AspectRatio::SixteenByNine => EnqueueVeo3p1FastTextToVideoAspectRatio::SixteenByNine,
|
||||
FalVeo3p1AspectRatio::NineBySixteen => EnqueueVeo3p1FastTextToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_t2v_resolution(r: FalVeo3p1Resolution) -> EnqueueVeo3p1FastTextToVideoResolution {
|
||||
match r {
|
||||
FalVeo3p1Resolution::SevenTwentyP => EnqueueVeo3p1FastTextToVideoResolution::SevenTwentyP,
|
||||
FalVeo3p1Resolution::TenEightyP => EnqueueVeo3p1FastTextToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_duration(d: FalVeo3p1Duration) -> EnqueueVeo3p1FastImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalVeo3p1Duration::Four => EnqueueVeo3p1FastImageToVideoDurationSeconds::Four,
|
||||
FalVeo3p1Duration::Six => EnqueueVeo3p1FastImageToVideoDurationSeconds::Six,
|
||||
FalVeo3p1Duration::Eight => EnqueueVeo3p1FastImageToVideoDurationSeconds::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_aspect_ratio(a: FalVeo3p1AspectRatio) -> EnqueueVeo3p1FastImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalVeo3p1AspectRatio::Auto => EnqueueVeo3p1FastImageToVideoAspectRatio::Auto,
|
||||
FalVeo3p1AspectRatio::SixteenByNine => EnqueueVeo3p1FastImageToVideoAspectRatio::SixteenByNine,
|
||||
FalVeo3p1AspectRatio::NineBySixteen => EnqueueVeo3p1FastImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i2v_resolution(r: FalVeo3p1Resolution) -> EnqueueVeo3p1FastImageToVideoResolution {
|
||||
match r {
|
||||
FalVeo3p1Resolution::SevenTwentyP => EnqueueVeo3p1FastImageToVideoResolution::SevenTwentyP,
|
||||
FalVeo3p1Resolution::TenEightyP => EnqueueVeo3p1FastImageToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flf_duration(d: FalVeo3p1Duration) -> EnqueueVeo3p1FastFirstLastFrameImageToVideoDurationSeconds {
|
||||
match d {
|
||||
FalVeo3p1Duration::Four => EnqueueVeo3p1FastFirstLastFrameImageToVideoDurationSeconds::Four,
|
||||
FalVeo3p1Duration::Six => EnqueueVeo3p1FastFirstLastFrameImageToVideoDurationSeconds::Six,
|
||||
FalVeo3p1Duration::Eight => EnqueueVeo3p1FastFirstLastFrameImageToVideoDurationSeconds::Eight,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flf_aspect_ratio(a: FalVeo3p1AspectRatio) -> EnqueueVeo3p1FastFirstLastFrameImageToVideoAspectRatio {
|
||||
match a {
|
||||
FalVeo3p1AspectRatio::Auto => EnqueueVeo3p1FastFirstLastFrameImageToVideoAspectRatio::Auto,
|
||||
FalVeo3p1AspectRatio::SixteenByNine => EnqueueVeo3p1FastFirstLastFrameImageToVideoAspectRatio::SixteenByNine,
|
||||
FalVeo3p1AspectRatio::NineBySixteen => EnqueueVeo3p1FastFirstLastFrameImageToVideoAspectRatio::NineBySixteen,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flf_resolution(r: FalVeo3p1Resolution) -> EnqueueVeo3p1FastFirstLastFrameImageToVideoResolution {
|
||||
match r {
|
||||
FalVeo3p1Resolution::SevenTwentyP => EnqueueVeo3p1FastFirstLastFrameImageToVideoResolution::SevenTwentyP,
|
||||
FalVeo3p1Resolution::TenEightyP => EnqueueVeo3p1FastFirstLastFrameImageToVideoResolution::TenEightyP,
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
pub mod generate_video_fal_kling_1_6_pro;
|
||||
pub mod generate_video_fal_kling_2_1_master;
|
||||
pub mod generate_video_fal_kling_2_1_pro;
|
||||
pub mod generate_video_fal_kling_2_5_turbo_pro;
|
||||
pub mod generate_video_fal_kling_2_6_pro;
|
||||
pub mod generate_video_fal_kling_3p0_pro;
|
||||
pub mod generate_video_fal_kling_3p0_standard;
|
||||
pub mod generate_video_fal_seedance_1_0_lite;
|
||||
pub mod generate_video_fal_seedance_1p5_pro;
|
||||
pub mod generate_video_fal_sora_2;
|
||||
pub mod generate_video_fal_sora_2_pro;
|
||||
pub mod generate_video_fal_veo_2;
|
||||
pub mod generate_video_fal_veo_3;
|
||||
pub mod generate_video_fal_veo_3_fast;
|
||||
pub mod generate_video_fal_veo_3p1;
|
||||
pub mod generate_video_fal_veo_3p1_fast;
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod artcraft;
|
||||
pub mod fal;
|
||||
pub mod muapi;
|
||||
pub mod seedance2pro;
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
use crate::client::router_muapi_client::RouterMuapiClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
GenerateVideoResponse, MuapiVideoResponsePayload,
|
||||
};
|
||||
use crate::generate::generate_video::plan::muapi::plan_generate_video_muapi_seedance2p0::{
|
||||
MuapiSeedance2p0Mode, PlanMuapiSeedance2p0,
|
||||
};
|
||||
use muapi_client::requests::seedance_2p0_image_to_video::seedance_2p0_image_to_video::{
|
||||
Seedance2p0ImageToVideoArgs, seedance_2p0_image_to_video,
|
||||
};
|
||||
use muapi_client::requests::seedance_2p0_text_to_video::seedance_2p0_text_to_video::{
|
||||
Seedance2p0TextToVideoArgs, seedance_2p0_text_to_video,
|
||||
};
|
||||
|
||||
pub async fn execute_muapi_seedance2p0(
|
||||
plan: &PlanMuapiSeedance2p0,
|
||||
muapi_client: &RouterMuapiClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &muapi_client.session;
|
||||
|
||||
let request_id = match &plan.mode {
|
||||
MuapiSeedance2p0Mode::TextToVideo { aspect_ratio, duration, quality } => {
|
||||
let args = Seedance2p0TextToVideoArgs {
|
||||
session,
|
||||
prompt: plan.prompt.clone(),
|
||||
aspect_ratio: *aspect_ratio,
|
||||
duration: *duration,
|
||||
quality: *quality,
|
||||
};
|
||||
let response = seedance_2p0_text_to_video(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Muapi(err)))?;
|
||||
response.request_id
|
||||
}
|
||||
MuapiSeedance2p0Mode::ImageToVideo { image_urls, aspect_ratio, duration, quality } => {
|
||||
let args = Seedance2p0ImageToVideoArgs {
|
||||
session,
|
||||
prompt: plan.prompt.clone(),
|
||||
image_urls: image_urls.clone(),
|
||||
aspect_ratio: *aspect_ratio,
|
||||
duration: *duration,
|
||||
quality: *quality,
|
||||
};
|
||||
let response = seedance_2p0_image_to_video(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Muapi(err)))?;
|
||||
response.request_id
|
||||
}
|
||||
};
|
||||
|
||||
Ok(GenerateVideoResponse::Muapi(MuapiVideoResponsePayload {
|
||||
request_id,
|
||||
}))
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
pub mod execute_muapi_seedance2p0;
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
GenerateVideoResponse, Seedance2proVideoResponsePayload,
|
||||
};
|
||||
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,
|
||||
};
|
||||
use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{
|
||||
prepare_file_upload, PrepareFileUploadArgs,
|
||||
};
|
||||
use seedance2pro_client::requests::upload_file::upload_file::{
|
||||
upload_file, UploadFileArgs,
|
||||
};
|
||||
use url_utils::extension::extract_extension_from_url::{
|
||||
extract_extension_from_url_str, ExtractExtensions,
|
||||
};
|
||||
|
||||
pub async fn execute_seedance2pro_seedance2p0(
|
||||
plan: &PlanSeedance2proSeedance2p0,
|
||||
seedance2pro_client: &RouterSeedance2ProClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &seedance2pro_client.session;
|
||||
|
||||
// Upload media files to seedance2pro CDN
|
||||
let start_frame_url = upload_optional_url(session, plan.request.start_frame_url.as_deref()).await?;
|
||||
let end_frame_url = upload_optional_url(session, plan.request.end_frame_url.as_deref()).await?;
|
||||
let reference_image_urls = upload_optional_url_list(session, plan.request.reference_image_urls.as_deref()).await?;
|
||||
let reference_video_urls = upload_optional_url_list(session, plan.request.reference_video_urls.as_deref()).await?;
|
||||
let reference_audio_urls = upload_optional_url_list(session, plan.request.reference_audio_urls.as_deref()).await?;
|
||||
|
||||
let mut request = plan.request.clone();
|
||||
request.start_frame_url = start_frame_url;
|
||||
request.end_frame_url = end_frame_url;
|
||||
request.reference_image_urls = reference_image_urls;
|
||||
request.reference_video_urls = reference_video_urls;
|
||||
request.reference_audio_urls = reference_audio_urls;
|
||||
|
||||
let args = GenerateVideoArgs {
|
||||
session,
|
||||
host_override: None,
|
||||
request,
|
||||
};
|
||||
|
||||
let response = generate_video(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
maybe_task_ids: response.task_ids,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Downloads a file from a source URL and re-uploads it to seedance2pro CDN.
|
||||
pub(crate) async fn upload_to_seedance2pro(
|
||||
session: &seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession,
|
||||
source_url: &str,
|
||||
) -> Result<String, ArtcraftRouterError> {
|
||||
let extension = extract_extension_from_url_str(source_url, &ExtractExtensions::All)
|
||||
.map(|ext| ext.without_period().to_string())
|
||||
.unwrap_or_else(|| "jpg".to_string());
|
||||
|
||||
let file_bytes = download_file(source_url).await?;
|
||||
|
||||
let prepare_response = prepare_file_upload(PrepareFileUploadArgs {
|
||||
session,
|
||||
extension,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
|
||||
let upload_response = upload_file(UploadFileArgs {
|
||||
upload_url: prepare_response.upload_url,
|
||||
file_bytes,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
|
||||
Ok(upload_response.public_url)
|
||||
}
|
||||
|
||||
pub(crate) async fn upload_optional_url(
|
||||
session: &seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession,
|
||||
url: Option<&str>,
|
||||
) -> Result<Option<String>, ArtcraftRouterError> {
|
||||
match url {
|
||||
None => Ok(None),
|
||||
Some(u) => Ok(Some(upload_to_seedance2pro(session, u).await?)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn upload_optional_url_list(
|
||||
session: &seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession,
|
||||
urls: Option<&[String]>,
|
||||
) -> Result<Option<Vec<String>>, ArtcraftRouterError> {
|
||||
match urls {
|
||||
None => Ok(None),
|
||||
Some(url_list) if url_list.is_empty() => Ok(None),
|
||||
Some(url_list) => {
|
||||
let mut uploaded = Vec::with_capacity(url_list.len());
|
||||
for url in url_list {
|
||||
uploaded.push(upload_to_seedance2pro(session, url).await?);
|
||||
}
|
||||
Ok(Some(uploaded))
|
||||
}
|
||||
}
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::execute::seedance2pro::execute_seedance2pro_seedance2p0::{
|
||||
upload_optional_url, upload_optional_url_list,
|
||||
};
|
||||
use crate::generate::generate_video::generate_video_response::{
|
||||
GenerateVideoResponse, Seedance2proVideoResponsePayload,
|
||||
};
|
||||
use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0_fast::PlanSeedance2proSeedance2p0Fast;
|
||||
use seedance2pro_client::requests::generate_video::generate_video::{
|
||||
generate_video, GenerateVideoArgs,
|
||||
};
|
||||
|
||||
/// Execute Seedance 2.0 Fast via the Seedance2Pro/Kinovi provider.
|
||||
///
|
||||
/// Identical to the Seedance 2.0 Pro executor except it uses
|
||||
/// `ModelType::Seedance2Fast` instead of `ModelType::Seedance2Pro`.
|
||||
pub async fn execute_seedance2pro_seedance2p0_fast(
|
||||
plan: &PlanSeedance2proSeedance2p0Fast,
|
||||
seedance2pro_client: &RouterSeedance2ProClient,
|
||||
) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &seedance2pro_client.session;
|
||||
|
||||
// Upload media files to seedance2pro CDN
|
||||
let start_frame_url = upload_optional_url(session, plan.request.start_frame_url.as_deref()).await?;
|
||||
let end_frame_url = upload_optional_url(session, plan.request.end_frame_url.as_deref()).await?;
|
||||
let reference_image_urls = upload_optional_url_list(session, plan.request.reference_image_urls.as_deref()).await?;
|
||||
let reference_video_urls = upload_optional_url_list(session, plan.request.reference_video_urls.as_deref()).await?;
|
||||
let reference_audio_urls = upload_optional_url_list(session, plan.request.reference_audio_urls.as_deref()).await?;
|
||||
|
||||
let mut request = plan.request.clone();
|
||||
request.start_frame_url = start_frame_url;
|
||||
request.end_frame_url = end_frame_url;
|
||||
request.reference_image_urls = reference_image_urls;
|
||||
request.reference_video_urls = reference_video_urls;
|
||||
request.reference_audio_urls = reference_audio_urls;
|
||||
|
||||
let args = GenerateVideoArgs {
|
||||
session,
|
||||
host_override: None,
|
||||
request,
|
||||
};
|
||||
|
||||
let response = generate_video(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
maybe_task_ids: response.task_ids,
|
||||
}))
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
pub mod execute_seedance2pro_seedance2p0;
|
||||
pub mod execute_seedance2pro_seedance2p0_fast;
|
||||
+7
-179
@@ -9,44 +9,6 @@ use crate::api::provider::Provider;
|
||||
use crate::api::video_list_ref::VideoListRef;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_pro::plan_generate_video_artcraft_kling3p0_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling3p0_standard::plan_generate_video_artcraft_kling3p0_standard;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_1_6_pro::plan_generate_video_artcraft_kling_1_6_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_master::plan_generate_video_artcraft_kling_2_1_master;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_1_pro::plan_generate_video_artcraft_kling_2_1_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_5_turbo_pro::plan_generate_video_artcraft_kling_2_5_turbo_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_kling_2_6_pro::plan_generate_video_artcraft_kling_2_6_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance1p5_pro::plan_generate_video_artcraft_seedance1p5_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0::plan_generate_video_artcraft_seedance2p0;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance2p0_fast::plan_generate_video_artcraft_seedance2p0_fast;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_seedance_1_0_lite::plan_generate_video_artcraft_seedance_1_0_lite;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_sora_2::plan_generate_video_artcraft_sora_2;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_sora_2_pro::plan_generate_video_artcraft_sora_2_pro;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_2::plan_generate_video_artcraft_veo_2;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3::plan_generate_video_artcraft_veo_3;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3_fast::plan_generate_video_artcraft_veo_3_fast;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3p1::plan_generate_video_artcraft_veo_3p1;
|
||||
use crate::generate::generate_video::plan::artcraft::plan_generate_video_artcraft_veo_3p1_fast::plan_generate_video_artcraft_veo_3p1_fast;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_1_6_pro::plan_generate_video_fal_kling_1_6_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_1_master::plan_generate_video_fal_kling_2_1_master;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_1_pro::plan_generate_video_fal_kling_2_1_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_5_turbo_pro::plan_generate_video_fal_kling_2_5_turbo_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_2_6_pro::plan_generate_video_fal_kling_2_6_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_pro::plan_generate_video_fal_kling_3p0_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_kling_3p0_standard::plan_generate_video_fal_kling_3p0_standard;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_seedance_1_0_lite::plan_generate_video_fal_seedance_1_0_lite;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_seedance_1p5_pro::plan_generate_video_fal_seedance_1p5_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_sora_2::plan_generate_video_fal_sora_2;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_sora_2_pro::plan_generate_video_fal_sora_2_pro;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_2::plan_generate_video_fal_veo_2;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3::plan_generate_video_fal_veo_3;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3_fast::plan_generate_video_fal_veo_3_fast;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1::plan_generate_video_fal_veo_3p1;
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3p1_fast::plan_generate_video_fal_veo_3p1_fast;
|
||||
use crate::generate::generate_video::plan::muapi::plan_generate_video_muapi_seedance2p0::plan_generate_video_muapi_seedance2p0;
|
||||
use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0::plan_generate_video_seedance2pro_seedance2p0;
|
||||
use crate::generate::generate_video::plan::seedance2pro::plan_generate_video_seedance2pro_seedance2p0_fast::plan_generate_video_seedance2pro_seedance2p0_fast;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::grok_imagine_video::build::build_artcraft_grok_imagine_video;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::happy_horse_1p0::build::build_artcraft_happy_horse_1p0;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::kling_1_6_pro::build::build_artcraft_kling_1_6_pro;
|
||||
@@ -58,6 +20,8 @@ use crate::generate::generate_video_v2::providers::artcraft::kling_3p0_pro::buil
|
||||
use crate::generate::generate_video_v2::providers::artcraft::kling_3p0_standard::build::build_artcraft_kling_3p0_standard;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::preview_model::build::build_artcraft_preview_model;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::preview_model_fast::build::build_artcraft_preview_model_fast;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_1p0_lite::build::build_artcraft_seedance_1p0_lite;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_1p5_pro::build::build_artcraft_seedance_1p5_pro;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0::build::build_artcraft_seedance_2p0;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_fast::build::build_artcraft_seedance_2p0_fast;
|
||||
use crate::generate::generate_video_v2::providers::artcraft::seedance_2p0_u::build::build_artcraft_seedance_2p0_u;
|
||||
@@ -95,9 +59,8 @@ use crate::generate::generate_video_v2::providers::fal::veo_3p1_fast::build::bui
|
||||
use crate::generate::generate_video_v2::providers::kinovi::seedance_2p0_fast::build::build_kinovi_seedance_2p0_fast;
|
||||
use crate::generate::generate_video_v2::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
|
||||
/// Plan to either (1) generate a video or (2) determine how much it costs to generate that video.
|
||||
/// This works across multiple providers by shaping a generic "GenerateVideoRequest" into a provider-specific plan.
|
||||
/// That plan can then be used to return a cost estimate for that given provider or return a struct that can be used to send a real generation request.
|
||||
/// Provider-agnostic video generation request. Distilled by `build2()` into a
|
||||
/// `VideoGenerationDraftOrRequest` for the selected (provider, model) pair.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GenerateVideoRequestBuilder {
|
||||
/// Which model to use.
|
||||
@@ -182,64 +145,6 @@ impl Default for GenerateVideoRequestBuilder {
|
||||
|
||||
impl GenerateVideoRequestBuilder {
|
||||
|
||||
pub fn use_new_builder(&self) -> bool {
|
||||
match (self.provider, self.model) {
|
||||
// Artcraft
|
||||
(Provider::Artcraft, CommonVideoModel::GrokImagineVideo) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::HappyHorse1p0) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling16Pro) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling21Master) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling21Pro) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling2p5TurboPro) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling2p6Pro) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling3p0Pro) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Kling3p0Standard) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Fast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0Ultra) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0UltraFast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0BytePlus) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0BytePlusFast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModel) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModelFast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Sora2) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Sora2Pro) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Veo2) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Veo3) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Veo3Fast) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Veo3p1) => true,
|
||||
(Provider::Artcraft, CommonVideoModel::Veo3p1Fast) => true,
|
||||
// Fal
|
||||
(Provider::Fal, CommonVideoModel::Kling16Pro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Kling21Master) => true,
|
||||
(Provider::Fal, CommonVideoModel::Kling21Pro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Kling2p5TurboPro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Kling2p6Pro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Kling3p0Pro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Kling3p0Standard) => true,
|
||||
(Provider::Fal, CommonVideoModel::Seedance10Lite) => true,
|
||||
(Provider::Fal, CommonVideoModel::Seedance1p5Pro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Sora2) => true,
|
||||
(Provider::Fal, CommonVideoModel::Sora2Pro) => true,
|
||||
(Provider::Fal, CommonVideoModel::Veo2) => true,
|
||||
(Provider::Fal, CommonVideoModel::Veo3) => true,
|
||||
(Provider::Fal, CommonVideoModel::Veo3Fast) => true,
|
||||
(Provider::Fal, CommonVideoModel::Veo3p1) => true,
|
||||
(Provider::Fal, CommonVideoModel::Veo3p1Fast) => true,
|
||||
// GmiCloud
|
||||
(Provider::GmiCloud, CommonVideoModel::Seedance2p0Ultra) => true,
|
||||
(Provider::GmiCloud, CommonVideoModel::Seedance2p0UltraFast) => true,
|
||||
// Grok
|
||||
(Provider::GrokApi, CommonVideoModel::GrokImagineVideo) => true,
|
||||
// Kinovi
|
||||
(Provider::Seedance2Pro, CommonVideoModel::HappyHorse1p0) => true,
|
||||
(Provider::Seedance2Pro, CommonVideoModel::Seedance2p0) => true,
|
||||
(Provider::Seedance2Pro, CommonVideoModel::Seedance2p0Fast) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// New builder (transitional state)
|
||||
pub fn build2(self) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
match (self.provider, self.model) {
|
||||
// Artcraft
|
||||
@@ -260,6 +165,8 @@ impl GenerateVideoRequestBuilder {
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance2p0BytePlusFast) => build_artcraft_seedance_2p0_bp_fast(self),
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModel) => build_artcraft_preview_model(self),
|
||||
(Provider::Artcraft, CommonVideoModel::PreviewModelFast) => build_artcraft_preview_model_fast(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance10Lite) => build_artcraft_seedance_1p0_lite(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Seedance1p5Pro) => build_artcraft_seedance_1p5_pro(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Sora2) => build_artcraft_sora_2(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Sora2Pro) => build_artcraft_sora_2_pro(self),
|
||||
(Provider::Artcraft, CommonVideoModel::Veo2) => build_artcraft_veo_2(self),
|
||||
@@ -297,88 +204,9 @@ impl GenerateVideoRequestBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the video generation request, construct a plan, then yield a means to execute it.
|
||||
pub fn build(&self) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
match self.provider {
|
||||
Provider::Artcraft => self.build_artcraft(),
|
||||
Provider::Fal => self.build_fal(),
|
||||
Provider::GmiCloud => self.unsupported_provider(), // GmiCloud uses build2() only
|
||||
Provider::GrokApi => self.unsupported_provider(), // Grok uses build2() only
|
||||
Provider::Muapi => self.build_muapi(),
|
||||
Provider::Seedance2Pro => self.build_seedance2pro(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_artcraft(&self) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
match self.model {
|
||||
CommonVideoModel::Kling16Pro => plan_generate_video_artcraft_kling_1_6_pro(self),
|
||||
CommonVideoModel::Kling21Master => plan_generate_video_artcraft_kling_2_1_master(self),
|
||||
CommonVideoModel::Kling21Pro => plan_generate_video_artcraft_kling_2_1_pro(self),
|
||||
CommonVideoModel::Kling2p5TurboPro => plan_generate_video_artcraft_kling_2_5_turbo_pro(self),
|
||||
CommonVideoModel::Kling2p6Pro => plan_generate_video_artcraft_kling_2_6_pro(self),
|
||||
CommonVideoModel::Kling3p0Pro => plan_generate_video_artcraft_kling3p0_pro(self),
|
||||
CommonVideoModel::Kling3p0Standard => plan_generate_video_artcraft_kling3p0_standard(self),
|
||||
CommonVideoModel::Seedance10Lite => plan_generate_video_artcraft_seedance_1_0_lite(self),
|
||||
CommonVideoModel::Seedance1p5Pro => plan_generate_video_artcraft_seedance1p5_pro(self),
|
||||
CommonVideoModel::Seedance2p0 => plan_generate_video_artcraft_seedance2p0(self),
|
||||
CommonVideoModel::Seedance2p0Fast => plan_generate_video_artcraft_seedance2p0_fast(self),
|
||||
CommonVideoModel::Sora2 => plan_generate_video_artcraft_sora_2(self),
|
||||
CommonVideoModel::Sora2Pro => plan_generate_video_artcraft_sora_2_pro(self),
|
||||
CommonVideoModel::Veo2 => plan_generate_video_artcraft_veo_2(self),
|
||||
CommonVideoModel::Veo3 => plan_generate_video_artcraft_veo_3(self),
|
||||
CommonVideoModel::Veo3Fast => plan_generate_video_artcraft_veo_3_fast(self),
|
||||
CommonVideoModel::Veo3p1 => plan_generate_video_artcraft_veo_3p1(self),
|
||||
CommonVideoModel::Veo3p1Fast => plan_generate_video_artcraft_veo_3p1_fast(self),
|
||||
_ => Err(ArtcraftRouterError::UnsupportedModel(format!("{:?}", self.model))),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_fal(&self) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
match self.model {
|
||||
CommonVideoModel::Veo2 => plan_generate_video_fal_veo_2(self),
|
||||
CommonVideoModel::Veo3 => plan_generate_video_fal_veo_3(self),
|
||||
CommonVideoModel::Veo3Fast => plan_generate_video_fal_veo_3_fast(self),
|
||||
CommonVideoModel::Veo3p1 => plan_generate_video_fal_veo_3p1(self),
|
||||
CommonVideoModel::Veo3p1Fast => plan_generate_video_fal_veo_3p1_fast(self),
|
||||
CommonVideoModel::Kling16Pro => plan_generate_video_fal_kling_1_6_pro(self),
|
||||
CommonVideoModel::Kling21Pro => plan_generate_video_fal_kling_2_1_pro(self),
|
||||
CommonVideoModel::Kling21Master => plan_generate_video_fal_kling_2_1_master(self),
|
||||
CommonVideoModel::Kling2p5TurboPro => plan_generate_video_fal_kling_2_5_turbo_pro(self),
|
||||
CommonVideoModel::Kling2p6Pro => plan_generate_video_fal_kling_2_6_pro(self),
|
||||
CommonVideoModel::Kling3p0Pro => plan_generate_video_fal_kling_3p0_pro(self),
|
||||
CommonVideoModel::Kling3p0Standard => plan_generate_video_fal_kling_3p0_standard(self),
|
||||
CommonVideoModel::Seedance10Lite => plan_generate_video_fal_seedance_1_0_lite(self),
|
||||
CommonVideoModel::Seedance1p5Pro => plan_generate_video_fal_seedance_1p5_pro(self),
|
||||
CommonVideoModel::Sora2 => plan_generate_video_fal_sora_2(self),
|
||||
CommonVideoModel::Sora2Pro => plan_generate_video_fal_sora_2_pro(self),
|
||||
_ => Err(ArtcraftRouterError::UnsupportedModel(format!("{:?}", self.model))),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_muapi(&self) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
match self.model {
|
||||
CommonVideoModel::Seedance2p0 => plan_generate_video_muapi_seedance2p0(self),
|
||||
_ => Err(ArtcraftRouterError::UnsupportedModel(format!("{:?}", self.model))),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_seedance2pro(&self) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
match self.model {
|
||||
CommonVideoModel::Seedance2p0 => plan_generate_video_seedance2pro_seedance2p0(self),
|
||||
CommonVideoModel::Seedance2p0Fast => plan_generate_video_seedance2pro_seedance2p0_fast(self),
|
||||
_ => Err(ArtcraftRouterError::UnsupportedModel(format!("{:?}", self.model))),
|
||||
}
|
||||
}
|
||||
|
||||
fn unsupported_provider(&self) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
Err(ArtcraftRouterError::UnsupportedModel(
|
||||
format!("Video generation for model `{:?}` is not supported for provider {:?}", self.model, self.provider)
|
||||
))
|
||||
}
|
||||
|
||||
fn unsupported_provider_and_model(&self) -> Result<VideoGenerationDraftOrRequest, ArtcraftRouterError> {
|
||||
Err(ArtcraftRouterError::UnsupportedProviderAndModelForNewApi(
|
||||
format!("Video generation for model `{:?}` is not supported for provider {:?} using the new API", self.model, self.provider)
|
||||
format!("Video generation for model `{:?}` is not supported for provider {:?}", self.model, self.provider)
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
-14
@@ -1,7 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use muapi_client::api_types::request_id::RequestId as MuapiRequestId;
|
||||
use tokens::tokens::generic_inference_jobs::InferenceJobToken;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -10,11 +9,6 @@ pub struct ArtcraftVideoResponsePayload {
|
||||
pub all_inference_job_tokens: Vec<InferenceJobToken>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MuapiVideoResponsePayload {
|
||||
pub request_id: MuapiRequestId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Seedance2proVideoResponsePayload {
|
||||
pub order_id: String,
|
||||
@@ -56,7 +50,6 @@ pub enum GenerateVideoResponse {
|
||||
Artcraft(ArtcraftVideoResponsePayload),
|
||||
GmiCloud(GmiCloudVideoResponsePayload),
|
||||
Grok(GrokVideoResponsePayload),
|
||||
Muapi(MuapiVideoResponsePayload),
|
||||
Seedance2Pro(Seedance2proVideoResponsePayload),
|
||||
Fal(FalVideoResponsePayload),
|
||||
}
|
||||
@@ -69,13 +62,6 @@ impl GenerateVideoResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_muapi_payload(&self) -> Option<MuapiVideoResponsePayload> {
|
||||
match self {
|
||||
Self::Muapi(p) => Some(p.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_payload(&self) -> Option<Seedance2proVideoResponsePayload> {
|
||||
match self {
|
||||
Self::Seedance2Pro(p) => Some(p.clone()),
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
pub mod cost;
|
||||
pub mod execute;
|
||||
pub mod generate_video_request_builder;
|
||||
pub mod generate_video_response;
|
||||
pub mod plan;
|
||||
pub mod video_generation_cost_estimate;
|
||||
pub mod video_generation_plan;
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
pub mod plan_generate_video_artcraft_kling_1_6_pro;
|
||||
pub mod plan_generate_video_artcraft_kling_2_1_master;
|
||||
pub mod plan_generate_video_artcraft_kling_2_1_pro;
|
||||
pub mod plan_generate_video_artcraft_kling_2_5_turbo_pro;
|
||||
pub mod plan_generate_video_artcraft_kling_2_6_pro;
|
||||
pub mod plan_generate_video_artcraft_kling3p0_pro;
|
||||
pub mod plan_generate_video_artcraft_kling3p0_standard;
|
||||
pub mod plan_generate_video_artcraft_seedance_1_0_lite;
|
||||
pub mod plan_generate_video_artcraft_seedance1p5_pro;
|
||||
pub mod plan_generate_video_artcraft_seedance2p0;
|
||||
pub mod plan_generate_video_artcraft_seedance2p0_fast;
|
||||
pub mod plan_generate_video_artcraft_sora_2;
|
||||
pub mod plan_generate_video_artcraft_sora_2_pro;
|
||||
pub mod plan_generate_video_artcraft_veo_2;
|
||||
pub mod plan_generate_video_artcraft_veo_3;
|
||||
pub mod plan_generate_video_artcraft_veo_3_fast;
|
||||
pub mod plan_generate_video_artcraft_veo_3p1;
|
||||
pub mod plan_generate_video_artcraft_veo_3p1_fast;
|
||||
-276
@@ -1,276 +0,0 @@
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_3p0_pro_multi_function_video_gen::{
|
||||
Kling3p0ProMultiFunctionVideoGenAspectRatio,
|
||||
Kling3p0ProMultiFunctionVideoGenDuration,
|
||||
};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PlanArtcraftKling3p0Pro {
|
||||
pub prompt: Option<String>,
|
||||
pub negative_prompt: Option<String>,
|
||||
pub start_frame: Option<MediaFileToken>,
|
||||
pub end_frame: Option<MediaFileToken>,
|
||||
pub aspect_ratio: Option<Kling3p0ProMultiFunctionVideoGenAspectRatio>,
|
||||
pub duration: Option<Kling3p0ProMultiFunctionVideoGenDuration>,
|
||||
pub generate_audio: Option<bool>,
|
||||
pub idempotency_token: String,
|
||||
}
|
||||
|
||||
pub fn plan_generate_video_artcraft_kling3p0_pro(
|
||||
request: &GenerateVideoRequestBuilder,
|
||||
) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
let strategy = request.request_mismatch_mitigation_strategy;
|
||||
|
||||
let start_frame = resolve_image_ref(request.start_frame.clone())?;
|
||||
let end_frame = resolve_image_ref(request.end_frame.clone())?;
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?;
|
||||
let duration = plan_duration(request.duration_seconds, strategy)?;
|
||||
|
||||
Ok(VideoGenerationPlan::ArtcraftKling3p0Pro(PlanArtcraftKling3p0Pro {
|
||||
prompt: request.prompt.clone(),
|
||||
negative_prompt: request.negative_prompt.clone(),
|
||||
start_frame,
|
||||
end_frame,
|
||||
aspect_ratio,
|
||||
duration,
|
||||
generate_audio: request.generate_audio,
|
||||
idempotency_token: request.get_or_generate_idempotency_token(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn resolve_image_ref(
|
||||
image_ref: Option<ImageRef>,
|
||||
) -> Result<Option<MediaFileToken>, ArtcraftRouterError> {
|
||||
match image_ref {
|
||||
None => Ok(None),
|
||||
Some(ImageRef::MediaFileToken(t)) => Ok(Some(t)),
|
||||
Some(ImageRef::Url(_)) => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kling 3.0 Pro supports: Square (1:1), SixteenByNine (16:9), NineBySixteen (9:16)
|
||||
fn plan_aspect_ratio(
|
||||
aspect_ratio: Option<CommonAspectRatio>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<Kling3p0ProMultiFunctionVideoGenAspectRatio>, ArtcraftRouterError> {
|
||||
match aspect_ratio {
|
||||
None => Ok(None),
|
||||
|
||||
// Direct mappings
|
||||
Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => {
|
||||
Ok(Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::SixteenByNine))
|
||||
}
|
||||
Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => {
|
||||
Ok(Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::NineBySixteen))
|
||||
}
|
||||
Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => {
|
||||
Ok(Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::Square))
|
||||
}
|
||||
|
||||
// Auto defaults to 16:9
|
||||
Some(CommonAspectRatio::Auto)
|
||||
| Some(CommonAspectRatio::Auto2k)
|
||||
| Some(CommonAspectRatio::Auto4k) => {
|
||||
Ok(Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::SixteenByNine))
|
||||
}
|
||||
|
||||
// Mismatches
|
||||
Some(unsupported) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "aspect_ratio",
|
||||
value: format!("{:?}", unsupported),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(nearest_aspect_ratio(unsupported)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn nearest_aspect_ratio(aspect_ratio: CommonAspectRatio) -> Kling3p0ProMultiFunctionVideoGenAspectRatio {
|
||||
match aspect_ratio {
|
||||
// Wide-ish → 16:9
|
||||
CommonAspectRatio::WideFourByThree
|
||||
| CommonAspectRatio::WideFiveByFour
|
||||
| CommonAspectRatio::WideThreeByTwo
|
||||
| CommonAspectRatio::WideTwentyOneByNine => Kling3p0ProMultiFunctionVideoGenAspectRatio::SixteenByNine,
|
||||
// Tall-ish → 9:16
|
||||
CommonAspectRatio::TallThreeByFour
|
||||
| CommonAspectRatio::TallFourByFive
|
||||
| CommonAspectRatio::TallTwoByThree
|
||||
| CommonAspectRatio::TallNineByTwentyOne => Kling3p0ProMultiFunctionVideoGenAspectRatio::NineBySixteen,
|
||||
// Fallback
|
||||
_ => Kling3p0ProMultiFunctionVideoGenAspectRatio::Square,
|
||||
}
|
||||
}
|
||||
|
||||
// Kling 3.0 Pro supports durations of 3–15 seconds.
|
||||
fn plan_duration(
|
||||
duration_seconds: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<Kling3p0ProMultiFunctionVideoGenDuration>, ArtcraftRouterError> {
|
||||
const MIN: u16 = 3;
|
||||
const MAX: u16 = 15;
|
||||
match duration_seconds {
|
||||
None => Ok(None),
|
||||
Some(d) if d >= MIN && d <= MAX => Ok(Some(seconds_to_duration(d))),
|
||||
Some(d) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "duration_seconds",
|
||||
value: format!("{}", d),
|
||||
}))
|
||||
}
|
||||
_ => Ok(Some(seconds_to_duration(d.clamp(MIN, MAX)))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn seconds_to_duration(seconds: u16) -> Kling3p0ProMultiFunctionVideoGenDuration {
|
||||
match seconds {
|
||||
3 => Kling3p0ProMultiFunctionVideoGenDuration::ThreeSeconds,
|
||||
4 => Kling3p0ProMultiFunctionVideoGenDuration::FourSeconds,
|
||||
5 => Kling3p0ProMultiFunctionVideoGenDuration::FiveSeconds,
|
||||
6 => Kling3p0ProMultiFunctionVideoGenDuration::SixSeconds,
|
||||
7 => Kling3p0ProMultiFunctionVideoGenDuration::SevenSeconds,
|
||||
8 => Kling3p0ProMultiFunctionVideoGenDuration::EightSeconds,
|
||||
9 => Kling3p0ProMultiFunctionVideoGenDuration::NineSeconds,
|
||||
10 => Kling3p0ProMultiFunctionVideoGenDuration::TenSeconds,
|
||||
11 => Kling3p0ProMultiFunctionVideoGenDuration::ElevenSeconds,
|
||||
12 => Kling3p0ProMultiFunctionVideoGenDuration::TwelveSeconds,
|
||||
13 => Kling3p0ProMultiFunctionVideoGenDuration::ThirteenSeconds,
|
||||
14 => Kling3p0ProMultiFunctionVideoGenDuration::FourteenSeconds,
|
||||
_ => Kling3p0ProMultiFunctionVideoGenDuration::FifteenSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
|
||||
fn base_request() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Kling3p0Pro,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: Some("a cat in space".to_string()),
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_direct_16x9() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
..base_request()
|
||||
};
|
||||
let VideoGenerationPlan::ArtcraftKling3p0Pro(plan) = request.build().unwrap() else { panic!("wrong variant") };
|
||||
assert!(matches!(plan.aspect_ratio, Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::SixteenByNine)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_direct_9x16() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::TallNineBySixteen),
|
||||
..base_request()
|
||||
};
|
||||
let VideoGenerationPlan::ArtcraftKling3p0Pro(plan) = request.build().unwrap() else { panic!("wrong variant") };
|
||||
assert!(matches!(plan.aspect_ratio, Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::NineBySixteen)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_direct_square() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::Square),
|
||||
..base_request()
|
||||
};
|
||||
let VideoGenerationPlan::ArtcraftKling3p0Pro(plan) = request.build().unwrap() else { panic!("wrong variant") };
|
||||
assert!(matches!(plan.aspect_ratio, Some(Kling3p0ProMultiFunctionVideoGenAspectRatio::Square)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_error_out_on_unsupported() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::WideThreeByTwo),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { .. }))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_valid_range() {
|
||||
for d in 3..=15 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
duration_seconds: Some(d),
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(result.is_ok(), "duration {} should be valid", d);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_out_of_range_error_out() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
duration_seconds: Some(16),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { .. }))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn url_image_ref_returns_error() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::Url("https://example.com/image.jpg".to_string())),
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
|
||||
));
|
||||
}
|
||||
}
|
||||
-276
@@ -1,276 +0,0 @@
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use artcraft_api_defs::generate::video::multi_function::kling_3p0_standard_multi_function_video_gen::{
|
||||
Kling3p0StandardMultiFunctionVideoGenAspectRatio,
|
||||
Kling3p0StandardMultiFunctionVideoGenDuration,
|
||||
};
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PlanArtcraftKling3p0Standard {
|
||||
pub prompt: Option<String>,
|
||||
pub negative_prompt: Option<String>,
|
||||
pub start_frame: Option<MediaFileToken>,
|
||||
pub end_frame: Option<MediaFileToken>,
|
||||
pub aspect_ratio: Option<Kling3p0StandardMultiFunctionVideoGenAspectRatio>,
|
||||
pub duration: Option<Kling3p0StandardMultiFunctionVideoGenDuration>,
|
||||
pub generate_audio: Option<bool>,
|
||||
pub idempotency_token: String,
|
||||
}
|
||||
|
||||
pub fn plan_generate_video_artcraft_kling3p0_standard(
|
||||
request: &GenerateVideoRequestBuilder,
|
||||
) -> Result<VideoGenerationPlan, ArtcraftRouterError> {
|
||||
let strategy = request.request_mismatch_mitigation_strategy;
|
||||
|
||||
let start_frame = resolve_image_ref(request.start_frame.clone())?;
|
||||
let end_frame = resolve_image_ref(request.end_frame.clone())?;
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?;
|
||||
let duration = plan_duration(request.duration_seconds, strategy)?;
|
||||
|
||||
Ok(VideoGenerationPlan::ArtcraftKling3p0Standard(PlanArtcraftKling3p0Standard {
|
||||
prompt: request.prompt.clone(),
|
||||
negative_prompt: request.negative_prompt.clone(),
|
||||
start_frame,
|
||||
end_frame,
|
||||
aspect_ratio,
|
||||
duration,
|
||||
generate_audio: request.generate_audio,
|
||||
idempotency_token: request.get_or_generate_idempotency_token(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn resolve_image_ref(
|
||||
image_ref: Option<ImageRef>,
|
||||
) -> Result<Option<MediaFileToken>, ArtcraftRouterError> {
|
||||
match image_ref {
|
||||
None => Ok(None),
|
||||
Some(ImageRef::MediaFileToken(t)) => Ok(Some(t)),
|
||||
Some(ImageRef::Url(_)) => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kling 3.0 Standard supports: Square (1:1), SixteenByNine (16:9), NineBySixteen (9:16)
|
||||
fn plan_aspect_ratio(
|
||||
aspect_ratio: Option<CommonAspectRatio>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<Kling3p0StandardMultiFunctionVideoGenAspectRatio>, ArtcraftRouterError> {
|
||||
match aspect_ratio {
|
||||
None => Ok(None),
|
||||
|
||||
// Direct mappings
|
||||
Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => {
|
||||
Ok(Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::SixteenByNine))
|
||||
}
|
||||
Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => {
|
||||
Ok(Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::NineBySixteen))
|
||||
}
|
||||
Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => {
|
||||
Ok(Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::Square))
|
||||
}
|
||||
|
||||
// Auto defaults to 16:9
|
||||
Some(CommonAspectRatio::Auto)
|
||||
| Some(CommonAspectRatio::Auto2k)
|
||||
| Some(CommonAspectRatio::Auto4k) => {
|
||||
Ok(Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::SixteenByNine))
|
||||
}
|
||||
|
||||
// Mismatches
|
||||
Some(unsupported) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "aspect_ratio",
|
||||
value: format!("{:?}", unsupported),
|
||||
}))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade
|
||||
| RequestMismatchMitigationStrategy::PayLessDowngrade => {
|
||||
Ok(Some(nearest_aspect_ratio(unsupported)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn nearest_aspect_ratio(aspect_ratio: CommonAspectRatio) -> Kling3p0StandardMultiFunctionVideoGenAspectRatio {
|
||||
match aspect_ratio {
|
||||
// Wide-ish → 16:9
|
||||
CommonAspectRatio::WideFourByThree
|
||||
| CommonAspectRatio::WideFiveByFour
|
||||
| CommonAspectRatio::WideThreeByTwo
|
||||
| CommonAspectRatio::WideTwentyOneByNine => Kling3p0StandardMultiFunctionVideoGenAspectRatio::SixteenByNine,
|
||||
// Tall-ish → 9:16
|
||||
CommonAspectRatio::TallThreeByFour
|
||||
| CommonAspectRatio::TallFourByFive
|
||||
| CommonAspectRatio::TallTwoByThree
|
||||
| CommonAspectRatio::TallNineByTwentyOne => Kling3p0StandardMultiFunctionVideoGenAspectRatio::NineBySixteen,
|
||||
// Fallback
|
||||
_ => Kling3p0StandardMultiFunctionVideoGenAspectRatio::Square,
|
||||
}
|
||||
}
|
||||
|
||||
// Kling 3.0 Standard supports durations of 3–15 seconds.
|
||||
fn plan_duration(
|
||||
duration_seconds: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Option<Kling3p0StandardMultiFunctionVideoGenDuration>, ArtcraftRouterError> {
|
||||
const MIN: u16 = 3;
|
||||
const MAX: u16 = 15;
|
||||
match duration_seconds {
|
||||
None => Ok(None),
|
||||
Some(d) if d >= MIN && d <= MAX => Ok(Some(seconds_to_duration(d))),
|
||||
Some(d) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption {
|
||||
field: "duration_seconds",
|
||||
value: format!("{}", d),
|
||||
}))
|
||||
}
|
||||
_ => Ok(Some(seconds_to_duration(d.clamp(MIN, MAX)))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn seconds_to_duration(seconds: u16) -> Kling3p0StandardMultiFunctionVideoGenDuration {
|
||||
match seconds {
|
||||
3 => Kling3p0StandardMultiFunctionVideoGenDuration::ThreeSeconds,
|
||||
4 => Kling3p0StandardMultiFunctionVideoGenDuration::FourSeconds,
|
||||
5 => Kling3p0StandardMultiFunctionVideoGenDuration::FiveSeconds,
|
||||
6 => Kling3p0StandardMultiFunctionVideoGenDuration::SixSeconds,
|
||||
7 => Kling3p0StandardMultiFunctionVideoGenDuration::SevenSeconds,
|
||||
8 => Kling3p0StandardMultiFunctionVideoGenDuration::EightSeconds,
|
||||
9 => Kling3p0StandardMultiFunctionVideoGenDuration::NineSeconds,
|
||||
10 => Kling3p0StandardMultiFunctionVideoGenDuration::TenSeconds,
|
||||
11 => Kling3p0StandardMultiFunctionVideoGenDuration::ElevenSeconds,
|
||||
12 => Kling3p0StandardMultiFunctionVideoGenDuration::TwelveSeconds,
|
||||
13 => Kling3p0StandardMultiFunctionVideoGenDuration::ThirteenSeconds,
|
||||
14 => Kling3p0StandardMultiFunctionVideoGenDuration::FourteenSeconds,
|
||||
_ => Kling3p0StandardMultiFunctionVideoGenDuration::FifteenSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::api::common_aspect_ratio::CommonAspectRatio;
|
||||
use crate::api::common_video_model::CommonVideoModel;
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::provider::Provider;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
|
||||
fn base_request() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: CommonVideoModel::Kling3p0Standard,
|
||||
provider: Provider::Artcraft,
|
||||
prompt: Some("a cat in space".to_string()),
|
||||
negative_prompt: None,
|
||||
start_frame: None,
|
||||
end_frame: None,
|
||||
reference_images: None,
|
||||
reference_videos: None,
|
||||
reference_audio: None,
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
idempotency_token: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_direct_16x9() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::WideSixteenByNine),
|
||||
..base_request()
|
||||
};
|
||||
let VideoGenerationPlan::ArtcraftKling3p0Standard(plan) = request.build().unwrap() else { panic!("wrong variant") };
|
||||
assert!(matches!(plan.aspect_ratio, Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::SixteenByNine)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_direct_9x16() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::TallNineBySixteen),
|
||||
..base_request()
|
||||
};
|
||||
let VideoGenerationPlan::ArtcraftKling3p0Standard(plan) = request.build().unwrap() else { panic!("wrong variant") };
|
||||
assert!(matches!(plan.aspect_ratio, Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::NineBySixteen)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_direct_square() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::Square),
|
||||
..base_request()
|
||||
};
|
||||
let VideoGenerationPlan::ArtcraftKling3p0Standard(plan) = request.build().unwrap() else { panic!("wrong variant") };
|
||||
assert!(matches!(plan.aspect_ratio, Some(Kling3p0StandardMultiFunctionVideoGenAspectRatio::Square)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_error_out_on_unsupported() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(CommonAspectRatio::WideThreeByTwo),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { .. }))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_valid_range() {
|
||||
for d in 3..=15 {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
duration_seconds: Some(d),
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(result.is_ok(), "duration {} should be valid", d);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_out_of_range_error_out() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
duration_seconds: Some(16),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { .. }))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn url_image_ref_returns_error() {
|
||||
let request = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::Url("https://example.com/image.jpg".to_string())),
|
||||
..base_request()
|
||||
};
|
||||
let result = request.build();
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ArtcraftRouterError::Client(ClientError::ArtcraftOnlySupportsMediaTokens))
|
||||
));
|
||||
}
|
||||
}
|
||||
Loaded 100 of 188 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user