mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-31 15:37:49 +00:00
seedance2pro -> kinovi domain name migration (#1181)
This commit is contained in:
1 parent
72bf6738e1
commit
58d2665f0e
13 files changed
+136
-30
No files matched your search
+1
@@ -34,6 +34,7 @@ pub (crate) fn estimate_video_cost_artcraft_seedance2p0(
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
|
||||
let cost_in_usd_cents = args.estimate_cost_in_usd_cents();
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ pub(crate) fn estimate_video_cost_seedance2pro_seedance2p0(
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
|
||||
let cost_in_credits = args.estimate_credits();
|
||||
|
||||
+3
@@ -44,6 +44,7 @@ pub async fn execute_seedance2pro_seedance2p0(
|
||||
reference_video_urls,
|
||||
reference_audio_urls,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
|
||||
let response = generate_video(args)
|
||||
@@ -70,6 +71,7 @@ async fn upload_to_seedance2pro(
|
||||
let prepare_response = prepare_file_upload(PrepareFileUploadArgs {
|
||||
session,
|
||||
extension,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
@@ -77,6 +79,7 @@ async fn upload_to_seedance2pro(
|
||||
let upload_response = upload_file(UploadFileArgs {
|
||||
upload_url: prepare_response.upload_url,
|
||||
file_bytes,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| ArtcraftRouterError::Provider(ProviderError::Seedance2Pro(err)))?;
|
||||
|
||||
+22
-5
@@ -4,13 +4,12 @@ use crate::error::seedance2pro_error::Seedance2ProError;
|
||||
use crate::error::seedance2pro_generic_api_error::Seedance2ProGenericApiError;
|
||||
use crate::error::seedance2pro_specific_api_error::Seedance2ProSpecificApiError;
|
||||
use crate::requests::generate_video::request_types::*;
|
||||
use crate::requests::kinovi_host::{KinoviHost, resolve_host};
|
||||
use crate::utils::common_headers::FIREFOX_USER_AGENT;
|
||||
use log::info;
|
||||
use wreq::Client;
|
||||
use wreq_util::Emulation;
|
||||
|
||||
const RUN_TASK_URL: &str = "https://seedance2-pro.com/api/trpc/workflow.runTask?batch=1";
|
||||
|
||||
// --- Request args ---
|
||||
|
||||
pub struct GenerateVideoArgs<'a> {
|
||||
@@ -48,6 +47,9 @@ pub struct GenerateVideoArgs<'a> {
|
||||
|
||||
/// Controls the `faceBlurMode` field: true sends "on", false sends "off", None omits it.
|
||||
pub use_face_blur_hack: Option<bool>,
|
||||
|
||||
/// Override the default host (kinovi.ai).
|
||||
pub host_override: Option<KinoviHost>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for GenerateVideoArgs<'_> {
|
||||
@@ -63,6 +65,7 @@ impl std::fmt::Debug for GenerateVideoArgs<'_> {
|
||||
.field("reference_video_urls", &self.reference_video_urls)
|
||||
.field("reference_audio_urls", &self.reference_audio_urls)
|
||||
.field("use_face_blur_hack", &self.use_face_blur_hack)
|
||||
.field("host_override", &self.host_override)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -158,6 +161,9 @@ pub struct GenerateVideoResponse {
|
||||
// --- Implementation ---
|
||||
|
||||
pub async fn generate_video(args: GenerateVideoArgs<'_>) -> Result<GenerateVideoResponse, Seedance2ProError> {
|
||||
let host = resolve_host(args.host_override.as_ref());
|
||||
let base_url = host.api_base_url();
|
||||
let run_task_url = format!("{}/api/trpc/workflow.runTask?batch=1", base_url);
|
||||
|
||||
info!("Requesting video from Seedance2Pro: {:?}", args);
|
||||
|
||||
@@ -240,15 +246,17 @@ pub async fn generate_video(args: GenerateVideoArgs<'_>) -> Result<GenerateVideo
|
||||
.build()
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
|
||||
let response = client.post(RUN_TASK_URL)
|
||||
let referer = format!("{}/", base_url);
|
||||
|
||||
let response = client.post(&run_task_url)
|
||||
.header("User-Agent", FIREFOX_USER_AGENT)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.9")
|
||||
.header("Accept-Encoding", "gzip, deflate, br, zstd")
|
||||
.header("Referer", "https://seedance2-pro.com/")
|
||||
.header("Referer", &referer)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("x-trpc-source", "client")
|
||||
.header("Origin", "https://seedance2-pro.com")
|
||||
.header("Origin", base_url)
|
||||
.header("Connection", "keep-alive")
|
||||
.header("Cookie", cookie)
|
||||
.header("Sec-Fetch-Dest", "empty")
|
||||
@@ -331,6 +339,7 @@ mod tests {
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,6 +405,7 @@ mod tests {
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
let result = generate_video(args).await?;
|
||||
println!("Task ID: {}", result.task_id);
|
||||
@@ -423,6 +433,7 @@ mod tests {
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
let result = generate_video(args).await?;
|
||||
println!("Task ID: {}", result.task_id);
|
||||
@@ -452,6 +463,7 @@ mod tests {
|
||||
reference_video_urls: None,
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
let result = generate_video(args).await?;
|
||||
println!("Task ID: {}", result.task_id);
|
||||
@@ -480,6 +492,7 @@ mod tests {
|
||||
]),
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
let result = generate_video(args).await?;
|
||||
println!("Task ID: {}", result.task_id);
|
||||
@@ -510,6 +523,7 @@ mod tests {
|
||||
]),
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
let result = generate_video(args).await?;
|
||||
println!("Task ID: {}", result.task_id);
|
||||
@@ -530,6 +544,7 @@ mod tests {
|
||||
let prepare_args = PrepareFileUploadArgs {
|
||||
session: &session,
|
||||
extension: "mp4".to_string(),
|
||||
host_override: None,
|
||||
};
|
||||
let prepare_result = prepare_file_upload(prepare_args).await?;
|
||||
println!("Upload URL: {}", prepare_result.upload_url);
|
||||
@@ -542,6 +557,7 @@ mod tests {
|
||||
let upload_args = UploadFileArgs {
|
||||
upload_url: prepare_result.upload_url,
|
||||
file_bytes,
|
||||
host_override: None,
|
||||
};
|
||||
let result = upload_file(upload_args).await?;
|
||||
println!("Public URL: {}", result.public_url);
|
||||
@@ -560,6 +576,7 @@ mod tests {
|
||||
]),
|
||||
reference_audio_urls: None,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
let result = generate_video(args).await?;
|
||||
println!("Task ID: {}", result.task_id);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/// The host to target for API requests.
|
||||
///
|
||||
/// The service was originally at `seedance2-pro.com` but migrated to `kinovi.ai`.
|
||||
/// This enum allows configuring which host to use.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum KinoviHost {
|
||||
/// https://kinovi.ai (current default)
|
||||
Kinovi,
|
||||
|
||||
/// https://seedance2-pro.com (legacy)
|
||||
Seedance2Pro,
|
||||
|
||||
/// Custom hosts for API and CDN.
|
||||
/// Must include the URL scheme but no trailing slash.
|
||||
/// e.g. api_host: "https://example.com", cdn_host: "http://static.example.com:1234"
|
||||
CustomHost { api_host: String, cdn_host: String },
|
||||
}
|
||||
|
||||
impl Default for KinoviHost {
|
||||
fn default() -> Self {
|
||||
Self::Kinovi
|
||||
}
|
||||
}
|
||||
|
||||
impl KinoviHost {
|
||||
/// Returns the API base URL (scheme + domain, no trailing slash).
|
||||
pub fn api_base_url(&self) -> &str {
|
||||
match self {
|
||||
Self::Kinovi => "https://kinovi.ai",
|
||||
Self::Seedance2Pro => "https://seedance2-pro.com",
|
||||
Self::CustomHost { api_host, .. } => api_host.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the CDN base URL for uploaded/static files (no trailing slash).
|
||||
pub fn cdn_base_url(&self) -> &str {
|
||||
match self {
|
||||
Self::Kinovi => "https://static.kinovi.ai",
|
||||
Self::Seedance2Pro => "https://static.seedance2-pro.com",
|
||||
Self::CustomHost { cdn_host, .. } => cdn_host.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves an optional host override to the effective host.
|
||||
pub fn resolve_host(host_override: Option<&KinoviHost>) -> &KinoviHost {
|
||||
// Use a static default to avoid needing to return owned data
|
||||
static DEFAULT: KinoviHost = KinoviHost::Kinovi;
|
||||
host_override.unwrap_or(&DEFAULT)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod generate_video;
|
||||
pub mod kinovi_host;
|
||||
pub mod poll_orders;
|
||||
pub mod prepare_file_upload;
|
||||
pub mod upload_file;
|
||||
@@ -2,6 +2,7 @@ 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::requests::kinovi_host::{KinoviHost, resolve_host};
|
||||
use crate::requests::poll_orders::failure_reason::FailureReason;
|
||||
use crate::requests::poll_orders::request_types::*;
|
||||
use crate::utils::common_headers::FIREFOX_USER_AGENT;
|
||||
@@ -9,8 +10,6 @@ use log::info;
|
||||
use wreq::Client;
|
||||
use wreq_util::Emulation;
|
||||
|
||||
const GET_ORDERS_BASE_URL: &str = "https://seedance2-pro.com/api/trpc/userOrder.getOrders";
|
||||
|
||||
|
||||
// --- Args & response ---
|
||||
|
||||
@@ -20,6 +19,9 @@ pub struct PollOrdersArgs<'a> {
|
||||
/// Optional cursor from a previous `PollOrdersResponse::next_cursor`.
|
||||
/// When `None`, the most recent orders are returned.
|
||||
pub cursor: Option<u64>,
|
||||
|
||||
/// Override the default host (kinovi.ai).
|
||||
pub host_override: Option<KinoviHost>,
|
||||
}
|
||||
|
||||
pub struct PollOrdersResponse {
|
||||
@@ -97,6 +99,10 @@ pub struct OrderStatus {
|
||||
// --- Implementation ---
|
||||
|
||||
pub async fn poll_orders(args: PollOrdersArgs<'_>) -> Result<PollOrdersResponse, Seedance2ProError> {
|
||||
let host = resolve_host(args.host_override.as_ref());
|
||||
let base_url = host.api_base_url();
|
||||
let get_orders_url = format!("{}/api/trpc/userOrder.getOrders", base_url);
|
||||
|
||||
info!("Polling orders (cursor: {:?})...", args.cursor);
|
||||
|
||||
let input_json = build_input_json(args.cursor);
|
||||
@@ -107,14 +113,15 @@ pub async fn poll_orders(args: PollOrdersArgs<'_>) -> Result<PollOrdersResponse,
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
|
||||
let cookie = args.session.cookies.as_str();
|
||||
let referer = format!("{}/app/gallery", base_url);
|
||||
|
||||
let request = client.get(GET_ORDERS_BASE_URL)
|
||||
let request = client.get(&get_orders_url)
|
||||
.query(&[("batch", "1"), ("input", input_json.as_str())])
|
||||
.header("User-Agent", FIREFOX_USER_AGENT)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.9")
|
||||
.header("Accept-Encoding", "gzip, deflate, br, zstd")
|
||||
.header("Referer", "https://seedance2-pro.com/app/gallery")
|
||||
.header("Referer", &referer)
|
||||
.header("content-type", "application/json")
|
||||
.header("x-trpc-source", "client")
|
||||
.header("Connection", "keep-alive")
|
||||
@@ -193,9 +200,9 @@ pub async fn poll_orders(args: PollOrdersArgs<'_>) -> Result<PollOrdersResponse,
|
||||
/// When `cursor` is `Some`, it is included in the JSON payload.
|
||||
fn build_input_json(cursor: Option<u64>) -> String {
|
||||
match cursor {
|
||||
None => r#"{"0":{"json":{"limit":30,"format":null,"direction":"forward"},"meta":{"values":{"format":["undefined"]},"v":1}}}"#.to_string(),
|
||||
None => r#"{"0":{"json":{"limit":30,"format":null,"toolId":null,"direction":"forward"},"meta":{"values":{"format":["undefined"],"toolId":["undefined"]},"v":1}}}"#.to_string(),
|
||||
Some(c) => format!(
|
||||
r#"{{"0":{{"json":{{"limit":30,"format":null,"cursor":{cursor},"direction":"forward"}},"meta":{{"values":{{"format":["undefined"]}},"v":1}}}}}}"#,
|
||||
r#"{{"0":{{"json":{{"limit":30,"format":null,"toolId":null,"cursor":{cursor},"direction":"forward"}},"meta":{{"values":{{"format":["undefined"],"toolId":["undefined"]}},"v":1}}}}}}"#,
|
||||
cursor = c
|
||||
),
|
||||
}
|
||||
@@ -220,7 +227,7 @@ mod tests {
|
||||
async fn test_poll_all_orders() -> AnyhowResult<()> {
|
||||
setup_test_logging(LevelFilter::Trace);
|
||||
let session = test_session()?;
|
||||
let result = poll_orders(PollOrdersArgs { session: &session, cursor: None }).await?;
|
||||
let result = poll_orders(PollOrdersArgs { session: &session, cursor: None, host_override: None }).await?;
|
||||
println!("Orders returned: {}", result.orders.len());
|
||||
println!("Next cursor: {:?}", result.next_cursor);
|
||||
for order in &result.orders {
|
||||
@@ -238,7 +245,7 @@ mod tests {
|
||||
let session = test_session()?;
|
||||
// Use the cursor value returned from a prior call (e.g. 394062 from the example responses).
|
||||
let cursor: u64 = 394062;
|
||||
let result = poll_orders(PollOrdersArgs { session: &session, cursor: Some(cursor) }).await?;
|
||||
let result = poll_orders(PollOrdersArgs { session: &session, cursor: Some(cursor), host_override: None }).await?;
|
||||
println!("Orders returned (with cursor {}): {}", cursor, result.orders.len());
|
||||
println!("Next cursor: {:?}", result.next_cursor);
|
||||
for order in &result.orders {
|
||||
@@ -261,7 +268,7 @@ mod tests {
|
||||
|
||||
loop {
|
||||
page += 1;
|
||||
let result = poll_orders(PollOrdersArgs { session: &session, cursor }).await?;
|
||||
let result = poll_orders(PollOrdersArgs { session: &session, cursor, host_override: None }).await?;
|
||||
let page_count = result.orders.len();
|
||||
total_orders += page_count;
|
||||
|
||||
|
||||
+15
-6
@@ -2,6 +2,7 @@ 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::requests::kinovi_host::{KinoviHost, resolve_host};
|
||||
use crate::requests::prepare_file_upload::request_types::*;
|
||||
use crate::utils::common_headers::FIREFOX_USER_AGENT;
|
||||
use chrono::Utc;
|
||||
@@ -10,8 +11,6 @@ use rand::Rng;
|
||||
use wreq::Client;
|
||||
use wreq_util::Emulation;
|
||||
|
||||
const SIGNED_UPLOAD_URL: &str = "https://seedance2-pro.com/api/trpc/uploads.signedUploadUrl?batch=1";
|
||||
|
||||
/// Generates a material path based on the current time.
|
||||
/// Format: `materials/YYYYMMDD/<unix_millis>-<random_hex>.<extension>`
|
||||
fn generate_material_path(extension: &str) -> String {
|
||||
@@ -28,6 +27,9 @@ pub struct PrepareFileUploadArgs<'a> {
|
||||
|
||||
/// File extension without the dot, e.g. "png", "mp4", "mp3".
|
||||
pub extension: String,
|
||||
|
||||
/// Override the default host (kinovi.ai).
|
||||
pub host_override: Option<KinoviHost>,
|
||||
}
|
||||
|
||||
pub struct PrepareFileUploadResponse {
|
||||
@@ -39,6 +41,10 @@ pub struct PrepareFileUploadResponse {
|
||||
}
|
||||
|
||||
pub async fn prepare_file_upload(args: PrepareFileUploadArgs<'_>) -> Result<PrepareFileUploadResponse, Seedance2ProError> {
|
||||
let host = resolve_host(args.host_override.as_ref());
|
||||
let base_url = host.api_base_url();
|
||||
let signed_upload_url = format!("{}/api/trpc/uploads.signedUploadUrl?batch=1", base_url);
|
||||
|
||||
let material_path = generate_material_path(&args.extension);
|
||||
|
||||
info!("Preparing file upload with path: {}", material_path);
|
||||
@@ -57,16 +63,17 @@ pub async fn prepare_file_upload(args: PrepareFileUploadArgs<'_>) -> Result<Prep
|
||||
};
|
||||
|
||||
let cookie = args.session.cookies.as_str();
|
||||
let referer = format!("{}/", base_url);
|
||||
|
||||
let response = client.post(SIGNED_UPLOAD_URL)
|
||||
let response = client.post(&signed_upload_url)
|
||||
.header("User-Agent", FIREFOX_USER_AGENT)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.9")
|
||||
.header("Accept-Encoding", "gzip, deflate, br, zstd")
|
||||
.header("Referer", "https://seedance2-pro.com/")
|
||||
.header("Referer", &referer)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("x-trpc-source", "client")
|
||||
.header("Origin", "https://seedance2-pro.com")
|
||||
.header("Origin", base_url)
|
||||
.header("Connection", "keep-alive")
|
||||
.header("Cookie", cookie)
|
||||
.header("Sec-Fetch-Dest", "empty")
|
||||
@@ -130,12 +137,13 @@ mod tests {
|
||||
let args = PrepareFileUploadArgs {
|
||||
session: &session,
|
||||
extension: "png".to_string(),
|
||||
host_override: None,
|
||||
};
|
||||
let result = prepare_file_upload(args).await?;
|
||||
println!("Upload URL: {}", result.upload_url);
|
||||
println!("Material path: {}", result.material_path);
|
||||
assert!(!result.upload_url.is_empty());
|
||||
assert!(result.upload_url.contains("cloudflarestorage.com"));
|
||||
assert!(result.upload_url.contains("cloudflarestorage.com") || result.upload_url.contains("kinovi"));
|
||||
assert!(result.material_path.starts_with("materials/"));
|
||||
assert!(result.material_path.ends_with(".png"));
|
||||
|
||||
@@ -152,6 +160,7 @@ mod tests {
|
||||
let args = PrepareFileUploadArgs {
|
||||
session: &session,
|
||||
extension: "mp4".to_string(),
|
||||
host_override: None,
|
||||
};
|
||||
let result = prepare_file_upload(args).await?;
|
||||
println!("Upload URL: {}", result.upload_url);
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
use crate::error::seedance2pro_client_error::Seedance2ProClientError;
|
||||
use crate::error::seedance2pro_error::Seedance2ProError;
|
||||
use crate::error::seedance2pro_generic_api_error::Seedance2ProGenericApiError;
|
||||
use crate::requests::kinovi_host::{KinoviHost, resolve_host};
|
||||
use crate::utils::common_headers::FIREFOX_USER_AGENT;
|
||||
use log::info;
|
||||
use url::Url;
|
||||
use wreq::Client;
|
||||
use wreq_util::Emulation;
|
||||
|
||||
const STATIC_BASE_URL: &str = "https://static.seedance2-pro.com";
|
||||
|
||||
pub struct UploadFileArgs {
|
||||
/// The signed upload URL returned by `prepare_file_upload`.
|
||||
pub upload_url: String,
|
||||
|
||||
/// The raw file bytes to upload.
|
||||
pub file_bytes: Vec<u8>,
|
||||
|
||||
/// Override the default host (kinovi.ai).
|
||||
pub host_override: Option<KinoviHost>,
|
||||
}
|
||||
|
||||
pub struct UploadFileResponse {
|
||||
@@ -25,15 +27,18 @@ pub struct UploadFileResponse {
|
||||
|
||||
/// Extracts the path from the R2 upload URL and builds the static public URL.
|
||||
/// e.g. `https://comm.….r2.cloudflarestorage.com/materials/20260219/…?X-Amz-…`
|
||||
/// -> `https://static.seedance2-pro.com/materials/20260219/…`
|
||||
fn build_public_url(upload_url: &str) -> Result<String, Seedance2ProError> {
|
||||
/// -> `https://static.kinovi.ai/materials/20260219/…`
|
||||
fn build_public_url(upload_url: &str, host: &KinoviHost) -> Result<String, Seedance2ProError> {
|
||||
let parsed = Url::parse(upload_url)
|
||||
.map_err(|err| Seedance2ProClientError::UrlParseError(err))?;
|
||||
let path = parsed.path(); // e.g. "/materials/20260219/1771463564512-b14bfe90.png"
|
||||
Ok(format!("{}{}", STATIC_BASE_URL, path))
|
||||
Ok(format!("{}{}", host.cdn_base_url(), path))
|
||||
}
|
||||
|
||||
pub async fn upload_file(args: UploadFileArgs) -> Result<UploadFileResponse, Seedance2ProError> {
|
||||
let host = resolve_host(args.host_override.as_ref());
|
||||
let base_url = host.api_base_url();
|
||||
|
||||
info!("Uploading file to: {}", args.upload_url);
|
||||
|
||||
let client = Client::builder()
|
||||
@@ -41,13 +46,15 @@ pub async fn upload_file(args: UploadFileArgs) -> Result<UploadFileResponse, See
|
||||
.build()
|
||||
.map_err(|err| Seedance2ProClientError::WreqClientError(err))?;
|
||||
|
||||
let referer = format!("{}/", base_url);
|
||||
|
||||
let response = client.put(&args.upload_url)
|
||||
.header("User-Agent", FIREFOX_USER_AGENT)
|
||||
.header("Accept", "*/*")
|
||||
.header("Accept-Language", "en-US,en;q=0.9")
|
||||
.header("Accept-Encoding", "gzip, deflate, br, zstd")
|
||||
.header("Referer", "https://seedance2-pro.com/")
|
||||
.header("Origin", "https://seedance2-pro.com")
|
||||
.header("Referer", &referer)
|
||||
.header("Origin", base_url)
|
||||
.header("Connection", "keep-alive")
|
||||
.header("Sec-Fetch-Dest", "empty")
|
||||
.header("Sec-Fetch-Mode", "cors")
|
||||
@@ -73,7 +80,7 @@ pub async fn upload_file(args: UploadFileArgs) -> Result<UploadFileResponse, See
|
||||
}.into());
|
||||
}
|
||||
|
||||
let public_url = build_public_url(&args.upload_url)?;
|
||||
let public_url = build_public_url(&args.upload_url, host)?;
|
||||
|
||||
info!("Public URL: {}", public_url);
|
||||
|
||||
@@ -104,6 +111,7 @@ mod tests {
|
||||
let prepare_args = PrepareFileUploadArgs {
|
||||
session: &session,
|
||||
extension: "jpg".to_string(),
|
||||
host_override: None,
|
||||
};
|
||||
let prepare_result = prepare_file_upload(prepare_args).await?;
|
||||
println!("Upload URL: {}", prepare_result.upload_url);
|
||||
@@ -116,11 +124,12 @@ mod tests {
|
||||
let upload_args = UploadFileArgs {
|
||||
upload_url: prepare_result.upload_url,
|
||||
file_bytes,
|
||||
host_override: None,
|
||||
};
|
||||
let result = upload_file(upload_args).await?;
|
||||
println!("Public URL: {}", result.public_url);
|
||||
|
||||
assert!(result.public_url.starts_with("https://static.seedance2-pro.com/materials/"));
|
||||
assert!(result.public_url.starts_with("https://static.kinovi.ai/materials/"));
|
||||
assert_eq!(1, 2); // NB: Intentional failure to check the response.
|
||||
|
||||
Ok(())
|
||||
@@ -137,6 +146,7 @@ mod tests {
|
||||
let prepare_args = PrepareFileUploadArgs {
|
||||
session: &session,
|
||||
extension: "mp4".to_string(),
|
||||
host_override: None,
|
||||
};
|
||||
let prepare_result = prepare_file_upload(prepare_args).await?;
|
||||
println!("Upload URL: {}", prepare_result.upload_url);
|
||||
@@ -149,11 +159,12 @@ mod tests {
|
||||
let upload_args = UploadFileArgs {
|
||||
upload_url: prepare_result.upload_url,
|
||||
file_bytes,
|
||||
host_override: None,
|
||||
};
|
||||
let result = upload_file(upload_args).await?;
|
||||
println!("Public URL: {}", result.public_url);
|
||||
|
||||
assert!(result.public_url.starts_with("https://static.seedance2-pro.com/materials/"));
|
||||
assert!(result.public_url.starts_with("https://static.kinovi.ai/materials/"));
|
||||
assert_eq!(1, 2); // NB: Intentional failure to check the response.
|
||||
|
||||
Ok(())
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ pub async fn run(state: &Seedance2ProState) -> anyhow::Result<()> {
|
||||
let result = poll_orders(PollOrdersArgs {
|
||||
session: &session,
|
||||
cursor,
|
||||
host_override: None,
|
||||
}).await
|
||||
.map_err(|err| anyhow!("Error polling orders on page {}: {:?}", page, err))?;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ pub async fn run(state: &Seedance2ProState, args: FindJobArgs) -> anyhow::Result
|
||||
let result = poll_orders(PollOrdersArgs {
|
||||
session: &session,
|
||||
cursor,
|
||||
host_override: None,
|
||||
}).await
|
||||
.map_err(|err| anyhow!("Error polling orders on page {}: {:?}", page, err))?;
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ async fn run_poll_iteration(deps: &JobDependencies) -> anyhow::Result<()> {
|
||||
let response = poll_orders(PollOrdersArgs {
|
||||
session: &deps.seedance2pro_session,
|
||||
cursor,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| {
|
||||
|
||||
+3
@@ -211,6 +211,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
|
||||
reference_video_urls,
|
||||
reference_audio_urls,
|
||||
use_face_blur_hack: None,
|
||||
host_override: None,
|
||||
};
|
||||
|
||||
// --- Calculate cost and charge wallet ---
|
||||
@@ -453,6 +454,7 @@ async fn upload_to_seedance2pro(
|
||||
let prepare_result = prepare_file_upload(PrepareFileUploadArgs {
|
||||
session,
|
||||
extension,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| {
|
||||
@@ -463,6 +465,7 @@ async fn upload_to_seedance2pro(
|
||||
let upload_result = upload_file(UploadFileArgs {
|
||||
upload_url: prepare_result.upload_url,
|
||||
file_bytes,
|
||||
host_override: None,
|
||||
})
|
||||
.await
|
||||
.map_err(|err| {
|
||||
|
||||
Reference in new issue
Block a user