mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
remove image-to-video aspect ratio for veo2
This commit is contained in:
1 parent
5fada6b30f
commit
ee6fd70907
8 files changed
+52
-69
No files matched your search
+2
-1
@@ -16,7 +16,8 @@ pub struct GenerateVeo2ImageToVideoRequest {
|
||||
|
||||
/// Optional text prompt.
|
||||
pub prompt: Option<String>,
|
||||
|
||||
|
||||
/// **NB(bt): ASPECT RATIO IS ACTUALLY NOT USED IN VEO 2 IMAGE-TO-VIDEO**
|
||||
/// Optional: aspect ratio of the generated video.
|
||||
pub aspect_ratio: Option<GenerateVeo2AspectRatio>,
|
||||
|
||||
|
||||
+4
-3
@@ -8,7 +8,7 @@ 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,
|
||||
enqueue_veo_2_image_to_video_webhook, Veo2Args, Veo2AspectRatio,
|
||||
};
|
||||
use fal_client::requests::webhook::video::text::enqueue_veo_2_text_to_video_webhook::{
|
||||
enqueue_veo_2_text_to_video_webhook, Veo2TextToVideoArgs,
|
||||
@@ -25,7 +25,7 @@ pub async fn execute_fal_veo_2(
|
||||
negative_prompt: plan.negative_prompt.as_deref(),
|
||||
api_key: &fal_client.api_key,
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
aspect_ratio: plan.aspect_ratio.unwrap_or(Veo2AspectRatio::Auto),
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
enqueue_veo_2_text_to_video_webhook(args)
|
||||
@@ -33,12 +33,13 @@ pub async fn execute_fal_veo_2(
|
||||
.map_err(|e| ArtcraftRouterError::Provider(ProviderError::Fal(e)))?
|
||||
}
|
||||
FalVeo2Mode::ImageToVideo { image_url } => {
|
||||
// Image-to-video does not support aspect_ratio — the output
|
||||
// inherits the source image's aspect ratio.
|
||||
let args = Veo2Args {
|
||||
image_url: image_url.as_str(),
|
||||
prompt: plan.prompt.as_str(),
|
||||
api_key: &fal_client.api_key,
|
||||
duration: plan.duration,
|
||||
aspect_ratio: plan.aspect_ratio,
|
||||
webhook_url: fal_client.webhook_url.as_str(),
|
||||
};
|
||||
enqueue_veo_2_image_to_video_webhook(args)
|
||||
|
||||
+8
-1
@@ -44,7 +44,14 @@ pub fn plan_generate_video_artcraft_veo_2<'a>(
|
||||
}));
|
||||
}
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?;
|
||||
// Aspect ratio only applies to text-to-video; image-to-video inherits
|
||||
// the source frame's aspect ratio.
|
||||
let is_image_mode = request.start_frame.is_some();
|
||||
let aspect_ratio = if is_image_mode {
|
||||
None
|
||||
} else {
|
||||
plan_aspect_ratio(request.aspect_ratio, strategy)?
|
||||
};
|
||||
let duration = plan_duration(request.duration_seconds, strategy)?;
|
||||
|
||||
Ok(VideoGenerationPlan::ArtcraftVeo2(PlanArtcraftVeo2 {
|
||||
|
||||
+9
-2
@@ -22,7 +22,9 @@ pub struct PlanFalVeo2 {
|
||||
pub prompt: String,
|
||||
pub negative_prompt: Option<String>,
|
||||
pub mode: FalVeo2Mode,
|
||||
pub aspect_ratio: Veo2AspectRatio,
|
||||
/// Only set for text-to-video. Image-to-video inherits the source frame's
|
||||
/// aspect ratio and doesn't accept this parameter.
|
||||
pub aspect_ratio: Option<Veo2AspectRatio>,
|
||||
pub duration: Veo2Duration,
|
||||
}
|
||||
|
||||
@@ -45,7 +47,12 @@ pub fn plan_generate_video_fal_veo_2<'a>(
|
||||
None => FalVeo2Mode::TextToVideo,
|
||||
};
|
||||
|
||||
let aspect_ratio = plan_aspect_ratio(request.aspect_ratio, strategy)?;
|
||||
// Aspect ratio only applies to text-to-video; image-to-video inherits
|
||||
// the source frame's aspect ratio.
|
||||
let aspect_ratio = match &mode {
|
||||
FalVeo2Mode::TextToVideo => Some(plan_aspect_ratio(request.aspect_ratio, strategy)?),
|
||||
FalVeo2Mode::ImageToVideo { .. } => None,
|
||||
};
|
||||
let duration = plan_duration(request.duration_seconds, strategy)?;
|
||||
|
||||
Ok(VideoGenerationPlan::FalVeo2(PlanFalVeo2 {
|
||||
|
||||
-4
@@ -7,10 +7,6 @@ pub struct Veo2ImageToVideoInput {
|
||||
|
||||
pub prompt: String,
|
||||
|
||||
/// Options: "auto", "16:9", "9:16"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub aspect_ratio: Option<String>,
|
||||
|
||||
/// Options: "4s", "6s", "8s"
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<String>,
|
||||
|
||||
+8
-36
@@ -6,16 +6,17 @@ use crate::requests::http::video::image::http_veo_2_image_to_video::{veo_2_image
|
||||
use crate::requests::traits::fal_request_cost_calculator_trait::{FalRequestCostCalculator, UsdCents};
|
||||
use reqwest::IntoUrl;
|
||||
|
||||
/// Args for Veo 2 image-to-video. Note: image-to-video does NOT support
|
||||
/// aspect_ratio — the output inherits the source image's aspect ratio.
|
||||
pub struct Veo2Args<'a, U: IntoUrl, V: IntoUrl> {
|
||||
pub image_url: U,
|
||||
pub webhook_url: V,
|
||||
pub prompt: &'a str,
|
||||
pub api_key: &'a FalApiKey,
|
||||
pub duration: Veo2Duration,
|
||||
pub aspect_ratio: Veo2AspectRatio,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Veo2Duration {
|
||||
Default, // NB: Default is 5 seconds
|
||||
FiveSeconds,
|
||||
@@ -24,6 +25,9 @@ pub enum Veo2Duration {
|
||||
EightSeconds,
|
||||
}
|
||||
|
||||
/// Aspect ratio enum — used by the text-to-video endpoint only.
|
||||
/// Kept here because it's the canonical location that the text-to-video
|
||||
/// webhook and the router plan both re-export from.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Veo2AspectRatio {
|
||||
Auto,
|
||||
@@ -59,22 +63,13 @@ pub async fn enqueue_veo_2_image_to_video_webhook<U: IntoUrl, V: IntoUrl>(
|
||||
Veo2Duration::SevenSeconds => Some("7s".to_string()),
|
||||
Veo2Duration::EightSeconds => Some("8s".to_string()),
|
||||
};
|
||||
|
||||
let aspect_ratio = match args.aspect_ratio {
|
||||
Veo2AspectRatio::Auto => Some("auto".to_string()),
|
||||
Veo2AspectRatio::AutoPreferPortrait => Some("auto_prefer_portrait".to_string()),
|
||||
Veo2AspectRatio::WideSixteenNine => Some("16:9".to_string()),
|
||||
Veo2AspectRatio::TallNineSixteen => Some("9:16".to_string()),
|
||||
};
|
||||
|
||||
let image_url = args.image_url.as_str().to_string();
|
||||
|
||||
let request = Veo2ImageToVideoInput {
|
||||
image_url,
|
||||
prompt: args.prompt.to_string(),
|
||||
aspect_ratio,
|
||||
duration,
|
||||
// Maybe expose these later
|
||||
};
|
||||
|
||||
let result = veo_2_image_to_video(request)
|
||||
@@ -89,14 +84,14 @@ pub async fn enqueue_veo_2_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_2_image_to_video_webhook::{enqueue_veo_2_image_to_video_webhook, Veo2Args, Veo2AspectRatio, Veo2Duration};
|
||||
use crate::requests::webhook::video::image::enqueue_veo_2_image_to_video_webhook::{enqueue_veo_2_image_to_video_webhook, Veo2Args, Veo2Duration};
|
||||
use errors::AnyhowResult;
|
||||
use std::fs::read_to_string;
|
||||
use test_data::web::image_urls::MOUNTAIN_TREE_IMAGE_URL;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test_veo_2_video_wide() -> AnyhowResult<()> {
|
||||
async fn test_veo_2_image_to_video() -> AnyhowResult<()> {
|
||||
// XXX: Don't commit secrets!
|
||||
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
|
||||
|
||||
@@ -107,29 +102,6 @@ mod tests {
|
||||
prompt: "a shot of the mountains as the sun sets and reveals the moon and stars",
|
||||
api_key: &api_key,
|
||||
duration: Veo2Duration::Default,
|
||||
aspect_ratio: Veo2AspectRatio::WideSixteenNine,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
let result = enqueue_veo_2_image_to_video_webhook(args).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test_veo_2_video_tall() -> 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 = Veo2Args {
|
||||
image_url: MOUNTAIN_TREE_IMAGE_URL,
|
||||
prompt: "a shot of the mountains as the sun sets and reveals the moon and stars",
|
||||
api_key: &api_key,
|
||||
duration: Veo2Duration::Default,
|
||||
aspect_ratio: Veo2AspectRatio::TallNineSixteen,
|
||||
webhook_url: "https://example.com/webhook",
|
||||
};
|
||||
|
||||
|
||||
+4
-14
@@ -148,13 +148,9 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
.map(|prompt| prompt.trim())
|
||||
.unwrap_or_else(|| "");
|
||||
|
||||
let aspect_ratio = match &request.aspect_ratio {
|
||||
Some(GenerateVeo2AspectRatio::Auto) => Veo2AspectRatio::Auto,
|
||||
Some(GenerateVeo2AspectRatio::AutoPreferPortrait) => Veo2AspectRatio::AutoPreferPortrait,
|
||||
Some(GenerateVeo2AspectRatio::WideSixteenNine) => Veo2AspectRatio::WideSixteenNine,
|
||||
Some(GenerateVeo2AspectRatio::TallNineSixteen) => Veo2AspectRatio::TallNineSixteen,
|
||||
None => Veo2AspectRatio::WideSixteenNine, // Default to 16:9
|
||||
};
|
||||
if let Some(aspect_ratio) = &request.aspect_ratio {
|
||||
warn!("Aspect ratio is not read in Veo 2 image-to-video");
|
||||
}
|
||||
|
||||
let duration = match &request.duration {
|
||||
Some(GenerateVeo2Duration::FiveSeconds) => Veo2Duration::FiveSeconds,
|
||||
@@ -169,7 +165,6 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
webhook_url: &server_state.fal.webhook_url,
|
||||
duration,
|
||||
prompt,
|
||||
aspect_ratio,
|
||||
api_key: &server_state.fal.api_key,
|
||||
};
|
||||
|
||||
@@ -222,12 +217,7 @@ pub async fn generate_veo_2_image_to_video_handler(
|
||||
maybe_negative_prompt: None,
|
||||
maybe_other_args: None,
|
||||
maybe_generation_mode: Some(CommonGenerationMode::Keyframe), // TODO: This endpoint only supports keyframes for now
|
||||
maybe_aspect_ratio: request.aspect_ratio.as_ref().map(|ar| match ar {
|
||||
GenerateVeo2AspectRatio::Auto => CommonAspectRatio::Auto,
|
||||
GenerateVeo2AspectRatio::AutoPreferPortrait => CommonAspectRatio::Auto,
|
||||
GenerateVeo2AspectRatio::WideSixteenNine => CommonAspectRatio::WideSixteenByNine,
|
||||
GenerateVeo2AspectRatio::TallNineSixteen => CommonAspectRatio::TallNineBySixteen,
|
||||
}),
|
||||
maybe_aspect_ratio: None, // NB: The aspect ratio is not read for Veo 2 image-to-video
|
||||
maybe_resolution: None,
|
||||
maybe_batch_count: None,
|
||||
maybe_generate_audio: None,
|
||||
|
||||
+17
-8
@@ -244,7 +244,7 @@ mod tests {
|
||||
#[test]
|
||||
fn default_aspect_ratio_is_auto() {
|
||||
with_text_plan(&make_request(Some("p"), None, None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Veo2AspectRatio::Auto));
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo2AspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ mod tests {
|
||||
fn auto_yields_auto() {
|
||||
for ar in [CommonAspectRatio::Auto, CommonAspectRatio::Auto2k, CommonAspectRatio::Auto4k] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Veo2AspectRatio::Auto));
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo2AspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -261,7 +261,7 @@ mod tests {
|
||||
fn wide_16x9_yields_wide() {
|
||||
for ar in [CommonAspectRatio::WideSixteenByNine, CommonAspectRatio::Wide] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Veo2AspectRatio::WideSixteenNine));
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo2AspectRatio::WideSixteenNine)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ 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), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Veo2AspectRatio::TallNineSixteen));
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo2AspectRatio::TallNineSixteen)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -282,7 +282,7 @@ mod tests {
|
||||
CommonAspectRatio::WideFourByThree, CommonAspectRatio::TallThreeByFour,
|
||||
] {
|
||||
with_text_plan(&make_request(Some("p"), Some(ar), None, None), |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Veo2AspectRatio::Auto));
|
||||
assert!(matches!(plan.aspect_ratio, Some(Veo2AspectRatio::Auto)));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -380,14 +380,23 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Aspect ratio in image mode ────────────────────────────────────────
|
||||
// ── Aspect ratio is None in image mode (i2v inherits source AR) ────────
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_passes_through_in_image_mode() {
|
||||
fn aspect_ratio_is_none_in_image_mode() {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(CommonAspectRatio::WideSixteenByNine), None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(matches!(plan.aspect_ratio, Veo2AspectRatio::WideSixteenNine));
|
||||
assert!(plan.aspect_ratio.is_none(), "image-to-video should not have aspect_ratio");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_is_none_in_image_mode_even_with_tall() {
|
||||
let (start, map) = fake_token("mf_start0000000000000000000000");
|
||||
let request = make_request(Some("p"), Some(CommonAspectRatio::TallNineBySixteen), None, Some(start));
|
||||
with_image_plan(&request, &map, |plan| {
|
||||
assert!(plan.aspect_ratio.is_none());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user