mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-27 11:37:02 +00:00
veo 3
This commit is contained in:
+4
@@ -42,6 +42,10 @@ pub struct GenerateVeo3ImageToVideoRequest {
|
||||
pub enum GenerateVeo3AspectRatio {
|
||||
WideSixteenNine, // 16:9
|
||||
TallNineSixteen, // 9:16
|
||||
|
||||
/// NB: DO NOT REMOVE. This is an unsupported resolution for Veo 3, but we can't
|
||||
/// impact existing callers by removing it. The handler will convert this to a
|
||||
/// supported resolution or 400 the request instead.
|
||||
Square, // 1:1
|
||||
}
|
||||
|
||||
|
||||
+62
-9
@@ -5,15 +5,68 @@ use crate::generate::generate_video::generate_video_response::{
|
||||
FalVideoResponsePayload, GenerateVideoResponse,
|
||||
};
|
||||
use crate::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3::{
|
||||
FalVeo3Mode, PlanFalVeo3,
|
||||
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, Veo3AspectRatio,
|
||||
enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3I2vAspectRatio, Veo3I2vDuration,
|
||||
Veo3I2vResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_3_text_to_video_webhook::{
|
||||
enqueue_veo_3_text_to_video_webhook, Veo3TextToVideoArgs,
|
||||
enqueue_veo_3_text_to_video_webhook, Veo3T2vAspectRatio, Veo3T2vDuration, Veo3T2vResolution,
|
||||
Veo3TextToVideoArgs,
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute_fal_veo_3(
|
||||
plan: &PlanFalVeo3,
|
||||
fal_client: &RouterFalClient,
|
||||
@@ -24,9 +77,9 @@ pub async fn execute_fal_veo_3(
|
||||
prompt: plan.prompt.as_str(),
|
||||
negative_prompt: plan.negative_prompt.as_deref(),
|
||||
api_key: &fal_client.api_key,
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio.unwrap_or(Veo3AspectRatio::Default),
|
||||
resolution: plan.resolution,
|
||||
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,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
@@ -38,9 +91,9 @@ pub async fn execute_fal_veo_3(
|
||||
let args = Veo3Args {
|
||||
image_url: image_url.as_str(),
|
||||
prompt: plan.prompt.as_str(),
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio.unwrap_or(Veo3AspectRatio::Default),
|
||||
resolution: plan.resolution,
|
||||
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,
|
||||
api_key: &fal_client.api_key,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
|
||||
+95
-31
@@ -7,7 +7,10 @@ use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::generate_video_request::GenerateVideoRequest;
|
||||
use crate::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{
|
||||
Veo3AspectRatio, Veo3Duration, Veo3Resolution,
|
||||
Veo3I2vAspectRatio, Veo3I2vDuration, Veo3I2vResolution,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_3_text_to_video_webhook::{
|
||||
Veo3T2vAspectRatio, Veo3T2vDuration, Veo3T2vResolution,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -18,16 +21,51 @@ pub enum FalVeo3Mode {
|
||||
},
|
||||
}
|
||||
|
||||
/// Router-level duration shared between both modes.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FalVeo3Duration {
|
||||
Default,
|
||||
FourSeconds,
|
||||
SixSeconds,
|
||||
EightSeconds,
|
||||
}
|
||||
|
||||
/// Router-level resolution shared between both modes.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FalVeo3Resolution {
|
||||
Default,
|
||||
SevenTwentyP,
|
||||
TenEightyP,
|
||||
}
|
||||
|
||||
/// Router-level aspect ratio for text-to-video only (no Auto, no Square).
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FalVeo3T2vAspectRatio {
|
||||
Default,
|
||||
WideSixteenNine,
|
||||
TallNineSixteen,
|
||||
}
|
||||
|
||||
/// Router-level aspect ratio for image-to-video only (has Auto, no Square).
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum FalVeo3I2vAspectRatio {
|
||||
Auto,
|
||||
WideSixteenNine,
|
||||
TallNineSixteen,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PlanFalVeo3 {
|
||||
pub prompt: String,
|
||||
pub negative_prompt: Option<String>,
|
||||
pub mode: FalVeo3Mode,
|
||||
/// Only set for text-to-video. Image-to-video inherits the source frame's
|
||||
/// aspect ratio.
|
||||
pub aspect_ratio: Option<Veo3AspectRatio>,
|
||||
pub resolution: Veo3Resolution,
|
||||
pub duration: Veo3Duration,
|
||||
/// aspect ratio and uses `i2v_aspect_ratio` instead.
|
||||
pub t2v_aspect_ratio: Option<FalVeo3T2vAspectRatio>,
|
||||
/// Only set for image-to-video.
|
||||
pub i2v_aspect_ratio: Option<FalVeo3I2vAspectRatio>,
|
||||
pub resolution: FalVeo3Resolution,
|
||||
pub duration: FalVeo3Duration,
|
||||
pub generate_audio: bool,
|
||||
}
|
||||
|
||||
@@ -50,11 +88,13 @@ pub fn plan_generate_video_fal_veo_3<'a>(
|
||||
None => FalVeo3Mode::TextToVideo,
|
||||
};
|
||||
|
||||
// Aspect ratio only applies to text-to-video; image-to-video inherits
|
||||
// the source frame's aspect ratio.
|
||||
let aspect_ratio = match &mode {
|
||||
FalVeo3Mode::TextToVideo => Some(plan_aspect_ratio(request.aspect_ratio, strategy)?),
|
||||
FalVeo3Mode::ImageToVideo { .. } => None,
|
||||
let (t2v_aspect_ratio, i2v_aspect_ratio) = match &mode {
|
||||
FalVeo3Mode::TextToVideo => {
|
||||
(Some(plan_t2v_aspect_ratio(request.aspect_ratio, strategy)?), None)
|
||||
}
|
||||
FalVeo3Mode::ImageToVideo { .. } => {
|
||||
(None, Some(plan_i2v_aspect_ratio(request.aspect_ratio, strategy)?))
|
||||
}
|
||||
};
|
||||
let resolution = plan_resolution(request.resolution, strategy)?;
|
||||
let duration = plan_duration(request.duration_seconds, strategy)?;
|
||||
@@ -64,34 +104,58 @@ pub fn plan_generate_video_fal_veo_3<'a>(
|
||||
prompt: request.prompt.unwrap_or("").to_string(),
|
||||
negative_prompt: request.negative_prompt.map(|s| s.to_string()),
|
||||
mode,
|
||||
aspect_ratio,
|
||||
t2v_aspect_ratio,
|
||||
i2v_aspect_ratio,
|
||||
resolution,
|
||||
duration,
|
||||
generate_audio,
|
||||
}))
|
||||
}
|
||||
|
||||
fn plan_aspect_ratio(
|
||||
/// Text-to-video: only 16:9 and 9:16 (no Auto, no Square).
|
||||
fn plan_t2v_aspect_ratio(
|
||||
aspect_ratio: Option<CommonAspectRatio>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Veo3AspectRatio, ArtcraftRouterError> {
|
||||
use Veo3AspectRatio as Ar;
|
||||
) -> Result<FalVeo3T2vAspectRatio, ArtcraftRouterError> {
|
||||
use FalVeo3T2vAspectRatio as Ar;
|
||||
match aspect_ratio {
|
||||
None => Ok(Ar::Default),
|
||||
|
||||
Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Ar::WideSixteenNine),
|
||||
Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Ar::TallNineSixteen),
|
||||
|
||||
// Everything else (Auto*, Square, unsupported) → Default (16:9).
|
||||
Some(_) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(unsupported("aspect_ratio", &format!("{:?}", aspect_ratio.unwrap())))
|
||||
}
|
||||
_ => Ok(Ar::Default),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Image-to-video: Auto, 16:9, 9:16 (no Square).
|
||||
fn plan_i2v_aspect_ratio(
|
||||
aspect_ratio: Option<CommonAspectRatio>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<FalVeo3I2vAspectRatio, ArtcraftRouterError> {
|
||||
use FalVeo3I2vAspectRatio as Ar;
|
||||
match aspect_ratio {
|
||||
None
|
||||
| Some(CommonAspectRatio::Auto)
|
||||
| Some(CommonAspectRatio::Auto2k)
|
||||
| Some(CommonAspectRatio::Auto3k)
|
||||
| Some(CommonAspectRatio::Auto4k) => Ok(Ar::Default),
|
||||
| Some(CommonAspectRatio::Auto4k) => Ok(Ar::Auto),
|
||||
|
||||
Some(CommonAspectRatio::Square) | Some(CommonAspectRatio::SquareHd) => Ok(Ar::Square),
|
||||
Some(CommonAspectRatio::WideSixteenByNine) | Some(CommonAspectRatio::Wide) => Ok(Ar::WideSixteenNine),
|
||||
Some(CommonAspectRatio::TallNineBySixteen) | Some(CommonAspectRatio::Tall) => Ok(Ar::TallNineSixteen),
|
||||
|
||||
Some(unsupported_ar) => match strategy {
|
||||
// Everything else (Square, unsupported) → Auto.
|
||||
Some(_) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(unsupported("aspect_ratio", &format!("{:?}", unsupported_ar)))
|
||||
Err(unsupported("aspect_ratio", &format!("{:?}", aspect_ratio.unwrap())))
|
||||
}
|
||||
_ => Ok(Ar::Default),
|
||||
_ => Ok(Ar::Auto),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -99,8 +163,8 @@ fn plan_aspect_ratio(
|
||||
fn plan_resolution(
|
||||
resolution: Option<CommonResolution>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Veo3Resolution, ArtcraftRouterError> {
|
||||
use Veo3Resolution as R;
|
||||
) -> Result<FalVeo3Resolution, ArtcraftRouterError> {
|
||||
use FalVeo3Resolution as R;
|
||||
match resolution {
|
||||
None => Ok(R::Default),
|
||||
Some(CommonResolution::SevenTwentyP) => Ok(R::SevenTwentyP),
|
||||
@@ -118,18 +182,18 @@ fn plan_resolution(
|
||||
fn plan_duration(
|
||||
duration_seconds: Option<u16>,
|
||||
strategy: RequestMismatchMitigationStrategy,
|
||||
) -> Result<Veo3Duration, ArtcraftRouterError> {
|
||||
) -> Result<FalVeo3Duration, ArtcraftRouterError> {
|
||||
match duration_seconds {
|
||||
None => Ok(Veo3Duration::Default),
|
||||
Some(4) => Ok(Veo3Duration::FourSeconds),
|
||||
Some(6) => Ok(Veo3Duration::SixSeconds),
|
||||
Some(8) => Ok(Veo3Duration::EightSeconds),
|
||||
None => Ok(FalVeo3Duration::Default),
|
||||
Some(4) => Ok(FalVeo3Duration::FourSeconds),
|
||||
Some(6) => Ok(FalVeo3Duration::SixSeconds),
|
||||
Some(8) => Ok(FalVeo3Duration::EightSeconds),
|
||||
Some(other) => match strategy {
|
||||
RequestMismatchMitigationStrategy::ErrorOut => {
|
||||
Err(unsupported("duration_seconds", &format!("{}", other)))
|
||||
}
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(Veo3Duration::EightSeconds),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(Veo3Duration::FourSeconds),
|
||||
RequestMismatchMitigationStrategy::PayMoreUpgrade => Ok(FalVeo3Duration::EightSeconds),
|
||||
RequestMismatchMitigationStrategy::PayLessDowngrade => Ok(FalVeo3Duration::FourSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -144,9 +208,9 @@ fn unsupported(field: &'static str, value: &str) -> ArtcraftRouterError {
|
||||
impl PlanFalVeo3 {
|
||||
pub fn duration_seconds_for_cost(&self) -> u64 {
|
||||
match self.duration {
|
||||
Veo3Duration::Default | Veo3Duration::EightSeconds => 8,
|
||||
Veo3Duration::SixSeconds => 6,
|
||||
Veo3Duration::FourSeconds => 4,
|
||||
FalVeo3Duration::Default | FalVeo3Duration::EightSeconds => 8,
|
||||
FalVeo3Duration::SixSeconds => 6,
|
||||
FalVeo3Duration::FourSeconds => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-39
@@ -9,32 +9,35 @@ use reqwest::IntoUrl;
|
||||
pub struct Veo3Args<'a, U: IntoUrl, V: IntoUrl> {
|
||||
pub image_url: U,
|
||||
pub prompt: &'a str,
|
||||
pub duration: Veo3Duration,
|
||||
pub aspect_ratio: Veo3AspectRatio,
|
||||
pub resolution: Veo3Resolution,
|
||||
pub duration: Veo3I2vDuration,
|
||||
pub aspect_ratio: Veo3I2vAspectRatio,
|
||||
pub resolution: Veo3I2vResolution,
|
||||
pub generate_audio: bool,
|
||||
pub api_key: &'a FalApiKey,
|
||||
pub webhook_url: V,
|
||||
}
|
||||
|
||||
/// Duration for Veo 3 image-to-video. Default is 8 seconds.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3Duration {
|
||||
pub enum Veo3I2vDuration {
|
||||
Default, // Default is 8 seconds
|
||||
FourSeconds,
|
||||
SixSeconds,
|
||||
EightSeconds,
|
||||
}
|
||||
|
||||
/// Aspect ratio for Veo 3 image-to-video.
|
||||
/// Supports Auto (inherit source), 16:9, and 9:16. No Square.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3AspectRatio {
|
||||
Default,
|
||||
WideSixteenNine,
|
||||
TallNineSixteen,
|
||||
Square,
|
||||
pub enum Veo3I2vAspectRatio {
|
||||
Auto,
|
||||
WideSixteenNine, // 16:9
|
||||
TallNineSixteen, // 9:16
|
||||
}
|
||||
|
||||
/// Resolution for Veo 3 image-to-video.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3Resolution {
|
||||
pub enum Veo3I2vResolution {
|
||||
Default,
|
||||
SevenTwentyP,
|
||||
TenEightyP,
|
||||
@@ -48,14 +51,14 @@ impl <U: IntoUrl, V: IntoUrl> FalRequestCostCalculator for Veo3Args<'_, U, V> {
|
||||
// For example, a 5s video with audio on will cost $2."
|
||||
|
||||
match (self.duration, self.generate_audio) {
|
||||
(Veo3Duration::FourSeconds, false) => 80,
|
||||
(Veo3Duration::SixSeconds, false) => 120,
|
||||
(Veo3Duration::EightSeconds, false) => 160,
|
||||
(Veo3Duration::Default, false) => 160, // NB: Default is 8 seconds
|
||||
(Veo3Duration::FourSeconds, true) => 160,
|
||||
(Veo3Duration::SixSeconds, true) => 240,
|
||||
(Veo3Duration::EightSeconds, true) => 320,
|
||||
(Veo3Duration::Default, true) => 320, // NB: Default is 8 seconds320
|
||||
(Veo3I2vDuration::FourSeconds, false) => 80,
|
||||
(Veo3I2vDuration::SixSeconds, false) => 120,
|
||||
(Veo3I2vDuration::EightSeconds, false) => 160,
|
||||
(Veo3I2vDuration::Default, false) => 160,
|
||||
(Veo3I2vDuration::FourSeconds, true) => 160,
|
||||
(Veo3I2vDuration::SixSeconds, true) => 240,
|
||||
(Veo3I2vDuration::EightSeconds, true) => 320,
|
||||
(Veo3I2vDuration::Default, true) => 320,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,23 +70,22 @@ pub async fn enqueue_veo_3_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
|
||||
args: Veo3Args<'_, U, V>
|
||||
) -> Result<WebhookResponse, FalErrorPlus> {
|
||||
let duration = match args.duration {
|
||||
Veo3Duration::Default => None,
|
||||
Veo3Duration::FourSeconds => Some("4s".to_string()),
|
||||
Veo3Duration::SixSeconds => Some("6s".to_string()),
|
||||
Veo3Duration::EightSeconds => Some("8s".to_string()),
|
||||
Veo3I2vDuration::Default => None,
|
||||
Veo3I2vDuration::FourSeconds => Some("4s".to_string()),
|
||||
Veo3I2vDuration::SixSeconds => Some("6s".to_string()),
|
||||
Veo3I2vDuration::EightSeconds => Some("8s".to_string()),
|
||||
};
|
||||
|
||||
let aspect_ratio = match args.aspect_ratio {
|
||||
Veo3AspectRatio::Default => None,
|
||||
Veo3AspectRatio::WideSixteenNine => Some("16:9".to_string()),
|
||||
Veo3AspectRatio::TallNineSixteen => Some("9:16".to_string()),
|
||||
Veo3AspectRatio::Square => Some("1:1".to_string()),
|
||||
Veo3I2vAspectRatio::Auto => Some("auto".to_string()),
|
||||
Veo3I2vAspectRatio::WideSixteenNine => Some("16:9".to_string()),
|
||||
Veo3I2vAspectRatio::TallNineSixteen => Some("9:16".to_string()),
|
||||
};
|
||||
|
||||
let resolution= match args.resolution {
|
||||
Veo3Resolution::Default => None,
|
||||
Veo3Resolution::SevenTwentyP => Some("720p".to_string()),
|
||||
Veo3Resolution::TenEightyP => Some("1080p".to_string()),
|
||||
|
||||
let resolution = match args.resolution {
|
||||
Veo3I2vResolution::Default => None,
|
||||
Veo3I2vResolution::SevenTwentyP => Some("720p".to_string()),
|
||||
Veo3I2vResolution::TenEightyP => Some("1080p".to_string()),
|
||||
};
|
||||
|
||||
let image_url = args.image_url.as_str().to_string();
|
||||
@@ -109,7 +111,10 @@ pub async fn enqueue_veo_3_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::creds::fal_api_key::FalApiKey;
|
||||
use crate::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3AspectRatio, Veo3Duration, Veo3Resolution};
|
||||
use crate::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{
|
||||
enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3I2vAspectRatio, Veo3I2vDuration,
|
||||
Veo3I2vResolution,
|
||||
};
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
use test_data::web::image_urls::TREX_SKELETON_IMAGE_URL;
|
||||
@@ -117,24 +122,21 @@ mod tests {
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test() -> AnyhowResult<()> {
|
||||
// XXX: Don't commit secrets!
|
||||
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
|
||||
|
||||
let api_key = FalApiKey::from_str(&secret);
|
||||
|
||||
let args = Veo3Args {
|
||||
image_url: TREX_SKELETON_IMAGE_URL,
|
||||
prompt: "the t-rex skeleton starts walking towards the camera and roars",
|
||||
api_key: &api_key,
|
||||
duration: Veo3Duration::EightSeconds,
|
||||
aspect_ratio: Veo3AspectRatio::WideSixteenNine,
|
||||
resolution: Veo3Resolution::TenEightyP,
|
||||
duration: Veo3I2vDuration::EightSeconds,
|
||||
aspect_ratio: Veo3I2vAspectRatio::WideSixteenNine,
|
||||
resolution: Veo3I2vResolution::TenEightyP,
|
||||
generate_audio: true,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
let result = enqueue_veo_3_image_to_video_webhook(args).await?;
|
||||
|
||||
let _result = enqueue_veo_3_image_to_video_webhook(args).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+52
-32
@@ -8,36 +8,57 @@ use crate::requests::http::video::text::http_veo_3_text_to_video::{
|
||||
use crate::requests::traits::fal_request_cost_calculator_trait::{
|
||||
FalRequestCostCalculator, UsdCents,
|
||||
};
|
||||
// Re-use the same duration/aspect-ratio/resolution enums as image-to-video.
|
||||
pub use crate::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{
|
||||
Veo3AspectRatio, Veo3Duration, Veo3Resolution,
|
||||
};
|
||||
use reqwest::IntoUrl;
|
||||
|
||||
pub struct Veo3TextToVideoArgs<'a, V: IntoUrl> {
|
||||
pub prompt: &'a str,
|
||||
pub negative_prompt: Option<&'a str>,
|
||||
pub duration: Veo3Duration,
|
||||
pub aspect_ratio: Veo3AspectRatio,
|
||||
pub resolution: Veo3Resolution,
|
||||
pub duration: Veo3T2vDuration,
|
||||
pub aspect_ratio: Veo3T2vAspectRatio,
|
||||
pub resolution: Veo3T2vResolution,
|
||||
pub generate_audio: bool,
|
||||
pub webhook_url: V,
|
||||
pub api_key: &'a FalApiKey,
|
||||
}
|
||||
|
||||
/// Duration for Veo 3 text-to-video. Default is 8 seconds.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3T2vDuration {
|
||||
Default, // Default is 8 seconds
|
||||
FourSeconds,
|
||||
SixSeconds,
|
||||
EightSeconds,
|
||||
}
|
||||
|
||||
/// Aspect ratio for Veo 3 text-to-video.
|
||||
/// Only 16:9 and 9:16 are supported. No Auto, no Square.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3T2vAspectRatio {
|
||||
Default, // Default is 16:9
|
||||
WideSixteenNine, // 16:9
|
||||
TallNineSixteen, // 9:16
|
||||
}
|
||||
|
||||
/// Resolution for Veo 3 text-to-video.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo3T2vResolution {
|
||||
Default,
|
||||
SevenTwentyP,
|
||||
TenEightyP,
|
||||
}
|
||||
|
||||
impl<V: IntoUrl> FalRequestCostCalculator for Veo3TextToVideoArgs<'_, V> {
|
||||
fn calculate_cost_in_cents(&self) -> UsdCents {
|
||||
// Same pricing as image-to-video:
|
||||
// $0.20/sec audio off, $0.40/sec audio on.
|
||||
match (self.duration, self.generate_audio) {
|
||||
(Veo3Duration::FourSeconds, false) => 80,
|
||||
(Veo3Duration::SixSeconds, false) => 120,
|
||||
(Veo3Duration::EightSeconds, false) => 160,
|
||||
(Veo3Duration::Default, false) => 160,
|
||||
(Veo3Duration::FourSeconds, true) => 160,
|
||||
(Veo3Duration::SixSeconds, true) => 240,
|
||||
(Veo3Duration::EightSeconds, true) => 320,
|
||||
(Veo3Duration::Default, true) => 320,
|
||||
(Veo3T2vDuration::FourSeconds, false) => 80,
|
||||
(Veo3T2vDuration::SixSeconds, false) => 120,
|
||||
(Veo3T2vDuration::EightSeconds, false) => 160,
|
||||
(Veo3T2vDuration::Default, false) => 160,
|
||||
(Veo3T2vDuration::FourSeconds, true) => 160,
|
||||
(Veo3T2vDuration::SixSeconds, true) => 240,
|
||||
(Veo3T2vDuration::EightSeconds, true) => 320,
|
||||
(Veo3T2vDuration::Default, true) => 320,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,23 +69,22 @@ pub async fn enqueue_veo_3_text_to_video_webhook<V: IntoUrl>(
|
||||
args: Veo3TextToVideoArgs<'_, V>,
|
||||
) -> Result<WebhookResponse, FalErrorPlus> {
|
||||
let duration = match args.duration {
|
||||
Veo3Duration::Default => None,
|
||||
Veo3Duration::FourSeconds => Some("4s".to_string()),
|
||||
Veo3Duration::SixSeconds => Some("6s".to_string()),
|
||||
Veo3Duration::EightSeconds => Some("8s".to_string()),
|
||||
Veo3T2vDuration::Default => None,
|
||||
Veo3T2vDuration::FourSeconds => Some("4s".to_string()),
|
||||
Veo3T2vDuration::SixSeconds => Some("6s".to_string()),
|
||||
Veo3T2vDuration::EightSeconds => Some("8s".to_string()),
|
||||
};
|
||||
|
||||
let aspect_ratio = match args.aspect_ratio {
|
||||
Veo3AspectRatio::Default => None,
|
||||
Veo3AspectRatio::WideSixteenNine => Some("16:9".to_string()),
|
||||
Veo3AspectRatio::TallNineSixteen => Some("9:16".to_string()),
|
||||
Veo3AspectRatio::Square => Some("1:1".to_string()),
|
||||
Veo3T2vAspectRatio::Default => None,
|
||||
Veo3T2vAspectRatio::WideSixteenNine => Some("16:9".to_string()),
|
||||
Veo3T2vAspectRatio::TallNineSixteen => Some("9:16".to_string()),
|
||||
};
|
||||
|
||||
let resolution = match args.resolution {
|
||||
Veo3Resolution::Default => None,
|
||||
Veo3Resolution::SevenTwentyP => Some("720p".to_string()),
|
||||
Veo3Resolution::TenEightyP => Some("1080p".to_string()),
|
||||
Veo3T2vResolution::Default => None,
|
||||
Veo3T2vResolution::SevenTwentyP => Some("720p".to_string()),
|
||||
Veo3T2vResolution::TenEightyP => Some("1080p".to_string()),
|
||||
};
|
||||
|
||||
let request = Veo3TextToVideoInput {
|
||||
@@ -88,8 +108,8 @@ pub async fn enqueue_veo_3_text_to_video_webhook<V: IntoUrl>(
|
||||
mod tests {
|
||||
use crate::creds::fal_api_key::FalApiKey;
|
||||
use crate::requests::webhook::video::text::enqueue_veo_3_text_to_video_webhook::{
|
||||
enqueue_veo_3_text_to_video_webhook, Veo3TextToVideoArgs, Veo3AspectRatio, Veo3Duration,
|
||||
Veo3Resolution,
|
||||
enqueue_veo_3_text_to_video_webhook, Veo3TextToVideoArgs, Veo3T2vAspectRatio,
|
||||
Veo3T2vDuration, Veo3T2vResolution,
|
||||
};
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
@@ -104,9 +124,9 @@ mod tests {
|
||||
prompt: "a drone shot of a coastal sunset with waves crashing on rocks",
|
||||
negative_prompt: None,
|
||||
api_key: &api_key,
|
||||
duration: Veo3Duration::EightSeconds,
|
||||
aspect_ratio: Veo3AspectRatio::WideSixteenNine,
|
||||
resolution: Veo3Resolution::TenEightyP,
|
||||
duration: Veo3T2vDuration::EightSeconds,
|
||||
aspect_ratio: Veo3T2vAspectRatio::WideSixteenNine,
|
||||
resolution: Veo3T2vResolution::TenEightyP,
|
||||
generate_audio: true,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
+14
-13
@@ -14,13 +14,13 @@ use anyhow::anyhow;
|
||||
use artcraft_api_defs::generate::video::generate_veo_3_image_to_video::{GenerateVeo3AspectRatio, GenerateVeo3Duration, GenerateVeo3ImageToVideoRequest, GenerateVeo3ImageToVideoResponse, GenerateVeo3Resolution};
|
||||
use bucket_paths::legacy::typified_paths::public::media_files::bucket_file_path::MediaFileBucketPath;
|
||||
use enums::by_table::prompts::prompt_type::PromptType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::generation::common_model_type::CommonModelType;
|
||||
use enums::common::visibility::Visibility;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
|
||||
use enums::common::generation::common_generation_mode::CommonGenerationMode;
|
||||
use enums::common::generation::common_model_type::CommonModelType;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use enums::common::visibility::Visibility;
|
||||
use fal_client::requests::traits::fal_request_cost_calculator_trait::FalRequestCostCalculator;
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{enqueue_veo_3_image_to_video_webhook, Veo3Args, Veo3AspectRatio, Veo3Duration, Veo3Resolution};
|
||||
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};
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use log::{error, info, warn};
|
||||
use mysql_queries::queries::generic_inference::fal::insert_generic_inference_job_for_fal_queue::insert_generic_inference_job_for_fal_queue;
|
||||
@@ -143,21 +143,22 @@ pub async fn generate_veo_3_image_to_video_handler(
|
||||
.unwrap_or_else(|| "");
|
||||
|
||||
let aspect_ratio = match &request.aspect_ratio {
|
||||
Some(GenerateVeo3AspectRatio::WideSixteenNine) => Veo3AspectRatio::WideSixteenNine,
|
||||
Some(GenerateVeo3AspectRatio::TallNineSixteen) => Veo3AspectRatio::TallNineSixteen,
|
||||
Some(GenerateVeo3AspectRatio::Square) => Veo3AspectRatio::Square,
|
||||
None => Veo3AspectRatio::WideSixteenNine,
|
||||
Some(GenerateVeo3AspectRatio::WideSixteenNine) => Veo3I2vAspectRatio::WideSixteenNine,
|
||||
Some(GenerateVeo3AspectRatio::TallNineSixteen) => Veo3I2vAspectRatio::TallNineSixteen,
|
||||
// Square is not supported by Veo 3 i2v — fall back to Auto.
|
||||
Some(GenerateVeo3AspectRatio::Square) => Veo3I2vAspectRatio::Auto,
|
||||
None => Veo3I2vAspectRatio::WideSixteenNine,
|
||||
};
|
||||
|
||||
let resolution = match &request.resolution {
|
||||
Some(GenerateVeo3Resolution::SevenTwentyP) => Veo3Resolution::SevenTwentyP,
|
||||
Some(GenerateVeo3Resolution::TenEightyP) => Veo3Resolution::TenEightyP,
|
||||
None => Veo3Resolution::Default,
|
||||
Some(GenerateVeo3Resolution::SevenTwentyP) => Veo3I2vResolution::SevenTwentyP,
|
||||
Some(GenerateVeo3Resolution::TenEightyP) => Veo3I2vResolution::TenEightyP,
|
||||
None => Veo3I2vResolution::Default,
|
||||
};
|
||||
|
||||
let duration = match &request.duration {
|
||||
Some(GenerateVeo3Duration::EightSeconds) => Veo3Duration::EightSeconds,
|
||||
None => Veo3Duration::EightSeconds,
|
||||
Some(GenerateVeo3Duration::EightSeconds) => Veo3I2vDuration::EightSeconds,
|
||||
None => Veo3I2vDuration::EightSeconds,
|
||||
};
|
||||
|
||||
let generate_audio = request.generate_audio
|
||||
|
||||
+95
-33
@@ -208,10 +208,8 @@ mod tests {
|
||||
|
||||
use artcraft_router::generate::generate_video::video_generation_plan::VideoGenerationPlan;
|
||||
use artcraft_router::generate::generate_video::plan::fal::plan_generate_video_fal_veo_3::{
|
||||
FalVeo3Mode, PlanFalVeo3,
|
||||
};
|
||||
use fal_client::requests::webhook::video::image::enqueue_veo_3_image_to_video_webhook::{
|
||||
Veo3AspectRatio, Veo3Duration, Veo3Resolution,
|
||||
FalVeo3Duration, FalVeo3I2vAspectRatio, FalVeo3Mode, FalVeo3Resolution,
|
||||
FalVeo3T2vAspectRatio, PlanFalVeo3,
|
||||
};
|
||||
|
||||
fn with_text_plan<F: FnOnce(&PlanFalVeo3)>(
|
||||
@@ -270,24 +268,15 @@ mod tests {
|
||||
#[test]
|
||||
fn default_aspect_ratio_is_default() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo3AspectRatio::Default)));
|
||||
assert!(matches!(plan.t2v_aspect_ratio, Some(FalVeo3T2vAspectRatio::Default)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_yields_default() {
|
||||
for ar in [CommonAspectRatio::Auto, CommonAspectRatio::Auto2k, CommonAspectRatio::Auto4k] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo3AspectRatio::Default)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wide_16x9_yields_wide() {
|
||||
for ar in [CommonAspectRatio::WideSixteenByNine, CommonAspectRatio::Wide] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo3AspectRatio::WideSixteenNine)));
|
||||
assert!(matches!(plan.t2v_aspect_ratio, Some(FalVeo3T2vAspectRatio::WideSixteenNine)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -296,16 +285,27 @@ mod tests {
|
||||
fn tall_9x16_yields_tall() {
|
||||
for ar in [CommonAspectRatio::TallNineBySixteen, CommonAspectRatio::Tall] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo3AspectRatio::TallNineSixteen)));
|
||||
assert!(matches!(plan.t2v_aspect_ratio, Some(FalVeo3T2vAspectRatio::TallNineSixteen)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn square_yields_square() {
|
||||
fn square_falls_back_to_default() {
|
||||
// Veo 3 t2v has no Square — falls back to Default (16:9).
|
||||
for ar in [CommonAspectRatio::Square, CommonAspectRatio::SquareHd] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo3AspectRatio::Square)));
|
||||
assert!(matches!(plan.t2v_aspect_ratio, Some(FalVeo3T2vAspectRatio::Default)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_falls_back_to_default() {
|
||||
// Veo 3 t2v has no Auto — falls back to Default (16:9).
|
||||
for ar in [CommonAspectRatio::Auto, CommonAspectRatio::Auto2k, CommonAspectRatio::Auto4k] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.t2v_aspect_ratio, Some(FalVeo3T2vAspectRatio::Default)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -314,7 +314,7 @@ mod tests {
|
||||
fn unsupported_aspect_ratio_falls_back_to_default() {
|
||||
for ar in [CommonAspectRatio::WideFourByThree, CommonAspectRatio::TallThreeByFour, CommonAspectRatio::WideFiveByFour] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo3AspectRatio::Default)));
|
||||
assert!(matches!(plan.t2v_aspect_ratio, Some(FalVeo3T2vAspectRatio::Default)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -324,35 +324,35 @@ mod tests {
|
||||
#[test]
|
||||
fn default_duration_is_default() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::Default));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::Default));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_4_yields_four() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, Some(4), None, None), |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::FourSeconds));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::FourSeconds));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_6_yields_six() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, Some(6), None, None), |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::SixSeconds));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::SixSeconds));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_8_yields_eight() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, Some(8), None, None), |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::EightSeconds));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::EightSeconds));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_above_8_clamps_to_eight() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, Some(20), None, None), |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::EightSeconds));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::EightSeconds));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -361,21 +361,21 @@ mod tests {
|
||||
#[test]
|
||||
fn default_resolution_is_default() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, None, None, None), |plan| {
|
||||
assert!(matches!(plan.resolution, Veo3Resolution::Default));
|
||||
assert!(matches!(plan.resolution, FalVeo3Resolution::Default));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seven_twenty_p_yields_seven_twenty_p() {
|
||||
with_text_plan(&make_request(Some("p"), None, Some(CommonResolution::SevenTwentyP), None, None, None), |plan| {
|
||||
assert!(matches!(plan.resolution, Veo3Resolution::SevenTwentyP));
|
||||
assert!(matches!(plan.resolution, FalVeo3Resolution::SevenTwentyP));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ten_eighty_p_yields_ten_eighty_p() {
|
||||
with_text_plan(&make_request(Some("p"), None, Some(CommonResolution::TenEightyP), None, None, None), |plan| {
|
||||
assert!(matches!(plan.resolution, Veo3Resolution::TenEightyP));
|
||||
assert!(matches!(plan.resolution, FalVeo3Resolution::TenEightyP));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -443,15 +443,77 @@ mod tests {
|
||||
|
||||
// ── Aspect ratio is None in image mode ────────────────────────────────
|
||||
|
||||
// ── Aspect ratio in image mode: uses i2v (has Auto, no Square) ─────────
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_is_none_in_image_mode() {
|
||||
fn t2v_aspect_ratio_is_none_in_image_mode() {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(CommonAspectRatio::WideSixteenByNine), None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(plan.aspect_ratio.is_none());
|
||||
assert!(plan.t2v_aspect_ratio.is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2v_default_aspect_ratio_is_auto() {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), None, None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.i2v_aspect_ratio, Some(FalVeo3I2vAspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2v_wide_16x9_yields_wide() {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(CommonAspectRatio::WideSixteenByNine), None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.i2v_aspect_ratio, Some(FalVeo3I2vAspectRatio::WideSixteenNine)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2v_tall_9x16_yields_tall() {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(CommonAspectRatio::TallNineBySixteen), None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.i2v_aspect_ratio, Some(FalVeo3I2vAspectRatio::TallNineSixteen)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2v_auto_yields_auto() {
|
||||
for ar in [CommonAspectRatio::Auto, CommonAspectRatio::Auto2k, CommonAspectRatio::Auto4k] {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(ar), None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.i2v_aspect_ratio, Some(FalVeo3I2vAspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2v_square_falls_back_to_auto() {
|
||||
for ar in [CommonAspectRatio::Square, CommonAspectRatio::SquareHd] {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(ar), None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.i2v_aspect_ratio, Some(FalVeo3I2vAspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i2v_unsupported_falls_back_to_auto() {
|
||||
for ar in [CommonAspectRatio::WideFourByThree, CommonAspectRatio::TallThreeByFour] {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(ar), None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.i2v_aspect_ratio, Some(FalVeo3I2vAspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Prompt passthrough in image mode ──────────────────────────────────
|
||||
|
||||
#[test]
|
||||
@@ -470,7 +532,7 @@ mod tests {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), None, None, Some(4), None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::FourSeconds));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::FourSeconds));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -479,7 +541,7 @@ mod tests {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), None, None, Some(8), None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::EightSeconds));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::EightSeconds));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -488,7 +550,7 @@ mod tests {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), None, None, None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.duration, Veo3Duration::Default));
|
||||
assert!(matches!(plan.duration, FalVeo3Duration::Default));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -499,7 +561,7 @@ mod tests {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), None, Some(CommonResolution::TenEightyP), None, None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.resolution, Veo3Resolution::TenEightyP));
|
||||
assert!(matches!(plan.resolution, FalVeo3Resolution::TenEightyP));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user