mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-29 04:52:51 -04:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e67de36b8f | ||
|
|
74ce0534ff | ||
|
|
98e4e83f22 | ||
|
|
137acdfdc7 | ||
|
|
c0da33b1c4 | ||
|
|
b202e7b351 | ||
|
|
a2a6b699bb |
+5
@@ -2,6 +2,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate;
|
||||
use enums::common::generation::common_quality::CommonQuality;
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
@@ -50,6 +51,10 @@ pub struct OmniGenVideoCostAndGenerateRequest {
|
||||
/// The aspect ratio to use.
|
||||
pub aspect_ratio: Option<CommonAspectRatio>,
|
||||
|
||||
/// The output bitrate to use.
|
||||
/// Not all models support this; models that don't simply ignore it.
|
||||
pub bitrate: Option<CommonBitrate>,
|
||||
|
||||
/// The quality to use.
|
||||
pub quality: Option<CommonQuality>,
|
||||
|
||||
|
||||
+9
@@ -6,6 +6,7 @@ use enums::common::generation::model_creator::ModelCreator;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use utoipa::{IntoParams, ToSchema};
|
||||
use enums::common::generation::common_bitrate::CommonBitrate;
|
||||
|
||||
/// Video model to default to if none is specified
|
||||
const DEFAULT_VIDEO_MODEL : CommonVideoModel = CommonVideoModel::Seedance2p0;
|
||||
@@ -150,6 +151,12 @@ pub struct OmniGenVideoModelDetails {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub resolution_default: Option<CommonResolution>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bitrate_options: Option<Vec<CommonBitrate>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bitrate_default: Option<CommonBitrate>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub quality_options: Option<Vec<CommonQuality>>,
|
||||
|
||||
@@ -220,6 +227,8 @@ impl Default for OmniGenVideoModelDetails {
|
||||
aspect_ratio_default: None,
|
||||
resolution_options: None,
|
||||
resolution_default: None,
|
||||
bitrate_options: None,
|
||||
bitrate_default: None,
|
||||
quality_options: None,
|
||||
default_quality: None,
|
||||
duration_seconds_min: None,
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod character_list_ref;
|
||||
pub mod image_list_ref;
|
||||
pub mod image_ref;
|
||||
pub mod router_aspect_ratio;
|
||||
pub mod router_bitrate;
|
||||
pub mod router_image_model;
|
||||
pub mod router_provider;
|
||||
pub mod router_quality;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
/// Output bitrate levels you can specify when enqueuing a generation.
|
||||
/// Not every model will use this; models that don't simply ignore it.
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RouterBitrate {
|
||||
Normal,
|
||||
High,
|
||||
}
|
||||
+6
@@ -1,6 +1,7 @@
|
||||
use crate::api::audio_list_ref::AudioListRef;
|
||||
use crate::api::character_list_ref::CharacterListRef;
|
||||
use crate::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::api::router_video_model::RouterVideoModel;
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -103,6 +104,10 @@ pub struct GenerateVideoRequestBuilder {
|
||||
/// The aspect ratio to use
|
||||
pub aspect_ratio: Option<RouterAspectRatio>,
|
||||
|
||||
/// The output bitrate to use.
|
||||
/// Not all models support this; models that don't simply ignore it.
|
||||
pub bitrate: Option<RouterBitrate>,
|
||||
|
||||
/// How many seconds to generate.
|
||||
pub duration_seconds: Option<u16>,
|
||||
|
||||
@@ -139,6 +144,7 @@ impl Default for GenerateVideoRequestBuilder {
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
|
||||
+41
@@ -1,9 +1,11 @@
|
||||
use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost_and_generate_request::OmniGenVideoCostAndGenerateRequest;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
use crate::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
@@ -49,6 +51,7 @@ pub fn build_artcraft_omni_video_request(
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy, resolutions)?;
|
||||
let batch_count = plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
let bitrate = plan_bitrate(builder.bitrate.take());
|
||||
let prompt = builder.prompt.take();
|
||||
|
||||
let start_frame = resolve_image_ref(builder.start_frame.take())?;
|
||||
@@ -71,6 +74,7 @@ pub fn build_artcraft_omni_video_request(
|
||||
reference_character_tokens: reference_characters,
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
bitrate,
|
||||
duration_seconds: duration_seconds.map(|d| d as u16),
|
||||
video_batch_count: Some(batch_count),
|
||||
negative_prompt: None,
|
||||
@@ -234,6 +238,19 @@ pub fn plan_batch_count(
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate the router-facing bitrate into the API `CommonBitrate`.
|
||||
///
|
||||
/// Bitrate does not affect cost. An unset value leaves the field `None` (the
|
||||
/// API applies its default); `Normal` and `High` map to their `CommonBitrate`
|
||||
/// counterparts.
|
||||
fn plan_bitrate(bitrate: Option<RouterBitrate>) -> Option<CommonBitrateEnum> {
|
||||
match bitrate {
|
||||
None => None,
|
||||
Some(RouterBitrate::Normal) => Some(CommonBitrateEnum::Normal),
|
||||
Some(RouterBitrate::High) => Some(CommonBitrateEnum::High),
|
||||
}
|
||||
}
|
||||
|
||||
/// Duration: 4-15 seconds.
|
||||
pub fn plan_duration(
|
||||
duration_seconds: Option<u16>,
|
||||
@@ -255,3 +272,27 @@ pub fn plan_duration(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
mod bitrate_translation {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn none_stays_none() {
|
||||
assert_eq!(plan_bitrate(None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_maps_to_common_normal() {
|
||||
assert_eq!(plan_bitrate(Some(RouterBitrate::Normal)), Some(CommonBitrateEnum::Normal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn high_maps_to_common_high() {
|
||||
assert_eq!(plan_bitrate(Some(RouterBitrate::High)), Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -24,6 +24,8 @@ pub fn build_artcraft_seedance_2p0(builder: GenerateVideoRequestBuilder) -> Resu
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -72,6 +74,12 @@ mod tests {
|
||||
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(make_builder(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Resolution ──
|
||||
|
||||
+8
@@ -24,6 +24,8 @@ pub fn build_artcraft_seedance_2p0_bp(builder: GenerateVideoRequestBuilder) -> R
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -70,6 +72,12 @@ mod tests {
|
||||
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(make_builder(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
mod resolution_tests {
|
||||
|
||||
+8
@@ -24,6 +24,8 @@ pub fn build_artcraft_seedance_2p0_bp_fast(builder: GenerateVideoRequestBuilder)
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -72,6 +74,12 @@ mod tests {
|
||||
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(make_builder(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
mod resolution_tests {
|
||||
|
||||
+8
@@ -24,6 +24,8 @@ pub fn build_artcraft_seedance_2p0_bpu(builder: GenerateVideoRequestBuilder) ->
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -70,6 +72,12 @@ mod tests {
|
||||
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(make_builder(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
mod resolution_tests {
|
||||
|
||||
+8
@@ -24,6 +24,8 @@ pub fn build_artcraft_seedance_2p0_bpu_fast(builder: GenerateVideoRequestBuilder
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -72,6 +74,12 @@ mod tests {
|
||||
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(make_builder(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
mod resolution_tests {
|
||||
|
||||
+8
@@ -25,6 +25,8 @@ mod tests {
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -83,6 +85,12 @@ mod tests {
|
||||
let req = unwrap_request(artcraft_fast_builder_with(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(artcraft_fast_builder_with(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Aspect ratio ──
|
||||
|
||||
+8
@@ -24,6 +24,8 @@ pub fn build_artcraft_seedance_2p0_u(builder: GenerateVideoRequestBuilder) -> Re
|
||||
mod tests {
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -72,6 +74,12 @@ mod tests {
|
||||
let req = unwrap_request(make_builder(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(make_builder(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Resolution ──
|
||||
|
||||
+8
@@ -25,6 +25,8 @@ mod tests {
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use tokens::tokens::characters::CharacterToken;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
@@ -83,6 +85,12 @@ mod tests {
|
||||
let req = unwrap_request(artcraft_fast_builder_with(|b| { b.video_batch_count = Some(4); }));
|
||||
assert_eq!(req.request.video_batch_count, Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_passed_through() {
|
||||
let req = unwrap_request(artcraft_fast_builder_with(|b| { b.bitrate = Some(RouterBitrate::High); }));
|
||||
assert_eq!(req.request.bitrate, Some(CommonBitrateEnum::High));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Aspect ratio ──
|
||||
|
||||
+47
@@ -1,10 +1,12 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
KinoviSeedance2p0AspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0Bitrate as KinoviBitrate,
|
||||
KinoviSeedance2p0BatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p0OutputResolution as KinoviOutputResolution,
|
||||
};
|
||||
|
||||
use crate::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
@@ -26,6 +28,7 @@ fn do_build_kinovi_seedance_2p0(mut builder: GenerateVideoRequestBuilder) -> Res
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy)?;
|
||||
let batch_count = plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
let bitrate = plan_bitrate(builder.bitrate.take());
|
||||
let prompt = builder.prompt.take().unwrap_or_default();
|
||||
|
||||
let unhandled_request_state = KinoviSeedance2p0RemainingItems {
|
||||
@@ -42,6 +45,7 @@ fn do_build_kinovi_seedance_2p0(mut builder: GenerateVideoRequestBuilder) -> Res
|
||||
resolution,
|
||||
batch_count,
|
||||
duration_seconds,
|
||||
bitrate,
|
||||
prompt,
|
||||
unhandled_request_state: Some(unhandled_request_state),
|
||||
})
|
||||
@@ -195,6 +199,16 @@ fn plan_duration(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance 2.0 Pro bitrate: "standard" (default, field omitted) or "high".
|
||||
// Bitrate does not affect cost. `Normal` and an unset value both map to the
|
||||
// standard bitrate (None); only `High` requests the higher bitrate.
|
||||
fn plan_bitrate(bitrate: Option<RouterBitrate>) -> Option<KinoviBitrate> {
|
||||
match bitrate {
|
||||
Some(RouterBitrate::High) => Some(KinoviBitrate::High),
|
||||
Some(RouterBitrate::Normal) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
@@ -391,6 +405,39 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bitrate conversions ──
|
||||
|
||||
mod bitrate_conversions {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bitrate_high() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::High),
|
||||
..seedance2pro_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.bitrate, Some(KinoviBitrate::High)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_normal_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::Normal),
|
||||
..seedance2pro_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_none_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder { bitrate: None, ..seedance2pro_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
// ── unhandled_request_state: media refs are placed there ──
|
||||
|
||||
mod unhandled_request_state {
|
||||
|
||||
+2
@@ -63,6 +63,7 @@ impl KinoviSeedance2p0CostState {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
};
|
||||
|
||||
let costs = pricing_request.calculate_costs();
|
||||
@@ -476,6 +477,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
GenerateSeedance2p0Request, KinoviSeedance2p0AspectRatio,
|
||||
GenerateSeedance2p0Request, KinoviSeedance2p0AspectRatio, KinoviSeedance2p0Bitrate,
|
||||
KinoviSeedance2p0BatchCount, KinoviSeedance2p0OutputResolution,
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ pub struct KinoviSeedance2p0DraftState {
|
||||
pub resolution: Option<KinoviSeedance2p0OutputResolution>,
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: KinoviSeedance2p0BatchCount,
|
||||
pub bitrate: Option<KinoviSeedance2p0Bitrate>,
|
||||
|
||||
// Pending types that need to be queried.
|
||||
pub unhandled_request_state: Option<KinoviSeedance2p0RemainingItems>,
|
||||
@@ -93,6 +94,7 @@ impl KinoviSeedance2p0DraftState {
|
||||
reference_audio_urls,
|
||||
character_ids,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: self.bitrate,
|
||||
};
|
||||
|
||||
Ok(KinoviSeedance2p0RequestState { request })
|
||||
|
||||
+47
@@ -1,10 +1,12 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
KinoviSeedance2p0FastAspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0FastBitrate as KinoviBitrate,
|
||||
KinoviSeedance2p0FastBatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p0FastOutputResolution as KinoviOutputResolution,
|
||||
};
|
||||
|
||||
use crate::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use crate::api::router_bitrate::RouterBitrate;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
@@ -26,6 +28,7 @@ fn do_build_kinovi_seedance_2p0_fast(mut builder: GenerateVideoRequestBuilder) -
|
||||
let resolution = plan_output_resolution(builder.resolution.take(), strategy)?;
|
||||
let batch_count = plan_batch_count(builder.video_batch_count.take(), strategy)?;
|
||||
let duration_seconds = plan_duration(builder.duration_seconds.take(), strategy)?;
|
||||
let bitrate = plan_bitrate(builder.bitrate.take());
|
||||
let prompt = builder.prompt.take().unwrap_or_default();
|
||||
|
||||
let unhandled_request_state = KinoviSeedance2p0FastRemainingItems {
|
||||
@@ -42,6 +45,7 @@ fn do_build_kinovi_seedance_2p0_fast(mut builder: GenerateVideoRequestBuilder) -
|
||||
resolution,
|
||||
batch_count,
|
||||
duration_seconds,
|
||||
bitrate,
|
||||
prompt,
|
||||
unhandled_request_state: Some(unhandled_request_state),
|
||||
})
|
||||
@@ -210,6 +214,16 @@ fn plan_duration(
|
||||
}
|
||||
}
|
||||
|
||||
// Seedance 2.0 Fast bitrate: "standard" (default, field omitted) or "high".
|
||||
// Bitrate does not affect cost. `Normal` and an unset value both map to the
|
||||
// standard bitrate (None); only `High` requests the higher bitrate.
|
||||
fn plan_bitrate(bitrate: Option<RouterBitrate>) -> Option<KinoviBitrate> {
|
||||
match bitrate {
|
||||
Some(RouterBitrate::High) => Some(KinoviBitrate::High),
|
||||
Some(RouterBitrate::Normal) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
@@ -430,6 +444,39 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bitrate conversions ──
|
||||
|
||||
mod bitrate_conversions {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bitrate_high() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::High),
|
||||
..seedance2pro_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.bitrate, Some(KinoviBitrate::High)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_normal_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::Normal),
|
||||
..seedance2pro_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitrate_none_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder { bitrate: None, ..seedance2pro_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
// ── unhandled_request_state: media refs are placed there ──
|
||||
|
||||
mod unhandled_request_state {
|
||||
|
||||
+2
@@ -63,6 +63,7 @@ impl KinoviSeedance2p0FastCostState {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
};
|
||||
|
||||
let costs = pricing_request.calculate_costs();
|
||||
@@ -428,6 +429,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
GenerateSeedance2p0FastRequest, KinoviSeedance2p0FastAspectRatio,
|
||||
GenerateSeedance2p0FastRequest, KinoviSeedance2p0FastAspectRatio, KinoviSeedance2p0FastBitrate,
|
||||
KinoviSeedance2p0FastBatchCount, KinoviSeedance2p0FastOutputResolution,
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ pub struct KinoviSeedance2p0FastDraftState {
|
||||
pub resolution: Option<KinoviSeedance2p0FastOutputResolution>,
|
||||
pub duration_seconds: u8,
|
||||
pub batch_count: KinoviSeedance2p0FastBatchCount,
|
||||
pub bitrate: Option<KinoviSeedance2p0FastBitrate>,
|
||||
|
||||
pub unhandled_request_state: Option<KinoviSeedance2p0FastRemainingItems>,
|
||||
}
|
||||
@@ -90,6 +91,7 @@ impl KinoviSeedance2p0FastDraftState {
|
||||
reference_audio_urls,
|
||||
character_ids,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: self.bitrate,
|
||||
};
|
||||
|
||||
Ok(KinoviSeedance2p0FastRequestState { request })
|
||||
|
||||
@@ -351,6 +351,7 @@ pub fn base_video_request() -> GenerateVideoRequestBuilder {
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
|
||||
@@ -129,6 +129,7 @@ pub async fn generate_happy_horse_1p0(
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: Some(false),
|
||||
bitrate: None,
|
||||
};
|
||||
|
||||
let raw_response = workflow_run_task(WorkflowRunTaskArgs {
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::error::seedance2pro_error::Seedance2ProError;
|
||||
use crate::cost::kinovi_seedance_generation_cost::KinoviSeedanceGenerationCost;
|
||||
use crate::requests::kinovi_host::KinoviHost;
|
||||
use crate::requests::workflow_run_task::workflow_run_task::{
|
||||
workflow_run_task, KinoviAspectRatioRaw, KinoviBatchCountRaw,
|
||||
workflow_run_task, KinoviAspectRatioRaw, KinoviBatchCountRaw, KinoviBitrateRaw,
|
||||
KinoviModelTypeRaw, KinoviOutputResolutionRaw, WorkflowRunTaskArgs,
|
||||
WorkflowRunTaskRequest,
|
||||
};
|
||||
@@ -32,6 +32,9 @@ pub struct GenerateSeedance2p0Request {
|
||||
pub reference_audio_urls: Option<Vec<String>>,
|
||||
pub character_ids: Option<Vec<String>>,
|
||||
pub use_face_blur_hack: Option<bool>,
|
||||
/// Output video bitrate. None defaults to "standard"; `High` requests a
|
||||
/// higher bitrate. Does not affect cost.
|
||||
pub bitrate: Option<KinoviSeedance2p0Bitrate>,
|
||||
}
|
||||
|
||||
// ── Enums ──
|
||||
@@ -60,6 +63,11 @@ pub enum KinoviSeedance2p0BatchCount {
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KinoviSeedance2p0Bitrate {
|
||||
High,
|
||||
}
|
||||
|
||||
// ── Pricing ──
|
||||
//
|
||||
// Seedance 2.0 Pro credit pricing:
|
||||
@@ -178,6 +186,7 @@ pub async fn generate_seedance_2p0(
|
||||
reference_audio_urls: req.reference_audio_urls,
|
||||
character_ids: req.character_ids,
|
||||
use_face_blur_hack: req.use_face_blur_hack,
|
||||
bitrate: map_bitrate(req.bitrate),
|
||||
};
|
||||
|
||||
let raw_response = workflow_run_task(WorkflowRunTaskArgs {
|
||||
@@ -224,6 +233,13 @@ fn map_batch_count(bc: Option<KinoviSeedance2p0BatchCount>) -> KinoviBatchCountR
|
||||
}
|
||||
}
|
||||
|
||||
fn map_bitrate(bitrate: Option<KinoviSeedance2p0Bitrate>) -> Option<KinoviBitrateRaw> {
|
||||
match bitrate {
|
||||
Some(KinoviSeedance2p0Bitrate::High) => Some(KinoviBitrateRaw::High),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -256,6 +272,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,6 +827,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
};
|
||||
assert_eq!(
|
||||
req.calculate_costs().total_cost.kinovi_credits, baseline,
|
||||
@@ -817,6 +835,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bitrate doesn't affect cost ──
|
||||
|
||||
#[test]
|
||||
fn high_bitrate_does_not_affect_credits() {
|
||||
let baseline = r720(5).calculate_costs().total_cost.kinovi_credits;
|
||||
|
||||
let mut high = r720(5);
|
||||
high.bitrate = Some(KinoviSeedance2p0Bitrate::High);
|
||||
|
||||
assert_eq!(
|
||||
high.calculate_costs().total_cost.kinovi_credits, baseline,
|
||||
"High bitrate should not change credits from baseline {}", baseline,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
use crate::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
@@ -849,6 +882,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v default — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -879,6 +913,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v 1080p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -912,6 +947,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v 21:9 — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -943,6 +979,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("keyframe 21:9 — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -978,6 +1015,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("keyframe — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1010,6 +1048,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("keyframe start+end — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1046,6 +1085,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("image ref — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1082,6 +1122,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("video ref — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1116,6 +1157,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![STEAMPUNK_CLOWN_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("character ref — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1146,6 +1188,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![STEAMPUNK_CLOWN_ID.to_string(), MOCHI_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("two characters — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
|
||||
+43
-1
@@ -3,7 +3,7 @@ use crate::error::seedance2pro_error::Seedance2ProError;
|
||||
use crate::cost::kinovi_seedance_generation_cost::KinoviSeedanceGenerationCost;
|
||||
use crate::requests::kinovi_host::KinoviHost;
|
||||
use crate::requests::workflow_run_task::workflow_run_task::{
|
||||
workflow_run_task, KinoviAspectRatioRaw, KinoviBatchCountRaw,
|
||||
workflow_run_task, KinoviAspectRatioRaw, KinoviBatchCountRaw, KinoviBitrateRaw,
|
||||
KinoviModelTypeRaw, KinoviOutputResolutionRaw, WorkflowRunTaskArgs,
|
||||
WorkflowRunTaskRequest,
|
||||
};
|
||||
@@ -32,6 +32,9 @@ pub struct GenerateSeedance2p0FastRequest {
|
||||
pub reference_audio_urls: Option<Vec<String>>,
|
||||
pub character_ids: Option<Vec<String>>,
|
||||
pub use_face_blur_hack: Option<bool>,
|
||||
/// Output video bitrate. None defaults to "standard"; `High` requests a
|
||||
/// higher bitrate. Does not affect cost.
|
||||
pub bitrate: Option<KinoviSeedance2p0FastBitrate>,
|
||||
}
|
||||
|
||||
// ── Enums ──
|
||||
@@ -59,6 +62,11 @@ pub enum KinoviSeedance2p0FastBatchCount {
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KinoviSeedance2p0FastBitrate {
|
||||
High,
|
||||
}
|
||||
|
||||
// ── Pricing ──
|
||||
//
|
||||
// Seedance 2.0 Fast credit pricing:
|
||||
@@ -173,6 +181,7 @@ pub async fn generate_seedance_2p0_fast(
|
||||
reference_audio_urls: req.reference_audio_urls,
|
||||
character_ids: req.character_ids,
|
||||
use_face_blur_hack: req.use_face_blur_hack,
|
||||
bitrate: map_bitrate(req.bitrate),
|
||||
};
|
||||
|
||||
let raw_response = workflow_run_task(WorkflowRunTaskArgs {
|
||||
@@ -218,6 +227,13 @@ fn map_batch_count(bc: Option<KinoviSeedance2p0FastBatchCount>) -> KinoviBatchCo
|
||||
}
|
||||
}
|
||||
|
||||
fn map_bitrate(bitrate: Option<KinoviSeedance2p0FastBitrate>) -> Option<KinoviBitrateRaw> {
|
||||
match bitrate {
|
||||
Some(KinoviSeedance2p0FastBitrate::High) => Some(KinoviBitrateRaw::High),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -250,6 +266,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,6 +717,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
};
|
||||
assert_eq!(
|
||||
req.calculate_costs().total_cost.kinovi_credits, baseline,
|
||||
@@ -707,6 +725,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bitrate doesn't affect cost ──
|
||||
|
||||
#[test]
|
||||
fn high_bitrate_does_not_affect_credits() {
|
||||
let baseline = r720(5).calculate_costs().total_cost.kinovi_credits;
|
||||
|
||||
let mut high = r720(5);
|
||||
high.bitrate = Some(KinoviSeedance2p0FastBitrate::High);
|
||||
|
||||
assert_eq!(
|
||||
high.calculate_costs().total_cost.kinovi_credits, baseline,
|
||||
"High bitrate should not change credits from baseline {}", baseline,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
use crate::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
@@ -739,6 +772,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v fast default — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -769,6 +803,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("t2v fast 480p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -802,6 +837,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast t2v 21:9 480p — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -833,6 +869,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast keyframe 21:9 — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -868,6 +905,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast keyframe — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -904,6 +942,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast image ref — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -940,6 +979,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast video ref — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -974,6 +1014,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![MOCHI_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast character — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
@@ -1004,6 +1045,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![STEAMPUNK_CLOWN_ID.to_string(), MOCHI_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}).await?;
|
||||
println!("fast two characters — task_id={}, order_id={}", result.task_id, result.order_id);
|
||||
|
||||
@@ -40,6 +40,8 @@ pub (super) struct ApiParams {
|
||||
pub audio_urls: Option<Vec<String>>,
|
||||
#[serde(rename = "batchCount", skip_serializing_if = "Option::is_none")]
|
||||
pub batch_count: Option<u8>,
|
||||
#[serde(rename = "bitrate_mode", skip_serializing_if = "Option::is_none")]
|
||||
pub bitrate_mode: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
|
||||
+86
@@ -69,6 +69,10 @@ pub struct WorkflowRunTaskRequest {
|
||||
|
||||
/// Controls the `faceBlurMode` field: true sends "on", false sends "off", None omits it.
|
||||
pub use_face_blur_hack: Option<bool>,
|
||||
|
||||
/// Output video bitrate. None defaults to "standard" (the field is omitted);
|
||||
/// `High` sends `bitrate_mode: "high"`. Does not affect cost.
|
||||
pub bitrate: Option<KinoviBitrateRaw>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkflowRunTaskRequest {
|
||||
@@ -87,6 +91,7 @@ impl std::fmt::Debug for WorkflowRunTaskRequest {
|
||||
.field("character_ids", &self.character_ids)
|
||||
.field("output_resolution", &self.output_resolution)
|
||||
.field("use_face_blur_hack", &self.use_face_blur_hack)
|
||||
.field("bitrate", &self.bitrate)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -268,6 +273,23 @@ impl KinoviModelTypeRaw {
|
||||
}
|
||||
}
|
||||
|
||||
/// Output video bitrate. When omitted, defaults to "standard".
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KinoviBitrateRaw {
|
||||
/// High bitrate (`bitrate_mode: "high"`).
|
||||
High,
|
||||
}
|
||||
|
||||
impl KinoviBitrateRaw {
|
||||
/// Returns the API string to send, or None for "standard" (the default,
|
||||
/// which is expressed by omitting the field entirely).
|
||||
pub fn as_api_str(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::High => Some("high"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Response ---
|
||||
|
||||
pub struct WorkflowRunTaskResponse {
|
||||
@@ -361,6 +383,7 @@ pub async fn workflow_run_task(args: WorkflowRunTaskArgs<'_>) -> Result<Workflow
|
||||
uploaded_urls,
|
||||
audio_urls,
|
||||
batch_count,
|
||||
bitrate_mode: req.bitrate.and_then(|bitrate| bitrate.as_api_str()),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -470,6 +493,7 @@ mod tests {
|
||||
character_ids: None,
|
||||
output_resolution,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,6 +659,7 @@ mod tests {
|
||||
character_ids: None,
|
||||
output_resolution: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
};
|
||||
assert_eq!(
|
||||
req.estimate_credits(), baseline,
|
||||
@@ -738,6 +763,49 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bitrate serialization ──
|
||||
//
|
||||
// The optional `bitrate_mode` field is sent only when `High` is requested;
|
||||
// the standard (default) bitrate omits the field entirely.
|
||||
|
||||
mod bitrate_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn high_bitrate_serializes_to_high() {
|
||||
assert_eq!(KinoviBitrateRaw::High.as_api_str(), Some("high"));
|
||||
|
||||
let api_params = base_api_params(Some("high"));
|
||||
let json = serde_json::to_string(&api_params).unwrap();
|
||||
assert!(json.contains(r#""bitrate_mode":"high""#), "expected bitrate_mode in {json}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_bitrate_omits_field() {
|
||||
let api_params = base_api_params(None);
|
||||
let json = serde_json::to_string(&api_params).unwrap();
|
||||
assert!(!json.contains("bitrate_mode"), "expected no bitrate_mode in {json}");
|
||||
}
|
||||
|
||||
fn base_api_params(bitrate_mode: Option<&'static str>) -> ApiParams {
|
||||
ApiParams {
|
||||
prompt: "a corgi".to_string(),
|
||||
resolution: "1280x720".to_string(),
|
||||
content_mode: "normal",
|
||||
model: "seedance-20",
|
||||
duration: "5s".to_string(),
|
||||
mode: "keyframe",
|
||||
output_resolution: None,
|
||||
face_blur_mode: None,
|
||||
character_ids: None,
|
||||
uploaded_urls: None,
|
||||
audio_urls: None,
|
||||
batch_count: None,
|
||||
bitrate_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod real_requests {
|
||||
use super::*;
|
||||
|
||||
@@ -767,6 +835,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -800,6 +869,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -835,6 +905,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -869,6 +940,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -905,6 +977,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -958,6 +1031,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1010,6 +1084,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1062,6 +1137,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1123,6 +1199,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1177,6 +1254,7 @@ mod tests {
|
||||
reference_audio_urls: Some(vec![upload_result.public_url]),
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1216,6 +1294,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![STEAMPUNK_CLOWN_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1249,6 +1328,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![MOCHI_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1284,6 +1364,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: Some(vec![STEAMPUNK_CLOWN_ID.to_string()]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1320,6 +1401,7 @@ mod tests {
|
||||
MOCHI_ID.to_string(),
|
||||
]),
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1357,6 +1439,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: Some(false),
|
||||
bitrate: None,
|
||||
output_resolution: Some(KinoviOutputResolutionRaw::TenEightyP),
|
||||
},
|
||||
};
|
||||
@@ -1408,6 +1491,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: Some(false),
|
||||
bitrate: None,
|
||||
output_resolution: None,
|
||||
},
|
||||
};
|
||||
@@ -1459,6 +1543,7 @@ mod tests {
|
||||
reference_audio_urls: None,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: Some(false),
|
||||
bitrate: None,
|
||||
output_resolution: Some(KinoviOutputResolutionRaw::TenEightyP),
|
||||
},
|
||||
};
|
||||
@@ -1503,6 +1588,7 @@ mod tests {
|
||||
character_ids: None,
|
||||
output_resolution,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ pub async fn run(state: &ArtcraftState, args: GenerateVideoArgs) -> anyhow::Resu
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds: Some(duration),
|
||||
video_batch_count: None,
|
||||
|
||||
+2
@@ -427,6 +427,7 @@ async fn generate(
|
||||
reference_audio_urls: media.reference_audio_urls,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
session,
|
||||
host_override: None,
|
||||
@@ -456,6 +457,7 @@ async fn generate(
|
||||
reference_audio_urls: media.reference_audio_urls,
|
||||
character_ids: None,
|
||||
use_face_blur_hack: None,
|
||||
bitrate: None,
|
||||
},
|
||||
session,
|
||||
host_override: None,
|
||||
|
||||
+1
@@ -146,6 +146,7 @@ mod tests {
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ pub(super) async fn handle_artcraft_video_via_router(
|
||||
reference_character_tokens,
|
||||
resolution: request.resolution,
|
||||
aspect_ratio: request.aspect_ratio,
|
||||
bitrate: None,
|
||||
duration_seconds: request.duration_seconds,
|
||||
video_batch_count: request.video_batch_count,
|
||||
generate_audio: request.generate_audio,
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use strum::EnumCount;
|
||||
#[cfg(test)]
|
||||
use strum::EnumIter;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::error::enum_error::EnumError;
|
||||
|
||||
/// Maximum serialized string length for database storage.
|
||||
pub const MAX_LENGTH: usize = 16;
|
||||
|
||||
/// Common output bitrate levels for generation.
|
||||
///
|
||||
/// Most models default to `Normal`; `High` requests a higher bitrate where the
|
||||
/// model supports it. Models that don't support it simply ignore the value.
|
||||
///
|
||||
/// NB: Keep the max serialized length to 16 characters.
|
||||
#[cfg_attr(test, derive(EnumIter, EnumCount))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CommonBitrate {
|
||||
Normal,
|
||||
High,
|
||||
}
|
||||
|
||||
impl_enum_display_and_debug_using_to_str!(CommonBitrate);
|
||||
impl_mysql_enum_coders!(CommonBitrate);
|
||||
impl_mysql_from_row!(CommonBitrate);
|
||||
|
||||
impl CommonBitrate {
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Normal => "normal",
|
||||
Self::High => "high",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(value: &str) -> Result<Self, EnumError> {
|
||||
match value {
|
||||
"normal" => Ok(Self::Normal),
|
||||
"high" => Ok(Self::High),
|
||||
_ => Err(EnumError::CouldNotConvertFromString(value.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_variants() -> BTreeSet<Self> {
|
||||
BTreeSet::from([
|
||||
Self::Normal,
|
||||
Self::High,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::common::generation::common_bitrate::CommonBitrate;
|
||||
use crate::common::generation::common_bitrate::MAX_LENGTH;
|
||||
use crate::test_helpers::assert_serialization;
|
||||
|
||||
mod explicit_checks {
|
||||
use super::*;
|
||||
use crate::error::enum_error::EnumError;
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
assert_serialization(CommonBitrate::Normal, "normal");
|
||||
assert_serialization(CommonBitrate::High, "high");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_str() {
|
||||
assert_eq!(CommonBitrate::Normal.to_str(), "normal");
|
||||
assert_eq!(CommonBitrate::High.to_str(), "high");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert_eq!(CommonBitrate::from_str("normal").unwrap(), CommonBitrate::Normal);
|
||||
assert_eq!(CommonBitrate::from_str("high").unwrap(), CommonBitrate::High);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str_err() {
|
||||
let result = CommonBitrate::from_str("invalid");
|
||||
assert!(result.is_err());
|
||||
if let Err(EnumError::CouldNotConvertFromString(value)) = result {
|
||||
assert_eq!(value, "invalid");
|
||||
} else {
|
||||
panic!("Expected EnumError::CouldNotConvertFromString");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variants() {
|
||||
let mut variants = CommonBitrate::all_variants();
|
||||
assert_eq!(variants.len(), 2);
|
||||
assert_eq!(variants.pop_first(), Some(CommonBitrate::Normal));
|
||||
assert_eq!(variants.pop_first(), Some(CommonBitrate::High));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod mechanical_checks {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn variant_length() {
|
||||
use strum::IntoEnumIterator;
|
||||
assert_eq!(CommonBitrate::all_variants().len(), CommonBitrate::iter().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
for variant in CommonBitrate::all_variants() {
|
||||
assert_eq!(variant, CommonBitrate::from_str(variant.to_str()).unwrap());
|
||||
assert_eq!(variant, CommonBitrate::from_str(&format!("{}", variant)).unwrap());
|
||||
assert_eq!(variant, CommonBitrate::from_str(&format!("{:?}", variant)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_length_ok_for_database() {
|
||||
for variant in CommonBitrate::all_variants() {
|
||||
let serialized = variant.to_str();
|
||||
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
|
||||
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long via to_str()", variant);
|
||||
}
|
||||
for variant in CommonBitrate::all_variants() {
|
||||
let json = serde_json::to_string(&variant).unwrap().replace('"', "");
|
||||
assert!(json.len() <= MAX_LENGTH, "variant {:?} is too long via JSON: {:?}", variant, json);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_names_must_not_contain_dots() {
|
||||
for variant in CommonBitrate::all_variants() {
|
||||
let to_str_value = variant.to_str();
|
||||
assert!(!to_str_value.contains('.'), "to_str() for {:?} contains a dot: {:?}", variant, to_str_value);
|
||||
|
||||
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
|
||||
assert!(!json_value.contains('.'), "JSON serialization for {:?} contains a dot: {:?}", variant, json_value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_names_must_only_contain_lowercase_alphanumeric_and_underscore() {
|
||||
let valid_pattern = regex::Regex::new(r"^[a-z0-9_]+$").unwrap();
|
||||
|
||||
for variant in CommonBitrate::all_variants() {
|
||||
let to_str_value = variant.to_str();
|
||||
assert!(valid_pattern.is_match(to_str_value),
|
||||
"to_str() for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, to_str_value);
|
||||
|
||||
let json_value = serde_json::to_string(&variant).unwrap().replace('"', "");
|
||||
assert!(valid_pattern.is_match(&json_value),
|
||||
"JSON serialization for {:?} contains invalid characters: {:?} (only a-z, 0-9, _ allowed)", variant, json_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod common_aspect_ratio;
|
||||
pub mod common_bitrate;
|
||||
pub mod common_generation_mode;
|
||||
pub mod common_image_model;
|
||||
pub mod common_quality;
|
||||
|
||||
@@ -5,6 +5,7 @@ use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use enums::common::generation::model_creator::ModelCreator;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
use once_cell::sync::Lazy;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate;
|
||||
|
||||
pub const OMNI_GEN_VIDEO_MODELS_AND_PROVIDERS: Lazy<OmniGenVideoModelsResponse> = Lazy::new(|| {
|
||||
let models = build_omni_gen_video_models();
|
||||
@@ -246,6 +247,11 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::TenEightyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -287,6 +293,11 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::SevenTwentyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -329,6 +340,11 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::TenEightyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -370,6 +386,11 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::SevenTwentyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -412,6 +433,11 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::TenEightyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
bitrate_default: Some(CommonBitrate::Normal),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
@@ -453,6 +479,10 @@ fn build_omni_gen_video_models() -> Vec<OmniGenVideoModelDetails> {
|
||||
CommonResolution::SevenTwentyP,
|
||||
]),
|
||||
resolution_default: Some(CommonResolution::SevenTwentyP),
|
||||
bitrate_options: Some(vec![
|
||||
CommonBitrate::Normal,
|
||||
CommonBitrate::High,
|
||||
]),
|
||||
batch_size_options: Some(vec![1, 2, 4]),
|
||||
batch_size_default: Some(1),
|
||||
duration_seconds_min: Some(4),
|
||||
|
||||
@@ -268,6 +268,7 @@ use enums::by_table::users::user_feature_flag::UserFeatureFlag;
|
||||
use enums::common::generation::common_model_class::CommonModelClass;
|
||||
use enums::common::generation::common_model_type::CommonModelType;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate;
|
||||
use enums::common::generation::common_generation_mode::CommonGenerationMode;
|
||||
use enums::common::generation::common_resolution::CommonResolution;
|
||||
use enums::common::generation_provider::GenerationProvider;
|
||||
@@ -668,6 +669,7 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux
|
||||
CommonModelClass,
|
||||
CommonModelType,
|
||||
CommonAspectRatio,
|
||||
CommonBitrate,
|
||||
CommonGenerationMode,
|
||||
CommonResolution,
|
||||
PromptContextSemanticType,
|
||||
|
||||
+1
@@ -51,6 +51,7 @@ pub async fn estimate_video_cost_handler(
|
||||
reference_character_tokens: None,
|
||||
resolution: router_resolution,
|
||||
aspect_ratio: router_aspect_ratio,
|
||||
bitrate: None,
|
||||
duration_seconds: request.duration_seconds,
|
||||
video_batch_count: request.video_batch_count,
|
||||
generate_audio: request.generate_audio,
|
||||
|
||||
+76
@@ -3,6 +3,7 @@ use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost
|
||||
use artcraft_router::api::audio_list_ref::AudioListRef;
|
||||
use artcraft_router::api::character_list_ref::CharacterListRef;
|
||||
use artcraft_router::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use artcraft_router::api::router_bitrate::RouterBitrate;
|
||||
use artcraft_router::api::router_resolution::RouterResolution;
|
||||
use artcraft_router::api::router_video_model::RouterVideoModel;
|
||||
use artcraft_router::api::image_list_ref::ImageListRef;
|
||||
@@ -12,6 +13,7 @@ use artcraft_router::api::video_list_ref::VideoListRef;
|
||||
use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use artcraft_router::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
@@ -36,6 +38,11 @@ pub fn hydrate_to_router_request(
|
||||
.map(convert_resolution)
|
||||
.transpose()?;
|
||||
|
||||
let bitrate = request.bitrate
|
||||
.as_ref()
|
||||
.map(convert_bitrate)
|
||||
.transpose()?;
|
||||
|
||||
Ok(GenerateVideoRequestBuilder {
|
||||
model,
|
||||
provider: RouterProvider::Artcraft,
|
||||
@@ -55,6 +62,7 @@ pub fn hydrate_to_router_request(
|
||||
.map(CharacterListRef::CharacterTokens),
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
bitrate,
|
||||
duration_seconds: request.duration_seconds,
|
||||
video_batch_count: request.video_batch_count,
|
||||
generate_audio: request.generate_audio,
|
||||
@@ -95,3 +103,71 @@ fn convert_resolution(
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_bitrate(
|
||||
bitrate: &CommonBitrateEnum,
|
||||
) -> Result<RouterBitrate, CommonWebError> {
|
||||
let json = serde_json::to_string(bitrate)?;
|
||||
serde_json::from_str(&json).map_err(|e| {
|
||||
CommonWebError::BadInputWithSimpleMessage(
|
||||
format!("Unsupported bitrate: {}", e),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
mod bitrate_hydration {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn high_is_hydrated() {
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
bitrate: Some(CommonBitrateEnum::High),
|
||||
..base_request()
|
||||
};
|
||||
let builder = hydrate_to_router_request(&request).expect("hydrate should succeed");
|
||||
assert_eq!(builder.bitrate, Some(RouterBitrate::High));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_is_hydrated() {
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
bitrate: Some(CommonBitrateEnum::Normal),
|
||||
..base_request()
|
||||
};
|
||||
let builder = hydrate_to_router_request(&request).expect("hydrate should succeed");
|
||||
assert_eq!(builder.bitrate, Some(RouterBitrate::Normal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_stays_none() {
|
||||
let builder = hydrate_to_router_request(&base_request()).expect("hydrate should succeed");
|
||||
assert!(builder.bitrate.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
fn base_request() -> OmniGenVideoCostAndGenerateRequest {
|
||||
OmniGenVideoCostAndGenerateRequest {
|
||||
idempotency_token: None,
|
||||
model: Some(CommonVideoModelEnum::Seedance2p0),
|
||||
prompt: None,
|
||||
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,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -56,6 +56,7 @@ mod tests {
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
|
||||
+76
@@ -3,6 +3,7 @@ use artcraft_api_defs::omni_gen::cost_and_generate_requests::omni_gen_video_cost
|
||||
use artcraft_router::api::audio_list_ref::AudioListRef;
|
||||
use artcraft_router::api::character_list_ref::CharacterListRef;
|
||||
use artcraft_router::api::router_aspect_ratio::RouterAspectRatio;
|
||||
use artcraft_router::api::router_bitrate::RouterBitrate;
|
||||
use artcraft_router::api::router_resolution::RouterResolution;
|
||||
use artcraft_router::api::router_video_model::RouterVideoModel;
|
||||
use artcraft_router::api::image_list_ref::ImageListRef;
|
||||
@@ -12,6 +13,7 @@ use artcraft_router::api::video_list_ref::VideoListRef;
|
||||
use artcraft_router::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use artcraft_router::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use enums::common::generation::common_aspect_ratio::CommonAspectRatio as CommonAspectRatioEnum;
|
||||
use enums::common::generation::common_bitrate::CommonBitrate as CommonBitrateEnum;
|
||||
use enums::common::generation::common_resolution::CommonResolution as CommonResolutionEnum;
|
||||
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
|
||||
|
||||
@@ -36,6 +38,11 @@ pub fn hydrate_to_router_request(
|
||||
.map(convert_resolution)
|
||||
.transpose()?;
|
||||
|
||||
let bitrate = request.bitrate
|
||||
.as_ref()
|
||||
.map(convert_bitrate)
|
||||
.transpose()?;
|
||||
|
||||
Ok(GenerateVideoRequestBuilder {
|
||||
model,
|
||||
provider: RouterProvider::Artcraft,
|
||||
@@ -55,6 +62,7 @@ pub fn hydrate_to_router_request(
|
||||
.map(CharacterListRef::CharacterTokens),
|
||||
resolution,
|
||||
aspect_ratio,
|
||||
bitrate,
|
||||
duration_seconds: request.duration_seconds,
|
||||
video_batch_count: request.video_batch_count,
|
||||
generate_audio: request.generate_audio,
|
||||
@@ -95,3 +103,71 @@ fn convert_resolution(
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_bitrate(
|
||||
bitrate: &CommonBitrateEnum,
|
||||
) -> Result<RouterBitrate, CommonWebError> {
|
||||
let json = serde_json::to_string(bitrate)?;
|
||||
serde_json::from_str(&json).map_err(|e| {
|
||||
CommonWebError::BadInputWithSimpleMessage(
|
||||
format!("Unsupported bitrate: {}", e),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
mod bitrate_hydration {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn high_is_hydrated() {
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
bitrate: Some(CommonBitrateEnum::High),
|
||||
..base_request()
|
||||
};
|
||||
let builder = hydrate_to_router_request(&request).expect("hydrate should succeed");
|
||||
assert_eq!(builder.bitrate, Some(RouterBitrate::High));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_is_hydrated() {
|
||||
let request = OmniGenVideoCostAndGenerateRequest {
|
||||
bitrate: Some(CommonBitrateEnum::Normal),
|
||||
..base_request()
|
||||
};
|
||||
let builder = hydrate_to_router_request(&request).expect("hydrate should succeed");
|
||||
assert_eq!(builder.bitrate, Some(RouterBitrate::Normal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_stays_none() {
|
||||
let builder = hydrate_to_router_request(&base_request()).expect("hydrate should succeed");
|
||||
assert!(builder.bitrate.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
fn base_request() -> OmniGenVideoCostAndGenerateRequest {
|
||||
OmniGenVideoCostAndGenerateRequest {
|
||||
idempotency_token: None,
|
||||
model: Some(CommonVideoModelEnum::Seedance2p0),
|
||||
prompt: None,
|
||||
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,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds: None,
|
||||
video_batch_count: None,
|
||||
generate_audio: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -56,6 +56,7 @@ mod tests {
|
||||
reference_character_tokens: None,
|
||||
resolution: None,
|
||||
aspect_ratio: None,
|
||||
bitrate: None,
|
||||
quality: None,
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
|
||||
Reference in New Issue
Block a user