diff --git a/crates/api_clients/seedance2pro/src/requests/mod.rs b/crates/api_clients/seedance2pro/src/requests/mod.rs index c3defb5199..c335075452 100644 --- a/crates/api_clients/seedance2pro/src/requests/mod.rs +++ b/crates/api_clients/seedance2pro/src/requests/mod.rs @@ -1,4 +1,4 @@ pub mod generate_video; pub mod poll_orders; -pub mod prepare_image_upload; -pub mod upload_image; +pub mod prepare_file_upload; +pub mod upload_file; diff --git a/crates/api_clients/seedance2pro/src/requests/prepare_file_upload/mod.rs b/crates/api_clients/seedance2pro/src/requests/prepare_file_upload/mod.rs new file mode 100644 index 0000000000..6de086d735 --- /dev/null +++ b/crates/api_clients/seedance2pro/src/requests/prepare_file_upload/mod.rs @@ -0,0 +1,2 @@ +pub mod prepare_file_upload; +mod request_types; diff --git a/crates/api_clients/seedance2pro/src/requests/prepare_image_upload/prepare_image_upload.rs b/crates/api_clients/seedance2pro/src/requests/prepare_file_upload/prepare_file_upload.rs similarity index 68% rename from crates/api_clients/seedance2pro/src/requests/prepare_image_upload/prepare_image_upload.rs rename to crates/api_clients/seedance2pro/src/requests/prepare_file_upload/prepare_file_upload.rs index 0b5bd642a5..32a0b34325 100644 --- a/crates/api_clients/seedance2pro/src/requests/prepare_image_upload/prepare_image_upload.rs +++ b/crates/api_clients/seedance2pro/src/requests/prepare_file_upload/prepare_file_upload.rs @@ -2,7 +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::prepare_image_upload::request_types::*; +use crate::requests::prepare_file_upload::request_types::*; use crate::utils::common_headers::FIREFOX_USER_AGENT; use chrono::Utc; use log::info; @@ -13,32 +13,35 @@ 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/-.png` -fn generate_material_path() -> String { +/// Format: `materials/YYYYMMDD/-.` +fn generate_material_path(extension: &str) -> String { let now = Utc::now(); let date_part = now.format("%Y%m%d").to_string(); let timestamp_millis = now.timestamp_millis(); let random_hex: u32 = rand::rng().random(); let hex_part = format!("{:08x}", random_hex); - format!("materials/{}/{}-{}.png", date_part, timestamp_millis, hex_part) + format!("materials/{}/{}-{}.{}", date_part, timestamp_millis, hex_part, extension) } -pub struct PrepareImageUploadArgs<'a> { +pub struct PrepareFileUploadArgs<'a> { pub session: &'a Seedance2ProSession, + + /// File extension without the dot, e.g. "png", "mp4", "mp3". + pub extension: String, } -pub struct PrepareImageUploadResponse { - /// The signed URL to upload the image to (Cloudflare R2 / S3-compatible). +pub struct PrepareFileUploadResponse { + /// The signed URL to upload the file to (Cloudflare R2 / S3-compatible). pub upload_url: String, /// The material path that was generated for this upload. pub material_path: String, } -pub async fn prepare_image_upload(args: PrepareImageUploadArgs<'_>) -> Result { - let material_path = generate_material_path(); +pub async fn prepare_file_upload(args: PrepareFileUploadArgs<'_>) -> Result { + let material_path = generate_material_path(&args.extension); - info!("Preparing image upload with path: {}", material_path); + info!("Preparing file upload with path: {}", material_path); let client = Client::builder() .emulation(Emulation::Firefox143) @@ -103,7 +106,7 @@ pub async fn prepare_image_upload(args: PrepareImageUploadArgs<'_>) -> Result AnyhowResult<()> { + async fn test_prepare_file_upload_png() -> AnyhowResult<()> { setup_test_logging(LevelFilter::Trace); let cookies = get_test_cookies()?; let session = Seedance2ProSession::from_cookies_string(cookies); - let args = PrepareImageUploadArgs { + let args = PrepareFileUploadArgs { session: &session, + extension: "png".to_string(), }; - let result = prepare_image_upload(args).await?; + 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.material_path.starts_with("materials/")); + assert!(result.material_path.ends_with(".png")); + + assert_eq!(1, 2); // NB: Intentional failure to check the response. + Ok(()) + } + + #[tokio::test] + #[ignore] // manually test — requires real cookies + async fn test_prepare_file_upload_mp4() -> AnyhowResult<()> { + setup_test_logging(LevelFilter::Trace); + let cookies = get_test_cookies()?; + let session = Seedance2ProSession::from_cookies_string(cookies); + let args = PrepareFileUploadArgs { + session: &session, + extension: "mp4".to_string(), + }; + 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.material_path.ends_with(".mp4")); assert_eq!(1, 2); // NB: Intentional failure to check the response. Ok(()) diff --git a/crates/api_clients/seedance2pro/src/requests/prepare_image_upload/request_types.rs b/crates/api_clients/seedance2pro/src/requests/prepare_file_upload/request_types.rs similarity index 100% rename from crates/api_clients/seedance2pro/src/requests/prepare_image_upload/request_types.rs rename to crates/api_clients/seedance2pro/src/requests/prepare_file_upload/request_types.rs diff --git a/crates/api_clients/seedance2pro/src/requests/prepare_image_upload/mod.rs b/crates/api_clients/seedance2pro/src/requests/prepare_image_upload/mod.rs deleted file mode 100644 index 1508a767ab..0000000000 --- a/crates/api_clients/seedance2pro/src/requests/prepare_image_upload/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod prepare_image_upload; -mod request_types; diff --git a/crates/api_clients/seedance2pro/src/requests/upload_file/mod.rs b/crates/api_clients/seedance2pro/src/requests/upload_file/mod.rs new file mode 100644 index 0000000000..e24b75ef1b --- /dev/null +++ b/crates/api_clients/seedance2pro/src/requests/upload_file/mod.rs @@ -0,0 +1 @@ +pub mod upload_file; diff --git a/crates/api_clients/seedance2pro/src/requests/upload_image/upload_image.rs b/crates/api_clients/seedance2pro/src/requests/upload_file/upload_file.rs similarity index 76% rename from crates/api_clients/seedance2pro/src/requests/upload_image/upload_image.rs rename to crates/api_clients/seedance2pro/src/requests/upload_file/upload_file.rs index 0cb8984fc9..9f426bac03 100644 --- a/crates/api_clients/seedance2pro/src/requests/upload_image/upload_image.rs +++ b/crates/api_clients/seedance2pro/src/requests/upload_file/upload_file.rs @@ -9,16 +9,16 @@ use wreq_util::Emulation; const STATIC_BASE_URL: &str = "https://static.seedance2-pro.com"; -pub struct UploadImageArgs { - /// The signed upload URL returned by `prepare_image_upload`. +pub struct UploadFileArgs { + /// The signed upload URL returned by `prepare_file_upload`. pub upload_url: String, - /// The raw image bytes to upload. - pub image_bytes: Vec, + /// The raw file bytes to upload. + pub file_bytes: Vec, } -pub struct UploadImageResponse { - /// The public-facing URL for the uploaded image. +pub struct UploadFileResponse { + /// The public-facing URL for the uploaded file. /// e.g. `https://static.seedance2-pro.com/materials/20260219/1771463564512-b14bfe90.png` pub public_url: String, } @@ -33,8 +33,8 @@ fn build_public_url(upload_url: &str) -> Result { Ok(format!("{}{}", STATIC_BASE_URL, path)) } -pub async fn upload_image(args: UploadImageArgs) -> Result { - info!("Uploading image to: {}", args.upload_url); +pub async fn upload_file(args: UploadFileArgs) -> Result { + info!("Uploading file to: {}", args.upload_url); let client = Client::builder() .emulation(Emulation::Firefox143) @@ -53,7 +53,7 @@ pub async fn upload_image(args: UploadImageArgs) -> Result Result AnyhowResult<()> { + async fn test_upload_image_file() -> AnyhowResult<()> { setup_test_logging(LevelFilter::Trace); // Step 1: Get a signed upload URL let cookies = get_test_cookies()?; let session = Seedance2ProSession::from_cookies_string(cookies); - let prepare_args = PrepareImageUploadArgs { + let prepare_args = PrepareFileUploadArgs { session: &session, + extension: "jpg".to_string(), }; - let prepare_result = prepare_image_upload(prepare_args).await?; + let prepare_result = prepare_file_upload(prepare_args).await?; println!("Upload URL: {}", prepare_result.upload_url); // Step 2: Read a test image - let image_bytes = fs::read("/Users/bt/dev/storyteller/artcraft/test_data/image/juno.jpg")?; - println!("Image size: {} bytes", image_bytes.len()); + let file_bytes = fs::read("/Users/bt/dev/storyteller/artcraft/test_data/image/juno.jpg")?; + println!("File size: {} bytes", file_bytes.len()); // Step 3: Upload - let upload_args = UploadImageArgs { + let upload_args = UploadFileArgs { upload_url: prepare_result.upload_url, - image_bytes, + file_bytes, }; - let result = upload_image(upload_args).await?; + 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/")); diff --git a/crates/api_clients/seedance2pro/src/requests/upload_image/mod.rs b/crates/api_clients/seedance2pro/src/requests/upload_image/mod.rs deleted file mode 100644 index a2a1daf0c3..0000000000 --- a/crates/api_clients/seedance2pro/src/requests/upload_image/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod upload_image; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs index fefa64b091..16b679ef83 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/generate/video/multi_function/seedance_2p0_multi_function_video_gen_handler.rs @@ -34,10 +34,10 @@ use seedance2pro::creds::seedance2pro_session::Seedance2ProSession; use seedance2pro::requests::generate_video::generate_video::{ generate_video, BatchCount, GenerateVideoArgs, Resolution, }; -use seedance2pro::requests::prepare_image_upload::prepare_image_upload::{ - prepare_image_upload, PrepareImageUploadArgs, +use seedance2pro::requests::prepare_file_upload::prepare_file_upload::{ + prepare_file_upload, PrepareFileUploadArgs, }; -use seedance2pro::requests::upload_image::upload_image::{upload_image, UploadImageArgs}; +use seedance2pro::requests::upload_file::upload_file::{upload_file, UploadFileArgs}; use sqlx::Acquire; use tokens::tokens::generic_inference_jobs::InferenceJobToken; @@ -389,20 +389,23 @@ async fn upload_to_seedance2pro( })? .to_vec(); - let prepare_result = prepare_image_upload(PrepareImageUploadArgs { session }) - .await - .map_err(|err| { - warn!("Error preparing seedance2pro image upload: {:?}", err); - CommonWebError::ServerError - })?; - - let upload_result = upload_image(UploadImageArgs { - upload_url: prepare_result.upload_url, - image_bytes, + let prepare_result = prepare_file_upload(PrepareFileUploadArgs { + session, + extension: "png".to_string(), }) .await .map_err(|err| { - warn!("Error uploading image to seedance2pro: {:?}", err); + warn!("Error preparing seedance2pro file upload: {:?}", err); + CommonWebError::ServerError + })?; + + let upload_result = upload_file(UploadFileArgs { + upload_url: prepare_result.upload_url, + file_bytes: image_bytes, + }) + .await + .map_err(|err| { + warn!("Error uploading file to seedance2pro: {:?}", err); CommonWebError::ServerError })?;