mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
rename client (#1824)
This commit is contained in:
Generated
+27
-27
@@ -757,9 +757,9 @@ dependencies = [
|
||||
"fal_client",
|
||||
"gmicloud_client",
|
||||
"grok_api_client",
|
||||
"kinovi_web_client",
|
||||
"log",
|
||||
"reqwest 0.12.23",
|
||||
"seedance2pro_client",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -6359,6 +6359,29 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kinovi_web_client"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"cloudflare_errors",
|
||||
"env",
|
||||
"env_logger 0.11.9",
|
||||
"errors",
|
||||
"log",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"test_data",
|
||||
"test_utils",
|
||||
"tokio",
|
||||
"url",
|
||||
"workspace-build-acceleration",
|
||||
"wreq",
|
||||
"wreq-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kubectl-pod-cleanup"
|
||||
version = "0.0.1"
|
||||
@@ -10519,6 +10542,7 @@ dependencies = [
|
||||
"errors",
|
||||
"hashing",
|
||||
"jobs_common",
|
||||
"kinovi_web_client",
|
||||
"log",
|
||||
"mysql_queries",
|
||||
"pager",
|
||||
@@ -10526,7 +10550,6 @@ dependencies = [
|
||||
"reqwest 0.12.23",
|
||||
"rootly_client",
|
||||
"rootly_config",
|
||||
"seedance2pro_client",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -10538,29 +10561,6 @@ dependencies = [
|
||||
"workspace-build-acceleration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "seedance2pro_client"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"cloudflare_errors",
|
||||
"env",
|
||||
"env_logger 0.11.9",
|
||||
"errors",
|
||||
"log",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"test_data",
|
||||
"test_utils",
|
||||
"tokio",
|
||||
"url",
|
||||
"workspace-build-acceleration",
|
||||
"wreq",
|
||||
"wreq-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.24.0"
|
||||
@@ -11632,6 +11632,7 @@ dependencies = [
|
||||
"images",
|
||||
"infer 0.16.0",
|
||||
"jwt_signer",
|
||||
"kinovi_web_client",
|
||||
"language-tags",
|
||||
"lexical-sort",
|
||||
"limitation",
|
||||
@@ -11663,7 +11664,6 @@ dependencies = [
|
||||
"rootly_config",
|
||||
"rust-s3",
|
||||
"rustls 0.23.36",
|
||||
"seedance2pro_client",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -11867,8 +11867,8 @@ dependencies = [
|
||||
"easyenv",
|
||||
"enums",
|
||||
"errors",
|
||||
"kinovi_web_client",
|
||||
"log",
|
||||
"seedance2pro_client",
|
||||
"serde_json",
|
||||
"tokens",
|
||||
"tokio",
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ members = [
|
||||
"crates/api_clients/openart_client",
|
||||
"crates/api_clients/rootly_client",
|
||||
"crates/api_clients/runwayml_client",
|
||||
"crates/api_clients/seedance2pro_client",
|
||||
"crates/api_clients/kinovi_web_client",
|
||||
"crates/api_clients/thumbnail_generator",
|
||||
"crates/api_clients/worldlabs_api_client",
|
||||
"crates/api_clients/worldlabs_consumer_client",
|
||||
@@ -285,7 +285,7 @@ mysql_queries = { path = "crates/schema/database/mysql_queries" }
|
||||
pager = { path = "crates/lib/pager" }
|
||||
openai_sora_client = { path = "crates/api_clients/openai_sora_client" }
|
||||
primitives = { path = "crates/lib/data/primitives" }
|
||||
seedance2pro_client = { path = "crates/api_clients/seedance2pro_client" }
|
||||
kinovi_web_client = { path = "crates/api_clients/kinovi_web_client" }
|
||||
server_environment = { path = "crates/lib/server_environment" }
|
||||
sha1_hash = { path = "crates/lib/sha1_hash" }
|
||||
shared_service_components = { path = "crates/lib/shared_service_components" }
|
||||
|
||||
+2
-2
@@ -51,9 +51,9 @@ helpers go in the parent `mod tests` so sub-modules can `use super::*`.
|
||||
|
||||
- `storyteller_web` — main HTTP API monolith (actix-web)
|
||||
- `artcraft` (desktop) — Tauri desktop app
|
||||
- `artcraft_router` — provider routing for image/video generation (Artcraft, Fal, Seedance2Pro, Muapi)
|
||||
- `artcraft_router` — provider routing for image/video generation (Artcraft, Fal, KinoviWeb, Muapi)
|
||||
- `artcraft_api_defs` — shared HTTP API type definitions
|
||||
- `seedance2pro_client` — HTTP client for the Kinovi/Seedance2Pro video generation service
|
||||
- `kinovi_web_client` — HTTP client for the Kinovi/KinoviWeb video generation service
|
||||
- `enums` — database-backed enums stored as VARCHAR fields
|
||||
- `tokens` — primary key identifiers with Stripe-like prefixes (e.g. `user_`, `mf_`)
|
||||
- `mysql_queries` — SQLx MySQL queries for the backend
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ pub struct OmniGenVideoGenerateResponse {
|
||||
|
||||
/// All job tokens created by this request (including the primary).
|
||||
/// For single-job requests this will contain just one element matching
|
||||
/// `inference_job_token`. For batch requests (eg. Seedance2Pro with
|
||||
/// `inference_job_token`. For batch requests (eg. KinoviWeb with
|
||||
/// multiple order IDs) this will contain all of them.
|
||||
pub all_job_tokens: Vec<InferenceJobToken>,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ artcraft_client.workspace = true
|
||||
fal_client.workspace = true
|
||||
gmicloud_client.workspace = true
|
||||
grok_api_client.workspace = true
|
||||
seedance2pro_client.workspace = true
|
||||
kinovi_web_client.workspace = true
|
||||
tokens.workspace = true
|
||||
url_utils.workspace = true
|
||||
worldlabs_api_client.workspace = true
|
||||
|
||||
@@ -8,6 +8,6 @@ pub enum RouterProvider {
|
||||
Fal,
|
||||
GmiCloud,
|
||||
GrokApi,
|
||||
Seedance2Pro,
|
||||
KinoviWeb,
|
||||
WorldLabs,
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ pub mod router_client;
|
||||
pub mod router_fal_client;
|
||||
pub mod router_gmicloud_client;
|
||||
pub mod router_grok_api_client;
|
||||
pub mod router_seedance2pro_client;
|
||||
pub mod router_kinovi_web_client;
|
||||
pub mod router_worldlabs_client;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::client::router_gmicloud_client::RouterGmiCloudClient;
|
||||
use crate::client::router_grok_api_client::RouterGrokApiClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::client::router_worldlabs_client::RouterWorldLabsClient;
|
||||
use crate::errors::client_error::{ClientError, ClientType};
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct MultiRouterClient {
|
||||
pub(crate) fal_client: Option<RouterFalClient>,
|
||||
pub(crate) gmicloud_client: Option<RouterGmiCloudClient>,
|
||||
pub(crate) grok_api_client: Option<RouterGrokApiClient>,
|
||||
pub(crate) seedance2pro_client: Option<RouterSeedance2ProClient>,
|
||||
pub(crate) kinovi_web_client: Option<RouterKinoviWebClient>,
|
||||
pub(crate) worldlabs_client: Option<RouterWorldLabsClient>,
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ impl MultiRouterClient {
|
||||
.ok_or(ClientError::ClientNotConfigured(ClientType::GrokApi))
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ClientError> {
|
||||
self.seedance2pro_client.as_ref()
|
||||
.ok_or(ClientError::ClientNotConfigured(ClientType::Seedance2Pro))
|
||||
pub fn get_kinovi_web_client_ref(&self) -> Result<&RouterKinoviWebClient, ClientError> {
|
||||
self.kinovi_web_client.as_ref()
|
||||
.ok_or(ClientError::ClientNotConfigured(ClientType::KinoviWeb))
|
||||
}
|
||||
|
||||
pub fn get_worldlabs_client_ref(&self) -> Result<&RouterWorldLabsClient, ClientError> {
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@ use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::client::router_gmicloud_client::RouterGmiCloudClient;
|
||||
use crate::client::router_grok_api_client::RouterGrokApiClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::client::router_worldlabs_client::RouterWorldLabsClient;
|
||||
|
||||
pub struct MultiRouterClientBuilder {
|
||||
@@ -11,7 +11,7 @@ pub struct MultiRouterClientBuilder {
|
||||
fal_client: Option<RouterFalClient>,
|
||||
gmicloud_client: Option<RouterGmiCloudClient>,
|
||||
grok_api_client: Option<RouterGrokApiClient>,
|
||||
seedance2pro_client: Option<RouterSeedance2ProClient>,
|
||||
kinovi_web_client: Option<RouterKinoviWebClient>,
|
||||
worldlabs_client: Option<RouterWorldLabsClient>,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl MultiRouterClientBuilder {
|
||||
fal_client: None,
|
||||
gmicloud_client: None,
|
||||
grok_api_client: None,
|
||||
seedance2pro_client: None,
|
||||
kinovi_web_client: None,
|
||||
worldlabs_client: None,
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,8 @@ impl MultiRouterClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_seedance2pro_client(mut self, client: RouterSeedance2ProClient) -> Self {
|
||||
self.seedance2pro_client = Some(client);
|
||||
pub fn set_kinovi_web_client(mut self, client: RouterKinoviWebClient) -> Self {
|
||||
self.kinovi_web_client = Some(client);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ impl MultiRouterClientBuilder {
|
||||
fal_client: self.fal_client,
|
||||
gmicloud_client: self.gmicloud_client,
|
||||
grok_api_client: self.grok_api_client,
|
||||
seedance2pro_client: self.seedance2pro_client,
|
||||
kinovi_web_client: self.kinovi_web_client,
|
||||
worldlabs_client: self.worldlabs_client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::client::router_artcraft_client::RouterArtcraftClient;
|
||||
use crate::client::router_fal_client::RouterFalClient;
|
||||
use crate::client::router_gmicloud_client::RouterGmiCloudClient;
|
||||
use crate::client::router_grok_api_client::RouterGrokApiClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::client::router_worldlabs_client::RouterWorldLabsClient;
|
||||
use crate::errors::client_error::{ClientError, ClientType};
|
||||
|
||||
@@ -13,7 +13,7 @@ pub enum RouterClient {
|
||||
Fal(RouterFalClient),
|
||||
GmiCloud(RouterGmiCloudClient),
|
||||
GrokApi(RouterGrokApiClient),
|
||||
Seedance2Pro(RouterSeedance2ProClient),
|
||||
KinoviWeb(RouterKinoviWebClient),
|
||||
WorldLabs(RouterWorldLabsClient),
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ impl RouterClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ClientError> {
|
||||
pub fn get_kinovi_web_client_ref(&self) -> Result<&RouterKinoviWebClient, ClientError> {
|
||||
match self {
|
||||
RouterClient::Seedance2Pro(client) => Ok(client),
|
||||
RouterClient::Multi(multi) => multi.get_seedance2pro_client_ref(),
|
||||
_ => Err(ClientError::ClientNotConfigured(ClientType::Seedance2Pro)),
|
||||
RouterClient::KinoviWeb(client) => Ok(client),
|
||||
RouterClient::Multi(multi) => multi.get_kinovi_web_client_ref(),
|
||||
_ => Err(ClientError::ClientNotConfigured(ClientType::KinoviWeb)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
|
||||
pub struct RouterKinoviWebClient {
|
||||
pub(crate) session: KinoviWebSession,
|
||||
}
|
||||
|
||||
impl RouterKinoviWebClient {
|
||||
pub fn new(session: KinoviWebSession) -> Self {
|
||||
RouterKinoviWebClient { session }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
|
||||
pub struct RouterSeedance2ProClient {
|
||||
pub(crate) session: Seedance2ProSession,
|
||||
}
|
||||
|
||||
impl RouterSeedance2ProClient {
|
||||
pub fn new(session: Seedance2ProSession) -> Self {
|
||||
RouterSeedance2ProClient { session }
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ use crate::errors::client_error::ClientError;
|
||||
use crate::errors::download_error::DownloadError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use fal_client::error::fal_error_plus::FalErrorPlus;
|
||||
use seedance2pro_client::error::seedance2pro_error::Seedance2ProError;
|
||||
use seedance2pro_client::error::seedance2pro_specific_api_error::Seedance2ProSpecificApiError;
|
||||
use kinovi_web_client::error::kinovi_web_error::KinoviWebError;
|
||||
use kinovi_web_client::error::kinovi_web_specific_api_error::KinoviWebSpecificApiError;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use artcraft_client::error::api_error::ApiError;
|
||||
@@ -65,7 +65,7 @@ impl From<ProviderError> for ArtcraftRouterError {
|
||||
fn from(error: ProviderError) -> Self {
|
||||
let is_billing_error = match &error {
|
||||
ProviderError::Fal(FalErrorPlus::FalBillingError(_)) => true,
|
||||
ProviderError::Seedance2Pro(Seedance2ProError::ApiSpecific(Seedance2ProSpecificApiError::BillingError { .. })) => true,
|
||||
ProviderError::KinoviWeb(KinoviWebError::ApiSpecific(KinoviWebSpecificApiError::BillingError { .. })) => true,
|
||||
ProviderError::Storyteller(StorytellerError::Api(ApiError::PaymentRequired(_))) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ pub enum ClientType {
|
||||
Fal,
|
||||
GmiCloud,
|
||||
GrokApi,
|
||||
Seedance2Pro,
|
||||
KinoviWeb,
|
||||
WorldLabs,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Display for ClientType {
|
||||
Self::Fal => write!(f, "Fal"),
|
||||
Self::GmiCloud => write!(f, "GmiCloud"),
|
||||
Self::GrokApi => write!(f, "GrokApi"),
|
||||
Self::Seedance2Pro => write!(f, "Seedance2Pro"),
|
||||
Self::KinoviWeb => write!(f, "KinoviWeb"),
|
||||
Self::WorldLabs => write!(f, "WorldLabs"),
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,8 @@ pub enum ClientError {
|
||||
/// Fal only accepts image URLs for image inputs, not media tokens.
|
||||
FalOnlySupportsUrls,
|
||||
|
||||
/// Seedance2Pro only accepts URLs for media inputs, not media tokens.
|
||||
Seedance2ProOnlySupportsUrls,
|
||||
/// KinoviWeb only accepts URLs for media inputs, not media tokens.
|
||||
KinoviWebOnlySupportsUrls,
|
||||
|
||||
/// The Fal endpoint requires a webhook URL but the caller built the client
|
||||
/// in polling/queue mode (no webhook URL). Returned by webhook-only
|
||||
@@ -91,8 +91,8 @@ impl Display for ClientError {
|
||||
Self::FalOnlySupportsUrls => {
|
||||
write!(f, "Fal only supports image URLs for image inputs, not media tokens")
|
||||
}
|
||||
Self::Seedance2ProOnlySupportsUrls => {
|
||||
write!(f, "Seedance2Pro only supports URLs for media inputs; resolve media tokens to URLs before calling this provider")
|
||||
Self::KinoviWebOnlySupportsUrls => {
|
||||
write!(f, "KinoviWeb only supports URLs for media inputs; resolve media tokens to URLs before calling this provider")
|
||||
}
|
||||
Self::WebhookUrlRequired => {
|
||||
write!(f, "This Fal endpoint only supports webhook dispatch; the caller built RouterFalClient in polling-only mode (no webhook URL)")
|
||||
|
||||
@@ -4,7 +4,7 @@ use artcraft_client::error::storyteller_error::StorytellerError;
|
||||
use fal_client::error::fal_error_plus::FalErrorPlus;
|
||||
use gmicloud_client::error::gmicloud_error::GmiCloudError;
|
||||
use grok_api_client::error::grok_error::GrokError;
|
||||
use seedance2pro_client::error::seedance2pro_error::Seedance2ProError;
|
||||
use kinovi_web_client::error::kinovi_web_error::KinoviWebError;
|
||||
use worldlabs_api_client::error::world_labs_error::WorldLabsError;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -13,7 +13,7 @@ pub enum ProviderError {
|
||||
Fal(FalErrorPlus),
|
||||
GmiCloud(GmiCloudError),
|
||||
Grok(GrokError),
|
||||
Seedance2Pro(Seedance2ProError),
|
||||
KinoviWeb(KinoviWebError),
|
||||
WorldLabs(WorldLabsError),
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Display for ProviderError {
|
||||
Self::Fal(e) => write!(f, "Fal provider error: {}", e),
|
||||
Self::GmiCloud(e) => write!(f, "GmiCloud provider error: {}", e),
|
||||
Self::Grok(e) => write!(f, "Grok provider error: {}", e),
|
||||
Self::Seedance2Pro(e) => write!(f, "Seedance2Pro provider error: {}", e),
|
||||
Self::KinoviWeb(e) => write!(f, "KinoviWeb provider error: {}", e),
|
||||
Self::WorldLabs(e) => write!(f, "WorldLabs provider error: {}", e),
|
||||
}
|
||||
}
|
||||
@@ -56,9 +56,9 @@ impl From<GrokError> for ProviderError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Seedance2ProError> for ProviderError {
|
||||
fn from(error: Seedance2ProError) -> Self {
|
||||
Self::Seedance2Pro(error)
|
||||
impl From<KinoviWebError> for ProviderError {
|
||||
fn from(error: KinoviWebError) -> Self {
|
||||
Self::KinoviWeb(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -26,8 +26,8 @@ impl AudioGenerationDraftRequest {
|
||||
|
||||
pub fn get_provider(&self) -> RouterProvider {
|
||||
match self {
|
||||
Self::KinoviSunoRemix(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSunoSample(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSunoRemix(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSunoSample(_) => RouterProvider::KinoviWeb,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ use std::fmt::Debug;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
|
||||
@@ -19,9 +19,9 @@ pub struct AudioGenerationDraftContext<'a> {
|
||||
}
|
||||
|
||||
impl <'a> AudioGenerationDraftContext<'a> {
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ArtcraftRouterError> {
|
||||
pub fn get_kinovi_web_client_ref(&self) -> Result<&RouterKinoviWebClient, ArtcraftRouterError> {
|
||||
let client = self.client.ok_or(ArtcraftRouterError::Client(ClientError::RouterClientNotProvided))?;
|
||||
client.get_seedance2pro_client_ref()
|
||||
client.get_kinovi_web_client_ref()
|
||||
.map_err(|err| ArtcraftRouterError::Client(err))
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -48,10 +48,10 @@ impl AudioGenerationRequest {
|
||||
Self::ArtcraftSunoSample(_) => RouterProvider::Artcraft,
|
||||
Self::ArtcraftSeedAudio1p0(_) => RouterProvider::Artcraft,
|
||||
Self::FalSeedAudio1p0(_) => RouterProvider::Fal,
|
||||
Self::KinoviSunoMusic(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSunoRemix(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSunoSounds(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSunoSample(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSunoMusic(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSunoRemix(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSunoSounds(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSunoSample(_) => RouterProvider::KinoviWeb,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,19 +100,19 @@ impl AudioGenerationRequest {
|
||||
request.send(client_ref).await
|
||||
},
|
||||
AudioGenerationRequest::KinoviSunoMusic(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
AudioGenerationRequest::KinoviSunoRemix(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
AudioGenerationRequest::KinoviSunoSounds(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
AudioGenerationRequest::KinoviSunoSample(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
}
|
||||
|
||||
+9
-9
@@ -116,10 +116,10 @@ impl GenerateAudioRequestBuilder {
|
||||
// Fal
|
||||
(RouterProvider::Fal, RouterAudioModel::SeedAudio1p0) => build_fal_seed_audio_1p0(self),
|
||||
// Kinovi
|
||||
(RouterProvider::Seedance2Pro, RouterAudioModel::SunoMusic) => build_kinovi_suno_music(self),
|
||||
(RouterProvider::Seedance2Pro, RouterAudioModel::SunoRemix) => build_kinovi_suno_remix(self),
|
||||
(RouterProvider::Seedance2Pro, RouterAudioModel::SunoSounds) => build_kinovi_suno_sounds(self),
|
||||
(RouterProvider::Seedance2Pro, RouterAudioModel::SunoSample) => build_kinovi_suno_sample(self),
|
||||
(RouterProvider::KinoviWeb, RouterAudioModel::SunoMusic) => build_kinovi_suno_music(self),
|
||||
(RouterProvider::KinoviWeb, RouterAudioModel::SunoRemix) => build_kinovi_suno_remix(self),
|
||||
(RouterProvider::KinoviWeb, RouterAudioModel::SunoSounds) => build_kinovi_suno_sounds(self),
|
||||
(RouterProvider::KinoviWeb, RouterAudioModel::SunoSample) => build_kinovi_suno_sample(self),
|
||||
_ => self.unsupported_provider_and_model(),
|
||||
}
|
||||
}
|
||||
@@ -202,7 +202,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn kinovi_suno_music_dispatches_to_request() {
|
||||
let result = builder(RouterProvider::Seedance2Pro, RouterAudioModel::SunoMusic).build2().expect("build");
|
||||
let result = builder(RouterProvider::KinoviWeb, RouterAudioModel::SunoMusic).build2().expect("build");
|
||||
assert!(matches!(
|
||||
result,
|
||||
AudioGenerationDraftOrRequest::Request(AudioGenerationRequest::KinoviSunoMusic(_))
|
||||
@@ -211,7 +211,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn kinovi_suno_sounds_dispatches_to_request() {
|
||||
let result = builder(RouterProvider::Seedance2Pro, RouterAudioModel::SunoSounds).build2().expect("build");
|
||||
let result = builder(RouterProvider::KinoviWeb, RouterAudioModel::SunoSounds).build2().expect("build");
|
||||
assert!(matches!(
|
||||
result,
|
||||
AudioGenerationDraftOrRequest::Request(AudioGenerationRequest::KinoviSunoSounds(_))
|
||||
@@ -220,7 +220,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn kinovi_suno_remix_dispatches_to_draft() {
|
||||
let result = builder_with_audio_ref(RouterProvider::Seedance2Pro, RouterAudioModel::SunoRemix).build2().expect("build");
|
||||
let result = builder_with_audio_ref(RouterProvider::KinoviWeb, RouterAudioModel::SunoRemix).build2().expect("build");
|
||||
assert!(matches!(
|
||||
result,
|
||||
AudioGenerationDraftOrRequest::Draft(AudioGenerationDraftRequest::KinoviSunoRemix(_))
|
||||
@@ -229,7 +229,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn kinovi_suno_sample_dispatches_to_draft() {
|
||||
let result = builder_with_audio_ref(RouterProvider::Seedance2Pro, RouterAudioModel::SunoSample).build2().expect("build");
|
||||
let result = builder_with_audio_ref(RouterProvider::KinoviWeb, RouterAudioModel::SunoSample).build2().expect("build");
|
||||
assert!(matches!(
|
||||
result,
|
||||
AudioGenerationDraftOrRequest::Draft(AudioGenerationDraftRequest::KinoviSunoSample(_))
|
||||
@@ -242,7 +242,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn kinovi_seed_audio_1p0_is_unsupported() {
|
||||
let result = builder(RouterProvider::Seedance2Pro, RouterAudioModel::SeedAudio1p0).build2();
|
||||
let result = builder(RouterProvider::KinoviWeb, RouterAudioModel::SeedAudio1p0).build2();
|
||||
assert!(matches!(result, Err(ArtcraftRouterError::UnsupportedProviderAndModelForNewApi(_))));
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@ pub struct ArtcraftAudioResponsePayload {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Seedance2proAudioResponsePayload {
|
||||
pub struct KinoviWebAudioResponsePayload {
|
||||
pub order_id: String,
|
||||
pub task_id: String,
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub struct FalAudioResponsePayload {
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GenerateAudioResponse {
|
||||
Artcraft(ArtcraftAudioResponsePayload),
|
||||
Seedance2Pro(Seedance2proAudioResponsePayload),
|
||||
KinoviWeb(KinoviWebAudioResponsePayload),
|
||||
Fal(FalAudioResponsePayload),
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ impl GenerateAudioResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_payload(&self) -> Option<Seedance2proAudioResponsePayload> {
|
||||
pub fn get_kinovi_web_payload(&self) -> Option<KinoviWebAudioResponsePayload> {
|
||||
match self {
|
||||
Self::Seedance2Pro(p) => Some(p.clone()),
|
||||
Self::KinoviWeb(p) => Some(p.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -5,14 +5,14 @@
|
||||
//! draft `finalize()` phase. The upload plumbing is shared with the Kinovi
|
||||
//! video provider (`generate_video::providers::kinovi::upload`).
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::audio_list_ref::AudioListRef;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_audio::audio_generation_draft_context::AudioGenerationDraftContext;
|
||||
use crate::generate::generate_video::providers::kinovi::upload::upload_to_seedance2pro;
|
||||
use crate::generate::generate_video::providers::kinovi::upload::upload_to_kinovi_web;
|
||||
|
||||
/// A single unresolved audio reference: either an ArtCraft/public URL or a
|
||||
/// media file token that resolves to one via the draft context's map.
|
||||
@@ -55,7 +55,7 @@ pub(crate) fn require_single_audio_ref(
|
||||
/// Resolve a single audio reference to a source URL and re-upload it to the
|
||||
/// Kinovi CDN, returning the Kinovi CDN URL.
|
||||
pub(crate) async fn resolve_and_upload_audio_ref(
|
||||
session: &Seedance2ProSession,
|
||||
session: &KinoviWebSession,
|
||||
audio_ref: &SingleAudioRef,
|
||||
draft_context: &AudioGenerationDraftContext<'_>,
|
||||
) -> Result<String, ArtcraftRouterError> {
|
||||
@@ -71,7 +71,7 @@ pub(crate) async fn resolve_and_upload_audio_ref(
|
||||
})?
|
||||
}
|
||||
};
|
||||
upload_to_seedance2pro(session, &source_url).await
|
||||
upload_to_kinovi_web(session, &source_url).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_music::GenerateSunoMusicRequest;
|
||||
use kinovi_web_client::generate::audio::generate_suno_music::GenerateSunoMusicRequest;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_audio::audio_generation_draft_or_request::AudioGenerationDraftOrRequest;
|
||||
@@ -119,7 +119,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateAudioRequestBuilder {
|
||||
GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoMusic,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a song about corgis".to_string()),
|
||||
style_prompt: Some("sea shanty".to_string()),
|
||||
..Default::default()
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_music::GenerateSunoMusicRequest;
|
||||
use kinovi_web_client::generate::audio::generate_suno_music::GenerateSunoMusicRequest;
|
||||
|
||||
use crate::generate::generate_audio::audio_generation_cost_estimate::AudioGenerationCostEstimate;
|
||||
use crate::generate::generate_audio::providers::kinovi::suno_music::request::KinoviSunoMusicRequestState;
|
||||
@@ -13,7 +13,7 @@ impl KinoviSunoMusicCostState {
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> AudioGenerationCostEstimate {
|
||||
// Cost math is owned by seedance2pro_client's binding — the router just
|
||||
// Cost math is owned by kinovi_web_client's binding — the router just
|
||||
// forwards the result so router cost ≡ binding cost by construction.
|
||||
let pricing_request = GenerateSunoMusicRequest {
|
||||
prompt: String::new(),
|
||||
@@ -54,7 +54,7 @@ mod tests {
|
||||
fn cost_from_built_request_is_seven_cents() {
|
||||
let builder = GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoMusic,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a song".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
+17
-17
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_music::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_music::{
|
||||
generate_suno_music, GenerateSunoMusicArgs, GenerateSunoMusicRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_audio::generate_audio_response::{
|
||||
GenerateAudioResponse, Seedance2proAudioResponsePayload,
|
||||
GenerateAudioResponse, KinoviWebAudioResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -16,7 +16,7 @@ pub struct KinoviSunoMusicRequestState {
|
||||
}
|
||||
|
||||
impl KinoviSunoMusicRequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSunoMusicArgs {
|
||||
@@ -27,9 +27,9 @@ impl KinoviSunoMusicRequestState {
|
||||
|
||||
let response = generate_suno_music(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateAudioResponse::Seedance2Pro(Seedance2proAudioResponsePayload {
|
||||
Ok(GenerateAudioResponse::KinoviWeb(KinoviWebAudioResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
}))
|
||||
@@ -38,12 +38,12 @@ impl KinoviSunoMusicRequestState {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
|
||||
use crate::api::router_audio_model::RouterAudioModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_audio::audio_generation_draft_or_request::AudioGenerationDraftOrRequest;
|
||||
use crate::generate::generate_audio::generate_audio_request_builder::GenerateAudioRequestBuilder;
|
||||
use crate::generate::generate_audio::generate_audio_response::GenerateAudioResponse;
|
||||
@@ -56,7 +56,7 @@ mod tests {
|
||||
style_prompt: Some("Sea shanty, folk".to_string()),
|
||||
..suno_music_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateAudioResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateAudioResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ mod tests {
|
||||
is_instrumental: Some(true),
|
||||
..suno_music_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateAudioResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateAudioResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -78,20 +78,20 @@ mod tests {
|
||||
fn suno_music_builder() -> GenerateAudioRequestBuilder {
|
||||
GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoMusic,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_seedance2pro_client() -> RouterClient {
|
||||
fn get_kinovi_web_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
async fn run_pipeline(builder: GenerateAudioRequestBuilder) -> GenerateAudioResponse {
|
||||
let client = get_seedance2pro_client();
|
||||
let client = get_kinovi_web_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
@@ -102,7 +102,7 @@ mod tests {
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateAudioResponse::Seedance2Pro(p) => {
|
||||
GenerateAudioResponse::KinoviWeb(p) => {
|
||||
println!("task_id={}, order_id={}", p.task_id, p.order_id);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
|
||||
+1
-1
@@ -168,7 +168,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateAudioRequestBuilder {
|
||||
GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoRemix,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("make this electronic".to_string()),
|
||||
style_prompt: Some("EDM style".to_string()),
|
||||
audio_references: Some(AudioListRef::Urls(vec!["https://example.com/a.mp3".to_string()])),
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_remix::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_remix::{
|
||||
GenerateSunoRemixRequest, KinoviSunoRemixSource,
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ impl KinoviSunoRemixCostState {
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> AudioGenerationCostEstimate {
|
||||
// Cost math is owned by seedance2pro_client's binding — the router just
|
||||
// Cost math is owned by kinovi_web_client's binding — the router just
|
||||
// forwards the result so router cost ≡ binding cost by construction.
|
||||
let pricing_request = GenerateSunoRemixRequest {
|
||||
prompt: String::new(),
|
||||
@@ -63,7 +63,7 @@ mod tests {
|
||||
fn cost_from_draft_is_seven_cents() {
|
||||
let builder = GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoRemix,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("make this electronic".to_string()),
|
||||
audio_references: Some(AudioListRef::Urls(vec!["https://example.com/a.mp3".to_string()])),
|
||||
..Default::default()
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_remix::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_remix::{
|
||||
GenerateSunoRemixRequest, KinoviSunoRemixSource,
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ impl KinoviSunoRemixDraftState {
|
||||
&mut self,
|
||||
draft_context: &AudioGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviSunoRemixRequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let audio_source = self.audio_source.take().ok_or_else(|| {
|
||||
|
||||
+19
-19
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_remix::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_remix::{
|
||||
generate_suno_remix, GenerateSunoRemixArgs, GenerateSunoRemixRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_audio::generate_audio_response::{
|
||||
GenerateAudioResponse, Seedance2proAudioResponsePayload,
|
||||
GenerateAudioResponse, KinoviWebAudioResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -17,7 +17,7 @@ pub struct KinoviSunoRemixRequestState {
|
||||
}
|
||||
|
||||
impl KinoviSunoRemixRequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSunoRemixArgs {
|
||||
@@ -28,9 +28,9 @@ impl KinoviSunoRemixRequestState {
|
||||
|
||||
let response = generate_suno_remix(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateAudioResponse::Seedance2Pro(Seedance2proAudioResponsePayload {
|
||||
Ok(GenerateAudioResponse::KinoviWeb(KinoviWebAudioResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
}))
|
||||
@@ -39,16 +39,16 @@ impl KinoviSunoRemixRequestState {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use seedance2pro_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use kinovi_web_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use kinovi_web_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use test_utils::test_file_path::test_file_path;
|
||||
|
||||
use crate::api::audio_list_ref::AudioListRef;
|
||||
use crate::api::router_audio_model::RouterAudioModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_audio::audio_generation_draft_context::AudioGenerationDraftContext;
|
||||
use crate::generate::generate_audio::audio_generation_draft_or_request::AudioGenerationDraftOrRequest;
|
||||
use crate::generate::generate_audio::generate_audio_request_builder::GenerateAudioRequestBuilder;
|
||||
@@ -59,13 +59,13 @@ mod tests {
|
||||
#[tokio::test]
|
||||
#[ignore] // Sends a real generation to Kinovi; costs credits. Requires a local audio file.
|
||||
async fn remix_uploaded_audio() {
|
||||
let client = get_seedance2pro_client();
|
||||
let client = get_kinovi_web_client();
|
||||
let audio_url = upload_test_audio(&client).await;
|
||||
println!("Uploaded audio: {}", audio_url);
|
||||
|
||||
let builder = GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoRemix,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("Make this electronic".to_string()),
|
||||
style_prompt: Some("EDM style".to_string()),
|
||||
audio_references: Some(AudioListRef::Urls(vec![audio_url])),
|
||||
@@ -73,17 +73,17 @@ mod tests {
|
||||
};
|
||||
|
||||
let response = run_pipeline(&client, builder).await;
|
||||
assert!(matches!(response, GenerateAudioResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateAudioResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn get_seedance2pro_client() -> RouterClient {
|
||||
fn get_kinovi_web_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
async fn run_pipeline(client: &RouterClient, builder: GenerateAudioRequestBuilder) -> GenerateAudioResponse {
|
||||
@@ -102,7 +102,7 @@ mod tests {
|
||||
let response = request.send_request(client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateAudioResponse::Seedance2Pro(p) => {
|
||||
GenerateAudioResponse::KinoviWeb(p) => {
|
||||
println!("task_id={}, order_id={}", p.task_id, p.order_id);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
@@ -112,7 +112,7 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn upload_test_audio(client: &RouterClient) -> String {
|
||||
let session = &client.get_seedance2pro_client_ref().expect("seedance2pro client").session;
|
||||
let session = &client.get_kinovi_web_client_ref().expect("kinovi_web client").session;
|
||||
|
||||
let audio_path = test_file_path(TEST_AUDIO_PATH).expect("test audio should exist");
|
||||
let audio_bytes = std::fs::read(&audio_path).expect("read test audio");
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateAudioRequestBuilder {
|
||||
GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoSample,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("mystical RPG adventure".to_string()),
|
||||
style_prompt: Some("fantasy video game".to_string()),
|
||||
audio_references: Some(AudioListRef::Urls(vec!["https://example.com/a.aac".to_string()])),
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_sample::GenerateSunoSampleRequest;
|
||||
use kinovi_web_client::generate::audio::generate_suno_sample::GenerateSunoSampleRequest;
|
||||
|
||||
use crate::generate::generate_audio::audio_generation_cost_estimate::AudioGenerationCostEstimate;
|
||||
use crate::generate::generate_audio::providers::kinovi::suno_sample::draft::KinoviSunoSampleDraftState;
|
||||
@@ -18,7 +18,7 @@ impl KinoviSunoSampleCostState {
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> AudioGenerationCostEstimate {
|
||||
// Cost math is owned by seedance2pro_client's binding — the router just
|
||||
// Cost math is owned by kinovi_web_client's binding — the router just
|
||||
// forwards the result so router cost ≡ binding cost by construction.
|
||||
let pricing_request = GenerateSunoSampleRequest {
|
||||
prompt: String::new(),
|
||||
@@ -63,7 +63,7 @@ mod tests {
|
||||
fn cost_from_draft_is_seven_cents() {
|
||||
let builder = GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoSample,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("mystical RPG adventure".to_string()),
|
||||
audio_references: Some(AudioListRef::Urls(vec!["https://example.com/a.aac".to_string()])),
|
||||
..Default::default()
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_sample::GenerateSunoSampleRequest;
|
||||
use kinovi_web_client::generate::audio::generate_suno_sample::GenerateSunoSampleRequest;
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::generate::generate_audio::audio_generation_draft_context::AudioGenerationDraftContext;
|
||||
@@ -34,7 +34,7 @@ impl KinoviSunoSampleDraftState {
|
||||
&mut self,
|
||||
draft_context: &AudioGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviSunoSampleRequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let audio_source = self.audio_source.take().ok_or_else(|| {
|
||||
|
||||
+19
-19
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_sample::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_sample::{
|
||||
generate_suno_sample, GenerateSunoSampleArgs, GenerateSunoSampleRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_audio::generate_audio_response::{
|
||||
GenerateAudioResponse, Seedance2proAudioResponsePayload,
|
||||
GenerateAudioResponse, KinoviWebAudioResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -17,7 +17,7 @@ pub struct KinoviSunoSampleRequestState {
|
||||
}
|
||||
|
||||
impl KinoviSunoSampleRequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSunoSampleArgs {
|
||||
@@ -28,9 +28,9 @@ impl KinoviSunoSampleRequestState {
|
||||
|
||||
let response = generate_suno_sample(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateAudioResponse::Seedance2Pro(Seedance2proAudioResponsePayload {
|
||||
Ok(GenerateAudioResponse::KinoviWeb(KinoviWebAudioResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
}))
|
||||
@@ -39,16 +39,16 @@ impl KinoviSunoSampleRequestState {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use seedance2pro_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use kinovi_web_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use kinovi_web_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use test_utils::test_file_path::test_file_path;
|
||||
|
||||
use crate::api::audio_list_ref::AudioListRef;
|
||||
use crate::api::router_audio_model::RouterAudioModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_audio::audio_generation_draft_context::AudioGenerationDraftContext;
|
||||
use crate::generate::generate_audio::audio_generation_draft_or_request::AudioGenerationDraftOrRequest;
|
||||
use crate::generate::generate_audio::generate_audio_request_builder::GenerateAudioRequestBuilder;
|
||||
@@ -59,13 +59,13 @@ mod tests {
|
||||
#[tokio::test]
|
||||
#[ignore] // Sends a real generation to Kinovi; costs credits. Requires a local audio file.
|
||||
async fn sample_instrumental() {
|
||||
let client = get_seedance2pro_client();
|
||||
let client = get_kinovi_web_client();
|
||||
let audio_url = upload_test_audio(&client).await;
|
||||
println!("Uploaded audio: {}", audio_url);
|
||||
|
||||
let builder = GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoSample,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("Mystical RPG adventure, make it have a grand climax".to_string()),
|
||||
style_prompt: Some("Fantasy video game score".to_string()),
|
||||
is_instrumental: Some(true),
|
||||
@@ -74,17 +74,17 @@ mod tests {
|
||||
};
|
||||
|
||||
let response = run_pipeline(&client, builder).await;
|
||||
assert!(matches!(response, GenerateAudioResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateAudioResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn get_seedance2pro_client() -> RouterClient {
|
||||
fn get_kinovi_web_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
async fn run_pipeline(client: &RouterClient, builder: GenerateAudioRequestBuilder) -> GenerateAudioResponse {
|
||||
@@ -103,7 +103,7 @@ mod tests {
|
||||
let response = request.send_request(client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateAudioResponse::Seedance2Pro(p) => {
|
||||
GenerateAudioResponse::KinoviWeb(p) => {
|
||||
println!("task_id={}, order_id={}", p.task_id, p.order_id);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
@@ -113,7 +113,7 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn upload_test_audio(client: &RouterClient) -> String {
|
||||
let session = &client.get_seedance2pro_client_ref().expect("seedance2pro client").session;
|
||||
let session = &client.get_kinovi_web_client_ref().expect("kinovi_web client").session;
|
||||
|
||||
let audio_path = test_file_path(TEST_AUDIO_PATH).expect("test audio should exist");
|
||||
let audio_bytes = std::fs::read(&audio_path).expect("read test audio");
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use enums::common::generation::common_musical_key::CommonMusicalKey;
|
||||
use seedance2pro_client::generate::audio::generate_suno_sound::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_sound::{
|
||||
GenerateSunoSoundRequest, KinoviSunoSoundKey, KinoviSunoSoundType,
|
||||
};
|
||||
|
||||
@@ -201,7 +201,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateAudioRequestBuilder {
|
||||
GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoSounds,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("rain sound effects".to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_sound::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_sound::{
|
||||
GenerateSunoSoundRequest, KinoviSunoSoundKey, KinoviSunoSoundType,
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ impl KinoviSunoSoundsCostState {
|
||||
}
|
||||
|
||||
pub fn estimate_cost(&self) -> AudioGenerationCostEstimate {
|
||||
// Cost math is owned by seedance2pro_client's binding — the router just
|
||||
// Cost math is owned by kinovi_web_client's binding — the router just
|
||||
// forwards the result so router cost ≡ binding cost by construction.
|
||||
let pricing_request = GenerateSunoSoundRequest {
|
||||
prompt: String::new(),
|
||||
@@ -57,7 +57,7 @@ mod tests {
|
||||
fn cost_from_built_request_is_two_cents() {
|
||||
let builder = GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoSounds,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("thunder".to_string()),
|
||||
is_loopable: Some(true),
|
||||
bpm: Some(60),
|
||||
|
||||
+17
-17
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::audio::generate_suno_sound::{
|
||||
use kinovi_web_client::generate::audio::generate_suno_sound::{
|
||||
generate_suno_sound, GenerateSunoSoundArgs, GenerateSunoSoundRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_audio::generate_audio_response::{
|
||||
GenerateAudioResponse, Seedance2proAudioResponsePayload,
|
||||
GenerateAudioResponse, KinoviWebAudioResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -16,7 +16,7 @@ pub struct KinoviSunoSoundsRequestState {
|
||||
}
|
||||
|
||||
impl KinoviSunoSoundsRequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateAudioResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSunoSoundArgs {
|
||||
@@ -27,9 +27,9 @@ impl KinoviSunoSoundsRequestState {
|
||||
|
||||
let response = generate_suno_sound(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateAudioResponse::Seedance2Pro(Seedance2proAudioResponsePayload {
|
||||
Ok(GenerateAudioResponse::KinoviWeb(KinoviWebAudioResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
}))
|
||||
@@ -39,12 +39,12 @@ impl KinoviSunoSoundsRequestState {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use enums::common::generation::common_musical_key::CommonMusicalKey;
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
|
||||
use crate::api::router_audio_model::RouterAudioModel;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_audio::audio_generation_draft_or_request::AudioGenerationDraftOrRequest;
|
||||
use crate::generate::generate_audio::generate_audio_request_builder::GenerateAudioRequestBuilder;
|
||||
use crate::generate::generate_audio::generate_audio_response::GenerateAudioResponse;
|
||||
@@ -56,7 +56,7 @@ mod tests {
|
||||
prompt: Some("A heavy castle door creaking open".to_string()),
|
||||
..suno_sounds_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateAudioResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateAudioResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ mod tests {
|
||||
musical_key: Some(CommonMusicalKey::AMinor),
|
||||
..suno_sounds_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateAudioResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateAudioResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -79,20 +79,20 @@ mod tests {
|
||||
fn suno_sounds_builder() -> GenerateAudioRequestBuilder {
|
||||
GenerateAudioRequestBuilder {
|
||||
model: RouterAudioModel::SunoSounds,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_seedance2pro_client() -> RouterClient {
|
||||
fn get_kinovi_web_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
async fn run_pipeline(builder: GenerateAudioRequestBuilder) -> GenerateAudioResponse {
|
||||
let client = get_seedance2pro_client();
|
||||
let client = get_kinovi_web_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let request = match draft_or_request {
|
||||
@@ -103,7 +103,7 @@ mod tests {
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateAudioResponse::Seedance2Pro(p) => {
|
||||
GenerateAudioResponse::KinoviWeb(p) => {
|
||||
println!("task_id={}, order_id={}", p.task_id, p.order_id);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
|
||||
+6
-6
@@ -146,12 +146,12 @@ impl GenerateImageRequestBuilder {
|
||||
(RouterProvider::Fal, RouterImageModel::QwenEdit2511Angles) => build_fal_qwen_edit_2511_angles(self),
|
||||
(RouterProvider::Fal, RouterImageModel::Flux2LoraAngles) => build_fal_flux_2_lora_angles(self),
|
||||
|
||||
// Kinovi / Seedance2Pro (Midjourney + Seedream)
|
||||
(RouterProvider::Seedance2Pro, RouterImageModel::Midjourney7) => build_kinovi_midjourney_7(self),
|
||||
(RouterProvider::Seedance2Pro, RouterImageModel::Midjourney7Niji) => build_kinovi_midjourney_7_niji(self),
|
||||
(RouterProvider::Seedance2Pro, RouterImageModel::Midjourney8) => build_kinovi_midjourney_8(self),
|
||||
(RouterProvider::Seedance2Pro, RouterImageModel::Seedream5p0Pro) => build_kinovi_seedream_5p0_pro(self),
|
||||
(RouterProvider::Seedance2Pro, RouterImageModel::Seedream5p0ProUltra) => build_kinovi_seedream_5p0_pro(self),
|
||||
// Kinovi / KinoviWeb (Midjourney + Seedream)
|
||||
(RouterProvider::KinoviWeb, RouterImageModel::Midjourney7) => build_kinovi_midjourney_7(self),
|
||||
(RouterProvider::KinoviWeb, RouterImageModel::Midjourney7Niji) => build_kinovi_midjourney_7_niji(self),
|
||||
(RouterProvider::KinoviWeb, RouterImageModel::Midjourney8) => build_kinovi_midjourney_8(self),
|
||||
(RouterProvider::KinoviWeb, RouterImageModel::Seedream5p0Pro) => build_kinovi_seedream_5p0_pro(self),
|
||||
(RouterProvider::KinoviWeb, RouterImageModel::Seedream5p0ProUltra) => build_kinovi_seedream_5p0_pro(self),
|
||||
|
||||
_ => self.unsupported_provider_and_model(),
|
||||
}
|
||||
|
||||
+6
-6
@@ -25,11 +25,11 @@ pub struct FalImageResponsePayload {
|
||||
pub maybe_outbound_request: Option<Arc<dyn Debug + Send + Sync>>,
|
||||
}
|
||||
|
||||
/// Response from the Seedance2Pro/Kinovi provider (used for Midjourney
|
||||
/// image generation). Mirrors `Seedance2proVideoResponsePayload` on the
|
||||
/// Response from the KinoviWeb/Kinovi provider (used for Midjourney
|
||||
/// image generation). Mirrors `KinoviWebVideoResponsePayload` on the
|
||||
/// video side.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Seedance2proImageResponsePayload {
|
||||
pub struct KinoviWebImageResponsePayload {
|
||||
pub order_id: String,
|
||||
pub task_id: String,
|
||||
pub maybe_order_ids: Option<Vec<String>>,
|
||||
@@ -40,7 +40,7 @@ pub struct Seedance2proImageResponsePayload {
|
||||
pub enum GenerateImageResponse {
|
||||
Artcraft(ArtcraftImageResponsePayload),
|
||||
Fal(FalImageResponsePayload),
|
||||
Seedance2Pro(Seedance2proImageResponsePayload),
|
||||
KinoviWeb(KinoviWebImageResponsePayload),
|
||||
}
|
||||
|
||||
impl GenerateImageResponse {
|
||||
@@ -58,9 +58,9 @@ impl GenerateImageResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_payload(&self) -> Option<Seedance2proImageResponsePayload> {
|
||||
pub fn get_kinovi_web_payload(&self) -> Option<KinoviWebImageResponsePayload> {
|
||||
match self {
|
||||
Self::Seedance2Pro(p) => Some(p.clone()),
|
||||
Self::KinoviWeb(p) => Some(p.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -17,7 +17,7 @@ use crate::generate::generate_image::providers::kinovi::seedream_5p0_pro::draft:
|
||||
/// Drafts hold validated/planned parameters but may still need media
|
||||
/// resolution (e.g. uploading local files to a provider) before they can
|
||||
/// be sent. Today only the Kinovi models need this — when the caller
|
||||
/// supplies reference images, we must upload them to the Seedance2Pro
|
||||
/// supplies reference images, we must upload them to the KinoviWeb
|
||||
/// CDN before sending the request.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ImageGenerationDraftRequest {
|
||||
@@ -30,10 +30,10 @@ pub enum ImageGenerationDraftRequest {
|
||||
impl ImageGenerationDraftRequest {
|
||||
pub fn get_provider(&self) -> RouterProvider {
|
||||
match self {
|
||||
Self::KinoviMidjourney7(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviMidjourney7Niji(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviMidjourney8(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedream5p0Pro(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviMidjourney7(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviMidjourney7Niji(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviMidjourney8(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedream5p0Pro(_) => RouterProvider::KinoviWeb,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -4,21 +4,21 @@ use std::fmt::Debug;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
|
||||
/// Context passed when finalizing an image draft into a concrete request.
|
||||
///
|
||||
/// Drafts only exist for providers that need to upload assets before
|
||||
/// sending the request. Today that's the Seedance2Pro/Kinovi pipeline
|
||||
/// sending the request. Today that's the KinoviWeb/Kinovi pipeline
|
||||
/// (used for Midjourney image generation with reference images); other
|
||||
/// providers — Artcraft, Fal — accept image URLs directly and skip the
|
||||
/// draft phase entirely.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ImageGenerationDraftContext<'a> {
|
||||
/// Optional: the router client, needed for providers that require
|
||||
/// authenticated calls (e.g. Seedance2Pro/Kinovi uploads).
|
||||
/// authenticated calls (e.g. KinoviWeb/Kinovi uploads).
|
||||
pub client: Option<&'a RouterClient>,
|
||||
|
||||
/// Optional: a map of Media File Tokens to their ArtCraft URLs.
|
||||
@@ -29,9 +29,9 @@ pub struct ImageGenerationDraftContext<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ImageGenerationDraftContext<'a> {
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ArtcraftRouterError> {
|
||||
pub fn get_kinovi_web_client_ref(&self) -> Result<&RouterKinoviWebClient, ArtcraftRouterError> {
|
||||
let client = self.client.ok_or(ArtcraftRouterError::Client(ClientError::RouterClientNotProvided))?;
|
||||
client.get_seedance2pro_client_ref()
|
||||
client.get_kinovi_web_client_ref()
|
||||
.map_err(ArtcraftRouterError::Client)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -123,7 +123,7 @@ pub enum ImageGenerationRequest {
|
||||
FalQwenEdit2511Angles(FalQwenEdit2511AnglesRequestState),
|
||||
FalFlux2LoraAngles(FalFlux2LoraAnglesRequestState),
|
||||
|
||||
// ── Kinovi / Seedance2Pro provider (Midjourney + Seedream image generation) ──
|
||||
// ── Kinovi / KinoviWeb provider (Midjourney + Seedream image generation) ──
|
||||
KinoviMidjourney7(KinoviMidjourney7RequestState),
|
||||
KinoviMidjourney7Niji(KinoviMidjourney7NijiRequestState),
|
||||
KinoviMidjourney8(KinoviMidjourney8RequestState),
|
||||
@@ -170,10 +170,10 @@ impl ImageGenerationRequest {
|
||||
Self::FalQwenEdit2511Angles(_) => RouterProvider::Fal,
|
||||
Self::FalFlux2LoraAngles(_) => RouterProvider::Fal,
|
||||
|
||||
Self::KinoviMidjourney7(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviMidjourney7Niji(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviMidjourney8(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedream5p0Pro(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviMidjourney7(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviMidjourney7Niji(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviMidjourney8(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedream5p0Pro(_) => RouterProvider::KinoviWeb,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,24 +416,24 @@ impl ImageGenerationRequest {
|
||||
request.send(fal_client).await
|
||||
}
|
||||
|
||||
// ── Kinovi / Seedance2Pro (Midjourney + Seedream) ──
|
||||
// ── Kinovi / KinoviWeb (Midjourney + Seedream) ──
|
||||
Self::KinoviMidjourney7(request) => {
|
||||
let seedance_client = client.get_seedance2pro_client_ref()
|
||||
let seedance_client = client.get_kinovi_web_client_ref()
|
||||
.map_err(ArtcraftRouterError::Client)?;
|
||||
request.send(seedance_client).await
|
||||
}
|
||||
Self::KinoviMidjourney7Niji(request) => {
|
||||
let seedance_client = client.get_seedance2pro_client_ref()
|
||||
let seedance_client = client.get_kinovi_web_client_ref()
|
||||
.map_err(ArtcraftRouterError::Client)?;
|
||||
request.send(seedance_client).await
|
||||
}
|
||||
Self::KinoviMidjourney8(request) => {
|
||||
let seedance_client = client.get_seedance2pro_client_ref()
|
||||
let seedance_client = client.get_kinovi_web_client_ref()
|
||||
.map_err(ArtcraftRouterError::Client)?;
|
||||
request.send(seedance_client).await
|
||||
}
|
||||
Self::KinoviSeedream5p0Pro(request) => {
|
||||
let seedance_client = client.get_seedance2pro_client_ref()
|
||||
let seedance_client = client.get_kinovi_web_client_ref()
|
||||
.map_err(ArtcraftRouterError::Client)?;
|
||||
request.send(seedance_client).await
|
||||
}
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn price_covers_kinovi_cost() {
|
||||
use seedance2pro_client::generate::image::generate_seedream_5p0_pro::{
|
||||
use kinovi_web_client::generate::image::generate_seedream_5p0_pro::{
|
||||
KinoviSeedream5p0ProBatchCount, KinoviSeedream5p0ProResolution,
|
||||
};
|
||||
use crate::generate::generate_image::providers::kinovi::seedream_5p0_pro::cost::KinoviSeedream5p0ProCostState;
|
||||
|
||||
+1
-1
@@ -169,7 +169,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn price_covers_kinovi_cost() {
|
||||
use seedance2pro_client::generate::image::generate_seedream_5p0_pro::{
|
||||
use kinovi_web_client::generate::image::generate_seedream_5p0_pro::{
|
||||
KinoviSeedream5p0ProBatchCount, KinoviSeedream5p0ProResolution,
|
||||
};
|
||||
use crate::generate::generate_image::providers::kinovi::seedream_5p0_pro::cost::KinoviSeedream5p0ProCostState;
|
||||
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::{
|
||||
GenerateMidjourneyV7AspectRatio, GenerateMidjourneyV7Quality,
|
||||
GenerateMidjourneyV7Request, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
@@ -50,7 +50,7 @@ pub fn build_kinovi_midjourney_7(
|
||||
}
|
||||
|
||||
// Image inputs present → must go through the draft phase so we can
|
||||
// upload each image to the Seedance2Pro CDN before sending.
|
||||
// upload each image to the KinoviWeb CDN before sending.
|
||||
let draft = KinoviMidjourney7DraftState {
|
||||
prompt,
|
||||
aspect_ratio,
|
||||
@@ -159,7 +159,7 @@ pub(crate) fn plan_batch_count(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::KinoviMidjourneyBatchCount;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::KinoviMidjourneyBatchCount;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -178,7 +178,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney7,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a corgi astronaut".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
@@ -291,7 +291,7 @@ mod tests {
|
||||
|
||||
mod aspect_ratio_tests {
|
||||
use super::*;
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::GenerateMidjourneyV7AspectRatio as Ar;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::GenerateMidjourneyV7AspectRatio as Ar;
|
||||
|
||||
fn aspect_ratio_for(input: Option<RouterAspectRatio>) -> Ar {
|
||||
let builder = GenerateImageRequestBuilder { aspect_ratio: input, ..base_builder() };
|
||||
@@ -334,7 +334,7 @@ mod tests {
|
||||
|
||||
mod quality_tests {
|
||||
use super::*;
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::GenerateMidjourneyV7Quality as Q;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::GenerateMidjourneyV7Quality as Q;
|
||||
|
||||
fn quality_for(input: Option<RouterQuality>) -> Option<Q> {
|
||||
let builder = GenerateImageRequestBuilder { quality: input, ..base_builder() };
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::{
|
||||
GenerateMidjourneyV7AspectRatio, GenerateMidjourneyV7Request, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
|
||||
@@ -171,7 +171,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney7,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("test".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::{
|
||||
GenerateMidjourneyV7AspectRatio, GenerateMidjourneyV7Quality,
|
||||
GenerateMidjourneyV7Request, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
@@ -17,7 +17,7 @@ pub struct KinoviMidjourney7DraftState {
|
||||
pub quality: Option<GenerateMidjourneyV7Quality>,
|
||||
pub batch_count: KinoviMidjourneyBatchCount,
|
||||
|
||||
/// Image references that still need to be uploaded to Seedance2Pro CDN
|
||||
/// Image references that still need to be uploaded to KinoviWeb CDN
|
||||
/// before the request is sendable. `None` once `to_request()` has run.
|
||||
pub unhandled_request_state: Option<KinoviMidjourney7RemainingItems>,
|
||||
}
|
||||
@@ -32,7 +32,7 @@ impl KinoviMidjourney7DraftState {
|
||||
&mut self,
|
||||
draft_context: &ImageGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviMidjourney7RequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut reference_image_urls = None;
|
||||
@@ -63,10 +63,10 @@ impl KinoviMidjourney7DraftState {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::GenerateMidjourneyV7AspectRatio;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::GenerateMidjourneyV7AspectRatio;
|
||||
|
||||
// Note: most behavioral tests live in `build.rs` and `cost.rs`. The
|
||||
// `to_request()` method requires a live Seedance2Pro client to upload
|
||||
// `to_request()` method requires a live KinoviWeb client to upload
|
||||
// images, so its end-to-end behavior is exercised by the `request.rs`
|
||||
// #[ignore]'d integration tests rather than unit-tested here.
|
||||
|
||||
|
||||
+15
-15
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7::{
|
||||
generate_midjourney_v7, GenerateMidjourneyV7Args, GenerateMidjourneyV7Request,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_image::generate_image_response::{
|
||||
GenerateImageResponse, Seedance2proImageResponsePayload,
|
||||
GenerateImageResponse, KinoviWebImageResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -18,7 +18,7 @@ pub struct KinoviMidjourney7RequestState {
|
||||
impl KinoviMidjourney7RequestState {
|
||||
pub async fn send(
|
||||
&self,
|
||||
client: &RouterSeedance2ProClient,
|
||||
client: &RouterKinoviWebClient,
|
||||
) -> Result<GenerateImageResponse, ArtcraftRouterError> {
|
||||
let args = GenerateMidjourneyV7Args {
|
||||
session: &client.session,
|
||||
@@ -28,9 +28,9 @@ impl KinoviMidjourney7RequestState {
|
||||
|
||||
let response = generate_midjourney_v7(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateImageResponse::Seedance2Pro(Seedance2proImageResponsePayload {
|
||||
Ok(GenerateImageResponse::KinoviWeb(KinoviWebImageResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
@@ -43,7 +43,7 @@ impl KinoviMidjourney7RequestState {
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -52,7 +52,7 @@ mod tests {
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_image::generate_image_request_builder::GenerateImageRequestBuilder;
|
||||
use crate::generate::generate_image::generate_image_response::GenerateImageResponse;
|
||||
use crate::generate::generate_image::image_generation_draft::ImageGenerationDraftRequest;
|
||||
@@ -64,7 +64,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney7,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a serene mountain lake at sunrise, cinematic".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
@@ -82,9 +82,9 @@ mod tests {
|
||||
|
||||
fn get_kinovi_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -98,9 +98,9 @@ mod tests {
|
||||
_ => panic!("expected direct Request for text-to-image"),
|
||||
};
|
||||
|
||||
let response = request.send(client.get_seedance2pro_client_ref().unwrap())
|
||||
let response = request.send(client.get_kinovi_web_client_ref().unwrap())
|
||||
.await.expect("send should succeed");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("v7 t2i — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
@@ -135,7 +135,7 @@ mod tests {
|
||||
.finalize(ctx).await.expect("finalize");
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("v7 with ref — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::{
|
||||
GenerateMidjourneyV7NijiAspectRatio, GenerateMidjourneyV7NijiQuality,
|
||||
GenerateMidjourneyV7NijiRequest, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
@@ -147,7 +147,7 @@ pub(crate) fn plan_batch_count(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::KinoviMidjourneyBatchCount;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::KinoviMidjourneyBatchCount;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -166,7 +166,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney7Niji,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a magical anime forest scene".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
@@ -235,7 +235,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_passes_through_to_niji_enum() {
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::GenerateMidjourneyV7NijiAspectRatio as Ar;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::GenerateMidjourneyV7NijiAspectRatio as Ar;
|
||||
let builder = GenerateImageRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::WideTwentyOneByNine),
|
||||
..base_builder()
|
||||
@@ -249,7 +249,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn quality_high_maps_to_full() {
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::GenerateMidjourneyV7NijiQuality as Q;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::GenerateMidjourneyV7NijiQuality as Q;
|
||||
let builder = GenerateImageRequestBuilder {
|
||||
quality: Some(RouterQuality::High),
|
||||
..base_builder()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::{
|
||||
GenerateMidjourneyV7NijiAspectRatio, GenerateMidjourneyV7NijiRequest, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::{
|
||||
GenerateMidjourneyV7NijiAspectRatio, GenerateMidjourneyV7NijiQuality,
|
||||
GenerateMidjourneyV7NijiRequest, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
@@ -29,7 +29,7 @@ impl KinoviMidjourney7NijiDraftState {
|
||||
&mut self,
|
||||
draft_context: &ImageGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviMidjourney7NijiRequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut reference_image_urls = None;
|
||||
|
||||
+15
-15
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v7_niji::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v7_niji::{
|
||||
generate_midjourney_v7_niji, GenerateMidjourneyV7NijiArgs, GenerateMidjourneyV7NijiRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_image::generate_image_response::{
|
||||
GenerateImageResponse, Seedance2proImageResponsePayload,
|
||||
GenerateImageResponse, KinoviWebImageResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -17,7 +17,7 @@ pub struct KinoviMidjourney7NijiRequestState {
|
||||
impl KinoviMidjourney7NijiRequestState {
|
||||
pub async fn send(
|
||||
&self,
|
||||
client: &RouterSeedance2ProClient,
|
||||
client: &RouterKinoviWebClient,
|
||||
) -> Result<GenerateImageResponse, ArtcraftRouterError> {
|
||||
let args = GenerateMidjourneyV7NijiArgs {
|
||||
session: &client.session,
|
||||
@@ -27,9 +27,9 @@ impl KinoviMidjourney7NijiRequestState {
|
||||
|
||||
let response = generate_midjourney_v7_niji(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateImageResponse::Seedance2Pro(Seedance2proImageResponsePayload {
|
||||
Ok(GenerateImageResponse::KinoviWeb(KinoviWebImageResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
@@ -42,7 +42,7 @@ impl KinoviMidjourney7NijiRequestState {
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -51,7 +51,7 @@ mod tests {
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_image::generate_image_request_builder::GenerateImageRequestBuilder;
|
||||
use crate::generate::generate_image::image_generation_draft::ImageGenerationDraftRequest;
|
||||
use crate::generate::generate_image::image_generation_draft_context::ImageGenerationDraftContext;
|
||||
@@ -62,7 +62,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney7Niji,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a magical anime fox spirit in a moonlit bamboo forest".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
@@ -80,9 +80,9 @@ mod tests {
|
||||
|
||||
fn get_kinovi_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -96,9 +96,9 @@ mod tests {
|
||||
_ => panic!("expected direct Request for text-to-image"),
|
||||
};
|
||||
|
||||
let response = request.send(client.get_seedance2pro_client_ref().unwrap())
|
||||
let response = request.send(client.get_kinovi_web_client_ref().unwrap())
|
||||
.await.expect("send should succeed");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("v7-niji t2i — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
@@ -133,7 +133,7 @@ mod tests {
|
||||
.finalize(ctx).await.expect("finalize");
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("v7-niji with ref — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v8::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v8::{
|
||||
GenerateMidjourneyV8AspectRatio, GenerateMidjourneyV8Quality,
|
||||
GenerateMidjourneyV8Request, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
@@ -147,7 +147,7 @@ pub(crate) fn plan_batch_count(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v8::KinoviMidjourneyBatchCount;
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v8::KinoviMidjourneyBatchCount;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -164,7 +164,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney8,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a spooky horror scene".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v8::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v8::{
|
||||
GenerateMidjourneyV8AspectRatio, GenerateMidjourneyV8Request, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v8::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v8::{
|
||||
GenerateMidjourneyV8AspectRatio, GenerateMidjourneyV8Quality,
|
||||
GenerateMidjourneyV8Request, KinoviMidjourneyBatchCount,
|
||||
};
|
||||
@@ -29,7 +29,7 @@ impl KinoviMidjourney8DraftState {
|
||||
&mut self,
|
||||
draft_context: &ImageGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviMidjourney8RequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut reference_image_urls = None;
|
||||
|
||||
+15
-15
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::image::generate_midjourney_v8::{
|
||||
use kinovi_web_client::generate::image::generate_midjourney_v8::{
|
||||
generate_midjourney_v8, GenerateMidjourneyV8Args, GenerateMidjourneyV8Request,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_image::generate_image_response::{
|
||||
GenerateImageResponse, Seedance2proImageResponsePayload,
|
||||
GenerateImageResponse, KinoviWebImageResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -17,7 +17,7 @@ pub struct KinoviMidjourney8RequestState {
|
||||
impl KinoviMidjourney8RequestState {
|
||||
pub async fn send(
|
||||
&self,
|
||||
client: &RouterSeedance2ProClient,
|
||||
client: &RouterKinoviWebClient,
|
||||
) -> Result<GenerateImageResponse, ArtcraftRouterError> {
|
||||
let args = GenerateMidjourneyV8Args {
|
||||
session: &client.session,
|
||||
@@ -27,9 +27,9 @@ impl KinoviMidjourney8RequestState {
|
||||
|
||||
let response = generate_midjourney_v8(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateImageResponse::Seedance2Pro(Seedance2proImageResponsePayload {
|
||||
Ok(GenerateImageResponse::KinoviWeb(KinoviWebImageResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
@@ -42,7 +42,7 @@ impl KinoviMidjourney8RequestState {
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -51,7 +51,7 @@ mod tests {
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_image::generate_image_request_builder::GenerateImageRequestBuilder;
|
||||
use crate::generate::generate_image::image_generation_draft::ImageGenerationDraftRequest;
|
||||
use crate::generate::generate_image::image_generation_draft_context::ImageGenerationDraftContext;
|
||||
@@ -62,7 +62,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Midjourney8,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a spooky horror scene with ghosts in an abandoned mansion".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
@@ -80,9 +80,9 @@ mod tests {
|
||||
|
||||
fn get_kinovi_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -96,9 +96,9 @@ mod tests {
|
||||
_ => panic!("expected direct Request for text-to-image"),
|
||||
};
|
||||
|
||||
let response = request.send(client.get_seedance2pro_client_ref().unwrap())
|
||||
let response = request.send(client.get_kinovi_web_client_ref().unwrap())
|
||||
.await.expect("send should succeed");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("v8 t2i — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
@@ -133,7 +133,7 @@ mod tests {
|
||||
.finalize(ctx).await.expect("finalize");
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("v8 with ref — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,21 +1,21 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_image::providers::kinovi::upload::upload_to_seedance2pro;
|
||||
use crate::generate::generate_image::providers::kinovi::upload::upload_to_kinovi_web;
|
||||
|
||||
/// Resolve an [`ImageListRef`] to a list of public Seedance2Pro CDN URLs,
|
||||
/// Resolve an [`ImageListRef`] to a list of public KinoviWeb CDN URLs,
|
||||
/// uploading each image one at a time. Preserves order.
|
||||
///
|
||||
/// Returns `Ok(None)` when the input is `None` or an empty list — the
|
||||
/// caller can then omit the field from the wire request entirely. (The
|
||||
/// downstream Midjourney API rejects empty arrays.)
|
||||
pub(crate) async fn resolve_and_upload_image_list(
|
||||
session: &Seedance2ProSession,
|
||||
session: &KinoviWebSession,
|
||||
list: Option<ImageListRef>,
|
||||
maybe_map: Option<&HashMap<MediaFileToken, String>>,
|
||||
) -> Result<Option<Vec<String>>, ArtcraftRouterError> {
|
||||
@@ -29,7 +29,7 @@ pub(crate) async fn resolve_and_upload_image_list(
|
||||
|
||||
let mut uploaded = Vec::with_capacity(source_urls.len());
|
||||
for url in &source_urls {
|
||||
uploaded.push(upload_to_seedance2pro(session, url).await?);
|
||||
uploaded.push(upload_to_kinovi_web(session, url).await?);
|
||||
}
|
||||
Ok(Some(uploaded))
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_seedream_5p0_pro::{
|
||||
use kinovi_web_client::generate::image::generate_seedream_5p0_pro::{
|
||||
GenerateSeedream5p0ProRequest, KinoviSeedream5p0ProAspectRatio, KinoviSeedream5p0ProBatchCount,
|
||||
KinoviSeedream5p0ProResolution, MAX_REFERENCE_IMAGES,
|
||||
};
|
||||
@@ -67,7 +67,7 @@ fn has_image_inputs(image_inputs: Option<&ImageListRef>) -> bool {
|
||||
}
|
||||
|
||||
/// Reject over-limit reference lists before any uploads happen — the
|
||||
/// Seedance2Pro client would reject them anyway, but only after the draft
|
||||
/// KinoviWeb client would reject them anyway, but only after the draft
|
||||
/// stage has already uploaded every image to the Kinovi CDN.
|
||||
fn validate_reference_image_count(
|
||||
image_inputs: Option<&ImageListRef>,
|
||||
@@ -191,7 +191,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Seedream5p0Pro,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("an anime girl riding a dinosaur".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: None,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_seedream_5p0_pro::{
|
||||
use kinovi_web_client::generate::image::generate_seedream_5p0_pro::{
|
||||
GenerateSeedream5p0ProRequest, KinoviSeedream5p0ProAspectRatio, KinoviSeedream5p0ProBatchCount,
|
||||
KinoviSeedream5p0ProResolution,
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::image::generate_seedream_5p0_pro::{
|
||||
use kinovi_web_client::generate::image::generate_seedream_5p0_pro::{
|
||||
GenerateSeedream5p0ProRequest, KinoviSeedream5p0ProAspectRatio, KinoviSeedream5p0ProBatchCount,
|
||||
KinoviSeedream5p0ProResolution,
|
||||
};
|
||||
@@ -29,7 +29,7 @@ impl KinoviSeedream5p0ProDraftState {
|
||||
&mut self,
|
||||
draft_context: &ImageGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviSeedream5p0ProRequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut reference_image_urls = None;
|
||||
|
||||
+15
-15
@@ -1,12 +1,12 @@
|
||||
use seedance2pro_client::generate::image::generate_seedream_5p0_pro::{
|
||||
use kinovi_web_client::generate::image::generate_seedream_5p0_pro::{
|
||||
generate_seedream_5p0_pro, GenerateSeedream5p0ProArgs, GenerateSeedream5p0ProRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_image::generate_image_response::{
|
||||
GenerateImageResponse, Seedance2proImageResponsePayload,
|
||||
GenerateImageResponse, KinoviWebImageResponsePayload,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -17,7 +17,7 @@ pub struct KinoviSeedream5p0ProRequestState {
|
||||
impl KinoviSeedream5p0ProRequestState {
|
||||
pub async fn send(
|
||||
&self,
|
||||
client: &RouterSeedance2ProClient,
|
||||
client: &RouterKinoviWebClient,
|
||||
) -> Result<GenerateImageResponse, ArtcraftRouterError> {
|
||||
let args = GenerateSeedream5p0ProArgs {
|
||||
session: &client.session,
|
||||
@@ -27,9 +27,9 @@ impl KinoviSeedream5p0ProRequestState {
|
||||
|
||||
let response = generate_seedream_5p0_pro(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateImageResponse::Seedance2Pro(Seedance2proImageResponsePayload {
|
||||
Ok(GenerateImageResponse::KinoviWeb(KinoviWebImageResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
@@ -42,7 +42,7 @@ impl KinoviSeedream5p0ProRequestState {
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::image_list_ref::ImageListRef;
|
||||
@@ -52,7 +52,7 @@ mod tests {
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use crate::client::request_mismatch_mitigation_strategy::RequestMismatchMitigationStrategy;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_image::generate_image_request_builder::GenerateImageRequestBuilder;
|
||||
use crate::generate::generate_image::image_generation_draft::ImageGenerationDraftRequest;
|
||||
use crate::generate::generate_image::image_generation_draft_context::ImageGenerationDraftContext;
|
||||
@@ -63,7 +63,7 @@ mod tests {
|
||||
fn base_builder() -> GenerateImageRequestBuilder {
|
||||
GenerateImageRequestBuilder {
|
||||
model: RouterImageModel::Seedream5p0Pro,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("an anime girl riding a dinosaur through a neon city".to_string()),
|
||||
image_inputs: None,
|
||||
resolution: Some(RouterResolution::OneK),
|
||||
@@ -81,9 +81,9 @@ mod tests {
|
||||
|
||||
fn get_kinovi_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -97,9 +97,9 @@ mod tests {
|
||||
_ => panic!("expected direct Request for text-to-image"),
|
||||
};
|
||||
|
||||
let response = request.send(client.get_seedance2pro_client_ref().unwrap())
|
||||
let response = request.send(client.get_kinovi_web_client_ref().unwrap())
|
||||
.await.expect("send should succeed");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("seedream 5.0 pro t2i — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
@@ -134,7 +134,7 @@ mod tests {
|
||||
.finalize(ctx).await.expect("finalize");
|
||||
|
||||
let response = request.send_request(&client).await.expect("send_request");
|
||||
let payload = response.get_seedance2pro_payload().expect("expected seedance2pro payload");
|
||||
let payload = response.get_kinovi_web_payload().expect("expected kinovi_web payload");
|
||||
println!("seedream 5.0 pro with ref — task_id={}, order_id={}", payload.task_id, payload.order_id);
|
||||
assert!(!payload.task_id.is_empty());
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use seedance2pro_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use kinovi_web_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use kinovi_web_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use url_utils::extension::extract_extension_from_url::{extract_extension_from_url_str, ExtractExtensions};
|
||||
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
@@ -8,14 +8,14 @@ use crate::errors::provider_error::ProviderError;
|
||||
use crate::utils::download_file::download_file;
|
||||
|
||||
/// Downloads an image from a source URL and re-uploads it to the
|
||||
/// Seedance2Pro/Kinovi CDN. Returns the public URL.
|
||||
/// KinoviWeb/Kinovi CDN. Returns the public URL.
|
||||
///
|
||||
/// This is the image-side twin of `generate_video::providers::kinovi::upload`.
|
||||
/// The actual upload path doesn't care whether the file is a video or an
|
||||
/// image, but keeping the two helpers separate lets each side evolve
|
||||
/// independently and makes intent clear at call sites.
|
||||
pub(crate) async fn upload_to_seedance2pro(
|
||||
session: &Seedance2ProSession,
|
||||
pub(crate) async fn upload_to_kinovi_web(
|
||||
session: &KinoviWebSession,
|
||||
source_url: &str,
|
||||
) -> Result<String, ArtcraftRouterError> {
|
||||
let extension = extract_extension_from_url_str(source_url, &ExtractExtensions::All)
|
||||
@@ -30,7 +30,7 @@ pub(crate) async fn upload_to_seedance2pro(
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
let upload_response = upload_file(UploadFileArgs {
|
||||
upload_url: prepare_response.upload_url,
|
||||
@@ -38,7 +38,7 @@ pub(crate) async fn upload_to_seedance2pro(
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(upload_response.public_url)
|
||||
}
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ mod tests {
|
||||
for provider in [
|
||||
RouterProvider::GmiCloud,
|
||||
RouterProvider::GrokApi,
|
||||
RouterProvider::Seedance2Pro,
|
||||
RouterProvider::KinoviWeb,
|
||||
RouterProvider::WorldLabs,
|
||||
] {
|
||||
let result = builder_with_url_image(provider, RouterMeshModel::Hunyuan3d3).build2();
|
||||
|
||||
+1
-1
@@ -252,7 +252,7 @@ mod tests {
|
||||
RouterProvider::Fal,
|
||||
RouterProvider::GmiCloud,
|
||||
RouterProvider::GrokApi,
|
||||
RouterProvider::Seedance2Pro,
|
||||
RouterProvider::KinoviWeb,
|
||||
] {
|
||||
let result = GenerateSplatRequestBuilder {
|
||||
provider,
|
||||
|
||||
@@ -74,17 +74,17 @@ generate_video/
|
||||
│ ├── seedance_2p0/ ← Seedance 2.0 Pro model (skips draft, returns Request directly)
|
||||
│ │ ├── mod.rs
|
||||
│ │ ├── build.rs ← builder → Request conversion + plan helpers
|
||||
│ │ ├── cost.rs ← standalone cost estimation (independent of seedance2pro_client)
|
||||
│ │ ├── cost.rs ← standalone cost estimation (independent of kinovi_web_client)
|
||||
│ │ └── request.rs ← request state + send() via Artcraft multi-function API
|
||||
│ └── seedance_2p0_fast/ ← Seedance 2.0 Fast model (skips draft, 480p/720p only)
|
||||
│ ├── mod.rs
|
||||
│ ├── build.rs ← builder → Request conversion + plan helpers (no 1080p)
|
||||
│ ├── cost.rs ← standalone cost estimation with Fast-specific pricing
|
||||
│ └── request.rs ← request state + send() via Artcraft omni-gen API
|
||||
└── kinovi/ ← Kinovi/Seedance2Pro provider
|
||||
└── kinovi/ ← Kinovi/KinoviWeb provider
|
||||
├── mod.rs
|
||||
├── resolve.rs ← shared: media token resolution, upload helpers
|
||||
├── upload.rs ← shared: download + re-upload to Seedance2Pro CDN
|
||||
├── upload.rs ← shared: download + re-upload to KinoviWeb CDN
|
||||
├── seedance_2p0/ ← Seedance 2.0 Pro model
|
||||
│ ├── mod.rs
|
||||
│ ├── build.rs ← builder → draft conversion + plan helpers
|
||||
|
||||
+6
-6
@@ -230,12 +230,12 @@ impl GenerateVideoRequestBuilder {
|
||||
(RouterProvider::GrokApi, RouterVideoModel::GrokImagineVideo) => build_grok_api_grok_imagine_video(self),
|
||||
(RouterProvider::GrokApi, RouterVideoModel::GrokImagineVideo1p5) => build_grok_api_grok_imagine_video_1p5(self),
|
||||
// Kinovi
|
||||
(RouterProvider::Seedance2Pro, RouterVideoModel::HappyHorse1p0) => build_kinovi_happy_horse_1p0(self),
|
||||
(RouterProvider::Seedance2Pro, RouterVideoModel::Seedance2p0) => build_kinovi_seedance_2p0(self),
|
||||
(RouterProvider::Seedance2Pro, RouterVideoModel::Seedance2p0Fast) => build_kinovi_seedance_2p0_fast(self),
|
||||
(RouterProvider::Seedance2Pro, RouterVideoModel::Seedance2p0Mini) => build_kinovi_seedance_2p0_mini(self),
|
||||
(RouterProvider::Seedance2Pro, RouterVideoModel::Seedance2p0BytePlusMini) => build_kinovi_seedance_2p0_mini(self),
|
||||
(RouterProvider::Seedance2Pro, RouterVideoModel::Seedance2p0BytePlusUltraMini) => build_kinovi_seedance_2p0_mini(self),
|
||||
(RouterProvider::KinoviWeb, RouterVideoModel::HappyHorse1p0) => build_kinovi_happy_horse_1p0(self),
|
||||
(RouterProvider::KinoviWeb, RouterVideoModel::Seedance2p0) => build_kinovi_seedance_2p0(self),
|
||||
(RouterProvider::KinoviWeb, RouterVideoModel::Seedance2p0Fast) => build_kinovi_seedance_2p0_fast(self),
|
||||
(RouterProvider::KinoviWeb, RouterVideoModel::Seedance2p0Mini) => build_kinovi_seedance_2p0_mini(self),
|
||||
(RouterProvider::KinoviWeb, RouterVideoModel::Seedance2p0BytePlusMini) => build_kinovi_seedance_2p0_mini(self),
|
||||
(RouterProvider::KinoviWeb, RouterVideoModel::Seedance2p0BytePlusUltraMini) => build_kinovi_seedance_2p0_mini(self),
|
||||
_ => self.unsupported_provider_and_model(),
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@ pub struct ArtcraftVideoResponsePayload {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Seedance2proVideoResponsePayload {
|
||||
pub struct KinoviWebVideoResponsePayload {
|
||||
pub order_id: String,
|
||||
pub task_id: String,
|
||||
pub maybe_order_ids: Option<Vec<String>>,
|
||||
@@ -58,7 +58,7 @@ pub enum GenerateVideoResponse {
|
||||
Artcraft(ArtcraftVideoResponsePayload),
|
||||
GmiCloud(GmiCloudVideoResponsePayload),
|
||||
Grok(GrokVideoResponsePayload),
|
||||
Seedance2Pro(Seedance2proVideoResponsePayload),
|
||||
KinoviWeb(KinoviWebVideoResponsePayload),
|
||||
Fal(FalVideoResponsePayload),
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ impl GenerateVideoResponse {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seedance2pro_payload(&self) -> Option<Seedance2proVideoResponsePayload> {
|
||||
pub fn get_kinovi_web_payload(&self) -> Option<KinoviWebVideoResponsePayload> {
|
||||
match self {
|
||||
Self::Seedance2Pro(p) => Some(p.clone()),
|
||||
Self::KinoviWeb(p) => Some(p.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_happy_horse_1p0::{
|
||||
use kinovi_web_client::generate::video::generate_happy_horse_1p0::{
|
||||
KinoviHappyHorse1p0AspectRatio, KinoviHappyHorse1p0BatchCount,
|
||||
KinoviHappyHorse1p0OutputResolution,
|
||||
};
|
||||
@@ -441,7 +441,7 @@ mod tests {
|
||||
|
||||
fn happy_horse_builder() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a cat dancing".to_string()),
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_happy_horse_1p0::{
|
||||
use kinovi_web_client::generate::video::generate_happy_horse_1p0::{
|
||||
GenerateHappyHorse1p0Request, KinoviHappyHorse1p0BatchCount,
|
||||
KinoviHappyHorse1p0OutputResolution,
|
||||
};
|
||||
@@ -310,7 +310,7 @@ mod tests {
|
||||
use crate::api::router_video_model::RouterVideoModel;
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::HappyHorse1p0,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
resolution,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
video_batch_count: Some(video_batch_count),
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_happy_horse_1p0::{
|
||||
use kinovi_web_client::generate::video::generate_happy_horse_1p0::{
|
||||
GenerateHappyHorse1p0Request, KinoviHappyHorse1p0AspectRatio,
|
||||
KinoviHappyHorse1p0BatchCount, KinoviHappyHorse1p0OutputResolution,
|
||||
};
|
||||
@@ -30,7 +30,7 @@ impl KinoviHappyHorse1p0DraftState {
|
||||
&mut self,
|
||||
draft_context: &VideoGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviHappyHorse1p0RequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut start_frame_url = None;
|
||||
|
||||
+25
-25
@@ -1,11 +1,11 @@
|
||||
use seedance2pro_client::generate::video::generate_happy_horse_1p0::{
|
||||
use kinovi_web_client::generate::video::generate_happy_horse_1p0::{
|
||||
generate_happy_horse_1p0, GenerateHappyHorse1p0Args, GenerateHappyHorse1p0Request,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, Seedance2proVideoResponsePayload};
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, KinoviWebVideoResponsePayload};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KinoviHappyHorse1p0RequestState {
|
||||
@@ -13,7 +13,7 @@ pub struct KinoviHappyHorse1p0RequestState {
|
||||
}
|
||||
|
||||
impl KinoviHappyHorse1p0RequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateHappyHorse1p0Args {
|
||||
@@ -24,9 +24,9 @@ impl KinoviHappyHorse1p0RequestState {
|
||||
|
||||
let response = generate_happy_horse_1p0(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload {
|
||||
Ok(GenerateVideoResponse::KinoviWeb(KinoviWebVideoResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
@@ -47,12 +47,12 @@ mod tests {
|
||||
use crate::api::image_ref::ImageRef;
|
||||
use crate::api::router_provider::RouterProvider;
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::generate::generate_video::generate_video_request_builder::GenerateVideoRequestBuilder;
|
||||
use crate::generate::generate_video::generate_video_response::GenerateVideoResponse;
|
||||
use crate::generate::generate_video::video_generation_draft_context::VideoGenerationDraftContext;
|
||||
use crate::generate::generate_video::video_generation_draft_or_request::VideoGenerationDraftOrRequest;
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use test_data::web::image_urls::JUNO_AT_LAKE_IMAGE_URL;
|
||||
|
||||
mod aspect_ratio_tests {
|
||||
@@ -66,7 +66,7 @@ mod tests {
|
||||
aspect_ratio: Some(RouterAspectRatio::WideSixteenByNine),
|
||||
..happy_horse_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ mod tests {
|
||||
aspect_ratio: Some(RouterAspectRatio::TallNineBySixteen),
|
||||
..happy_horse_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ mod tests {
|
||||
aspect_ratio: Some(RouterAspectRatio::Square),
|
||||
..happy_horse_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ mod tests {
|
||||
resolution: Some(RouterResolution::SevenTwentyP),
|
||||
..happy_horse_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ mod tests {
|
||||
resolution: Some(RouterResolution::TenEightyP),
|
||||
..happy_horse_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ mod tests {
|
||||
aspect_ratio: Some(RouterAspectRatio::WideSixteenByNine),
|
||||
..happy_horse_builder()
|
||||
}).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ mod tests {
|
||||
aspect_ratio: Some(RouterAspectRatio::WideSixteenByNine),
|
||||
..happy_horse_builder()
|
||||
}, media_map).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ mod tests {
|
||||
duration_seconds: Some(15),
|
||||
..happy_horse_builder()
|
||||
}, media_map).await;
|
||||
assert!(matches!(response, GenerateVideoResponse::Seedance2Pro(_)));
|
||||
assert!(matches!(response, GenerateVideoResponse::KinoviWeb(_)));
|
||||
assert_eq!(1, 2, "Inspect output above");
|
||||
}
|
||||
}
|
||||
@@ -182,22 +182,22 @@ mod tests {
|
||||
fn happy_horse_builder() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::HappyHorse1p0,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
duration_seconds: Some(4),
|
||||
video_batch_count: Some(1),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_seedance2pro_client() -> RouterClient {
|
||||
fn get_kinovi_web_client() -> RouterClient {
|
||||
let cookies = std::fs::read_to_string("/Users/bt/Artcraft/credentials/seedance2pro_cookies.txt")
|
||||
.expect("Failed to read seedance2pro cookies");
|
||||
let session = Seedance2ProSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::Seedance2Pro(RouterSeedance2ProClient::new(session))
|
||||
.expect("Failed to read kinovi_web cookies");
|
||||
let session = KinoviWebSession::from_cookies_string(cookies.trim().to_string());
|
||||
RouterClient::KinoviWeb(RouterKinoviWebClient::new(session))
|
||||
}
|
||||
|
||||
async fn run_pipeline(builder: GenerateVideoRequestBuilder) -> GenerateVideoResponse {
|
||||
let client = get_seedance2pro_client();
|
||||
let client = get_kinovi_web_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let draft = match draft_or_request {
|
||||
@@ -214,7 +214,7 @@ mod tests {
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateVideoResponse::Seedance2Pro(p) => {
|
||||
GenerateVideoResponse::KinoviWeb(p) => {
|
||||
println!("task_id={}, order_id={}", p.task_id, p.order_id);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
@@ -227,7 +227,7 @@ mod tests {
|
||||
builder: GenerateVideoRequestBuilder,
|
||||
media_map: HashMap<MediaFileToken, String>,
|
||||
) -> GenerateVideoResponse {
|
||||
let client = get_seedance2pro_client();
|
||||
let client = get_kinovi_web_client();
|
||||
|
||||
let draft_or_request = builder.build2().expect("build2 should succeed");
|
||||
let draft = match draft_or_request {
|
||||
@@ -245,7 +245,7 @@ mod tests {
|
||||
let response = request.send_request(&client).await.expect("send_request should succeed");
|
||||
|
||||
match &response {
|
||||
GenerateVideoResponse::Seedance2Pro(p) => {
|
||||
GenerateVideoResponse::KinoviWeb(p) => {
|
||||
println!("task_id={}, order_id={}", p.task_id, p.order_id);
|
||||
}
|
||||
other => println!("response: {:?}", other),
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use tokens::tokens::media_files::MediaFileToken;
|
||||
|
||||
use crate::api::audio_list_ref::AudioListRef;
|
||||
@@ -10,7 +10,7 @@ use crate::api::image_ref::ImageRef;
|
||||
use crate::api::video_list_ref::VideoListRef;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use crate::generate::generate_video::providers::kinovi::upload::upload_to_seedance2pro;
|
||||
use crate::generate::generate_video::providers::kinovi::upload::upload_to_kinovi_web;
|
||||
use crate::generate::generate_video::video_generation_draft_context::VideoGenerationDraftContext;
|
||||
|
||||
/// Either resolved URLs or unresolved media file tokens.
|
||||
@@ -40,9 +40,9 @@ pub(crate) fn audio_list_ref_into_urls_or_tokens(list: AudioListRef) -> UrlsOrTo
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a single ImageRef and upload to Seedance2Pro CDN.
|
||||
/// Resolve a single ImageRef and upload to KinoviWeb CDN.
|
||||
pub(crate) async fn resolve_and_upload_single(
|
||||
session: &Seedance2ProSession,
|
||||
session: &KinoviWebSession,
|
||||
image_ref: Option<ImageRef>,
|
||||
maybe_map: Option<&HashMap<MediaFileToken, String>>,
|
||||
) -> Result<Option<String>, ArtcraftRouterError> {
|
||||
@@ -51,13 +51,13 @@ pub(crate) async fn resolve_and_upload_single(
|
||||
Some(ImageRef::Url(url)) => url,
|
||||
Some(ImageRef::MediaFileToken(token)) => resolve_token(maybe_map, &token)?,
|
||||
};
|
||||
Ok(Some(upload_to_seedance2pro(session, &source_url).await?))
|
||||
Ok(Some(upload_to_kinovi_web(session, &source_url).await?))
|
||||
}
|
||||
|
||||
/// Resolve a list of refs to URLs and upload each to Seedance2Pro CDN.
|
||||
/// Resolve a list of refs to URLs and upload each to KinoviWeb CDN.
|
||||
/// Order is preserved.
|
||||
pub(crate) async fn resolve_and_upload_list(
|
||||
session: &Seedance2ProSession,
|
||||
session: &KinoviWebSession,
|
||||
urls_or_tokens: Option<UrlsOrTokens>,
|
||||
maybe_map: Option<&HashMap<MediaFileToken, String>>,
|
||||
) -> Result<Option<Vec<String>>, ArtcraftRouterError> {
|
||||
@@ -71,7 +71,7 @@ pub(crate) async fn resolve_and_upload_list(
|
||||
|
||||
let mut uploaded = Vec::with_capacity(source_urls.len());
|
||||
for url in &source_urls {
|
||||
uploaded.push(upload_to_seedance2pro(session, url).await?);
|
||||
uploaded.push(upload_to_kinovi_web(session, url).await?);
|
||||
}
|
||||
Ok(Some(uploaded))
|
||||
}
|
||||
|
||||
+36
-36
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::{
|
||||
KinoviSeedance2p0AspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0Bitrate as KinoviBitrate,
|
||||
KinoviSeedance2p0BatchCount as KinoviBatchCount,
|
||||
@@ -212,7 +212,7 @@ fn plan_bitrate(bitrate: Option<RouterBitrate>) -> Option<KinoviBitrate> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::{
|
||||
KinoviSeedance2p0AspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0BatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p0OutputResolution as KinoviOutputResolution,
|
||||
@@ -242,27 +242,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn prompt_is_passed_through() {
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(seedance2pro_builder()));
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(kinovi_web_builder()));
|
||||
assert_eq!(draft.prompt, "a cat dancing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_defaults_to_empty() {
|
||||
let builder = GenerateVideoRequestBuilder { prompt: None, ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { prompt: None, ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert_eq!(draft.prompt, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_seconds_converted() {
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: Some(10), ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: Some(10), ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert_eq!(draft.duration_seconds, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_defaults_to_5() {
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: None, ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: None, ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert_eq!(draft.duration_seconds, 5);
|
||||
}
|
||||
@@ -271,7 +271,7 @@ mod tests {
|
||||
fn duration_clamped_to_max() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
duration_seconds: Some(99),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert_eq!(draft.duration_seconds, 15);
|
||||
@@ -279,21 +279,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn batch_count_one() {
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(1), ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(1), ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::One));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_two() {
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(2), ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(2), ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::Two));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_four() {
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(4), ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(4), ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::Four));
|
||||
}
|
||||
@@ -308,7 +308,7 @@ mod tests {
|
||||
fn aspect_ratio_wide() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::WideSixteenByNine),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Landscape16x9));
|
||||
@@ -318,7 +318,7 @@ mod tests {
|
||||
fn aspect_ratio_tall() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::TallNineBySixteen),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Portrait9x16));
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
fn aspect_ratio_square() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::Square),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Square1x1));
|
||||
@@ -336,7 +336,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_defaults_to_landscape() {
|
||||
let builder = GenerateVideoRequestBuilder { aspect_ratio: None, ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { aspect_ratio: None, ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Landscape16x9));
|
||||
}
|
||||
@@ -351,7 +351,7 @@ mod tests {
|
||||
fn resolution_480p() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::FourEightyP),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::FourEightyP)));
|
||||
@@ -361,7 +361,7 @@ mod tests {
|
||||
fn resolution_720p() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::SevenTwentyP),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::SevenTwentyP)));
|
||||
@@ -371,7 +371,7 @@ mod tests {
|
||||
fn resolution_1080p() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::TenEightyP),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::TenEightyP)));
|
||||
@@ -381,7 +381,7 @@ mod tests {
|
||||
fn resolution_4k() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::FourK),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::FourK)));
|
||||
@@ -389,7 +389,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolution_none() {
|
||||
let builder = GenerateVideoRequestBuilder { resolution: None, ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { resolution: None, ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(draft.resolution.is_none());
|
||||
}
|
||||
@@ -399,7 +399,7 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::ThreeK),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
assert!(build_kinovi_seedance_2p0(builder).is_err());
|
||||
}
|
||||
@@ -409,7 +409,7 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::ThreeK),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::TenEightyP)));
|
||||
@@ -425,7 +425,7 @@ mod tests {
|
||||
fn bitrate_high() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::High),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(matches!(draft.bitrate, Some(KinoviBitrate::High)));
|
||||
@@ -435,7 +435,7 @@ mod tests {
|
||||
fn bitrate_normal_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::Normal),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
@@ -443,7 +443,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn bitrate_none_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder { bitrate: None, ..seedance2pro_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { bitrate: None, ..kinovi_web_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
}
|
||||
@@ -456,7 +456,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn unhandled_state_is_present() {
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(seedance2pro_builder()));
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(kinovi_web_builder()));
|
||||
assert!(draft.unhandled_request_state.is_some());
|
||||
}
|
||||
|
||||
@@ -464,7 +464,7 @@ mod tests {
|
||||
fn start_frame_placed_in_unhandled() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::Url("https://example.com/start.jpg".to_string())),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -475,7 +475,7 @@ mod tests {
|
||||
fn end_frame_placed_in_unhandled() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
end_frame: Some(ImageRef::Url("https://example.com/end.jpg".to_string())),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -486,7 +486,7 @@ mod tests {
|
||||
fn start_frame_media_token_placed_in_unhandled() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_test123".to_string()))),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -500,7 +500,7 @@ mod tests {
|
||||
"https://example.com/ref1.jpg".to_string(),
|
||||
"https://example.com/ref2.jpg".to_string(),
|
||||
])),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -520,7 +520,7 @@ mod tests {
|
||||
reference_videos: Some(VideoListRef::Urls(vec![
|
||||
"https://example.com/vid.mp4".to_string(),
|
||||
])),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -533,7 +533,7 @@ mod tests {
|
||||
reference_audio: Some(AudioListRef::Urls(vec![
|
||||
"https://example.com/audio.mp3".to_string(),
|
||||
])),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -547,7 +547,7 @@ mod tests {
|
||||
CharacterToken::new("char_abc".to_string()),
|
||||
CharacterToken::new("char_def".to_string()),
|
||||
])),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -563,7 +563,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn empty_refs_are_none_in_unhandled() {
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(seedance2pro_builder()));
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(kinovi_web_builder()));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
assert!(remaining.start_frame.is_none());
|
||||
assert!(remaining.end_frame.is_none());
|
||||
@@ -592,7 +592,7 @@ mod tests {
|
||||
reference_character_tokens: Some(CharacterListRef::CharacterTokens(vec![
|
||||
CharacterToken::new("char_xyz".to_string()),
|
||||
])),
|
||||
..seedance2pro_builder()
|
||||
..kinovi_web_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0(builder));
|
||||
|
||||
@@ -613,9 +613,9 @@ mod tests {
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn seedance2pro_builder() -> GenerateVideoRequestBuilder {
|
||||
fn kinovi_web_builder() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a cat dancing".to_string()),
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::{
|
||||
GenerateSeedance2p0Request, KinoviSeedance2p0BatchCount,
|
||||
KinoviSeedance2p0OutputResolution,
|
||||
};
|
||||
@@ -85,7 +85,7 @@ impl KinoviSeedance2p0CostState {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::{
|
||||
KinoviSeedance2p0OutputResolution as KinoviOutputResolution,
|
||||
KinoviSeedance2p0BatchCount as KinoviBatchCount,
|
||||
};
|
||||
@@ -177,7 +177,7 @@ mod tests {
|
||||
|
||||
// ── 4K pricing ──
|
||||
//
|
||||
// Routes to the seedance2pro_client: 4K base is 200 credits/s (vs 90 at 1080p).
|
||||
// Routes to the kinovi_web_client: 4K base is 200 credits/s (vs 90 at 1080p).
|
||||
// Credits → cents at 243 credits/$1, rounded up.
|
||||
|
||||
mod pricing_4k {
|
||||
@@ -202,7 +202,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// ── 4K: router cost must match the seedance2pro_client binding ──
|
||||
// ── 4K: router cost must match the kinovi_web_client binding ──
|
||||
//
|
||||
// The router delegates to GenerateSeedance2p0Request::calculate_costs(), so these
|
||||
// assert the exact 4K numbers AND cross-check the router against the binding for
|
||||
@@ -213,7 +213,7 @@ mod tests {
|
||||
|
||||
mod four_k_matches_binding {
|
||||
use super::*;
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::GenerateSeedance2p0Request;
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::GenerateSeedance2p0Request;
|
||||
|
||||
/// Router-side 4K cost (batch 1) for a duration / video-reference combo.
|
||||
fn router(duration_seconds: u8, has_video_reference: bool) -> VideoGenerationCostEstimate {
|
||||
@@ -226,7 +226,7 @@ mod tests {
|
||||
.estimate_cost()
|
||||
}
|
||||
|
||||
/// The seedance2pro_client binding's own 4K cost for the same inputs.
|
||||
/// The kinovi_web_client binding's own 4K cost for the same inputs.
|
||||
/// Returns (kinovi_credits, usd_cents_rounded_up).
|
||||
fn binding(duration_seconds: u8, has_video_reference: bool) -> (u64, u64) {
|
||||
let reference_video_urls = if has_video_reference {
|
||||
@@ -579,7 +579,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
resolution,
|
||||
reference_videos,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::{
|
||||
GenerateSeedance2p0Request, KinoviSeedance2p0AspectRatio, KinoviSeedance2p0Bitrate,
|
||||
KinoviSeedance2p0BatchCount, KinoviSeedance2p0OutputResolution,
|
||||
};
|
||||
@@ -47,7 +47,7 @@ impl KinoviSeedance2p0DraftState {
|
||||
&mut self,
|
||||
draft_context: &VideoGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviSeedance2p0RequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut start_frame_url = None;
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0::{
|
||||
generate_seedance_2p0, GenerateSeedance2p0Args, GenerateSeedance2p0Request,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, Seedance2proVideoResponsePayload};
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, KinoviWebVideoResponsePayload};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KinoviSeedance2p0RequestState {
|
||||
@@ -14,7 +14,7 @@ pub struct KinoviSeedance2p0RequestState {
|
||||
}
|
||||
|
||||
impl KinoviSeedance2p0RequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSeedance2p0Args {
|
||||
@@ -25,9 +25,9 @@ impl KinoviSeedance2p0RequestState {
|
||||
|
||||
let response = generate_seedance_2p0(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload {
|
||||
Ok(GenerateVideoResponse::KinoviWeb(KinoviWebVideoResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
|
||||
+37
-37
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_fast::{
|
||||
KinoviSeedance2p0FastAspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0FastBitrate as KinoviBitrate,
|
||||
KinoviSeedance2p0FastBatchCount as KinoviBatchCount,
|
||||
@@ -226,7 +226,7 @@ fn plan_bitrate(bitrate: Option<RouterBitrate>) -> Option<KinoviBitrate> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_fast::{
|
||||
KinoviSeedance2p0FastAspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0FastBatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p0FastOutputResolution as KinoviOutputResolution,
|
||||
@@ -258,27 +258,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn prompt_is_passed_through() {
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(seedance2pro_fast_builder()));
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(kinovi_web_fast_builder()));
|
||||
assert_eq!(draft.prompt, "a cat dancing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_defaults_to_empty() {
|
||||
let builder = GenerateVideoRequestBuilder { prompt: None, ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { prompt: None, ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert_eq!(draft.prompt, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_seconds_converted() {
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: Some(10), ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: Some(10), ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert_eq!(draft.duration_seconds, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_defaults_to_5() {
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: None, ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { duration_seconds: None, ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert_eq!(draft.duration_seconds, 5);
|
||||
}
|
||||
@@ -287,7 +287,7 @@ mod tests {
|
||||
fn duration_clamped_to_max() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
duration_seconds: Some(99),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert_eq!(draft.duration_seconds, 15);
|
||||
@@ -295,21 +295,21 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn batch_count_one() {
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(1), ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(1), ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::One));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_two() {
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(2), ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(2), ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::Two));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_count_four() {
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(4), ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { video_batch_count: Some(4), ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.batch_count, KinoviBatchCount::Four));
|
||||
}
|
||||
@@ -324,7 +324,7 @@ mod tests {
|
||||
fn aspect_ratio_wide() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::WideSixteenByNine),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Landscape16x9));
|
||||
@@ -334,7 +334,7 @@ mod tests {
|
||||
fn aspect_ratio_tall() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::TallNineBySixteen),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Portrait9x16));
|
||||
@@ -344,7 +344,7 @@ mod tests {
|
||||
fn aspect_ratio_square() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
aspect_ratio: Some(RouterAspectRatio::Square),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Square1x1));
|
||||
@@ -352,7 +352,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn aspect_ratio_defaults_to_landscape() {
|
||||
let builder = GenerateVideoRequestBuilder { aspect_ratio: None, ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { aspect_ratio: None, ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.aspect_ratio, KinoviAspectRatio::Landscape16x9));
|
||||
}
|
||||
@@ -367,7 +367,7 @@ mod tests {
|
||||
fn resolution_480p() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::FourEightyP),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::FourEightyP)));
|
||||
@@ -377,7 +377,7 @@ mod tests {
|
||||
fn resolution_720p() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::SevenTwentyP),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::SevenTwentyP)));
|
||||
@@ -388,7 +388,7 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::TenEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayLessDowngrade,
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::SevenTwentyP)));
|
||||
@@ -399,7 +399,7 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::TenEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::SevenTwentyP)));
|
||||
@@ -410,14 +410,14 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::TenEightyP),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
assert!(build_kinovi_seedance_2p0_fast(builder).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolution_none() {
|
||||
let builder = GenerateVideoRequestBuilder { resolution: None, ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { resolution: None, ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(draft.resolution.is_none());
|
||||
}
|
||||
@@ -427,7 +427,7 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::FourK),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::ErrorOut,
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
assert!(build_kinovi_seedance_2p0_fast(builder).is_err());
|
||||
}
|
||||
@@ -437,7 +437,7 @@ mod tests {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
resolution: Some(RouterResolution::FourK),
|
||||
request_mismatch_mitigation_strategy: RequestMismatchMitigationStrategy::PayMoreUpgrade,
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.resolution, Some(KinoviOutputResolution::SevenTwentyP)));
|
||||
@@ -453,7 +453,7 @@ mod tests {
|
||||
fn bitrate_high() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::High),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(matches!(draft.bitrate, Some(KinoviBitrate::High)));
|
||||
@@ -463,7 +463,7 @@ mod tests {
|
||||
fn bitrate_normal_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
bitrate: Some(RouterBitrate::Normal),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
@@ -471,7 +471,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn bitrate_none_maps_to_standard() {
|
||||
let builder = GenerateVideoRequestBuilder { bitrate: None, ..seedance2pro_fast_builder() };
|
||||
let builder = GenerateVideoRequestBuilder { bitrate: None, ..kinovi_web_fast_builder() };
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
assert!(draft.bitrate.is_none());
|
||||
}
|
||||
@@ -484,7 +484,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn unhandled_state_is_present() {
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(seedance2pro_fast_builder()));
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(kinovi_web_fast_builder()));
|
||||
assert!(draft.unhandled_request_state.is_some());
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ mod tests {
|
||||
fn start_frame_placed_in_unhandled() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::Url("https://example.com/start.jpg".to_string())),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -503,7 +503,7 @@ mod tests {
|
||||
fn end_frame_placed_in_unhandled() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
end_frame: Some(ImageRef::Url("https://example.com/end.jpg".to_string())),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -514,7 +514,7 @@ mod tests {
|
||||
fn start_frame_media_token_placed_in_unhandled() {
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
start_frame: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_test123".to_string()))),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -528,7 +528,7 @@ mod tests {
|
||||
"https://example.com/ref1.jpg".to_string(),
|
||||
"https://example.com/ref2.jpg".to_string(),
|
||||
])),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -548,7 +548,7 @@ mod tests {
|
||||
reference_videos: Some(VideoListRef::Urls(vec![
|
||||
"https://example.com/vid.mp4".to_string(),
|
||||
])),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -561,7 +561,7 @@ mod tests {
|
||||
reference_audio: Some(AudioListRef::Urls(vec![
|
||||
"https://example.com/audio.mp3".to_string(),
|
||||
])),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -575,7 +575,7 @@ mod tests {
|
||||
CharacterToken::new("char_abc".to_string()),
|
||||
CharacterToken::new("char_def".to_string()),
|
||||
])),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
@@ -591,7 +591,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn empty_refs_are_none_in_unhandled() {
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(seedance2pro_fast_builder()));
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(kinovi_web_fast_builder()));
|
||||
let remaining = draft.unhandled_request_state.unwrap();
|
||||
assert!(remaining.start_frame.is_none());
|
||||
assert!(remaining.end_frame.is_none());
|
||||
@@ -620,7 +620,7 @@ mod tests {
|
||||
reference_character_tokens: Some(CharacterListRef::CharacterTokens(vec![
|
||||
CharacterToken::new("char_xyz".to_string()),
|
||||
])),
|
||||
..seedance2pro_fast_builder()
|
||||
..kinovi_web_fast_builder()
|
||||
};
|
||||
let draft = unwrap_draft(build_kinovi_seedance_2p0_fast(builder));
|
||||
|
||||
@@ -641,10 +641,10 @@ mod tests {
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
fn seedance2pro_fast_builder() -> GenerateVideoRequestBuilder {
|
||||
fn kinovi_web_fast_builder() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::Seedance2p0Fast,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a cat dancing".to_string()),
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_fast::{
|
||||
GenerateSeedance2p0FastRequest, KinoviSeedance2p0FastBatchCount,
|
||||
KinoviSeedance2p0FastOutputResolution,
|
||||
};
|
||||
@@ -86,7 +86,7 @@ impl KinoviSeedance2p0FastCostState {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_fast::{
|
||||
KinoviSeedance2p0FastOutputResolution as KinoviOutputResolution,
|
||||
KinoviSeedance2p0FastBatchCount as KinoviBatchCount,
|
||||
};
|
||||
@@ -386,7 +386,7 @@ mod tests {
|
||||
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::Seedance2p0Fast,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
resolution,
|
||||
reference_videos,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_fast::{
|
||||
GenerateSeedance2p0FastRequest, KinoviSeedance2p0FastAspectRatio, KinoviSeedance2p0FastBitrate,
|
||||
KinoviSeedance2p0FastBatchCount, KinoviSeedance2p0FastOutputResolution,
|
||||
};
|
||||
@@ -44,7 +44,7 @@ impl KinoviSeedance2p0FastDraftState {
|
||||
&mut self,
|
||||
draft_context: &VideoGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviSeedance2p0FastRequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut start_frame_url = None;
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_fast::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_fast::{
|
||||
generate_seedance_2p0_fast, GenerateSeedance2p0FastArgs, GenerateSeedance2p0FastRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, Seedance2proVideoResponsePayload};
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, KinoviWebVideoResponsePayload};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KinoviSeedance2p0FastRequestState {
|
||||
@@ -14,7 +14,7 @@ pub struct KinoviSeedance2p0FastRequestState {
|
||||
}
|
||||
|
||||
impl KinoviSeedance2p0FastRequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSeedance2p0FastArgs {
|
||||
@@ -25,9 +25,9 @@ impl KinoviSeedance2p0FastRequestState {
|
||||
|
||||
let response = generate_seedance_2p0_fast(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload {
|
||||
Ok(GenerateVideoResponse::KinoviWeb(KinoviWebVideoResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_mini::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_mini::{
|
||||
KinoviSeedance2p0MiniAspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0MiniBitrate as KinoviBitrate,
|
||||
KinoviSeedance2p0MiniBatchCount as KinoviBatchCount,
|
||||
@@ -221,7 +221,7 @@ fn plan_bitrate(bitrate: Option<RouterBitrate>) -> Option<KinoviBitrate> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_mini::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_mini::{
|
||||
KinoviSeedance2p0MiniAspectRatio as KinoviAspectRatio,
|
||||
KinoviSeedance2p0MiniBatchCount as KinoviBatchCount,
|
||||
KinoviSeedance2p0MiniOutputResolution as KinoviOutputResolution,
|
||||
@@ -491,7 +491,7 @@ mod tests {
|
||||
fn mini_builder() -> GenerateVideoRequestBuilder {
|
||||
GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::Seedance2p0Mini,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
prompt: Some("a cat dancing".to_string()),
|
||||
duration_seconds: Some(5),
|
||||
video_batch_count: Some(1),
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_mini::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_mini::{
|
||||
GenerateSeedance2p0MiniRequest, KinoviSeedance2p0MiniBatchCount,
|
||||
KinoviSeedance2p0MiniOutputResolution,
|
||||
};
|
||||
@@ -89,7 +89,7 @@ impl KinoviSeedance2p0MiniCostState {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::api::router_resolution::RouterResolution;
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_mini::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_mini::{
|
||||
KinoviSeedance2p0MiniOutputResolution as KinoviOutputResolution,
|
||||
KinoviSeedance2p0MiniBatchCount as KinoviBatchCount,
|
||||
};
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
|
||||
let builder = GenerateVideoRequestBuilder {
|
||||
model: RouterVideoModel::Seedance2p0Mini,
|
||||
provider: RouterProvider::Seedance2Pro,
|
||||
provider: RouterProvider::KinoviWeb,
|
||||
resolution,
|
||||
reference_videos,
|
||||
duration_seconds: Some(duration_seconds),
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_mini::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_mini::{
|
||||
GenerateSeedance2p0MiniRequest, KinoviSeedance2p0MiniAspectRatio, KinoviSeedance2p0MiniBitrate,
|
||||
KinoviSeedance2p0MiniBatchCount, KinoviSeedance2p0MiniOutputResolution,
|
||||
};
|
||||
@@ -44,7 +44,7 @@ impl KinoviSeedance2p0MiniDraftState {
|
||||
&mut self,
|
||||
draft_context: &VideoGenerationDraftContext<'_>,
|
||||
) -> Result<KinoviSeedance2p0MiniRequestState, ArtcraftRouterError> {
|
||||
let client = draft_context.get_seedance2pro_client_ref()?;
|
||||
let client = draft_context.get_kinovi_web_client_ref()?;
|
||||
let session = &client.session;
|
||||
|
||||
let mut start_frame_url = None;
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
use seedance2pro_client::generate::video::generate_seedance_2p0_mini::{
|
||||
use kinovi_web_client::generate::video::generate_seedance_2p0_mini::{
|
||||
generate_seedance_2p0_mini, GenerateSeedance2p0MiniArgs, GenerateSeedance2p0MiniRequest,
|
||||
};
|
||||
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, Seedance2proVideoResponsePayload};
|
||||
use crate::generate::generate_video::generate_video_response::{GenerateVideoResponse, KinoviWebVideoResponsePayload};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KinoviSeedance2p0MiniRequestState {
|
||||
@@ -14,7 +14,7 @@ pub struct KinoviSeedance2p0MiniRequestState {
|
||||
}
|
||||
|
||||
impl KinoviSeedance2p0MiniRequestState {
|
||||
pub async fn send(&self, client: &RouterSeedance2ProClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
pub async fn send(&self, client: &RouterKinoviWebClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
|
||||
let session = &client.session;
|
||||
|
||||
let args = GenerateSeedance2p0MiniArgs {
|
||||
@@ -25,9 +25,9 @@ impl KinoviSeedance2p0MiniRequestState {
|
||||
|
||||
let response = generate_seedance_2p0_mini(args)
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(GenerateVideoResponse::Seedance2Pro(Seedance2proVideoResponsePayload {
|
||||
Ok(GenerateVideoResponse::KinoviWeb(KinoviWebVideoResponsePayload {
|
||||
order_id: response.order_id,
|
||||
task_id: response.task_id,
|
||||
maybe_order_ids: response.order_ids,
|
||||
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::provider_error::ProviderError;
|
||||
use crate::utils::download_file::download_file;
|
||||
use seedance2pro_client::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use seedance2pro_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use seedance2pro_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use kinovi_web_client::creds::kinovi_web_session::KinoviWebSession;
|
||||
use kinovi_web_client::requests::prepare_file_upload::prepare_file_upload::{prepare_file_upload, PrepareFileUploadArgs};
|
||||
use kinovi_web_client::requests::upload_file::upload_file::{upload_file, UploadFileArgs};
|
||||
use url_utils::extension::extract_extension_from_url::{extract_extension_from_url_str, ExtractExtensions};
|
||||
|
||||
/// Downloads a file from a source URL and re-uploads it to seedance2pro CDN.
|
||||
pub(crate) async fn upload_to_seedance2pro(
|
||||
session: &Seedance2ProSession,
|
||||
/// Downloads a file from a source URL and re-uploads it to kinovi_web CDN.
|
||||
pub(crate) async fn upload_to_kinovi_web(
|
||||
session: &KinoviWebSession,
|
||||
source_url: &str,
|
||||
) -> Result<String, ArtcraftRouterError> {
|
||||
let extension = extract_extension_from_url_str(source_url, &ExtractExtensions::All)
|
||||
@@ -23,7 +23,7 @@ pub(crate) async fn upload_to_seedance2pro(
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
let upload_response = upload_file(UploadFileArgs {
|
||||
upload_url: prepare_response.upload_url,
|
||||
@@ -31,7 +31,7 @@ pub(crate) async fn upload_to_seedance2pro(
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::KinoviWeb(err)))?;
|
||||
|
||||
Ok(upload_response.public_url)
|
||||
}
|
||||
|
||||
+4
-4
@@ -27,10 +27,10 @@ impl VideoGenerationDraftRequest {
|
||||
|
||||
pub fn get_provider(&self) -> RouterProvider {
|
||||
match self {
|
||||
Self::KinoviHappyHorse1p0(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedance2p0(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedance2p0Fast(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedance2p0Mini(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviHappyHorse1p0(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedance2p0(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedance2p0Fast(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedance2p0Mini(_) => RouterProvider::KinoviWeb,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
use crate::client::router_client::RouterClient;
|
||||
use crate::client::router_seedance2pro_client::RouterSeedance2ProClient;
|
||||
use crate::client::router_kinovi_web_client::RouterKinoviWebClient;
|
||||
use crate::errors::artcraft_router_error::ArtcraftRouterError;
|
||||
use crate::errors::client_error::ClientError;
|
||||
use std::collections::HashMap;
|
||||
@@ -22,9 +22,9 @@ pub struct VideoGenerationDraftContext<'a> {
|
||||
}
|
||||
|
||||
impl <'a> VideoGenerationDraftContext<'a> {
|
||||
pub fn get_seedance2pro_client_ref(&self) -> Result<&RouterSeedance2ProClient, ArtcraftRouterError> {
|
||||
pub fn get_kinovi_web_client_ref(&self) -> Result<&RouterKinoviWebClient, ArtcraftRouterError> {
|
||||
let client = self.client.ok_or(ArtcraftRouterError::Client(ClientError::RouterClientNotProvided))?;
|
||||
client.get_seedance2pro_client_ref()
|
||||
client.get_kinovi_web_client_ref()
|
||||
.map_err(|err| ArtcraftRouterError::Client(err))
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -256,10 +256,10 @@ impl VideoGenerationRequest {
|
||||
Self::GmiCloudSeedance2p0UltraFast(_) => RouterProvider::GmiCloud,
|
||||
Self::GrokApiGrokImagineVideo(_) => RouterProvider::GrokApi,
|
||||
Self::GrokApiGrokImagineVideo1p5(_) => RouterProvider::GrokApi,
|
||||
Self::KinoviHappyHorse1p0(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedance2p0(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedance2p0Fast(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviSeedance2p0Mini(_) => RouterProvider::Seedance2Pro,
|
||||
Self::KinoviHappyHorse1p0(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedance2p0(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedance2p0Fast(_) => RouterProvider::KinoviWeb,
|
||||
Self::KinoviSeedance2p0Mini(_) => RouterProvider::KinoviWeb,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,19 +568,19 @@ impl VideoGenerationRequest {
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::KinoviHappyHorse1p0(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::KinoviSeedance2p0(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::KinoviSeedance2p0Fast(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
VideoGenerationRequest::KinoviSeedance2p0Mini(request) => {
|
||||
let client_ref = client.get_seedance2pro_client_ref()?;
|
||||
let client_ref = client.get_kinovi_web_client_ref()?;
|
||||
request.send(client_ref).await
|
||||
},
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "seedance2pro_client"
|
||||
name = "kinovi_web_client"
|
||||
edition = "2024"
|
||||
version = "0.0.1"
|
||||
publish = false
|
||||
@@ -8,7 +8,7 @@ publish = false
|
||||
normal = ["workspace-build-acceleration"]
|
||||
|
||||
[lib]
|
||||
name = "seedance2pro_client"
|
||||
name = "kinovi_web_client"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
+18
-18
@@ -4,10 +4,10 @@ use serde::Deserialize;
|
||||
use wreq::Client;
|
||||
use wreq_util::Emulation;
|
||||
|
||||
use crate::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use crate::error::seedance2pro_client_error::Seedance2ProClientError;
|
||||
use crate::error::seedance2pro_error::Seedance2ProError;
|
||||
use crate::error::seedance2pro_generic_api_error::Seedance2ProGenericApiError;
|
||||
use crate::creds::kinovi_web_session::KinoviWebSession;
|
||||
use crate::error::kinovi_web_client_error::KinoviWebClientError;
|
||||
use crate::error::kinovi_web_error::KinoviWebError;
|
||||
use crate::error::kinovi_web_generic_api_error::KinoviWebGenericApiError;
|
||||
use crate::requests::kinovi_host::{KinoviHost, resolve_host};
|
||||
use crate::utils::common_headers::FIREFOX_USER_AGENT;
|
||||
use crate::utils::number_coercion::de_u64_int_or_float;
|
||||
@@ -18,7 +18,7 @@ pub const DEFAULT_BILLING_PAGE_LIMIT: u32 = 20;
|
||||
// --- Args & response ---
|
||||
|
||||
pub struct GetBillingPaymentsHistoryArgs<'a> {
|
||||
pub session: &'a Seedance2ProSession,
|
||||
pub session: &'a KinoviWebSession,
|
||||
|
||||
/// Page size. The web dashboard uses [`DEFAULT_BILLING_PAGE_LIMIT`].
|
||||
pub limit: u32,
|
||||
@@ -114,7 +114,7 @@ impl BillingPaymentType {
|
||||
|
||||
pub async fn get_billing_payments_history(
|
||||
args: GetBillingPaymentsHistoryArgs<'_>,
|
||||
) -> Result<BillingPaymentsHistoryPage, Seedance2ProError> {
|
||||
) -> Result<BillingPaymentsHistoryPage, KinoviWebError> {
|
||||
let host = resolve_host(args.host_override.as_ref());
|
||||
let base_url = host.api_base_url();
|
||||
let url = format!("{}/api/trpc/billing.getPayments", base_url);
|
||||
@@ -126,7 +126,7 @@ pub async fn get_billing_payments_history(
|
||||
let client = Client::builder()
|
||||
.emulation(Emulation::Firefox143)
|
||||
.build()
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
.map_err(|err| KinoviWebClientError::WreqClientError(err))?;
|
||||
|
||||
let cookie = args.session.cookies.as_str();
|
||||
let referer = format!("{}/app/dashboard/billing", base_url);
|
||||
@@ -148,21 +148,21 @@ pub async fn get_billing_payments_history(
|
||||
.header("Priority", "u=4")
|
||||
.header("TE", "trailers")
|
||||
.build()
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
.map_err(|err| KinoviWebClientError::WreqClientError(err))?;
|
||||
|
||||
let response = client.execute(request)
|
||||
.await
|
||||
.map_err(|err| Seedance2ProGenericApiError::WreqError(err))?;
|
||||
.map_err(|err| KinoviWebGenericApiError::WreqError(err))?;
|
||||
|
||||
let status = response.status();
|
||||
let response_body = response.text()
|
||||
.await
|
||||
.map_err(|err| Seedance2ProGenericApiError::WreqError(err))?;
|
||||
.map_err(|err| KinoviWebGenericApiError::WreqError(err))?;
|
||||
|
||||
info!("Billing payments response status: {}", status);
|
||||
|
||||
if !status.is_success() {
|
||||
return Err(Seedance2ProGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
return Err(KinoviWebGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
status_code: status,
|
||||
body: response_body,
|
||||
}.into());
|
||||
@@ -171,14 +171,14 @@ pub async fn get_billing_payments_history(
|
||||
parse_response_body(&response_body)
|
||||
}
|
||||
|
||||
fn parse_response_body(response_body: &str) -> Result<BillingPaymentsHistoryPage, Seedance2ProError> {
|
||||
fn parse_response_body(response_body: &str) -> Result<BillingPaymentsHistoryPage, KinoviWebError> {
|
||||
let batch_response: Vec<BatchResponseItem> = serde_json::from_str(response_body)
|
||||
.map_err(|err| Seedance2ProGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string()))?;
|
||||
.map_err(|err| KinoviWebGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string()))?;
|
||||
|
||||
let json = batch_response
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| Seedance2ProGenericApiError::UnexpectedResponseShape {
|
||||
.ok_or_else(|| KinoviWebGenericApiError::UnexpectedResponseShape {
|
||||
explanation: "Empty batch response array".to_string(),
|
||||
raw_body: response_body.to_string(),
|
||||
})?
|
||||
@@ -274,7 +274,7 @@ struct RawPayment {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use crate::creds::kinovi_web_session::KinoviWebSession;
|
||||
use crate::test_utils::get_test_cookies::get_test_cookies;
|
||||
use crate::test_utils::setup_test_logging::setup_test_logging;
|
||||
use errors::AnyhowResult;
|
||||
@@ -358,7 +358,7 @@ mod tests {
|
||||
|
||||
/// Pages through the ENTIRE payments history with throttling and prints
|
||||
/// lifetime totals. ~1,000 rows / 50 pages as of 2026-07.
|
||||
/// cargo test -p seedance2pro_client live_paginate_entire_payments_history -- --ignored --nocapture
|
||||
/// cargo test -p kinovi_web_client live_paginate_entire_payments_history -- --ignored --nocapture
|
||||
#[tokio::test]
|
||||
#[ignore] // manual test — requires real cookies; fetches every page
|
||||
async fn live_paginate_entire_payments_history() -> AnyhowResult<()> {
|
||||
@@ -413,9 +413,9 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_session() -> AnyhowResult<Seedance2ProSession> {
|
||||
fn test_session() -> AnyhowResult<KinoviWebSession> {
|
||||
let cookies = get_test_cookies()?;
|
||||
Ok(Seedance2ProSession::from_cookies_string(cookies))
|
||||
Ok(KinoviWebSession::from_cookies_string(cookies))
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-18
@@ -4,10 +4,10 @@ use serde::Deserialize;
|
||||
use wreq::Client;
|
||||
use wreq_util::Emulation;
|
||||
|
||||
use crate::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use crate::error::seedance2pro_client_error::Seedance2ProClientError;
|
||||
use crate::error::seedance2pro_error::Seedance2ProError;
|
||||
use crate::error::seedance2pro_generic_api_error::Seedance2ProGenericApiError;
|
||||
use crate::creds::kinovi_web_session::KinoviWebSession;
|
||||
use crate::error::kinovi_web_client_error::KinoviWebClientError;
|
||||
use crate::error::kinovi_web_error::KinoviWebError;
|
||||
use crate::error::kinovi_web_generic_api_error::KinoviWebGenericApiError;
|
||||
use crate::requests::kinovi_host::{KinoviHost, resolve_host};
|
||||
use crate::utils::common_headers::FIREFOX_USER_AGENT;
|
||||
use crate::utils::number_coercion::{de_i64_int_or_float, de_u64_int_or_float};
|
||||
@@ -18,7 +18,7 @@ pub const DEFAULT_CREDITS_PAGE_LIMIT: u32 = 20;
|
||||
// --- Args & response ---
|
||||
|
||||
pub struct GetCreditsHistoryArgs<'a> {
|
||||
pub session: &'a Seedance2ProSession,
|
||||
pub session: &'a KinoviWebSession,
|
||||
|
||||
/// Page size. The web dashboard uses [`DEFAULT_CREDITS_PAGE_LIMIT`].
|
||||
pub limit: u32,
|
||||
@@ -106,7 +106,7 @@ impl CreditHistoryEntryType {
|
||||
|
||||
pub async fn get_credits_history(
|
||||
args: GetCreditsHistoryArgs<'_>,
|
||||
) -> Result<CreditsHistoryPage, Seedance2ProError> {
|
||||
) -> Result<CreditsHistoryPage, KinoviWebError> {
|
||||
let host = resolve_host(args.host_override.as_ref());
|
||||
let base_url = host.api_base_url();
|
||||
let url = format!("{}/api/trpc/credits.getCreditHistory", base_url);
|
||||
@@ -118,7 +118,7 @@ pub async fn get_credits_history(
|
||||
let client = Client::builder()
|
||||
.emulation(Emulation::Firefox143)
|
||||
.build()
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
.map_err(|err| KinoviWebClientError::WreqClientError(err))?;
|
||||
|
||||
let cookie = args.session.cookies.as_str();
|
||||
let referer = format!("{}/app/dashboard/billing", base_url);
|
||||
@@ -140,21 +140,21 @@ pub async fn get_credits_history(
|
||||
.header("Priority", "u=4")
|
||||
.header("TE", "trailers")
|
||||
.build()
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
.map_err(|err| KinoviWebClientError::WreqClientError(err))?;
|
||||
|
||||
let response = client.execute(request)
|
||||
.await
|
||||
.map_err(|err| Seedance2ProGenericApiError::WreqError(err))?;
|
||||
.map_err(|err| KinoviWebGenericApiError::WreqError(err))?;
|
||||
|
||||
let status = response.status();
|
||||
let response_body = response.text()
|
||||
.await
|
||||
.map_err(|err| Seedance2ProGenericApiError::WreqError(err))?;
|
||||
.map_err(|err| KinoviWebGenericApiError::WreqError(err))?;
|
||||
|
||||
info!("Credits history response status: {}", status);
|
||||
|
||||
if !status.is_success() {
|
||||
return Err(Seedance2ProGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
return Err(KinoviWebGenericApiError::UncategorizedBadResponseWithStatusAndBody {
|
||||
status_code: status,
|
||||
body: response_body,
|
||||
}.into());
|
||||
@@ -163,14 +163,14 @@ pub async fn get_credits_history(
|
||||
parse_response_body(&response_body)
|
||||
}
|
||||
|
||||
fn parse_response_body(response_body: &str) -> Result<CreditsHistoryPage, Seedance2ProError> {
|
||||
fn parse_response_body(response_body: &str) -> Result<CreditsHistoryPage, KinoviWebError> {
|
||||
let batch_response: Vec<BatchResponseItem> = serde_json::from_str(response_body)
|
||||
.map_err(|err| Seedance2ProGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string()))?;
|
||||
.map_err(|err| KinoviWebGenericApiError::SerdeResponseParseErrorWithBody(err, response_body.to_string()))?;
|
||||
|
||||
let json = batch_response
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| Seedance2ProGenericApiError::UnexpectedResponseShape {
|
||||
.ok_or_else(|| KinoviWebGenericApiError::UnexpectedResponseShape {
|
||||
explanation: "Empty batch response array".to_string(),
|
||||
raw_body: response_body.to_string(),
|
||||
})?
|
||||
@@ -251,7 +251,7 @@ struct RawCreditEntry {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::creds::seedance2pro_session::Seedance2ProSession;
|
||||
use crate::creds::kinovi_web_session::KinoviWebSession;
|
||||
use crate::test_utils::get_test_cookies::get_test_cookies;
|
||||
use crate::test_utils::setup_test_logging::setup_test_logging;
|
||||
use errors::AnyhowResult;
|
||||
@@ -344,7 +344,7 @@ mod tests {
|
||||
/// per-type tallies (this is where any refund-like entry types will
|
||||
/// surface). ~300k rows as of 2026-07 — expect a long run; the ledger
|
||||
/// also grows while paging, so entries are deduped by id.
|
||||
/// cargo test -p seedance2pro_client live_paginate_entire_credits_history -- --ignored --nocapture
|
||||
/// cargo test -p kinovi_web_client live_paginate_entire_credits_history -- --ignored --nocapture
|
||||
#[tokio::test]
|
||||
#[ignore] // manual test — requires real cookies; fetches every page
|
||||
async fn live_paginate_entire_credits_history() -> AnyhowResult<()> {
|
||||
@@ -407,9 +407,9 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_session() -> AnyhowResult<Seedance2ProSession> {
|
||||
fn test_session() -> AnyhowResult<KinoviWebSession> {
|
||||
let cookies = get_test_cookies()?;
|
||||
Ok(Seedance2ProSession::from_cookies_string(cookies))
|
||||
Ok(KinoviWebSession::from_cookies_string(cookies))
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
/// Cookies are the credential required to interact with the Seedance2 Pro API.
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Seedance2ProCookies {
|
||||
pub struct KinoviWebCookies {
|
||||
cookies: String,
|
||||
}
|
||||
|
||||
impl Seedance2ProCookies {
|
||||
impl KinoviWebCookies {
|
||||
pub fn new(cookies: String) -> Self {
|
||||
Seedance2ProCookies { cookies }
|
||||
KinoviWebCookies { cookies }
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
@@ -0,0 +1,19 @@
|
||||
use crate::creds::kinovi_web_cookies::KinoviWebCookies;
|
||||
|
||||
/// Holds the full session data needed to make authenticated requests to Seedance2 Pro.
|
||||
#[derive(Clone)]
|
||||
pub struct KinoviWebSession {
|
||||
pub cookies: KinoviWebCookies,
|
||||
}
|
||||
|
||||
impl KinoviWebSession {
|
||||
pub fn new(cookies: KinoviWebCookies) -> Self {
|
||||
KinoviWebSession { cookies }
|
||||
}
|
||||
|
||||
pub fn from_cookies_string(cookies: String) -> Self {
|
||||
KinoviWebSession {
|
||||
cookies: KinoviWebCookies::new(cookies),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod kinovi_web_cookies;
|
||||
pub mod kinovi_web_session;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user