pixverse v5

This commit is contained in:
Brandon Thomas committed 2025-12-03 18:50:57 -05:00
1 parent 97c48c79fb
commit c73a4470e7
8 files changed
+429 -2

No files matched your search

@@ -0,0 +1,152 @@
use crate::creds::fal_api_key::FalApiKey;
use crate::error::classify_fal_error::classify_fal_error;
use crate::error::fal_error_plus::FalErrorPlus;
use fal::endpoints::fal_ai::pixverse::v5::pixverse_v5_image_to_video::{pixverse_v5_image_to_video, PixverseV5ImageToVideoInput};
use fal::webhook::WebhookResponse;
use reqwest::IntoUrl;
pub struct EnqueuePixverseV5ImageToVideoArgs<'a, R: IntoUrl> {
// Request required
pub prompt: String,
pub image_url: String,
// Optional args
pub negative_prompt: Option<String>,
// NB: eight-second videos do not work with 1080P
pub duration: Option<EnqueuePixverseV5ImageToVideoDurationSeconds>,
pub aspect_ratio: Option<EnqueuePixverseV5ImageToVideoAspectRatio>,
pub resolution: Option<EnqueuePixverseV5ImageToVideoResolution>,
pub style: Option<EnqueuePixverseV5ImageToVideoStyle>,
// Fulfillment
pub webhook_url: R,
pub api_key: &'a FalApiKey,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5ImageToVideoDurationSeconds {
Five,
Eight,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5ImageToVideoResolution {
ThreeSixtyP,
FiveFortyP,
SevenTwentyP,
TenEightyP,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5ImageToVideoAspectRatio {
Square,
SixteenByNine,
FourByThree,
NineBySixteen,
ThreeByFour,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5ImageToVideoStyle {
Anime,
Animation3d,
Clay,
Comic,
Cyberpunk,
}
pub async fn enqueue_pixverse_v5_image_to_video_webhook<R: IntoUrl>(
args: EnqueuePixverseV5ImageToVideoArgs<'_, R>
) -> Result<WebhookResponse, FalErrorPlus> {
let duration = args.duration
.map(|resolution| match resolution {
EnqueuePixverseV5ImageToVideoDurationSeconds::Five => "5",
EnqueuePixverseV5ImageToVideoDurationSeconds::Eight=> "8",
})
.map(|s| s.to_string());
let aspect_ratio = args.aspect_ratio
.map(|aspect_ratio| match aspect_ratio {
EnqueuePixverseV5ImageToVideoAspectRatio::Square => "1:1",
EnqueuePixverseV5ImageToVideoAspectRatio::SixteenByNine => "16:9",
EnqueuePixverseV5ImageToVideoAspectRatio::FourByThree => "4:3",
EnqueuePixverseV5ImageToVideoAspectRatio::NineBySixteen => "9:16",
EnqueuePixverseV5ImageToVideoAspectRatio::ThreeByFour => "3:4",
})
.map(|s| s.to_string());
let resolution = args.resolution
.map(|resolution| match resolution {
EnqueuePixverseV5ImageToVideoResolution::ThreeSixtyP => "360p",
EnqueuePixverseV5ImageToVideoResolution::FiveFortyP => "540p",
EnqueuePixverseV5ImageToVideoResolution::SevenTwentyP => "720p",
EnqueuePixverseV5ImageToVideoResolution::TenEightyP => "1080p",
})
.map(|s| s.to_string());
let style = args.style
.map(|style| match style {
EnqueuePixverseV5ImageToVideoStyle::Anime => "anime",
EnqueuePixverseV5ImageToVideoStyle::Animation3d => "3d_animation",
EnqueuePixverseV5ImageToVideoStyle::Clay => "clay",
EnqueuePixverseV5ImageToVideoStyle::Comic => "comic",
EnqueuePixverseV5ImageToVideoStyle::Cyberpunk => "cyberpunk",
})
.map(|s| s.to_string());
let request = PixverseV5ImageToVideoInput {
prompt: args.prompt,
image_url: args.image_url,
// Optionals
duration,
negative_prompt: args.negative_prompt,
aspect_ratio,
resolution,
style,
// Constant
seed: None,
};
let result = pixverse_v5_image_to_video(request)
.with_api_key(&args.api_key.0)
.queue_webhook(args.webhook_url)
.await;
result.map_err(|err| classify_fal_error(err))
}
#[cfg(test)]
mod tests {
use crate::creds::fal_api_key::FalApiKey;
use crate::requests::webhook::video::image::enqueue_pixverse_v5_image_to_video_webhook::{enqueue_pixverse_v5_image_to_video_webhook, EnqueuePixverseV5ImageToVideoArgs, EnqueuePixverseV5ImageToVideoAspectRatio, EnqueuePixverseV5ImageToVideoDurationSeconds, EnqueuePixverseV5ImageToVideoResolution, EnqueuePixverseV5ImageToVideoStyle};
use errors::AnyhowResult;
use std::fs::read_to_string;
use test_data::web::image_urls::TREX_SKELETON_IMAGE_URL;
#[tokio::test]
#[ignore]
async fn test() -> AnyhowResult<()> {
// XXX: Don't commit secrets!
let secret = read_to_string("/home/bt/Artcraft/credentials/fal_api_key.txt")?;
let api_key = FalApiKey::from_str(&secret);
let args = EnqueuePixverseV5ImageToVideoArgs {
image_url: TREX_SKELETON_IMAGE_URL.to_string(),
prompt: "the t-rex skeleton gets off the podium and begins walking to the camera. the camera orbits slightly. The t-rex gets close and then bites.".to_string(),
negative_prompt: None,
duration: Some(EnqueuePixverseV5ImageToVideoDurationSeconds::Five),
style: Some(EnqueuePixverseV5ImageToVideoStyle::Cyberpunk),
aspect_ratio: Some(EnqueuePixverseV5ImageToVideoAspectRatio::FourByThree),
resolution: Some(EnqueuePixverseV5ImageToVideoResolution::TenEightyP),
api_key: &api_key,
webhook_url: "https://example.com/webhook",
};
let result = enqueue_pixverse_v5_image_to_video_webhook(args).await?;
Ok(())
}
}
@@ -1,3 +1,4 @@
pub mod enqueue_kling_v2p5_turbo_pro_image_to_video_webhook;
pub mod enqueue_kling_v2p5_turbo_standard_image_to_video_webhook;
pub mod enqueue_kling_v2p6_pro_image_to_video_webhook;
pub mod enqueue_kling_v2p6_pro_image_to_video_webhook;
pub mod enqueue_pixverse_v5_image_to_video_webhook;
@@ -0,0 +1,148 @@
use crate::creds::fal_api_key::FalApiKey;
use crate::error::classify_fal_error::classify_fal_error;
use crate::error::fal_error_plus::FalErrorPlus;
use fal::endpoints::fal_ai::pixverse::v5::pixverse_v5_text_to_video::{pixverse_v5_text_to_video, PixverseV5TextToVideoInput};
use fal::webhook::WebhookResponse;
use reqwest::IntoUrl;
pub struct EnqueuePixverseV5TextToVideoArgs<'a, R: IntoUrl> {
// Request required
pub prompt: String,
// Optional args
pub negative_prompt: Option<String>,
// NB: eight-second videos do not work with 1080P
pub duration: Option<EnqueuePixverseV5TextToVideoDurationSeconds>,
pub aspect_ratio: Option<EnqueuePixverseV5TextToVideoAspectRatio>,
pub resolution: Option<EnqueuePixverseV5TextToVideoResolution>,
pub style: Option<EnqueuePixverseV5TextToVideoStyle>,
// Fulfillment
pub webhook_url: R,
pub api_key: &'a FalApiKey,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5TextToVideoDurationSeconds {
Five,
Eight,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5TextToVideoResolution {
ThreeSixtyP,
FiveFortyP,
SevenTwentyP,
TenEightyP,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5TextToVideoAspectRatio {
Square,
SixteenByNine,
FourByThree,
NineBySixteen,
ThreeByFour,
}
#[derive(Copy, Clone, Debug)]
pub enum EnqueuePixverseV5TextToVideoStyle {
Anime,
Animation3d,
Clay,
Comic,
Cyberpunk,
}
pub async fn enqueue_pixverse_v5_text_to_video_webhook<R: IntoUrl>(
args: EnqueuePixverseV5TextToVideoArgs<'_, R>
) -> Result<WebhookResponse, FalErrorPlus> {
let duration = args.duration
.map(|resolution| match resolution {
EnqueuePixverseV5TextToVideoDurationSeconds::Five => "5",
EnqueuePixverseV5TextToVideoDurationSeconds::Eight=> "8",
})
.map(|s| s.to_string());
let aspect_ratio = args.aspect_ratio
.map(|aspect_ratio| match aspect_ratio {
EnqueuePixverseV5TextToVideoAspectRatio::Square => "1:1",
EnqueuePixverseV5TextToVideoAspectRatio::SixteenByNine => "16:9",
EnqueuePixverseV5TextToVideoAspectRatio::FourByThree => "4:3",
EnqueuePixverseV5TextToVideoAspectRatio::NineBySixteen => "9:16",
EnqueuePixverseV5TextToVideoAspectRatio::ThreeByFour => "3:4",
})
.map(|s| s.to_string());
let resolution = args.resolution
.map(|resolution| match resolution {
EnqueuePixverseV5TextToVideoResolution::ThreeSixtyP => "360p",
EnqueuePixverseV5TextToVideoResolution::FiveFortyP => "540p",
EnqueuePixverseV5TextToVideoResolution::SevenTwentyP => "720p",
EnqueuePixverseV5TextToVideoResolution::TenEightyP => "1080p",
})
.map(|s| s.to_string());
let style = args.style
.map(|style| match style {
EnqueuePixverseV5TextToVideoStyle::Anime => "anime",
EnqueuePixverseV5TextToVideoStyle::Animation3d => "3d_animation",
EnqueuePixverseV5TextToVideoStyle::Clay => "clay",
EnqueuePixverseV5TextToVideoStyle::Comic => "comic",
EnqueuePixverseV5TextToVideoStyle::Cyberpunk => "cyberpunk",
})
.map(|s| s.to_string());
let request = PixverseV5TextToVideoInput {
prompt: args.prompt,
// Optionals
duration,
negative_prompt: args.negative_prompt,
aspect_ratio,
resolution,
style,
// Constant
seed: None,
};
let result = pixverse_v5_text_to_video(request)
.with_api_key(&args.api_key.0)
.queue_webhook(args.webhook_url)
.await;
result.map_err(|err| classify_fal_error(err))
}
#[cfg(test)]
mod tests {
use crate::creds::fal_api_key::FalApiKey;
use crate::requests::webhook::video::text::enqueue_pixverse_v5_text_to_video_webhook::{enqueue_pixverse_v5_text_to_video_webhook, EnqueuePixverseV5TextToVideoArgs, EnqueuePixverseV5TextToVideoAspectRatio, EnqueuePixverseV5TextToVideoDurationSeconds, EnqueuePixverseV5TextToVideoResolution, EnqueuePixverseV5TextToVideoStyle};
use errors::AnyhowResult;
use std::fs::read_to_string;
#[tokio::test]
#[ignore]
async fn test() -> AnyhowResult<()> {
// XXX: Don't commit secrets!
let secret = read_to_string("/home/bt/Artcraft/credentials/fal_api_key.txt")?;
let api_key = FalApiKey::from_str(&secret);
let args = EnqueuePixverseV5TextToVideoArgs {
prompt: "an angry racoon shakes its fist at the garbage truck as it drives away, the camera orbits the racoon. the racoon sighs".to_string(),
negative_prompt: None,
duration: Some(EnqueuePixverseV5TextToVideoDurationSeconds::Eight),
style: Some(EnqueuePixverseV5TextToVideoStyle::Anime),
aspect_ratio: Some(EnqueuePixverseV5TextToVideoAspectRatio::FourByThree),
resolution: Some(EnqueuePixverseV5TextToVideoResolution::SevenTwentyP),
api_key: &api_key,
webhook_url: "https://example.com/webhook",
};
let result = enqueue_pixverse_v5_text_to_video_webhook(args).await?;
Ok(())
}
}
@@ -1,2 +1,3 @@
pub mod enqueue_kling_v2p5_turbo_pro_text_to_video_webhook;
pub mod enqueue_kling_v2p6_pro_text_to_video_webhook;
pub mod enqueue_kling_v2p6_pro_text_to_video_webhook;
pub mod enqueue_pixverse_v5_text_to_video_webhook;
@@ -12,3 +12,17 @@
)))
)]
pub mod v3_5;
#[cfg(any(
feature = "endpoints",
feature = "endpoints_fal-ai",
feature = "endpoints_fal-ai_pixverse"
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "endpoints",
feature = "endpoints_fal-ai",
feature = "endpoints_fal-ai_pixverse"
)))
)]
pub mod v5;
@@ -0,0 +1,2 @@
pub mod pixverse_v5_image_to_video;
pub mod pixverse_v5_text_to_video;
@@ -0,0 +1,56 @@
use crate::prelude::{Deserialize, FalRequest, Serialize};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct PixverseV5ImageToVideoInput {
pub prompt: String,
/// Starting frame
pub image_url: String,
/// Resolution
/// Possible enum values: 360p, 540p, 720p, 1080p
/// Default is 720p
#[serde(skip_serializing_if = "Option::is_none")]
pub resolution: Option<String>,
/// The aspect ratio of the generated video
/// Default value: "16:9"
/// Possible enum values: 16:9, 4:3, 1:1, 3:4, 9:16
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
/// Optional negative prompt
#[serde(skip_serializing_if = "Option::is_none")]
pub negative_prompt: Option<String>,
/// Duration in seconds
/// Options: "5", "8"
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<String>,
/// Optional style
/// Possible enum values: anime, 3d_animation, clay, comic, cyberpunk
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<String>,
/// Optional seed
#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PixverseV5ImageToVideoOutput {
pub video: VideoFile,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct VideoFile {
/// The URL where the file can be downloaded from.
pub url: String,
}
pub fn pixverse_v5_image_to_video(
params: PixverseV5ImageToVideoInput,
) -> FalRequest<PixverseV5ImageToVideoInput, PixverseV5ImageToVideoOutput> {
FalRequest::new("fal-ai/pixverse/v5/image-to-video", params)
}
@@ -0,0 +1,53 @@
use crate::prelude::{Deserialize, FalRequest, Serialize};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct PixverseV5TextToVideoInput {
pub prompt: String,
/// Resolution
/// Possible enum values: 360p, 540p, 720p, 1080p
/// Default is 720p
#[serde(skip_serializing_if = "Option::is_none")]
pub resolution: Option<String>,
/// The aspect ratio of the generated video
/// Default value: "16:9"
/// Possible enum values: 16:9, 4:3, 1:1, 3:4, 9:16
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
/// Optional negative prompt
#[serde(skip_serializing_if = "Option::is_none")]
pub negative_prompt: Option<String>,
/// Duration in seconds
/// Options: "5", "8"
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<String>,
/// Optional style
/// Possible enum values: anime, 3d_animation, clay, comic, cyberpunk
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<String>,
/// Optional seed
#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PixverseV5TextToVideoOutput {
pub video: VideoFile,
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct VideoFile {
/// The URL where the file can be downloaded from.
pub url: String,
}
pub fn pixverse_v5_text_to_video(
params: PixverseV5TextToVideoInput,
) -> FalRequest<PixverseV5TextToVideoInput, PixverseV5TextToVideoOutput> {
FalRequest::new("fal-ai/pixverse/v5/text-to-video", params)
}